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

fix 2930 signature & add tests #1199

Merged
merged 12 commits into from
Apr 10, 2024
67 changes: 64 additions & 3 deletions mock/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use eth_types::{
};
use ethers_core::{
rand::{CryptoRng, RngCore},
types::{Eip1559TransactionRequest, OtherFields, TransactionRequest},
types::{
Eip1559TransactionRequest, Eip2930TransactionRequest, OtherFields, TransactionRequest,
},
};
use ethers_signers::{LocalWallet, Signer};
use rand::SeedableRng;
Expand Down Expand Up @@ -333,8 +335,9 @@ impl MockTransaction {
pub fn build(&mut self) -> Self {
if self.transaction_type == U64::from(2) {
return self.build_1559();
} else if self.transaction_type == U64::from(1) {
return self.build_2930();
}
// TODO: handle eip2930 type later when add eip2930 tests.

let tx = TransactionRequest::new()
.from(self.from.address())
Expand Down Expand Up @@ -384,7 +387,7 @@ impl MockTransaction {
self.to_owned()
}

/// build 1559 type tx
/// build eip 1559 type tx
pub fn build_1559(&mut self) -> Self {
let tx = Eip1559TransactionRequest::new()
.from(self.from.address())
Expand Down Expand Up @@ -438,6 +441,64 @@ impl MockTransaction {
self.to_owned()
}

/// build eip 2930 type tx
pub fn build_2930(&mut self) -> Self {
let legacy_tx = TransactionRequest::new()
.from(self.from.address())
.nonce(self.nonce)
.value(self.value)
.data(self.input.clone())
.gas(self.gas)
.chain_id(self.chain_id);

let legacy_tx = if let Some(gas_price) = self.gas_price {
legacy_tx.gas_price(gas_price)
} else {
legacy_tx
};
let legacy_tx = if let Some(to_addr) = self.to.clone() {
legacy_tx.to(to_addr.address())
} else {
legacy_tx
};

let tx = Eip2930TransactionRequest::new(legacy_tx, self.access_list.clone());

match (self.v, self.r, self.s) {
(None, None, None) => {
// Compute sig params and set them in case we have a wallet as `from` attr.
if self.from.is_wallet() && self.hash.is_none() {
let mut sig = self
.from
.as_wallet()
.with_chain_id(self.chain_id)
.sign_transaction_sync(&tx.into())
.expect("sign mock eip 2930 tx");

// helper `sign_transaction_sync` in ethers-rs lib does not handle correctly
// about v for non legacy tx, here correct it for 2930 type.
sig.v = Self::normalize_v(sig.v, self.chain_id); // convert v to [0, 1]

self.sig_data((sig.v, sig.r, sig.s));
} else {
#[cfg(feature = "scroll")]
panic!("2930 type tx must have signature data, otherwise will be treated as L1Msg type in trace.go of l2geth");
}
}
_ => panic!("Either all or none of the SigData params have to be set"),
}

// Compute tx hash in case is not already set
if self.hash.is_none() {
let tmp_tx = Transaction::from(self.to_owned());
// FIXME: Note that tmp_tx does not have sigs if self.from.is_wallet() = false.
// This means tmp_tx.hash() is not correct.
self.hash(tmp_tx.hash());
}

self.to_owned()
}

// helper `sign_transaction_sync` in ethers-rs lib compute V using legacy tx pattern(V =
// recover_id + 2 * chain_id + 35), this method converts above V value to origin recover_id.
pub(crate) fn normalize_v(v: u64, chain_id: u64) -> u64 {
Expand Down
9 changes: 5 additions & 4 deletions zkevm-circuits/src/copy_circuit/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ use bus_mapping::{
use eth_types::{
address, bytecode, geth_types::GethData, word, AccessList, AccessListItem, ToWord, Word, H256,
};
use ethers_signers::Signer;
use halo2_proofs::{
dev::{MockProver, VerifyFailure},
halo2curves::bn256::Fr,
};
use mock::{
eth, gwei, test_ctx::helpers::account_0_code_account_1_no_code, MockTransaction, TestContext,
MOCK_ACCOUNTS,
MOCK_ACCOUNTS, MOCK_WALLETS,
};

const K: u32 = 20;
Expand Down Expand Up @@ -282,12 +283,12 @@ fn gen_access_list_data() -> CircuitInputBuilder {
let test_ctx = TestContext::<1, 1>::new(
None,
|accs| {
accs[0].address(MOCK_ACCOUNTS[0]).balance(eth(20));
accs[0].address(MOCK_WALLETS[0].address()).balance(eth(20));
},
|mut txs, _accs| {
txs[0]
.from(MOCK_ACCOUNTS[0])
.to(MOCK_ACCOUNTS[1])
.from(MOCK_WALLETS[0].clone())
.to(MOCK_ACCOUNTS[0])
.gas_price(gwei(2))
.gas(Word::from(0x10000))
.value(eth(2))
Expand Down
100 changes: 100 additions & 0 deletions zkevm-circuits/src/evm_circuit/util/common_gadget/tx_access_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,103 @@ impl<F: Field> TxAccessListGadget<F> {
address_len + storage_key_len
}
}

// tests for eip2930
#[cfg(test)]
mod test {
use crate::test_util::CircuitTestBuilder;
use eth_types::{address, AccessList, AccessListItem, Error, Word, H256};
use ethers_signers::Signer;
use mock::{eth, gwei, TestContext, MOCK_ACCOUNTS, MOCK_WALLETS};

// test with empty access list.
#[test]
fn test_eip2930_tx_for_empty_access_list() {
// CASE1: tx not set access list, `access_list` field is none.
let ctx = build_ctx(gwei(80_000), None).unwrap();
CircuitTestBuilder::new_from_test_ctx(ctx).run();

// CASE2: tx set empty (neither address nor storage keys at all) access list into
// `access_list` field. this field is not none.
let test_access_list: AccessList = AccessList(vec![]);

let ctx = build_ctx(gwei(80_000), Some(test_access_list)).unwrap();
CircuitTestBuilder::new_from_test_ctx(ctx).run();
}

// test with non empty access list(address + storage keys list)
#[test]
fn test_eip2930_non_empty_access_list() {
let test_access_list: AccessList = AccessList(vec![
AccessListItem {
address: address!("0xEeFca179F40D3B8b3D941E6A13e48835a3aF8241"),
// one storage key
storage_keys: [10].map(H256::from_low_u64_be).to_vec(),
},
AccessListItem {
address: address!("0x0000000000000000000000000000000000001111"),
// two storage keys
storage_keys: [10, 11].map(H256::from_low_u64_be).to_vec(),
},
AccessListItem {
address: address!("0x0000000000000000000000000000000000002222"),
// three storage keys
storage_keys: [20, 22, 50].map(H256::from_low_u64_be).to_vec(),
},
]);

let ctx = build_ctx(gwei(80_000), Some(test_access_list)).unwrap();
CircuitTestBuilder::new_from_test_ctx(ctx).run();
}

// test with non empty access list(only address list)
#[test]
fn test_eip2930_only_address_access_list() {
let test_access_list: AccessList = AccessList(vec![
AccessListItem {
address: address!("0xEeFca179F40D3B8b3D941E6A13e48835a3aF8241"),
// no storage keys
storage_keys: Vec::new(),
},
AccessListItem {
address: address!("0x0000000000000000000000000000000000001111"),
// no storage keys
storage_keys: Vec::new(),
},
]);

let ctx = build_ctx(gwei(80_000), Some(test_access_list)).unwrap();
CircuitTestBuilder::new_from_test_ctx(ctx).run();
}

// TODO: check if other types' test required.

fn build_ctx(
sender_balance: Word,
access_list: Option<AccessList>,
) -> Result<TestContext<2, 1>, Error> {
TestContext::new(
None,
|accs| {
accs[0]
.address(MOCK_WALLETS[0].address())
.balance(sender_balance);
accs[1].address(MOCK_ACCOUNTS[0]).balance(eth(1));
},
|mut txs, _accs| {
txs[0]
.from(MOCK_WALLETS[0].clone())
.to(MOCK_ACCOUNTS[0])
.gas(40_000.into())
.gas_price(30_000.into())
.value(gwei(20_000))
.transaction_type(1); // Set tx type to EIP-2930.

if let Some(acc_list) = access_list {
txs[0].access_list(acc_list);
}
},
|block, _tx| block.number(0xcafeu64),
)
}
}
Loading