diff --git a/.changelog/unreleased/improvements/1642-iter-prefix-full-match.md b/.changelog/unreleased/improvements/1642-iter-prefix-full-match.md new file mode 100644 index 00000000000..935e0277119 --- /dev/null +++ b/.changelog/unreleased/improvements/1642-iter-prefix-full-match.md @@ -0,0 +1,3 @@ +- Storage: Ensure that prefix iterator only returns key- + vals in which the prefix key segments are matched fully. + ([\#1642](https://github.com/anoma/namada/pull/1642)) \ No newline at end of file diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index 956ce2a7e96..a30f55f6bbd 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -1289,7 +1289,18 @@ fn iter_subspace_prefix<'iter>( .get_column_family(SUBSPACE_CF) .expect("{SUBSPACE_CF} column family should exist"); let db_prefix = "".to_owned(); - iter_prefix(db, subspace_cf, db_prefix, prefix.map(|k| k.to_string())) + iter_prefix( + db, + subspace_cf, + db_prefix, + prefix.map(|k| { + if k == &Key::default() { + k.to_string() + } else { + format!("{k}/") + } + }), + ) } fn iter_diffs_prefix( @@ -1613,28 +1624,24 @@ mod test { let key_1_a = prefix_1.push(&"a".to_string()).unwrap(); let key_1_b = prefix_1.push(&"b".to_string()).unwrap(); let key_1_c = prefix_1.push(&"c".to_string()).unwrap(); + let prefix_01 = Key::parse("01").unwrap(); + let key_01_a = prefix_01.push(&"a".to_string()).unwrap(); - let keys_0 = vec![key_0_a.clone(), key_0_b.clone(), key_0_c.clone()]; - let keys_1 = vec![key_1_a.clone(), key_1_b.clone(), key_1_c.clone()]; - let all_keys = vec![keys_0.clone(), keys_1.clone()].concat(); + let keys_0 = vec![key_0_a, key_0_b, key_0_c]; + let keys_1 = vec![key_1_a, key_1_b, key_1_c]; + let keys_01 = vec![key_01_a]; + let all_keys = vec![keys_0.clone(), keys_01, keys_1.clone()].concat(); // Write the keys let mut batch = RocksDB::batch(); let height = BlockHeight(1); - db.batch_write_subspace_val(&mut batch, height, &key_0_a, [0_u8]) - .unwrap(); - db.batch_write_subspace_val(&mut batch, height, &key_0_b, [0_u8]) - .unwrap(); - db.batch_write_subspace_val(&mut batch, height, &key_0_c, [0_u8]) - .unwrap(); - db.batch_write_subspace_val(&mut batch, height, &key_1_a, [0_u8]) - .unwrap(); - db.batch_write_subspace_val(&mut batch, height, &key_1_b, [0_u8]) - .unwrap(); - db.batch_write_subspace_val(&mut batch, height, &key_1_c, [0_u8]) - .unwrap(); + for key in &all_keys { + db.batch_write_subspace_val(&mut batch, height, key, [0_u8]) + .unwrap(); + } db.exec_batch(batch.0).unwrap(); + // Prefix "0" shouldn't match prefix "01" let itered_keys: Vec = db .iter_optional_prefix(Some(&prefix_0)) .map(|(key, _val, _)| Key::parse(key).unwrap()) diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index e53178b1578..3ce58f172fc 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -523,10 +523,14 @@ impl<'iter> DBIter<'iter> for MockDB { "{}{}", db_prefix, match prefix { - None => "".to_string(), Some(prefix) => { - prefix.to_string() + if prefix == &Key::default() { + prefix.to_string() + } else { + format!("{prefix}/") + } } + None => "".to_string(), } ); let iter = self.0.borrow().clone().into_iter();