Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a method to get the changes of a specific commit id #13

Merged
merged 4 commits into from
Feb 5, 2024
Merged
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
38 changes: 38 additions & 0 deletions src/key_value_db.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use crate::{trie::merkle_tree::bytes_to_bitvec, Change as ExternChange};
#[cfg(not(feature = "std"))]
use alloc::{collections::BTreeSet, format, string::ToString, vec::Vec};
use bitvec::{order::Msb0, vec::BitVec};
use hashbrown::HashMap;
use log::trace;
use parity_scale_codec::Decode;
use starknet_types_core::felt::Felt;
#[cfg(feature = "std")]
use std::collections::BTreeSet;

Expand Down Expand Up @@ -86,6 +91,39 @@
}
}

#[allow(clippy::type_complexity)]
pub(crate) fn get_changes(
&self,
id: ID,
) -> Result<HashMap<BitVec<u8, Msb0>, ExternChange>, BonsaiStorageError<DB::DatabaseError>>
{
if self.changes_store.id_queue.contains(&id) {
let mut leaf_changes = HashMap::new();
let changes = ChangeBatch::deserialize(
&id,
self.db
.get_by_prefix(&DatabaseKey::TrieLog(&id.to_bytes()))?,
);
for (k, v) in changes.0 {
if let TrieKey::Flat(k) = k {
leaf_changes.insert(
bytes_to_bitvec(&k),
ExternChange {
// SAFETY: We are sure that the values are valid Felt because they can be saved only by our crate
old_value: v.old_value.map(|x| Felt::decode(&mut x.as_ref()).unwrap()),
new_value: v.new_value.map(|x| Felt::decode(&mut x.as_ref()).unwrap()),
},
);
}
}
Ok(leaf_changes)
} else {
Err(BonsaiStorageError::GoTo(
"ID asked isn't in our ID records".to_string(),

Check warning on line 122 in src/key_value_db.rs

View check run for this annotation

Codecov / codecov/patch

src/key_value_db.rs#L120-L122

Added lines #L120 - L122 were not covered by tests
))
}
}

pub(crate) fn commit(&mut self, id: ID) -> Result<(), BonsaiStorageError<DB::DatabaseError>> {
if Some(&id) > self.changes_store.id_queue.back() {
self.changes_store.id_queue.push_back(id);
Expand All @@ -108,9 +146,9 @@
if let Some(max_saved_trie_logs) = self.config.max_saved_trie_logs {
while self.changes_store.id_queue.len() > max_saved_trie_logs {
// verified by previous conditional statement
let id = self.changes_store.id_queue.pop_front().unwrap();

Check warning on line 149 in src/key_value_db.rs

View check run for this annotation

Codecov / codecov/patch

src/key_value_db.rs#L149

Added line #L149 was not covered by tests
self.db
.remove_by_prefix(&DatabaseKey::TrieLog(&id.to_bytes()))?;

Check warning on line 151 in src/key_value_db.rs

View check run for this annotation

Codecov / codecov/patch

src/key_value_db.rs#L151

Added line #L151 was not covered by tests
}
}
Ok(())
Expand Down
22 changes: 21 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ extern crate alloc;
use crate::trie::merkle_tree::MerkleTree;
#[cfg(not(feature = "std"))]
use alloc::{format, vec::Vec};
use bitvec::{order::Msb0, slice::BitSlice};
use bitvec::{order::Msb0, slice::BitSlice, vec::BitVec};
use bonsai_database::{BonsaiPersistentDatabase, DatabaseKey};
use changes::ChangeBatch;
use hashbrown::HashMap;
use key_value_db::KeyValueDB;
use starknet_types_core::{
felt::Felt,
Expand Down Expand Up @@ -144,6 +145,16 @@ impl Default for BonsaiStorageConfig {
}
}

/// Structure used to represent a change in the trie for a specific value.
/// It contains the old value and the new value.
/// If the `old_value` is None, it means that the key was not present in the trie before the change.
/// If the `new_value` is None, it means that the key was removed from the trie.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Change {
pub old_value: Option<Felt>,
pub new_value: Option<Felt>,
}

/// Structure that hold the trie and all the necessary information to work with it.
///
/// This structure is the main entry point to work with this crate.
Expand Down Expand Up @@ -300,6 +311,15 @@ where
Ok(())
}

/// Get all changes applied at a certain commit ID.
#[allow(clippy::type_complexity)]
pub fn get_changes(
&self,
id: ChangeID,
) -> Result<HashMap<BitVec<u8, Msb0>, Change>, BonsaiStorageError<DB::DatabaseError>> {
self.trie.db_ref().get_changes(id)
}

#[cfg(test)]
pub fn dump_database(&self) {
self.trie.db_ref().db.dump_database();
Expand Down
43 changes: 42 additions & 1 deletion src/tests/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
databases::{create_rocks_db, RocksDB, RocksDBConfig},
id::BasicIdBuilder,
BonsaiStorage, BonsaiStorageConfig,
BonsaiStorage, BonsaiStorageConfig, Change,
};
use bitvec::vec::BitVec;
use starknet_types_core::{felt::Felt, hash::Pedersen};
Expand Down Expand Up @@ -52,3 +52,44 @@ fn basics() {
None
);
}

#[test]
fn get_changes() {
let tempdir = tempfile::tempdir().unwrap();
let db = create_rocks_db(tempdir.path()).unwrap();
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
let config = BonsaiStorageConfig::default();
let mut bonsai_storage: BonsaiStorage<_, _, Pedersen> =
BonsaiStorage::new(RocksDB::new(&db, RocksDBConfig::default()), config).unwrap();
let mut id_builder = BasicIdBuilder::new();
let pair1 = (vec![1, 2, 1], Felt::from_hex("0x01").unwrap());
let bitvec = BitVec::from_vec(pair1.0.clone());
bonsai_storage.insert(&bitvec, &pair1.1).unwrap();
bonsai_storage.commit(id_builder.new_id()).unwrap();
let pair2 = (vec![1, 2, 2], Felt::from_hex("0x01").unwrap());
let bitvec = BitVec::from_vec(pair2.0.clone());
bonsai_storage.insert(&bitvec, &pair2.1).unwrap();
let pair1_edited_1 = (vec![1, 2, 1], Felt::from_hex("0x02").unwrap());
let bitvec = BitVec::from_vec(pair1_edited_1.0.clone());
bonsai_storage.insert(&bitvec, &pair1_edited_1.1).unwrap();
let pair1_edited_2 = (vec![1, 2, 1], Felt::from_hex("0x03").unwrap());
let bitvec = BitVec::from_vec(pair1_edited_2.0.clone());
bonsai_storage.insert(&bitvec, &pair1_edited_2.1).unwrap();
let id = id_builder.new_id();
bonsai_storage.commit(id).unwrap();
let changes = bonsai_storage.get_changes(id).unwrap();
assert_eq!(changes.len(), 2);
assert_eq!(
changes.get(&BitVec::from_vec(pair1.0)).unwrap(),
&Change {
old_value: Some(pair1.1),
new_value: Some(pair1_edited_2.1),
}
);
assert_eq!(
changes.get(&BitVec::from_vec(pair2.0)).unwrap(),
&Change {
old_value: None,
new_value: Some(pair2.1),
}
);
}
6 changes: 5 additions & 1 deletion src/trie/merkle_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@
/// Wrapper type for a [HashMap<NodeId, Node>] object. (It's not really a wrapper it's a
/// copy of the type but we implement the necessary traits.)
#[derive(Clone, Debug, PartialEq, Eq, Default, Constructor)]
pub struct NodesMapping(HashMap<NodeId, Node>);

Check warning on line 38 in src/trie/merkle_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/trie/merkle_tree.rs#L38

Added line #L38 was not covered by tests

/// A node used in proof generated by the trie.
///
/// See pathfinders merkle-tree crate for more information.
#[derive(Debug, Clone, PartialEq)]
pub enum ProofNode {
Binary { left: Felt, right: Felt },
Edge { child: Felt, path: Path },

Check warning on line 46 in src/trie/merkle_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/trie/merkle_tree.rs#L45-L46

Added lines #L45 - L46 were not covered by tests
}

impl ProofNode {
Expand Down Expand Up @@ -537,26 +537,26 @@
.get(&node_id)
.ok_or(BonsaiStorageError::Trie(
"Node not found in memory".to_string(),
))?;

Check warning on line 540 in src/trie/merkle_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/trie/merkle_tree.rs#L540

Added line #L540 was not covered by tests
// If it's an edge node and the child is in memory and it's an edge too we
// return the child otherwise we leave
let child =
if let Node::Edge(edge) = node {
match edge.child {
NodeHandle::Hash(_) => return Ok(()),

Check warning on line 546 in src/trie/merkle_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/trie/merkle_tree.rs#L546

Added line #L546 was not covered by tests
NodeHandle::InMemory(child_id) => {
let child_node = self.storage_nodes.0.get(&child_id).ok_or(
BonsaiStorageError::Trie("Node not found in memory".to_string()),
)?;

Check warning on line 550 in src/trie/merkle_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/trie/merkle_tree.rs#L550

Added line #L550 was not covered by tests
if let Node::Edge(child_edge) = child_node {
child_edge.clone()
} else {
return Ok(());

Check warning on line 554 in src/trie/merkle_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/trie/merkle_tree.rs#L554

Added line #L554 was not covered by tests
}
}
}
} else {
return Ok(());

Check warning on line 559 in src/trie/merkle_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/trie/merkle_tree.rs#L559

Added line #L559 was not covered by tests
};
// Get a mutable reference to the parent node to merge them
let edge = self
Expand Down Expand Up @@ -1091,10 +1091,14 @@
}
}

fn bitslice_to_bytes(bitslice: &BitSlice<u8, Msb0>) -> Vec<u8> {
pub(crate) fn bitslice_to_bytes(bitslice: &BitSlice<u8, Msb0>) -> Vec<u8> {
[&[bitslice.len() as u8], bitslice.to_bitvec().as_raw_slice()].concat()
}

pub(crate) fn bytes_to_bitvec(bytes: &[u8]) -> BitVec<u8, Msb0> {
BitSlice::from_slice(&bytes[1..]).to_bitvec()
}

#[cfg(test)]
#[cfg(all(test, feature = "std"))]
mod tests {
Expand Down
Loading