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

docs: add rust doc #534

Merged
merged 1 commit into from
Oct 13, 2023
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
19 changes: 16 additions & 3 deletions crates/ef-testing/src/evm_sequencer/evm_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<U256> {
let keys = split_u256(key).map(Into::into);
let keys = get_uint256_storage_var_addresses("storage_", &keys).unwrap(); // safe unwrap: all vars are ASCII
Expand All @@ -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<U256> {
let starknet_address = compute_starknet_address(evm_address);

Expand Down Expand Up @@ -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<Bytes> {
let starknet_address = compute_starknet_address(evm_address);

Expand All @@ -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<U256> {
let starknet_address = compute_starknet_address(evm_address);
let (low, high) = (&mut self.0.state)
Expand Down
3 changes: 3 additions & 0 deletions crates/ef-testing/src/evm_sequencer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<State>);

#[allow(dead_code)]
Expand All @@ -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<Self> {
let storage = vec![
("Ownable_owner", *KAKAROT_OWNER_ADDRESS.0.key()),
Expand Down
1 change: 1 addition & 0 deletions crates/ef-testing/src/evm_sequencer/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
15 changes: 10 additions & 5 deletions crates/ef-testing/src/evm_sequencer/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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<StarkFelt> {
bytecode
.chunks(16)
Expand All @@ -36,22 +38,25 @@ pub(crate) fn split_bytecode_to_starkfelt(bytecode: &Bytes) -> Vec<StarkFelt> {
.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.
(value >> 128).try_into().unwrap(), // safe unwrap <= U128::MAX.
]
}

/// Converts a byte array to a vector of FieldElement.
pub fn bytes_to_felt_vec(bytes: &Bytes) -> Vec<FieldElement> {
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<BroadcastedInvokeTransaction, eyre::Error> {
Expand Down Expand Up @@ -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![
Expand All @@ -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]);
Expand All @@ -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());
Expand Down
3 changes: 3 additions & 0 deletions crates/sequencer/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S>
where
for<'any> &'any mut S: BlockifierState + BlockifierStateReader,
Expand Down
7 changes: 5 additions & 2 deletions crates/sequencer/src/sequencer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S>
where
Expand Down Expand Up @@ -47,6 +47,9 @@ impl<S> Execution for Sequencer<S>
where
for<'any> &'any mut S: State + StateReader + Committer<S>,
{
/// 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 {
Expand Down Expand Up @@ -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)?;
Expand Down
7 changes: 7 additions & 0 deletions crates/sequencer/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand All @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions crates/sequencer/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading