diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b1105c1..c09bd0b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,8 @@ - Fixed `bundled bootstrap` requiring `--validator.key.hex` or `--validator.key.kms-id` despite a default key being configured ([#1732](https://github.com/0xMiden/node/pull/1732)). - Fixed incorrectly classifying private notes with the network attachment as network notes ([#1378](https://github.com/0xMiden/node/pull/1738)). - Fixed accept header version negotiation rejecting all pre-release versions; pre-release label matching is now lenient, accepting any numeric suffix within the same label (e.g. `alpha.3` accepts `alpha.1`) ([#1755](https://github.com/0xMiden/node/pull/1755)). +- Fixed `GetAccount` returning an internal error for `AllEntries` requests on storage maps where all entries are in a single block (e.g. genesis accounts) ([#1816](https://github.com/0xMiden/node/pull/1816)). +- Fixed `GetAccount` returning empty storage map entries instead of `too_many_entries` when a genesis account's map exceeds the pagination limit ([#1816](https://github.com/0xMiden/node/pull/1816)). ## v0.13.8 (2026-03-12) diff --git a/Cargo.lock b/Cargo.lock index 06fb1bdad..9020af81e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,21 +141,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse 0.2.7", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstream" version = "1.0.0" @@ -163,7 +148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", - "anstyle-parse 1.0.0", + "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -177,15 +162,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - [[package]] name = "anstyle-parse" version = "1.0.0" @@ -915,9 +891,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.57" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "jobserver", @@ -1054,7 +1030,7 @@ version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ - "anstream 1.0.0", + "anstream", "anstyle", "clap_lex", "strsim", @@ -1080,9 +1056,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] @@ -1614,9 +1590,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" dependencies = [ "log", "regex", @@ -1624,11 +1600,11 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ - "anstream 0.6.21", + "anstream", "anstyle", "env_filter", "jiff", @@ -2440,9 +2416,9 @@ checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb" dependencies = [ "memchr", "serde", @@ -2517,7 +2493,7 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", @@ -2526,9 +2502,31 @@ dependencies = [ [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] [[package]] name = "jobserver" @@ -3351,7 +3349,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-stream", - "toml 1.0.7+spec-1.1.0", + "toml 1.1.0+spec-1.1.0", "tonic", "tonic-reflection", "tower-http", @@ -3492,7 +3490,7 @@ dependencies = [ "semver 1.0.27", "serde", "thiserror 2.0.18", - "toml 1.0.7+spec-1.1.0", + "toml 1.1.0+spec-1.1.0", "walkdir", ] @@ -3903,9 +3901,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-derive" @@ -4674,9 +4672,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", @@ -5565,9 +5563,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" dependencies = [ "serde_core", ] @@ -5959,12 +5957,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6196,14 +6194,14 @@ dependencies = [ [[package]] name = "toml" -version = "1.0.7+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96" +checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime 1.0.1+spec-1.1.0", + "toml_datetime 1.1.0+spec-1.1.0", "toml_parser", "toml_writer", "winnow 1.0.0", @@ -6220,39 +6218,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.0.1+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.5+spec-1.1.0" +version = "0.25.8+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1" +checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" dependencies = [ "indexmap", - "toml_datetime 1.0.1+spec-1.1.0", + "toml_datetime 1.1.0+spec-1.1.0", "toml_parser", "winnow 1.0.0", ] [[package]] name = "toml_parser" -version = "1.0.10+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" dependencies = [ "winnow 1.0.0", ] [[package]] name = "toml_writer" -version = "1.0.7+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" [[package]] name = "tonic" @@ -6595,7 +6593,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml 1.0.7+spec-1.1.0", + "toml 1.1.0+spec-1.1.0", ] [[package]] @@ -6642,9 +6640,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" @@ -6713,9 +6711,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index 2e2f32949..603926705 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -635,6 +635,12 @@ impl Db { values.extend(page.values); let mut last_block_included = page.last_block_included; + // If the first page returned no values, the block at block_range_start has more + // entries than the limit allows (e.g. genesis accounts with large storage maps). + if values.is_empty() && last_block_included == block_range_start { + return Ok(AccountStorageMapDetails::limit_exceeded(slot_name)); + } + loop { if page.last_block_included == block_num || page.last_block_included < block_range_start { diff --git a/crates/store/src/db/models/queries/accounts.rs b/crates/store/src/db/models/queries/accounts.rs index d0e3fa824..d41ee09b3 100644 --- a/crates/store/src/db/models/queries/accounts.rs +++ b/crates/store/src/db/models/queries/accounts.rs @@ -464,21 +464,20 @@ pub(crate) fn select_account_vault_assets( .limit(i64::try_from(MAX_ROWS + 1).expect("should fit within i64")) .load::<(i64, Vec, Option>)>(conn)?; - // Discard the last block in the response (assumes more than one block may be present) + // If we got more rows than the limit, the last block may be incomplete so we + // drop it entirely and derive last_block_included from the remaining rows. let (last_block_included, values) = if let Some(&(last_block_num, ..)) = raw.last() && raw.len() > MAX_ROWS { - // NOTE: If the query contains at least one more row than the amount of storage map updates - // allowed in a single block for an account, then the response is guaranteed to have at - // least two blocks - let values = raw .into_iter() .take_while(|(bn, ..)| *bn != last_block_num) .map(AccountVaultValue::from_raw_row) .collect::, DatabaseError>>()?; - (BlockNumber::from_raw_sql(last_block_num.saturating_sub(1))?, values) + let last_block_included = values.last().map_or(*block_range.start(), |v| v.block_num); + + (last_block_included, values) } else { ( *block_range.end(), @@ -712,22 +711,20 @@ pub(crate) fn select_account_storage_map_values_paged( .limit(i64::try_from(limit + 1).expect("limit fits within i64")) .load(conn)?; - // Discard the last block in the response (assumes more than one block may be present) - + // If we got more rows than the limit, the last block may be incomplete so we + // drop it entirely and derive last_block_included from the remaining rows. let (last_block_included, values) = if let Some(&(last_block_num, ..)) = raw.last() && raw.len() > limit { - // NOTE: If the query contains at least one more row than the amount of storage map updates - // allowed in a single block for an account, then the response is guaranteed to have at - // least two blocks - let values = raw .into_iter() .take_while(|(bn, ..)| *bn != last_block_num) .map(StorageMapValue::from_raw_row) .collect::, DatabaseError>>()?; - (BlockNumber::from_raw_sql(last_block_num.saturating_sub(1))?, values) + let last_block_included = values.last().map_or(*block_range.start(), |v| v.block_num); + + (last_block_included, values) } else { ( *block_range.end(), diff --git a/crates/store/src/db/models/queries/transactions.rs b/crates/store/src/db/models/queries/transactions.rs index 79d2bdfeb..992d70261 100644 --- a/crates/store/src/db/models/queries/transactions.rs +++ b/crates/store/src/db/models/queries/transactions.rs @@ -301,6 +301,8 @@ pub fn select_transactions_records( )?; // SAFETY: block_num came from the database and was previously validated + // Subtraction is safe under the assumption that genesis block (where it could fail) does + // not have any transactions. let last_included_block = BlockNumber::from_raw_sql(last_block_num.saturating_sub(1))?; Ok((last_included_block, filtered_transactions)) } else { diff --git a/crates/store/src/db/tests.rs b/crates/store/src/db/tests.rs index f3dd1c1d2..b91f3b9a2 100644 --- a/crates/store/src/db/tests.rs +++ b/crates/store/src/db/tests.rs @@ -1223,6 +1223,155 @@ fn select_storage_map_sync_values_paginates_until_last_block() { assert_eq!(page.values.len(), 1, "should include block 1 only"); } +/// Tests that `select_account_storage_map_values_paged` does not panic when all entries +/// exceed the limit and are in genesis block (block 0). Previously, this caused +/// `last_block_num.saturating_sub(1) = -1` which failed `BlockNumber::from_raw_sql`. +#[test] +fn select_storage_map_sync_values_all_entries_in_genesis_block() { + let mut conn = create_db(); + let account_id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + let slot_name = StorageSlotName::mock(8); + + let genesis = BlockNumber::GENESIS; + create_block(&mut conn, genesis); + + queries::upsert_accounts(&mut conn, &[mock_block_account_update(account_id, 0)], genesis) + .unwrap(); + + // Insert 3 entries, all in genesis block + for i in 0..3 { + queries::insert_account_storage_map_value( + &mut conn, + account_id, + genesis, + slot_name.clone(), + StorageMapKey::from_index(i), + num_to_word(u64::from(i) + 100), + ) + .unwrap(); + } + + // Query with limit=1 so that raw.len() (3) > limit (1), triggering the + // pagination branch. All entries are in block 0, so take_while produces + // nothing and last_block_num.saturating_sub(1) = -1. + let result = queries::select_account_storage_map_values_paged( + &mut conn, + account_id, + genesis..=genesis, + 1, + ); + + // Should not error - should return a valid page (possibly with empty values + // indicating no progress, which the caller interprets as limit_exceeded) + let page = result.expect("should not return an internal error for genesis block entries"); + // The page should indicate no progress was made (stuck at genesis) + assert!( + page.values.is_empty() || page.last_block_included == genesis, + "should indicate pagination did not make progress" + ); +} + +/// Tests that single-block overflow works for non-genesis blocks too. +/// All entries are in block 5 and exceed the limit. The function should +/// signal no progress rather than returning incorrect data. +#[test] +fn select_storage_map_sync_values_all_entries_in_single_non_genesis_block() { + let mut conn = create_db(); + let account_id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + let slot_name = StorageSlotName::mock(10); + + let block5 = BlockNumber::from(5); + create_block(&mut conn, block5); + + queries::upsert_accounts(&mut conn, &[mock_block_account_update(account_id, 0)], block5) + .unwrap(); + + for i in 0..3 { + queries::insert_account_storage_map_value( + &mut conn, + account_id, + block5, + slot_name.clone(), + StorageMapKey::from_index(i), + num_to_word(u64::from(i) + 200), + ) + .unwrap(); + } + + // limit=1, so 3 rows > 1 triggers pagination. All in block 5. + let page = + queries::select_account_storage_map_values_paged(&mut conn, account_id, block5..=block5, 1) + .unwrap(); + + assert!(page.values.is_empty(), "should have no values when single block exceeds limit"); + assert_eq!(page.last_block_included, block5, "should signal no progress at block 5"); +} + +/// Tests that normal multi-block pagination still works correctly: +/// entries in blocks 1, 2, 3 with limit causing block 3 to be dropped. +#[test] +fn select_storage_map_sync_values_multi_block_pagination() { + let mut conn = create_db(); + let account_id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + let slot_name = StorageSlotName::mock(11); + + let block1 = BlockNumber::from(1); + let block2 = BlockNumber::from(2); + let block3 = BlockNumber::from(3); + + create_block(&mut conn, block1); + create_block(&mut conn, block2); + create_block(&mut conn, block3); + + queries::upsert_accounts(&mut conn, &[mock_block_account_update(account_id, 0)], block1) + .unwrap(); + queries::upsert_accounts(&mut conn, &[mock_block_account_update(account_id, 1)], block2) + .unwrap(); + queries::upsert_accounts(&mut conn, &[mock_block_account_update(account_id, 2)], block3) + .unwrap(); + + // 1 entry in block 1, 1 in block 2, 1 in block 3 + queries::insert_account_storage_map_value( + &mut conn, + account_id, + block1, + slot_name.clone(), + StorageMapKey::from_index(1), + num_to_word(11), + ) + .unwrap(); + queries::insert_account_storage_map_value( + &mut conn, + account_id, + block2, + slot_name.clone(), + StorageMapKey::from_index(2), + num_to_word(22), + ) + .unwrap(); + queries::insert_account_storage_map_value( + &mut conn, + account_id, + block3, + slot_name.clone(), + StorageMapKey::from_index(3), + num_to_word(33), + ) + .unwrap(); + + // limit=2: query fetches 3 rows (limit+1), drops block 3, keeps blocks 1-2 + let page = queries::select_account_storage_map_values_paged( + &mut conn, + account_id, + BlockNumber::GENESIS..=block3, + 2, + ) + .unwrap(); + + assert_eq!(page.values.len(), 2, "should include entries from blocks 1 and 2"); + assert_eq!(page.last_block_included, block2, "last included block should be 2"); +} + #[tokio::test] #[miden_node_test_macro::enable_logging] async fn reconstruct_storage_map_from_db_pages_until_latest() { @@ -1289,6 +1438,57 @@ async fn reconstruct_storage_map_from_db_pages_until_latest() { }); } +/// Tests that `reconstruct_storage_map_from_db` returns `LimitExceeded` when the first +/// block in the range has more entries than the limit allows. Previously this returned +/// `AllEntries([])` because the pagination loop exited immediately (`last_block_included` == +/// `block_num`) without checking that no values were actually returned. +#[tokio::test] +#[miden_node_test_macro::enable_logging] +async fn reconstruct_storage_map_from_db_returns_limit_exceeded_for_single_block_overflow() { + let temp_dir = tempdir().unwrap(); + let db_path = temp_dir.path().join("store.sqlite"); + + let account_id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + let slot_name = StorageSlotName::mock(12); + + let block5 = BlockNumber::from(5); + + let db = crate::db::Db::load(db_path).await.unwrap(); + let slot_name_for_db = slot_name.clone(); + db.query("insert entries in single block", move |db_conn| { + db_conn.transaction(|db_conn| { + apply_migrations(db_conn)?; + create_block(db_conn, block5); + + queries::upsert_accounts(db_conn, &[mock_block_account_update(account_id, 0)], block5)?; + + // Insert 3 entries, all in the same block + for i in 1..=3 { + queries::insert_account_storage_map_value( + db_conn, + account_id, + block5, + slot_name_for_db.clone(), + num_to_storage_map_key(i), + num_to_word(i * 10), + )?; + } + Ok::<_, DatabaseError>(()) + }) + }) + .await + .unwrap(); + + // Use limit=1 so that 3 entries in a single block exceed the limit. + // block_range_start is block5 (the first block with data), and the target is also block5. + let details = db + .reconstruct_storage_map_from_db(account_id, slot_name.clone(), block5, Some(1)) + .await + .unwrap(); + + assert_matches!(details.entries, StorageMapEntries::LimitExceeded); +} + // UTILITIES // ------------------------------------------------------------------------------------------- fn num_to_word(n: u64) -> Word {