Skip to content

Commit

Permalink
fix(era:cc:broadcast): properly increment nonce
Browse files Browse the repository at this point in the history
tests(era:cc:broadcast): add test for value, gas limit and nonce
refactor(era:cc): extract get/set nonce into method
  • Loading branch information
Karrq committed Jan 4, 2024
1 parent 8b58eb5 commit 1775058
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 40 deletions.
121 changes: 81 additions & 40 deletions crates/era-cheatcodes/src/cheatcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use zksync_utils::{h256_to_u256, u256_to_h256};
type EraDb<DB> = StorageView<ForkStorage<RevmDatabaseForEra<DB>>>;

// address(uint160(uint256(keccak256('hevm cheat code'))))
// 0x7109709ecfa91a80626ff3989d68f67f5b1dd12d
const CHEATCODE_ADDRESS: H160 = H160([
113, 9, 112, 158, 207, 169, 26, 128, 98, 111, 243, 152, 157, 104, 246, 127, 91, 29, 209, 45,
]);
Expand Down Expand Up @@ -197,7 +198,6 @@ struct BroadcastOpts {
original_caller: H160,
new_origin: H160,
depth: usize,
nonce: u64,
}

impl<S: DatabaseExt + Send, H: HistoryMode> DynTracer<EraDb<S>, SimpleMemory<H>>
Expand Down Expand Up @@ -303,7 +303,7 @@ impl<S: DatabaseExt + Send, H: HistoryMode> DynTracer<EraDb<S>, SimpleMemory<H>>
if let Opcode::FarCall(_call) = data.opcode.variant.opcode {
let current = state.vm_local_state.callstack.current;
if current.code_address != CHEATCODE_ADDRESS {
if let Some(broadcast) = &mut self.permanent_actions.broadcast {
if let Some(broadcast) = self.permanent_actions.broadcast.as_ref() {
let prev_cs = state
.vm_local_state
.callstack
Expand All @@ -319,6 +319,7 @@ impl<S: DatabaseExt + Send, H: HistoryMode> DynTracer<EraDb<S>, SimpleMemory<H>>
origin: broadcast.new_origin,
});

let new_origin = broadcast.new_origin;
let revm_db_for_era = storage
.borrow_mut()
.storage_handle
Expand All @@ -339,25 +340,37 @@ impl<S: DatabaseExt + Send, H: HistoryMode> DynTracer<EraDb<S>, SimpleMemory<H>>
FarCallABI::from_u256(src0.value)
};

let gas_limit = current.ergs_remaining;
let (nonce, _) = Self::get_nonce(new_origin, &mut storage.borrow_mut());

let tx = BroadcastableTransaction {
rpc,
transaction:
ethers::types::transaction::eip2718::TypedTransaction::Legacy(
TransactionRequest {
from: Some(broadcast.new_origin),
from: Some(new_origin),
to: Some(ethers::types::NameOrAddress::Address(
current.code_address,
)),
gas: None, //FIXME: call.gas_limit if set from script
//FIXME: set only if set manually by user
gas: Some(gas_limit.into()),
//FIXME: retrieve proper value
value: Some(farcall_abi.ergs_passed.into()),
data: Some(get_calldata(&state, &memory).into()),
nonce: Some(broadcast.nonce.into()),
nonce: Some(nonce.into()),
..Default::default()
},
),
};
tracing::debug!(?tx, "storing for broadcast");

self.broadcastable_transactions.push(tx);
broadcast.nonce += 1;
//FIXME: detect if this is a deployment and increase the other nonce too
self.set_nonce(
new_origin,
(Some(nonce + 1), None),
&mut storage.borrow_mut(),
);
}
}
return
Expand Down Expand Up @@ -726,10 +739,8 @@ impl CheatcodeTracer {
}
getNonce_0(getNonce_0Call { account }) => {
tracing::info!("👷 Getting nonce for {account:?}");
let mut storage = storage.borrow_mut();
let nonce_key = get_nonce_key(&account.to_h160());
let full_nonce = storage.read_value(&nonce_key);
let (account_nonce, _) = decompose_full_nonce(h256_to_u256(full_nonce));
let (account_nonce, _) =
Self::get_nonce(account.to_h160(), &mut storage.borrow_mut());
tracing::info!(
"👷 Nonces for account {:?} are {}",
account,
Expand Down Expand Up @@ -904,36 +915,21 @@ impl CheatcodeTracer {
}
setNonce(setNonceCall { account, newNonce: new_nonce }) => {
tracing::info!("👷 Setting nonce for {account:?} to {new_nonce}");
let mut storage = storage.borrow_mut();
let nonce_key = get_nonce_key(&account.to_h160());
let full_nonce = storage.read_value(&nonce_key);
let (mut account_nonce, mut deployment_nonce) =
decompose_full_nonce(h256_to_u256(full_nonce));
if account_nonce.as_u64() >= new_nonce {
tracing::error!(
"SetNonce cheatcode failed: Account nonce is already set to a higher value ({}, requested {})",
account_nonce,
new_nonce
);
return
}
account_nonce = new_nonce.into();
if deployment_nonce.as_u64() >= new_nonce {
tracing::error!(
"SetNonce cheatcode failed: Deployment nonce is already set to a higher value ({}, requested {})",
deployment_nonce,
new_nonce
);
return
}
deployment_nonce = new_nonce.into();
let enforced_full_nonce = nonces_to_full_nonce(account_nonce, deployment_nonce);
tracing::info!(
"👷 Nonces for account {:?} have been set to {}",
account,
new_nonce
let new_full_nonce = self.set_nonce(
account.to_h160(),
(Some(new_nonce.into()), Some(new_nonce.into())),
&mut storage.borrow_mut(),
);
self.write_storage(nonce_key, u256_to_h256(enforced_full_nonce), &mut storage);

if new_full_nonce.is_some() {
tracing::info!(
"👷 Nonces for account {:?} have been set to {}",
account,
new_nonce
);
} else {
tracing::error!("👷 Setting nonces failed")
}
}
startBroadcast_0(startBroadcast_0Call {}) => {
tracing::info!("👷 Starting broadcast with default origin");
Expand Down Expand Up @@ -1188,6 +1184,52 @@ impl CheatcodeTracer {
});
}

/// Returns a given account's nonce
///
/// The first item of the tuple represents the total number of transactions,
/// meanwhile the second represents the number of contract deployed
fn get_nonce<S: ReadStorage>(account: H160, storage: &mut RefMut<S>) -> (U256, U256) {
let key = get_nonce_key(&account);
let full_nonce = storage.read_value(&key);

return decompose_full_nonce(h256_to_u256(full_nonce))
}

/// Sets a given account's nonces
///
/// Returns the new nonce
fn set_nonce<S: WriteStorage>(
&mut self,
account: H160,
(tx_nonce, deploy_nonce): (Option<U256>, Option<U256>),
storage: &mut RefMut<S>,
) -> Option<(U256, U256)> {
let key = get_nonce_key(&account);
let (mut account_nonce, mut deployment_nonce) = Self::get_nonce(account, storage);
if let Some(tx_nonce) = tx_nonce {
if account_nonce >= tx_nonce {
tracing::error!(?account, value = ?account_nonce, requested = ?tx_nonce, "account nonce is already set to a higher value");
return None
}

account_nonce = tx_nonce;
}

if let Some(deploy_nonce) = deploy_nonce {
if deployment_nonce >= deploy_nonce {
tracing::error!(?account, value = ?deployment_nonce, requested = ?deploy_nonce, "deployment nonce is already set to a higher value");
return None
}

deployment_nonce = deploy_nonce;
}

let new_full_nonce = nonces_to_full_nonce(account_nonce, deployment_nonce);
self.write_storage(key, u256_to_h256(new_full_nonce), storage);

return Some((account_nonce, deployment_nonce))
}

fn add_trimmed_return_data(&mut self, data: &[u8]) {
let data_length = data.len();
let mut data: Vec<U256> = data
Expand Down Expand Up @@ -1336,7 +1378,6 @@ impl CheatcodeTracer {
original_origin: original_tx_origin.into(),
original_caller: state.vm_local_state.callstack.current.msg_sender.into(),
depth,
nonce,
})
}

Expand Down
52 changes: 52 additions & 0 deletions crates/era-cheatcodes/tests/src/cheatcodes/Broadcast.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ contract ATest is Test {
return b;
}

function pt(uint256 a) public payable returns (uint256) {
return t(a);
}

function inc() public returns (uint256) {
changed += 1;
}
Expand Down Expand Up @@ -62,4 +66,52 @@ contract BroadcastTest is Test {
);
require(success, "stopBroadcast failed");
}

function test_BroadcastValue() public {
(bool success, ) = Constants.CHEATCODE_ADDRESS.call(
abi.encodeWithSignature("startBroadcast(address)", ACCOUNT_A)
);
require(success, "startBroadcast failed");

ATest test = new ATest();
test.pt{ value: 42 }(16);

(success, ) = Constants.CHEATCODE_ADDRESS.call(
abi.encodeWithSignature("stopBroadcast()")
);
require(success, "stopBroadcast failed");
}

function test_BroadcastGasLimit() public {
(bool success, ) = Constants.CHEATCODE_ADDRESS.call(
abi.encodeWithSignature("startBroadcast()")
);
require(success, "startBroadcast failed");

ATest test = new ATest();
test.t{gas: 12345678}(12345678);

(success, ) = Constants.CHEATCODE_ADDRESS.call(
abi.encodeWithSignature("stopBroadcast()")
);
require(success, "stopBroadcast failed");
}

function test_BroadcastNonces() public {
(bool success, ) = Constants.CHEATCODE_ADDRESS.call(
abi.encodeWithSignature("startBroadcast(address)", ACCOUNT_B)
);
require(success, "startBroadcast failed");

ATest test = new ATest();
test.t(1);
test.t(2);
test.t(3);
test.t(4);

(success, ) = Constants.CHEATCODE_ADDRESS.call(
abi.encodeWithSignature("stopBroadcast()")
);
require(success, "stopBroadcast failed");
}
}

0 comments on commit 1775058

Please sign in to comment.