Skip to content

Commit

Permalink
feat(client): Add current_output_root to block executor
Browse files Browse the repository at this point in the history
Adds a function to the block executor that allows for generating the
current output root, based on the parent timestamp and current trie.
  • Loading branch information
clabby committed Jun 8, 2024
1 parent f66be56 commit bc40428
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 8 deletions.
45 changes: 44 additions & 1 deletion bin/programs/client/src/l2/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use alloc::{sync::Arc, vec::Vec};
use alloy_consensus::{Header, Sealable, Sealed, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};
use alloy_eips::eip2718::{Decodable2718, Encodable2718};
use alloy_primitives::{address, Bytes, TxKind, B256, U256};
use alloy_primitives::{address, keccak256, Address, Bytes, TxKind, B256, U256};
use anyhow::{anyhow, Result};
use kona_derive::types::{L2PayloadAttributes, RawTransaction, RollupConfig};
use kona_mpt::{ordered_trie_with_encoder, TrieDB, TrieDBFetcher};
Expand Down Expand Up @@ -275,6 +275,49 @@ where
Ok(&self.parent_header)
}

/// Computes the current output root of the executor, based on the parent header and the
/// state's underlying trie.
///
/// **CONSTRUCTION:**
/// ```text
/// output_root = keccak256(version_byte .. payload)
/// payload = state_root .. withdrawal_storage_root .. latest_block_hash
/// ```
///
/// ## Returns
/// - `Ok(output_root)`: The computed output root.
/// - `Err(_)`: If an error occurred while computing the output root.
pub fn compute_output_root(&mut self) -> Result<B256> {
const OUTPUT_ROOT_VERSION: u8 = 0;
const L2_TO_L1_MESSAGE_PASSER_ADDRESS: Address =
address!("4200000000000000000000000000000000000016");

// Fetch the L2 to L1 message passer account from the cache or underlying trie.
let storage_root =
match self.state.database.storage_roots().get(&L2_TO_L1_MESSAGE_PASSER_ADDRESS) {
Some(storage_root) => storage_root
.blinded_commitment()
.ok_or(anyhow!("Account storage root is unblinded"))?,
None => {
self.state
.database
.get_trie_account(&L2_TO_L1_MESSAGE_PASSER_ADDRESS)?
.ok_or(anyhow!("L2 to L1 message passer account not found in trie"))?
.storage_root
}
};

// Construct the raw output.
let mut raw_output = [0u8; 97];
raw_output[0] = OUTPUT_ROOT_VERSION;
raw_output[1..33].copy_from_slice(self.parent_header.state_root.as_ref());
raw_output[33..65].copy_from_slice(storage_root.as_ref());
raw_output[65..97].copy_from_slice(self.parent_header.seal().as_ref());

// Hash the output and return
Ok(keccak256(raw_output))
}

/// Returns the active [SpecId] for the executor.
///
/// ## Takes
Expand Down
31 changes: 24 additions & 7 deletions crates/mpt/src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,29 @@ where
self.parent_block_hash = parent_block_hash;
}

/// Fetches the [TrieAccount] of an account from the trie DB.
///
/// ## Takes
/// - `address`: The address of the account.
///
/// ## Returns
/// - `Ok(Some(TrieAccount))`: The [TrieAccount] of the account.
/// - `Ok(None)`: If the account does not exist in the trie.
/// - `Err(_)`: If the account could not be fetched.
pub fn get_trie_account(&mut self, address: &Address) -> Result<Option<TrieAccount>> {
// Fetch the account from the trie.
let hashed_address_nibbles = Nibbles::unpack(keccak256(address.as_slice()));
let Some(trie_account_rlp) = self.root_node.open(&hashed_address_nibbles, &self.fetcher)?
else {
return Ok(None);
};

// Decode the trie account from the RLP bytes.
TrieAccount::decode(&mut trie_account_rlp.as_ref())
.map_err(|e| anyhow!("Error decoding trie account: {e}"))
.map(Some)
}

/// Modifies the accounts in the storage trie with the given [BundleState] changeset.
///
/// ## Takes
Expand Down Expand Up @@ -261,17 +284,11 @@ where

fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
// Fetch the account from the trie.
let hashed_address_nibbles = Nibbles::unpack(keccak256(address.as_slice()));
let Some(trie_account_rlp) = self.root_node.open(&hashed_address_nibbles, &self.fetcher)?
else {
let Some(trie_account) = self.get_trie_account(&address)? else {
// If the account does not exist in the trie, return `Ok(None)`.
return Ok(None);
};

// Decode the trie account from the RLP bytes.
let trie_account = TrieAccount::decode(&mut trie_account_rlp.as_ref())
.map_err(|e| anyhow!("Error decoding trie account: {e}"))?;

// Insert the account's storage root into the cache.
self.storage_roots.insert(address, TrieNode::new_blinded(trie_account.storage_root));

Expand Down

0 comments on commit bc40428

Please sign in to comment.