Skip to content

Commit d55f84c

Browse files
kariyclaude
andauthored
feat(trie): add revert functionality to state tries (#374)
## Summary - Add `revert_to` method to `ClassesTrie`, `ContractsTrie`, `StoragesTrie`, and `BonsaiTrie` for unwinding trie state - Implement `get_by_prefix` for `TrieDb` to support trie prefix operations - Update bonsai-trie dependency to rev `95de4c6` for revert support --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0be3ee6 commit d55f84c

File tree

7 files changed

+159
-8
lines changed

7 files changed

+159
-8
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/storage/db/src/trie/mod.rs

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,22 @@ where
173173

174174
fn get_by_prefix(
175175
&self,
176-
_: &DatabaseKey<'_>,
176+
prefix: &DatabaseKey<'_>,
177177
) -> Result<Vec<(ByteVec, ByteVec)>, Self::DatabaseError> {
178-
todo!()
178+
let mut results = Vec::new();
179+
180+
let mut cursor = self.tx.cursor::<Tb>()?;
181+
let walker = cursor.walk(None)?;
182+
183+
for entry in walker {
184+
let (TrieDatabaseKey { key, .. }, value) = entry?;
185+
186+
if key.starts_with(prefix.as_slice()) {
187+
results.push((key.to_smallvec(), value));
188+
}
189+
}
190+
191+
Ok(results)
179192
}
180193

181194
fn insert(
@@ -300,8 +313,7 @@ where
300313
&self,
301314
prefix: &DatabaseKey<'_>,
302315
) -> Result<Vec<(ByteVec, ByteVec)>, Self::DatabaseError> {
303-
let _ = prefix;
304-
todo!()
316+
TrieDb::<Tb, Tx>::new(self.tx.clone()).get_by_prefix(prefix)
305317
}
306318

307319
fn insert(
@@ -509,4 +521,67 @@ mod tests {
509521
assert_eq!(vec![value0, value1], result);
510522
}
511523
}
524+
525+
#[test]
526+
fn revert_to() {
527+
let db = test_utils::create_test_db();
528+
let db_tx = db.tx_mut().expect("failed to get tx");
529+
530+
let mut trie = ClassesTrie::new(TrieDbMut::<tables::ClassesTrie, _>::new(&db_tx));
531+
532+
// Insert values at block 0
533+
trie.insert(felt!("0x1"), felt!("0x100"));
534+
trie.insert(felt!("0x2"), felt!("0x200"));
535+
trie.commit(0);
536+
let root_at_block_0 = trie.root();
537+
538+
// Insert more values at block 1
539+
trie.insert(felt!("0x3"), felt!("0x300"));
540+
trie.insert(felt!("0x4"), felt!("0x400"));
541+
trie.commit(1);
542+
let root_at_block_1 = trie.root();
543+
544+
// Roots should be different
545+
assert_ne!(root_at_block_0, root_at_block_1);
546+
547+
// Insert even more values at block 2
548+
trie.insert(felt!("0x5"), felt!("0x500"));
549+
trie.commit(2);
550+
let root_at_block_2 = trie.root();
551+
552+
// Roots should be different
553+
assert_ne!(root_at_block_1, root_at_block_2);
554+
assert_ne!(root_at_block_0, root_at_block_2);
555+
556+
// Revert to block 1
557+
trie.revert_to(1, 2);
558+
let root_after_revert = trie.root();
559+
560+
// After revert, root should match block 1
561+
assert_eq!(root_after_revert, root_at_block_1);
562+
563+
// Revert to block 0
564+
trie.revert_to(0, 1);
565+
let root_after_second_revert = trie.root();
566+
567+
// After revert, root should match block 0
568+
assert_eq!(root_after_second_revert, root_at_block_0);
569+
570+
// Insert more values at block 1
571+
trie.insert(felt!("0x3"), felt!("0x300"));
572+
trie.insert(felt!("0x4"), felt!("0x400"));
573+
trie.commit(1);
574+
let root_at_block_1_after_insert = trie.root();
575+
576+
// After insertion, root should match block 1
577+
assert_eq!(root_at_block_1_after_insert, root_at_block_1);
578+
579+
// Insert even more values at block 2
580+
trie.insert(felt!("0x5"), felt!("0x500"));
581+
trie.commit(2);
582+
let root_at_block_2_after_insert = trie.root();
583+
584+
// After insertion, root should match block 2
585+
assert_eq!(root_at_block_2_after_insert, root_at_block_2);
586+
}
512587
}

crates/trie/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ starknet-types-core.workspace = true
1717
thiserror.workspace = true
1818

1919
[dependencies.bonsai-trie]
20-
rev = "8b509fd"
20+
rev = "95de4c6"
2121
default-features = false
2222
features = [ "std" ]
2323
git = "https://github.com/dojoengine/bonsai-trie/"

crates/trie/src/classes.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ impl<DB: BonsaiDatabase> ClassesTrie<DB> {
4747
pub fn multiproof(&mut self, class_hashes: Vec<ClassHash>) -> MultiProof {
4848
self.trie.multiproof(Self::BONSAI_IDENTIFIER, class_hashes)
4949
}
50+
51+
pub fn revert_to(&mut self, block: BlockNumber, latest_block: BlockNumber) {
52+
self.trie.revert_to(block, latest_block);
53+
}
5054
}
5155

5256
impl<DB> ClassesTrie<DB>

crates/trie/src/contracts.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ impl<DB: BonsaiDatabase> ContractsTrie<DB> {
3131
let keys = addresses.into_iter().map(Felt::from).collect::<Vec<Felt>>();
3232
self.trie.multiproof(Self::BONSAI_IDENTIFIER, keys)
3333
}
34+
35+
pub fn revert_to(&mut self, block: BlockNumber, latest_block: BlockNumber) {
36+
self.trie.revert_to(block, latest_block);
37+
}
3438
}
3539

3640
impl<DB> ContractsTrie<DB>

crates/trie/src/lib.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub use bonsai::{BitVec, MultiProof, Path, ProofNode};
33
pub use bonsai_trie::databases::HashMapDb;
44
use bonsai_trie::BonsaiStorage;
55
pub use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase, BonsaiStorageConfig};
6+
use katana_primitives::block::BlockNumber;
67
use katana_primitives::class::ClassHash;
78
use katana_primitives::Felt;
89
use starknet_types_core::hash::{Pedersen, StarkHash};
@@ -39,14 +40,23 @@ where
3940
{
4041
pub fn new(db: DB) -> Self {
4142
let config = BonsaiStorageConfig {
42-
// we have our own implementation of storing trie changes
43-
max_saved_trie_logs: Some(0),
43+
// This field controls what's the oldest block we can revert to.
44+
//
45+
// The value 5 is chosen arbitrarily as a placeholder. This value should be
46+
// configurable.
47+
max_saved_trie_logs: Some(5),
48+
4449
// in the bonsai-trie crate, this field seems to be only used in rocksdb impl.
4550
// i dont understand why would they add a config thats implementation specific ????
4651
//
4752
// this config should be used by our implementation of the
4853
// BonsaiPersistentDatabase::snapshot()
54+
//
55+
// note: currently, this value is not being used for anything. our trie will stores
56+
// all created snapshots.
4957
max_saved_snapshots: Some(64usize),
58+
59+
// creates a snapshot for every block
5060
snapshot_interval: 1,
5161
};
5262

@@ -61,6 +71,10 @@ where
6171
let keys = keys.into_iter().map(|key| key.to_bytes_be().as_bits()[5..].to_owned());
6272
self.storage.get_multi_proof(id, keys).expect("failed to get multiproof")
6373
}
74+
75+
pub fn revert_to(&mut self, block: BlockNumber, latest_block: BlockNumber) {
76+
self.storage.revert_to(block.into(), latest_block.into()).expect("failed to revert trie");
77+
}
6478
}
6579

6680
impl<DB, Hash> BonsaiTrie<DB, Hash>
@@ -169,4 +183,54 @@ mod tests {
169183

170184
assert_eq!(result, expected);
171185
}
186+
187+
#[test]
188+
fn test_revert_to() {
189+
use bonsai_trie::databases;
190+
191+
// the identifier for the trie
192+
const IDENTIFIER: &[u8] = b"test_trie";
193+
194+
// Create a BonsaiStorage with in-memory database and trie logs enabled
195+
let bonsai_db = databases::HashMapDb::<CommitId>::default();
196+
let mut trie = BonsaiTrie::<_, hash::Pedersen>::new(bonsai_db);
197+
198+
// Insert values at block 0
199+
trie.insert(IDENTIFIER, Felt::from(1), Felt::from(100));
200+
trie.insert(IDENTIFIER, Felt::from(2), Felt::from(200));
201+
trie.commit(0.into());
202+
let root_at_block_0 = trie.root(IDENTIFIER);
203+
204+
// Insert more values at block 1
205+
trie.insert(IDENTIFIER, Felt::from(3), Felt::from(300));
206+
trie.insert(IDENTIFIER, Felt::from(4), Felt::from(400));
207+
trie.commit(1.into());
208+
let root_at_block_1 = trie.root(IDENTIFIER);
209+
210+
// Roots should be different
211+
assert_ne!(root_at_block_0, root_at_block_1);
212+
213+
// Insert even more values at block 2
214+
trie.insert(IDENTIFIER, Felt::from(5), Felt::from(500));
215+
trie.commit(2.into());
216+
let root_at_block_2 = trie.root(IDENTIFIER);
217+
218+
// Roots should be different
219+
assert_ne!(root_at_block_1, root_at_block_2);
220+
assert_ne!(root_at_block_0, root_at_block_2);
221+
222+
// Revert to block 1
223+
trie.revert_to(1, 2);
224+
let root_after_revert = trie.root(IDENTIFIER);
225+
226+
// After revert, root should match block 1
227+
assert_eq!(root_after_revert, root_at_block_1);
228+
229+
// Revert to block 0
230+
trie.revert_to(0, 1);
231+
let root_after_second_revert = trie.root(IDENTIFIER);
232+
233+
// After revert, root should match block 0
234+
assert_eq!(root_after_second_revert, root_at_block_0);
235+
}
172236
}

crates/trie/src/storages.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ impl<DB: BonsaiDatabase> StoragesTrie<DB> {
2525
pub fn multiproof(&mut self, storage_keys: Vec<StorageKey>) -> MultiProof {
2626
self.trie.multiproof(&self.address.to_bytes_be(), storage_keys)
2727
}
28+
29+
pub fn revert_to(&mut self, block: BlockNumber, latest_block: BlockNumber) {
30+
self.trie.revert_to(block, latest_block);
31+
}
2832
}
2933

3034
impl<DB> StoragesTrie<DB>

0 commit comments

Comments
 (0)