Skip to content

Commit

Permalink
fix: ssj can run again (#737)
Browse files Browse the repository at this point in the history
* fix: ssj can run again

* pr review
  • Loading branch information
enitrat authored Aug 21, 2024
1 parent c597413 commit 7139f02
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 136 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

.env
vscode
.idea/
broadcast
.DS_Store

Expand Down
2 changes: 1 addition & 1 deletion crates/ef-testing/src/evm_sequencer/account/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl KakarotAccount {
};

let jumdpests_storage_address = get_storage_var_address(ACCOUNT_VALID_JUMPDESTS, &[]);
let jumdpests_storage_address = *jumdpests_storage_address.0.key();
let jumdpests_storage_address = Felt::from(jumdpests_storage_address);
valid_jumpdests.into_iter().for_each(|index| {
storage.push((
(jumdpests_storage_address + Felt::from(index))
Expand Down
133 changes: 59 additions & 74 deletions crates/ef-testing/src/evm_sequencer/account/v1.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,28 @@
use blockifier::abi::abi_utils::get_storage_var_address;
use reth_primitives::{Address, Bytes, U256};
use blockifier::abi::sierra_types::next_storage_key;
use reth_primitives::{keccak256, Address, Bytes, KECCAK_EMPTY, U256};
use revm_interpreter::analysis::to_analysed;
use revm_primitives::Bytecode;
use starknet::core::types::Felt;
use starknet_api::{core::Nonce, state::StorageKey, StarknetApiError};

use super::KakarotAccount;
use super::{inner_byte_array_pointer, pack_byte_array_to_starkfelt_array};
use crate::evm_sequencer::constants::storage_variables::{
ACCOUNT_EVM_ADDRESS, ACCOUNT_IS_INITIALIZED, ACCOUNT_NONCE, ACCOUNT_STORAGE,
ACCOUNT_BYTECODE_LEN, ACCOUNT_CODE_HASH, ACCOUNT_EVM_ADDRESS, ACCOUNT_IS_INITIALIZED,
ACCOUNT_NONCE, ACCOUNT_STORAGE, ACCOUNT_VALID_JUMPDESTS,
};
use crate::evm_sequencer::{
constants::storage_variables::ACCOUNT_BYTECODE, types::felt::FeltSequencer, utils::split_u256,
};
use crate::{
evm_sequencer::evm_state::v1::{compute_storage_base_address, offset_storage_key},
starknet_storage,
};

/// The layout of a `ByteArray` in storage is as follows:
/// * Only the length in bytes is stored in the original address where the byte array is logically
/// stored.
/// * The actual data is stored in chunks of 256 `bytes31`s in another place in storage
/// determined by the hash of:
/// - The address storing the length of the array.
/// - The chunk index.
/// - The short string `ByteArray`.
fn prepare_bytearray_storage(code: &Bytes) -> Vec<(StorageKey, Felt)> {
let bytecode_base_address = get_storage_var_address(ACCOUNT_BYTECODE, &[]);
let mut bytearray = vec![(bytecode_base_address, Felt::from(code.len()))];

let bytecode_storage: Vec<_> = pack_byte_array_to_starkfelt_array(code)
.enumerate()
.map(|(index, b)| {
let offset = index % 256;
let index = index / 256;
let key = inner_byte_array_pointer(*bytecode_base_address.0.key(), index.into());
(
offset_storage_key(key.try_into().unwrap(), offset as i64),
b,
)
})
.collect();
bytearray.extend(bytecode_storage);

bytearray
}
use crate::starknet_storage;

impl KakarotAccount {
pub fn new(
evm_address: &Address,
code: &Bytes,
nonce: U256,
_balance: U256,
balance: U256,
evm_storage: &[(U256, U256)],
) -> Result<Self, StarknetApiError> {
let nonce = Felt::from(TryInto::<u128>::try_into(nonce).map_err(|err| {
Expand All @@ -66,25 +38,70 @@ impl KakarotAccount {
let mut storage = vec![
starknet_storage!(ACCOUNT_EVM_ADDRESS, evm_address),
starknet_storage!(ACCOUNT_IS_INITIALIZED, 1u8),
starknet_storage!(ACCOUNT_BYTECODE_LEN, code.len() as u32),
];

// Write the nonce of the account is written to storage after each tx.
storage.append(&mut vec![starknet_storage!(ACCOUNT_NONCE, nonce)]);

// Initialize the bytecode storage vars.
// Assumes that the bytecode is stored as a ByteArray type, following the Store<ByteArray> implementation of
// the cairo core library
let mut bytecode_storage = prepare_bytearray_storage(code);
// Initialize the bytecode storage var.
let mut bytecode_storage = pack_byte_array_to_starkfelt_array(code)
.enumerate()
.map(|(i, bytes)| (StorageKey::from(i as u32), bytes))
.collect();
storage.append(&mut bytecode_storage);

// Initialize the code hash var
let account_is_empty =
code.is_empty() && nonce == Felt::from(0) && balance == U256::from(0);
let code_hash = if account_is_empty {
U256::from(0)
} else if code.is_empty() {
U256::from_be_slice(KECCAK_EMPTY.as_slice())
} else {
U256::from_be_slice(keccak256(code).as_slice())
};

let code_hash_values = split_u256(code_hash);
let code_hash_low_key = get_storage_var_address(ACCOUNT_CODE_HASH, &[]);
let code_hash_high_key = next_storage_key(&code_hash_low_key)?;
storage.extend([
(code_hash_low_key, Felt::from(code_hash_values[0])),
(code_hash_high_key, Felt::from(code_hash_values[1])),
]);

// Initialize the bytecode jumpdests.
let bytecode = to_analysed(Bytecode::new_raw(code.clone()));
let valid_jumpdests: Vec<usize> = match bytecode {
Bytecode::LegacyAnalyzed(legacy_analyzed_bytecode) => legacy_analyzed_bytecode
.jump_table()
.0
.iter()
.enumerate()
.filter_map(|(index, bit)| bit.as_ref().then(|| index))
.collect(),
_ => unreachable!("Bytecode should be analysed"),
};

let jumdpests_storage_address = get_storage_var_address(ACCOUNT_VALID_JUMPDESTS, &[]);
let jumdpests_storage_address = Felt::from(jumdpests_storage_address);
valid_jumpdests.into_iter().for_each(|index| {
storage.push((
(jumdpests_storage_address + Felt::from(index))
.try_into()
.unwrap(),
Felt::ONE,
))
});

// Initialize the storage vars.
let mut evm_storage_storage: Vec<(StorageKey, Felt)> = evm_storage
.iter()
.flat_map(|(k, v)| {
let keys = split_u256(*k).map(Into::into);
let values = split_u256(*v).map(Into::<Felt>::into);
let low_key = compute_storage_base_address(ACCOUNT_STORAGE, &keys);
let high_key = offset_storage_key(low_key, 1);
let low_key = get_storage_var_address(ACCOUNT_STORAGE, &keys);
let high_key = next_storage_key(&low_key).unwrap(); // can fail only if low is the max key
vec![(low_key, values[0]), (high_key, values[1])]
})
.collect();
Expand All @@ -97,35 +114,3 @@ impl KakarotAccount {
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use starknet::core::types::Felt;

#[test]
fn test_prepare_bytearray_storage() {
// Given
let code = Bytes::from(vec![0x01, 0x02, 0x03, 0x04, 0x05]);
let bytecode_base_address = get_storage_var_address(ACCOUNT_BYTECODE, &[]);

// When
let result = prepare_bytearray_storage(&code);

// Then
let expected_result = vec![
(bytecode_base_address, Felt::from(code.len())),
(
offset_storage_key(
inner_byte_array_pointer(*bytecode_base_address.0.key(), Felt::ZERO)
.try_into()
.unwrap(),
0,
),
Felt::from(0x0102030405u64),
),
];

assert_eq!(result, expected_result);
}
}
8 changes: 4 additions & 4 deletions crates/ef-testing/src/evm_sequencer/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use lazy_static::lazy_static;
use reth_primitives::alloy_primitives::{address, Address};
use serde::de::DeserializeOwned;
use starknet::core::types::contract::CompiledClass;
use starknet_api::felt;
use starknet::signers::VerifyingKey;
use starknet::{core::types::contract::legacy::LegacyContractClass, signers::SigningKey};
use starknet_api::felt;
use starknet_api::{
contract_address,
core::{ClassHash, ContractAddress, PatriciaKey},
Expand Down Expand Up @@ -86,7 +86,7 @@ lazy_static! {
// Main contract classes v1
pub static ref KAKAROT_CLASS: CompiledClass = load_contract_class("../../build/v1/contracts_KakarotCore.compiled_contract_class.json").expect("Failed to load Kakarot contract class");
pub static ref ACCOUNT_CONTRACT_CLASS: CompiledClass = load_contract_class("../../build/v1/contracts_AccountContract.compiled_contract_class.json").expect("Failed to load ContractAccount contract class");
pub static ref UNINITIALIZED_ACCOUNT_CLASS: CompiledClass = load_contract_class("../../build/v1/contracts_UninitializedAccount.compiled_contract_class.json").expect("Failed to load uninitialized account c contract class");
pub static ref UNINITIALIZED_ACCOUNT_CLASS: CompiledClass = load_contract_class("../../build/v1/contracts_UninitializedAccount.compiled_contract_class.json").expect("Failed to load uninitialized account contract class");

// Main class hashes
pub static ref KAKAROT_CLASS_HASH: ClassHash = ClassHash(KAKAROT_CLASS.class_hash().unwrap());
Expand Down Expand Up @@ -116,7 +116,7 @@ pub mod storage_variables {
pub const ACCOUNT_NONCE: &str = "Account_nonce";
pub const ACCOUNT_KAKAROT_ADDRESS: &str = "Account_kakarot_address";
pub const ACCOUNT_IMPLEMENTATION: &str = "Account_implementation";
pub const ACCOUNT_VALID_JUMPDESTS : &str = "Account_valid_jumpdests";
pub const ACCOUNT_VALID_JUMPDESTS: &str = "Account_valid_jumpdests";
pub const ACCOUNT_PUBLIC_KEY: &str = "Account_public_key";
pub const ACCOUNT_CODE_HASH: &str = "Account_code_hash";

Expand Down Expand Up @@ -152,4 +152,4 @@ pub mod tests {
pub static ref TEST_CONTRACT_ADDRESS: Address =
Address::left_padding_from(&0xdeadbeefu64.to_be_bytes());
}
}
}
72 changes: 17 additions & 55 deletions crates/ef-testing/src/evm_sequencer/evm_state/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use starknet_api::{core::L2_ADDRESS_UPPER_BOUND, state::StorageKey};
use starknet_crypto::{poseidon_hash_many, Felt};

use super::Evm;
use crate::evm_sequencer::constants::storage_variables::ACCOUNT_BYTECODE_LEN;
use crate::evm_sequencer::utils::felt_to_bytes;
use crate::{
evm_sequencer::{
account::{inner_byte_array_pointer, KakarotAccount},
Expand Down Expand Up @@ -185,13 +187,8 @@ impl Evm for KakarotSequencer {
fn nonce_at(&mut self, evm_address: &Address) -> StateResult<U256> {
let starknet_address = self.compute_starknet_address(evm_address)?;

let nonce = self
.state_mut()
.get_storage_at(
starknet_address,
get_storage_var_address(ACCOUNT_NONCE, &[]),
)
.unwrap();
let key = get_storage_var_address(ACCOUNT_NONCE, &[]);
let nonce = self.state_mut().get_storage_at(starknet_address, key)?;

Ok(U256::from_be_bytes(nonce.to_bytes_be()))
}
Expand All @@ -203,47 +200,31 @@ impl Evm for KakarotSequencer {
fn code_at(&mut self, evm_address: &Address) -> StateResult<Bytes> {
// Get all storage addresses.
let starknet_address = self.compute_starknet_address(evm_address)?;
let bytecode_base_address = get_storage_var_address(ACCOUNT_BYTECODE, &[]);

// Handle early return.
let bytecode_len = self
.state_mut()
.get_storage_at(starknet_address, bytecode_base_address)?;
let bytecode_len = self.state_mut().get_storage_at(
starknet_address,
get_storage_var_address(ACCOUNT_BYTECODE_LEN, &[]),
)?;
let bytecode_len: u64 = bytecode_len.to_biguint().try_into()?;

if bytecode_len == 0 {
return Ok(Bytes::default());
}

// Bytecode is stored in chunks of 31 bytes. At bytecode_base_address,
// we store the number of chunks.
let (num_chunks, pending_word_len) = bytecode_len.div_rem(&31);
let mut bytecode: Vec<u8> = Vec::with_capacity(bytecode_len as usize * 31);
// Assumes that the bytecode is stored in 31 byte chunks.
let num_chunks = bytecode_len / 31;
let mut bytecode: Vec<u8> = Vec::with_capacity(bytecode_len as usize);

for chunk_index in 0..num_chunks {
let index = chunk_index / 256;
let offset = chunk_index % 256;
let storage_pointer =
inner_byte_array_pointer(*bytecode_base_address.0.key(), Felt::from(index));
let key = offset_storage_key(storage_pointer.try_into().unwrap(), offset as i64);
let key = StorageKey::from(chunk_index);
let code = self.state_mut().get_storage_at(starknet_address, key)?;
bytecode.append(&mut code.to_bytes_be()[1..].to_vec());
bytecode.append(&mut felt_to_bytes(&code, 1).to_vec());
}

if pending_word_len != 0 {
let storage_chunk_index = num_chunks / 256;
let offset_in_chunk = num_chunks % 256;
let storage_pointer = inner_byte_array_pointer(
*bytecode_base_address.0.key(),
storage_chunk_index.into(),
);
let key =
offset_storage_key(storage_pointer.try_into().unwrap(), offset_in_chunk as i64);

let pending_word = self.state_mut().get_storage_at(starknet_address, key)?;
bytecode
.append(&mut pending_word.to_bytes_be()[32 - pending_word_len as usize..].to_vec());
}
let remainder = bytecode_len % 31;
let key = StorageKey::from(num_chunks);
let code = self.state_mut().get_storage_at(starknet_address, key)?;
bytecode.append(&mut felt_to_bytes(&code, (32 - remainder) as usize).to_vec());

Ok(Bytes::from(bytecode))
}
Expand Down Expand Up @@ -321,11 +302,6 @@ pub(crate) fn compute_storage_base_address(storage_var_name: &str, keys: &[Felt]
key_floored.try_into().unwrap() // infallible
}

pub(crate) fn offset_storage_key(key: StorageKey, offset: i64) -> StorageKey {
let base_address = *key.0.key() + Felt::from(offset);
base_address.try_into().unwrap() // infallible
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -343,20 +319,6 @@ mod tests {
use reth_primitives::{sign_message, Signature, TransactionSigned, TxLegacy, B256};
use starknet::core::types::Felt;

#[test]
fn test_offset_storage_base_address() {
// Given
let base_address = StorageKey(Felt::from(0x0102030405060708u64).try_into().unwrap());
let offset = -1;

// When
let result = offset_storage_key(base_address, offset);

// Then
let expected = StorageKey(Felt::from(0x0102030405060707u64).try_into().unwrap());
assert!(result == expected);
}

#[test]
fn test_store_bytecode() {
// Given
Expand Down
6 changes: 4 additions & 2 deletions crates/ef-testing/src/evm_sequencer/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ pub fn to_broadcasted_starknet_transaction(
}
#[cfg(feature = "v1")]
{
vec![
let mut execute_calldata = vec![
Felt::ONE, // call_array_len
*KAKAROT_ADDRESS.0.key(), // CallArray to
selector!("eth_send_transaction"), // CallArray selector
calldata.len().into(), // CallArray data_len
]
];
execute_calldata.append(&mut calldata);
execute_calldata
}
#[cfg(not(any(feature = "v0", feature = "v1")))]
{
Expand Down

0 comments on commit 7139f02

Please sign in to comment.