From 01d6a7483dcf0938dbeb30c1f81b110ccf6cfdb6 Mon Sep 17 00:00:00 2001 From: Gregory Edison Date: Thu, 12 Oct 2023 10:42:18 +0200 Subject: [PATCH] add rust doc --- .../ef-testing/src/evm_sequencer/evm_state.rs | 19 ++++++++++++++++--- crates/ef-testing/src/evm_sequencer/mod.rs | 3 +++ crates/ef-testing/src/evm_sequencer/types.rs | 1 + crates/ef-testing/src/evm_sequencer/utils.rs | 15 ++++++++++----- crates/sequencer/src/commit.rs | 3 +++ crates/sequencer/src/sequencer.rs | 7 +++++-- crates/sequencer/src/state.rs | 7 +++++++ crates/sequencer/src/transaction.rs | 3 +++ 8 files changed, 48 insertions(+), 10 deletions(-) diff --git a/crates/ef-testing/src/evm_sequencer/evm_state.rs b/crates/ef-testing/src/evm_sequencer/evm_state.rs index a74eae6e..ef80a825 100644 --- a/crates/ef-testing/src/evm_sequencer/evm_state.rs +++ b/crates/ef-testing/src/evm_sequencer/evm_state.rs @@ -16,10 +16,13 @@ use super::constants::{ }; use super::types::FeltSequencer; use super::utils::{ - compute_starknet_address, felt_to_bytes, split_bytecode_to_starkfelt, split_u256, + compute_starknet_address, high_16_bytes_of_felt_to_bytes, split_bytecode_to_starkfelt, + split_u256, }; use super::KakarotSequencer; +/// EVM state interface. Used to setup EOA and contract accounts, +/// fund them and get their state (balance, nonce, code, storage). pub trait EvmState { fn setup_account( &mut self, @@ -41,6 +44,7 @@ pub trait EvmState { } impl EvmState for KakarotSequencer { + /// Sets up an EOA or contract account. Writes nonce, code and storage to the sequencer storage. fn setup_account( &mut self, evm_address: &Address, @@ -122,6 +126,7 @@ impl EvmState for KakarotSequencer { Ok(()) } + /// Funds an EOA or contract account. Also gives allowance to the Kakarot contract. fn fund(&mut self, evm_address: &Address, balance: U256) -> StateResult<()> { let starknet_address = compute_starknet_address(evm_address); let balance_values = split_u256(balance); @@ -156,6 +161,7 @@ impl EvmState for KakarotSequencer { Ok(()) } + /// Returns the storage value at the given key evm storage key. fn get_storage_at(&mut self, evm_address: &Address, key: U256) -> StateResult { let keys = split_u256(key).map(Into::into); let keys = get_uint256_storage_var_addresses("storage_", &keys).unwrap(); // safe unwrap: all vars are ASCII @@ -171,6 +177,8 @@ impl EvmState for KakarotSequencer { Ok(high << 128 | low) } + /// Returns the nonce of the given address. For an EOA, uses the protocol level nonce. + /// For a contract account, uses the Kakarot managed nonce stored in the contract account's storage. fn get_nonce_at(&mut self, evm_address: &Address) -> StateResult { let starknet_address = compute_starknet_address(evm_address); @@ -198,6 +206,9 @@ impl EvmState for KakarotSequencer { )) } + /// Returns the bytecode of the given address. For an EOA, the bytecode_len_ storage variable will return 0, + /// and the function will return an empty vector. For a contract account, the function will return the bytecode + /// stored in the bytecode_ storage variables. The function assumes that the bytecode is stored in 16 byte big-endian chunks. fn get_code_at(&mut self, evm_address: &Address) -> StateResult { let starknet_address = compute_starknet_address(evm_address); @@ -217,18 +228,20 @@ impl EvmState for KakarotSequencer { for chunk_index in 0..num_chunks { let key = get_storage_var_address("bytecode_", &[StarkFelt::from(chunk_index)])?; let code = (&mut self.0.state).get_storage_at(starknet_address.try_into()?, key)?; - bytecode.append(&mut felt_to_bytes(&code.into(), 16).to_vec()); + bytecode.append(&mut high_16_bytes_of_felt_to_bytes(&code.into(), 16).to_vec()); } let remainder = bytecode_len % 16; let key = get_storage_var_address("bytecode_", &[StarkFelt::from(num_chunks)])?; let code = (&mut self.0.state).get_storage_at(starknet_address.try_into()?, key)?; - bytecode.append(&mut felt_to_bytes(&code.into(), remainder as usize).to_vec()); + bytecode + .append(&mut high_16_bytes_of_felt_to_bytes(&code.into(), remainder as usize).to_vec()); Ok(Bytes::from(bytecode)) } /// Returns the balance of native tokens at the given address. + /// Makes use of the default StateReader implementation from Blockifier. fn get_balance_at(&mut self, evm_address: &Address) -> StateResult { let starknet_address = compute_starknet_address(evm_address); let (low, high) = (&mut self.0.state) diff --git a/crates/ef-testing/src/evm_sequencer/mod.rs b/crates/ef-testing/src/evm_sequencer/mod.rs index 3a781eea..d70f346d 100644 --- a/crates/ef-testing/src/evm_sequencer/mod.rs +++ b/crates/ef-testing/src/evm_sequencer/mod.rs @@ -20,6 +20,7 @@ use self::constants::{ KAKAROT_CLASS_HASH, KAKAROT_OWNER_ADDRESS, PROXY_CLASS, PROXY_CLASS_HASH, }; +/// Kakarot wrapper around a sequencer. pub(crate) struct KakarotSequencer(Sequencer); #[allow(dead_code)] @@ -29,6 +30,8 @@ impl KakarotSequencer { Self(sequencer) } + /// Initializes the sequencer state with the Kakarot contract, its storage, + /// declares all necessary classes and deploys the fee token contract. pub fn initialize(mut self) -> StateResult { let storage = vec![ ("Ownable_owner", *KAKAROT_OWNER_ADDRESS.0.key()), diff --git a/crates/ef-testing/src/evm_sequencer/types.rs b/crates/ef-testing/src/evm_sequencer/types.rs index ed13d9d7..2101ac29 100644 --- a/crates/ef-testing/src/evm_sequencer/types.rs +++ b/crates/ef-testing/src/evm_sequencer/types.rs @@ -6,6 +6,7 @@ use starknet_api::{ StarknetApiError, }; +/// A wrapper around a FieldElement in order to facilitate conversion. #[derive(Debug, Clone, Copy)] pub struct FeltSequencer(FieldElement); diff --git a/crates/ef-testing/src/evm_sequencer/utils.rs b/crates/ef-testing/src/evm_sequencer/utils.rs index 7e411aa7..b49f0aad 100644 --- a/crates/ef-testing/src/evm_sequencer/utils.rs +++ b/crates/ef-testing/src/evm_sequencer/utils.rs @@ -14,6 +14,7 @@ use starknet::{ }; use starknet_api::hash::StarkFelt; +/// Computes the Starknet address of a contract given its EVM address. pub fn compute_starknet_address(evm_address: &Address) -> FeltSequencer { let evm_address: FeltSequencer = (*evm_address).into(); let starknet_address = get_contract_address( @@ -25,6 +26,7 @@ pub fn compute_starknet_address(evm_address: &Address) -> FeltSequencer { starknet_address.into() } +/// Splits a byte array into 16-byte chunks and converts each chunk to a StarkFelt. pub(crate) fn split_bytecode_to_starkfelt(bytecode: &Bytes) -> Vec { bytecode .chunks(16) @@ -36,6 +38,7 @@ pub(crate) fn split_bytecode_to_starkfelt(bytecode: &Bytes) -> Vec { .collect() } +/// Split a U256 into low and high u128. pub(crate) fn split_u256(value: U256) -> [u128; 2] { [ (value & U256::from(u128::MAX)).try_into().unwrap(), // safe unwrap <= U128::MAX. @@ -43,15 +46,17 @@ pub(crate) fn split_u256(value: U256) -> [u128; 2] { ] } +/// Converts a byte array to a vector of FieldElement. pub fn bytes_to_felt_vec(bytes: &Bytes) -> Vec { bytes.to_vec().into_iter().map(FieldElement::from).collect() } -pub fn felt_to_bytes(felt: &FieldElement, len: usize) -> Bytes { +/// Converts the high 16 bytes of a FieldElement to a byte array. +pub fn high_16_bytes_of_felt_to_bytes(felt: &FieldElement, len: usize) -> Bytes { Bytes::from(&felt.to_bytes_be()[16..len + 16]) } -#[allow(dead_code)] +/// Converts an rlp encoding of an evm signed transaction to a Starknet transaction. pub(crate) fn to_broadcasted_starknet_transaction( bytes: &Bytes, ) -> Result { @@ -100,7 +105,7 @@ mod tests { let felt = FieldElement::from_hex_be("0x1234567890abcdef1234567890abcdef").unwrap(); // When - let bytes = felt_to_bytes(&felt, 16); + let bytes = high_16_bytes_of_felt_to_bytes(&felt, 16); // Then let expected = Bytes::from(vec![ @@ -116,7 +121,7 @@ mod tests { let felt = FieldElement::from_hex_be("0x12345678900000000000000000000000").unwrap(); // When - let bytes = felt_to_bytes(&felt, 5); + let bytes = high_16_bytes_of_felt_to_bytes(&felt, 5); // Then let expected = Bytes::from(vec![0x12, 0x34, 0x56, 0x78, 0x90]); @@ -129,7 +134,7 @@ mod tests { let felt = FieldElement::from_hex_be("0x12345678900000000000000000000000").unwrap(); // When - let bytes = felt_to_bytes(&felt, 0); + let bytes = high_16_bytes_of_felt_to_bytes(&felt, 0); // Then assert_eq!(bytes, Bytes::default()); diff --git a/crates/sequencer/src/commit.rs b/crates/sequencer/src/commit.rs index 84b1bdba..f50e2ed0 100644 --- a/crates/sequencer/src/commit.rs +++ b/crates/sequencer/src/commit.rs @@ -3,6 +3,9 @@ use blockifier::state::{ state_api::{State as BlockifierState, StateReader as BlockifierStateReader, StateResult}, }; +/// Generic trait for committing changes from a cached state to a state. +/// The default implementation allows for any type S for which a mutable reference +/// implements the `BlockifierState` and `BlockifierStateReader` traits to be used. pub trait Committer where for<'any> &'any mut S: BlockifierState + BlockifierStateReader, diff --git a/crates/sequencer/src/sequencer.rs b/crates/sequencer/src/sequencer.rs index f6255a03..87a19411 100644 --- a/crates/sequencer/src/sequencer.rs +++ b/crates/sequencer/src/sequencer.rs @@ -18,7 +18,7 @@ use tracing::{trace, warn}; /// speed, as the type of the state is known at compile time. /// We bound S such that a mutable reference to S (&'a mut S) /// must implement State and `StateReader`. The `for` keyword -/// indicates that the bound must hold for any lifetime 'a. +/// indicates that the bound must hold for any lifetime 'any. /// For more details, check out [rust-lang docs](https://doc.rust-lang.org/nomicon/hrtb.html) pub struct Sequencer where @@ -47,6 +47,9 @@ impl Execution for Sequencer where for<'any> &'any mut S: State + StateReader + Committer, { + /// Executes the provided transaction on the current state and leads to a commitment of the + /// cached state in the case of success. Reversion of the transaction leads to a discarding + /// of the cached state but still increments the nonce of the sender. fn execute(&mut self, transaction: Transaction) -> Result<(), TransactionExecutionError> { let sender_address = match &transaction { Transaction::AccountTransaction(tx) => match tx { @@ -80,7 +83,7 @@ where warn!( "Transaction execution reverted: {}", err.replace("\\n", "\n") - ) + ); } else { // If the transaction succeeded, we commit the state. <&mut S>::commit(&mut cached_state)?; diff --git a/crates/sequencer/src/state.rs b/crates/sequencer/src/state.rs index 27cced57..d386ce92 100644 --- a/crates/sequencer/src/state.rs +++ b/crates/sequencer/src/state.rs @@ -32,6 +32,7 @@ pub struct State { } impl State { + /// Helper function allowing to set the nonce of a contract. pub fn set_nonce(&mut self, contract_address: ContractAddress, nonce: Nonce) { self.nonces.insert(contract_address, nonce); } @@ -51,6 +52,9 @@ impl BlockifierState for &mut State { self.storage.insert((contract_address, key), value); } + /// # Errors + /// + /// If the nonce overflows. fn increment_nonce(&mut self, contract_address: ContractAddress) -> StateResult<()> { let current_nonce = self .nonces @@ -70,6 +74,9 @@ impl BlockifierState for &mut State { Ok(()) } + /// # Errors + /// + /// If the contract address is linked to a class hash. fn set_class_hash_at( &mut self, contract_address: ContractAddress, diff --git a/crates/sequencer/src/transaction.rs b/crates/sequencer/src/transaction.rs index f979cb40..48f7d7a6 100644 --- a/crates/sequencer/src/transaction.rs +++ b/crates/sequencer/src/transaction.rs @@ -14,6 +14,9 @@ use starknet_api::transaction::{ Calldata, Fee, InvokeTransactionV1, TransactionHash, TransactionSignature, }; +/// Wrapper around a Starknet-rs transaction. +/// Allows for conversion from a Starknet-rs +/// transaction to a Blockifier-rs transaction. #[derive(Debug)] pub struct StarknetTransaction(BroadcastedTransaction);