Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@

### Fixes

* [FIX][rust] `parse_partial_blockchain_nodes` (sqlite-store) no longer panics when a `partial_blockchain_nodes.id` column reads back as `0` - `InOrderIndex` never constructs a zero index (its own constructor requires `NonZeroUsize`), so a stored `0` can only come from a corrupted or externally-tampered-with local database. Now surfaces as `StoreError::ParsingError` instead ([#2462](https://github.com/0xMiden/rust-sdk/pull/2462)).
* [FIX][rust] `ChainAnchor` deserialization no longer panics on crafted input: a partial blockchain whose tracked leaf is missing an ancestor sibling, or whose block-map key disagrees with its header, is rejected as an invalid value, and anchors tracking more blocks than a transaction can reference are rejected early with the new `ChainAnchorError::TooManyTrackedBlocks` ([#2421](https://github.com/0xMiden/rust-sdk/pull/2421)).
* [FIX][rust] `Client::execute_transaction_at` now fails with the new `ChainAnchorError::AnchoredTransactionExpired` when the executed transaction's expiration block has already been reached, instead of handing back a transaction the network would reject after proving ([#2421](https://github.com/0xMiden/rust-sdk/pull/2421)).
* [FIX][rust] A request that sets `ignore_invalid_input_notes` but carries no input notes, or whose notes are all screened out, no longer fails with an out-of-range note-count error from the consumption checker ([#2421](https://github.com/0xMiden/rust-sdk/pull/2421)).
Expand Down
29 changes: 24 additions & 5 deletions crates/sqlite-store/src/chain_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,16 @@ fn parse_partial_blockchain_nodes_columns(
fn parse_partial_blockchain_nodes(
serialized_partial_blockchain_node_parts: &SerializedPartialBlockchainNodeParts,
) -> Result<(InOrderIndex, Word), StoreError> {
let raw_id = serialized_partial_blockchain_node_parts.id;
let id = InOrderIndex::new(
NonZeroUsize::new(
usize::try_from(serialized_partial_blockchain_node_parts.id)
.expect("id is u64, should not fail"),
)
.unwrap(),
NonZeroUsize::new(usize::try_from(raw_id).expect("id is u64, should not fail")).ok_or_else(
|| {
StoreError::ParsingError(format!(
"partial_blockchain_nodes.id must be non-zero (0 is never a valid \
InOrderIndex), got {raw_id}"
))
},
)?,
);
let node: Word = Word::read_from_bytes(&serialized_partial_blockchain_node_parts.node)?;
Ok((id, node))
Expand Down Expand Up @@ -409,6 +413,21 @@ mod test {
use crate::SqliteStore;
use crate::tests::create_test_store;

#[test]
fn parse_partial_blockchain_nodes_rejects_zero_id_instead_of_panicking() {
// id=0 is never a valid InOrderIndex (its constructor requires NonZeroUsize, and
// from_leaf_pos's minimum output is 1), so a row with id=0 can only come from a
// corrupted or externally-tampered-with local database. This must surface as a
// StoreError, not panic the client.
let parts = super::SerializedPartialBlockchainNodeParts {
id: 0,
node: Word::default().to_bytes(),
};

let result = super::parse_partial_blockchain_nodes(&parts);
assert!(matches!(result, Err(miden_client::store::StoreError::ParsingError(_))));
}

async fn insert_dummy_block_headers(store: &mut SqliteStore) -> Vec<BlockHeader> {
let block_headers: Vec<BlockHeader> = (0..5)
.map(|block_num| {
Expand Down