Skip to content

Commit

Permalink
fix(state bridge): gossip contract storage tries (#1511)
Browse files Browse the repository at this point in the history
  • Loading branch information
morph-dev authored Oct 7, 2024
1 parent e5c3931 commit c423ee1
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 68 deletions.
4 changes: 2 additions & 2 deletions portal-bridge/src/bridge/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl StateBridge {
.process_range_of_blocks(start_block - 1, None)
.await?;
// flush the database cache
trin_execution.database.storage_cache.clear();
trin_execution.database.storage_cache.lock().clear();
}

info!("Gossiping state data from block {start_block} to {end_block} (inclusively)");
Expand Down Expand Up @@ -225,7 +225,7 @@ impl StateBridge {

// flush the database cache
// This is used for gossiping storage trie diffs
trin_execution.database.storage_cache.clear();
trin_execution.database.storage_cache.lock().clear();

Ok(())
}
Expand Down
8 changes: 5 additions & 3 deletions trin-execution/src/storage/evm_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub struct EvmDB {
/// State config
pub config: StateConfig,
/// Storage cache for the accounts used optionally for gossiping, keyed by address hash.
pub storage_cache: HashMap<B256, HashSet<B256>>,
pub storage_cache: Arc<Mutex<HashMap<B256, HashSet<B256>>>>,
/// The underlying database.
pub db: Arc<RocksDB>,
/// To get proofs and to verify trie state.
Expand All @@ -65,7 +65,7 @@ impl EvmDB {
},
));

let storage_cache = HashMap::new();
let storage_cache = Arc::new(Mutex::new(HashMap::new()));
Ok(Self {
config,
storage_cache,
Expand All @@ -79,6 +79,7 @@ impl EvmDB {

for key in self
.storage_cache
.lock()
.get(&address_hash)
.unwrap_or(&HashSet::new())
{
Expand Down Expand Up @@ -226,7 +227,8 @@ impl EvmDB {
} = trie.root_hash_with_changed_nodes()?;

if self.config.cache_contract_storage_changes {
let account_storage_cache = self.storage_cache.entry(address_hash).or_default();
let mut storage_cache_guard = self.storage_cache.lock();
let account_storage_cache = storage_cache_guard.entry(address_hash).or_default();
for key in trie_diff.keys() {
account_storage_cache.insert(*key);
}
Expand Down
110 changes: 47 additions & 63 deletions trin-execution/tests/content_generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,22 @@ use trin_execution::{
};
use trin_utils::dir::create_temp_test_dir;

#[derive(Default)]
#[derive(Default, Debug)]
struct Stats {
total_content_count: usize,
block_content_count: usize,
total_gossip_size: usize,
block_gossip_size: usize,
total_storage_size: usize,
block_storage_size: usize,
content_count: usize,
gossip_size: usize,
storage_size: usize,
account_trie_count: usize,
contract_storage_trie_count: usize,
contract_bytecode_count: usize,
}

impl Stats {
fn reset_block_stats(&mut self) {
self.block_content_count = 0;
self.block_gossip_size = 0;
self.block_storage_size = 0;
}

/// Panics if either is `Err`
fn check_content(&mut self, key: Result<StateContentKey>, value: Result<StateContentValue>) {
let key = key.expect("Content key should be present");
let value = value.expect("Content key should be present");
fn check_content(&mut self, key: &StateContentKey, value: &StateContentValue) {
let value_without_proof = match &value {
StateContentValue::AccountTrieNodeWithProof(account_trie_node_with_proof) => {
self.account_trie_count += 1;
StateContentValue::TrieNode(TrieNode {
node: account_trie_node_with_proof
.proof
Expand All @@ -58,14 +51,18 @@ impl Stats {
}
StateContentValue::ContractStorageTrieNodeWithProof(
contract_storage_trie_node_with_proof,
) => StateContentValue::TrieNode(TrieNode {
node: contract_storage_trie_node_with_proof
.storage_proof
.last()
.expect("Storage trie proof much have at least one trie node")
.clone(),
}),
) => {
self.contract_storage_trie_count += 1;
StateContentValue::TrieNode(TrieNode {
node: contract_storage_trie_node_with_proof
.storage_proof
.last()
.expect("Storage trie proof much have at least one trie node")
.clone(),
})
}
StateContentValue::ContractBytecodeWithProof(contract_bytecode_with_proof) => {
self.contract_bytecode_count += 1;
StateContentValue::ContractBytecode(ContractBytecode {
code: contract_bytecode_with_proof.code.clone(),
})
Expand All @@ -74,30 +71,9 @@ impl Stats {
};
let gossip_size = key.to_bytes().len() + value.encode().len();
let storage_size = 32 + key.to_bytes().len() + value_without_proof.encode().len();
self.block_content_count += 1;
self.total_content_count += 1;
self.block_gossip_size += gossip_size;
self.total_gossip_size += gossip_size;
self.block_storage_size += storage_size;
self.total_storage_size += storage_size;
}

fn print_block_stats(&self, block_number: u64) {
info!(
count = self.block_content_count,
gossip_size = self.block_gossip_size,
storage_size = self.block_storage_size,
"Block {block_number} finished.",
);
}

fn print_total_stats(&self, block_number: u64) {
info!(
count = self.total_content_count,
gossip_size = self.total_gossip_size,
storage_size = self.total_storage_size,
"Finished all {block_number} blocks.",
);
self.content_count += 1;
self.gossip_size += gossip_size;
self.storage_size += storage_size;
}
}

Expand Down Expand Up @@ -129,7 +105,7 @@ async fn test_we_can_generate_content_key_values_up_to_x() -> Result<()> {
let mut stats = Stats::default();

for block_number in 0..=blocks {
stats.reset_block_stats();
let mut block_stats = Stats::default();

let RootWithTrieDiff {
root: root_hash,
Expand Down Expand Up @@ -157,10 +133,12 @@ async fn test_we_can_generate_content_key_values_up_to_x() -> Result<()> {
let account_proof = walk_diff.get_proof(*node);

// check account content key/value
stats.check_content(
create_account_content_key(&account_proof),
create_account_content_value(block_hash, &account_proof),
);
let content_key =
create_account_content_key(&account_proof).expect("Content key should be present");
let content_value = create_account_content_value(block_hash, &account_proof)
.expect("Content key should be present");
block_stats.check_content(&content_key, &content_value);
stats.check_content(&content_key, &content_value);

let Some(encoded_last_node) = account_proof.proof.last() else {
panic!("Account proof is empty");
Expand All @@ -183,10 +161,13 @@ async fn test_we_can_generate_content_key_values_up_to_x() -> Result<()> {
let code = trin_execution
.database
.code_by_hash_ref(account.code_hash)?;
stats.check_content(
create_contract_content_key(address_hash, account.code_hash),
create_contract_content_value(block_hash, &account_proof, code),
);

let content_key = create_contract_content_key(address_hash, account.code_hash)
.expect("Content key should be present");
let content_value = create_contract_content_value(block_hash, &account_proof, code)
.expect("Content key should be present");
block_stats.check_content(&content_key, &content_value);
stats.check_content(&content_key, &content_value);
}

// check contract storage content key/value
Expand All @@ -195,19 +176,22 @@ async fn test_we_can_generate_content_key_values_up_to_x() -> Result<()> {
for storage_node in storage_walk_diff.nodes.keys() {
let storage_proof = storage_walk_diff.get_proof(*storage_node);

stats.check_content(
create_storage_content_key(&storage_proof, address_hash),
create_storage_content_value(block_hash, &account_proof, &storage_proof),
);
let content_key = create_storage_content_key(&storage_proof, address_hash)
.expect("Content key should be present");
let content_value =
create_storage_content_value(block_hash, &account_proof, &storage_proof)
.expect("Content key should be present");
block_stats.check_content(&content_key, &content_value);
stats.check_content(&content_key, &content_value);
}
}

// flush the database cache
// This is used for gossiping storage trie diffs
trin_execution.database.storage_cache.clear();
stats.print_block_stats(block_number);
trin_execution.database.storage_cache.lock().clear();
info!("Block {block_number} finished: {block_stats:?}");
}
temp_directory.close()?;
stats.print_total_stats(blocks);
info!("Finished all {blocks} blocks: {stats:?}");
Ok(())
}

0 comments on commit c423ee1

Please sign in to comment.