Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Commit 7eb270f

Browse files
committed
Merge branch 'develop' into proof-aggregation-circuit
2 parents c01fcfe + fcf0381 commit 7eb270f

File tree

36 files changed

+2434
-1662
lines changed

36 files changed

+2434
-1662
lines changed

bus-mapping/src/circuit_input_builder.rs

+41-49
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ use std::{
4848
collections::{BTreeMap, HashMap},
4949
iter,
5050
};
51-
pub use transaction::{Transaction, TransactionContext, TxL1Fee, TX_L1_FEE_PRECISION};
51+
pub use transaction::{
52+
Transaction, TransactionContext, TxL1Fee, TX_L1_COMMIT_EXTRA_COST, TX_L1_FEE_PRECISION,
53+
};
5254

5355
/// Circuit Setup Parameters
5456
#[derive(Debug, Clone, Copy)]
@@ -587,19 +589,18 @@ pub fn keccak_inputs(block: &Block, code_db: &CodeDB) -> Result<Vec<Vec<u8>>, Er
587589
let mut keccak_inputs = Vec::new();
588590
// Tx Circuit
589591
let txs: Vec<geth_types::Transaction> = block.txs.iter().map(|tx| tx.into()).collect();
590-
keccak_inputs.extend_from_slice(&keccak_inputs_tx_circuit(&txs, block.chain_id())?);
592+
keccak_inputs.extend_from_slice(&keccak_inputs_tx_circuit(&txs)?);
591593
log::debug!(
592594
"keccak total len after txs: {}",
593595
keccak_inputs.iter().map(|i| i.len()).sum::<usize>()
594596
);
595597
// PI circuit
596-
keccak_inputs.push(keccak_inputs_pi_circuit(
597-
block.chain_id(),
598+
keccak_inputs.extend(keccak_inputs_pi_circuit(
599+
block.chain_id,
598600
block.prev_state_root,
599601
block.withdraw_root,
600602
&block.headers,
601603
block.txs(),
602-
block.circuits_params.max_txs,
603604
));
604605
// Bytecode Circuit
605606
for _bytecode in code_db.0.values() {
@@ -645,10 +646,10 @@ pub fn keccak_inputs_sign_verify(sigs: &[SignData]) -> Vec<Vec<u8>> {
645646
inputs
646647
}
647648

648-
/// Generate a dummy tx in which
649-
/// (nonce=0, gas=0, gas_price=0, to=0, value=0, data="", chain_id)
649+
/// Generate a dummy pre-eip155 tx in which
650+
/// (nonce=0, gas=0, gas_price=0, to=0, value=0, data="")
650651
/// using the dummy private key = 1
651-
pub fn get_dummy_tx(chain_id: u64) -> (TransactionRequest, Signature) {
652+
pub fn get_dummy_tx() -> (TransactionRequest, Signature) {
652653
let mut sk_be_scalar = [0u8; 32];
653654
sk_be_scalar[31] = 1_u8;
654655

@@ -661,22 +662,28 @@ pub fn get_dummy_tx(chain_id: u64) -> (TransactionRequest, Signature) {
661662
.gas_price(U256::zero())
662663
.to(Address::zero())
663664
.value(U256::zero())
664-
.data(Bytes::default())
665-
.chain_id(chain_id);
665+
.data(Bytes::default());
666+
let sighash: H256 = keccak256(tx.rlp_unsigned()).into();
666667

667668
// FIXME: need to check if this is deterministic which means sig is fixed.
668-
let sig = wallet.sign_transaction_sync(&tx.clone().into());
669+
let sig = wallet.sign_hash(sighash);
670+
assert_eq!(sig.v, 28);
669671

670672
(tx, sig)
671673
}
672674

673675
/// Get the tx hash of the dummy tx (nonce=0, gas=0, gas_price=0, to=0, value=0,
674-
/// data="") for any chain_id
675-
pub fn get_dummy_tx_hash(chain_id: u64) -> H256 {
676-
let (tx, sig) = get_dummy_tx(chain_id);
676+
/// data="")
677+
pub fn get_dummy_tx_hash() -> H256 {
678+
let (tx, sig) = get_dummy_tx();
677679

678680
let tx_hash = keccak256(tx.rlp_signed(&sig));
679681

682+
assert_eq!(
683+
hex::encode(tx_hash),
684+
"137c41d53f2e633af81c75e938f6ccf7298ad6d2fa698b19a50545c1ae5b2b85"
685+
);
686+
680687
H256(tx_hash)
681688
}
682689

@@ -686,59 +693,43 @@ fn keccak_inputs_pi_circuit(
686693
withdraw_trie_root: Word,
687694
block_headers: &BTreeMap<u64, BlockHead>,
688695
transactions: &[Transaction],
689-
max_txs: usize,
690-
) -> Vec<u8> {
691-
let dummy_tx_hash = get_dummy_tx_hash(chain_id);
692-
693-
let result = iter::empty()
694-
// state roots
695-
.chain(prev_state_root.to_be_bytes())
696-
.chain(
697-
block_headers
698-
.last_key_value()
699-
.map(|(_, blk)| blk.eth_block.state_root)
700-
.unwrap_or(H256(prev_state_root.to_be_bytes()))
701-
.to_fixed_bytes(),
702-
)
703-
// withdraw trie root
704-
.chain(withdraw_trie_root.to_be_bytes())
696+
) -> Vec<Vec<u8>> {
697+
let data_bytes = iter::empty()
705698
.chain(block_headers.iter().flat_map(|(block_num, block)| {
706699
let num_txs = transactions
707700
.iter()
708701
.filter(|tx| tx.block_num == *block_num)
709702
.count() as u16;
710-
let parent_hash = block.eth_block.parent_hash;
711-
let block_hash = block.eth_block.hash.unwrap_or(H256::zero());
712-
let num_l1_msgs = 0_u16; // 0 for now
713703

714704
iter::empty()
715705
// Block Values
716-
.chain(block_hash.to_fixed_bytes())
717-
.chain(parent_hash.to_fixed_bytes()) // parent hash
718706
.chain(block.number.as_u64().to_be_bytes())
719707
.chain(block.timestamp.as_u64().to_be_bytes())
720708
.chain(block.base_fee.to_be_bytes())
721709
.chain(block.gas_limit.to_be_bytes())
722710
.chain(num_txs.to_be_bytes())
723-
.chain(num_l1_msgs.to_be_bytes())
724711
}))
725712
// Tx Hashes
726713
.chain(transactions.iter().flat_map(|tx| tx.hash.to_fixed_bytes()))
727-
.chain(
728-
(0..(max_txs - transactions.len()))
729-
.into_iter()
730-
.flat_map(|_| dummy_tx_hash.to_fixed_bytes()),
731-
)
714+
.collect::<Vec<u8>>();
715+
let data_hash = H256(keccak256(&data_bytes));
716+
let after_state_root = block_headers
717+
.last_key_value()
718+
.map(|(_, blk)| blk.eth_block.state_root)
719+
.unwrap_or(H256(prev_state_root.to_be_bytes()));
720+
let pi_bytes = iter::empty()
721+
.chain(chain_id.to_be_bytes())
722+
.chain(prev_state_root.to_be_bytes())
723+
.chain(after_state_root.to_fixed_bytes())
724+
.chain(withdraw_trie_root.to_be_bytes())
725+
.chain(data_hash.to_fixed_bytes())
732726
.collect::<Vec<u8>>();
733727

734-
result
728+
vec![data_bytes, pi_bytes]
735729
}
736730

737731
/// Generate the keccak inputs required by the Tx Circuit from the transactions.
738-
pub fn keccak_inputs_tx_circuit(
739-
txs: &[geth_types::Transaction],
740-
chain_id: u64,
741-
) -> Result<Vec<Vec<u8>>, Error> {
732+
pub fn keccak_inputs_tx_circuit(txs: &[geth_types::Transaction]) -> Result<Vec<Vec<u8>>, Error> {
742733
let mut inputs = Vec::new();
743734

744735
let hash_datas = txs
@@ -747,7 +738,7 @@ pub fn keccak_inputs_tx_circuit(
747738
.collect::<Vec<Vec<u8>>>();
748739
let dummy_hash_data = {
749740
// dummy tx is a legacy tx.
750-
let (dummy_tx, dummy_sig) = get_dummy_tx(chain_id);
741+
let (dummy_tx, dummy_sig) = get_dummy_tx();
751742
dummy_tx.rlp_signed(&dummy_sig).to_vec()
752743
};
753744
inputs.extend_from_slice(&hash_datas);
@@ -783,8 +774,9 @@ pub fn keccak_inputs_tx_circuit(
783774
// one that we use in get_dummy_tx, so we only need to include the tx sign
784775
// hash of the dummy tx.
785776
let dummy_sign_input = {
786-
let (dummy_tx, _) = get_dummy_tx(chain_id);
787-
dummy_tx.rlp().to_vec()
777+
let (dummy_tx, _) = get_dummy_tx();
778+
// dummy tx is of type pre-eip155
779+
dummy_tx.rlp_unsigned().to_vec()
788780
};
789781
inputs.push(dummy_sign_input);
790782

bus-mapping/src/circuit_input_builder/block.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
operation::{OperationContainer, RWCounter},
88
Error,
99
};
10-
use eth_types::{Address, Hash, ToWord, Word};
10+
use eth_types::{sign_types::SignData, Address, Hash, ToWord, Word};
1111
use std::collections::{BTreeMap, HashMap};
1212

1313
/// Context of a [`Block`] which can mutate in a [`Transaction`].
@@ -153,6 +153,8 @@ pub struct Block {
153153
pub code: HashMap<Hash, Vec<u8>>,
154154
/// Inputs to the SHA3 opcode
155155
pub sha3_inputs: Vec<Vec<u8>>,
156+
/// IO to/from the precompile Ecrecover calls.
157+
pub ecrecover_events: Vec<SignData>,
156158
/// Block-wise steps
157159
pub block_steps: BlockSteps,
158160
/// Exponentiation events in the block.
@@ -246,4 +248,8 @@ impl Block {
246248
pub fn add_exp_event(&mut self, event: ExpEvent) {
247249
self.exp_events.push(event);
248250
}
251+
/// Push an ecrecover event to the block.
252+
pub fn add_ecrecover_event(&mut self, event: SignData) {
253+
self.ecrecover_events.push(event);
254+
}
249255
}

bus-mapping/src/circuit_input_builder/transaction.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use super::{call::ReversionGroup, Call, CallContext, CallKind, CodeSource, ExecS
2020

2121
/// Precision of transaction L1 fee
2222
pub const TX_L1_FEE_PRECISION: u64 = 1_000_000_000;
23+
/// Extra cost as the bytes of rlped tx commited to L1 (assume to non-zero, overestimated a bit)
24+
pub const TX_L1_COMMIT_EXTRA_COST: u64 = 64;
2325

2426
#[derive(Debug, Default)]
2527
/// Context of a [`Transaction`] which can mutate in an [`ExecStep`].
@@ -460,7 +462,7 @@ impl TxL1Fee {
460462
/// Calculate L1 fee and remainder of transaction.
461463
pub fn tx_l1_fee(&self, tx_data_gas_cost: u64) -> (u64, u64) {
462464
// <https://github.com/scroll-tech/go-ethereum/blob/49192260a177f1b63fc5ea3b872fb904f396260c/rollup/fees/rollup_fee.go#L118>
463-
let tx_l1_gas = tx_data_gas_cost + self.fee_overhead;
465+
let tx_l1_gas = tx_data_gas_cost + self.fee_overhead + TX_L1_COMMIT_EXTRA_COST;
464466
let tx_l1_fee = self.fee_scalar as u128 * self.base_fee as u128 * tx_l1_gas as u128;
465467

466468
(

circuit-benchmarks/src/pi_circuit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ mod tests {
2525
use rand::SeedableRng;
2626
use rand_xorshift::XorShiftRng;
2727
use zkevm_circuits::{
28-
pi_circuit::{PiCircuit, PiTestCircuit},
28+
pi_circuit::{dev::PiTestCircuit, PiCircuit},
2929
util::SubCircuit,
3030
witness::{block_convert, Block},
3131
};

circuit-benchmarks/src/tx_circuit.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ mod tests {
2424
use rand::SeedableRng;
2525
use rand_chacha::ChaCha20Rng;
2626
use std::env::var;
27-
use zkevm_circuits::{tx_circuit::TxCircuit, util::SubCircuit, witness::block_convert};
27+
use zkevm_circuits::{
28+
tx_circuit::TestTxCircuit as TxCircuit, util::SubCircuit, witness::block_convert,
29+
};
2830

2931
use bus_mapping::rpc::GethClient;
3032
use ethers::providers::Http;

eth-types/src/geth_types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ impl Transaction {
333333
.try_into()
334334
.expect("hash length isn't 32 bytes");
335335
let v = self.tx_type.get_recovery_id(self.v);
336-
let pk = recover_pk(v, &self.r, &self.s, &msg_hash)?;
336+
let pk = recover_pk(v as u8, &self.r, &self.s, &msg_hash)?;
337337
// msg_hash = msg_hash % q
338338
let msg_hash = BigUint::from_bytes_be(msg_hash.as_slice());
339339
let msg_hash = msg_hash.mod_floor(&*SECP256K1_Q);
@@ -343,7 +343,7 @@ impl Transaction {
343343
libsecp256k1::Error::InvalidMessage,
344344
)?;
345345
Ok(SignData {
346-
signature: (sig_r, sig_s),
346+
signature: (sig_r, sig_s, v as u8),
347347
pk,
348348
msg,
349349
msg_hash,

eth-types/src/sign_types.rs

+34-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! secp256k1 signature types and helper functions.
22
3-
use crate::{ToBigEndian, Word};
4-
use ethers_core::types::Bytes;
3+
use crate::{ToBigEndian, ToWord, Word};
4+
use ethers_core::{
5+
types::{Address, Bytes},
6+
utils::keccak256,
7+
};
58
use halo2_proofs::{
69
arithmetic::{CurveAffine, FieldExt},
710
halo2curves::{
@@ -23,32 +26,33 @@ pub fn sign(
2326
randomness: secp256k1::Fq,
2427
sk: secp256k1::Fq,
2528
msg_hash: secp256k1::Fq,
26-
) -> (secp256k1::Fq, secp256k1::Fq) {
29+
) -> (secp256k1::Fq, secp256k1::Fq, u8) {
2730
let randomness_inv =
2831
Option::<secp256k1::Fq>::from(randomness.invert()).expect("cannot invert randomness");
2932
let generator = Secp256k1Affine::generator();
3033
let sig_point = generator * randomness;
34+
let sig_v: bool = sig_point.to_affine().y.is_odd().into();
35+
3136
let x = *Option::<Coordinates<_>>::from(sig_point.to_affine().coordinates())
3237
.expect("point is the identity")
3338
.x();
3439

35-
let x_repr = &mut vec![0u8; 32];
36-
x_repr.copy_from_slice(x.to_bytes().as_slice());
37-
3840
let mut x_bytes = [0u8; 64];
39-
x_bytes[..32].copy_from_slice(&x_repr[..]);
41+
x_bytes[..32].copy_from_slice(&x.to_bytes());
4042

4143
let sig_r = secp256k1::Fq::from_bytes_wide(&x_bytes); // get x cordinate (E::Base) on E::Scalar
44+
4245
let sig_s = randomness_inv * (msg_hash + sig_r * sk);
43-
(sig_r, sig_s)
46+
(sig_r, sig_s, u8::from(sig_v))
4447
}
4548

4649
/// Signature data required by the SignVerify Chip as input to verify a
4750
/// signature.
4851
#[derive(Clone, Debug)]
4952
pub struct SignData {
50-
/// Secp256k1 signature point
51-
pub signature: (secp256k1::Fq, secp256k1::Fq),
53+
/// Secp256k1 signature point (r, s, v)
54+
/// v must be 0 or 1
55+
pub signature: (secp256k1::Fq, secp256k1::Fq, u8),
5256
/// Secp256k1 public key
5357
pub pk: Secp256k1Affine,
5458
/// Message being hashed before signing.
@@ -57,7 +61,20 @@ pub struct SignData {
5761
pub msg_hash: secp256k1::Fq,
5862
}
5963

64+
impl SignData {
65+
/// Recover address of the signature
66+
pub fn get_addr(&self) -> Word {
67+
let pk_le = pk_bytes_le(&self.pk);
68+
let pk_be = pk_bytes_swap_endianness(&pk_le);
69+
let pk_hash = keccak256(pk_be);
70+
let mut addr_bytes = [0u8; 20];
71+
addr_bytes.copy_from_slice(&pk_hash[12..]);
72+
Address::from(addr_bytes).to_word()
73+
}
74+
}
75+
6076
lazy_static! {
77+
// FIXME: use Transaction::dummy().sign_data() instead when we merged the develop branch
6178
static ref SIGN_DATA_DEFAULT: SignData = {
6279
let generator = Secp256k1Affine::generator();
6380
let sk = secp256k1::Fq::one();
@@ -80,10 +97,10 @@ lazy_static! {
8097
.expect("hash length isn't 32 bytes");
8198
let msg_hash = secp256k1::Fq::from_bytes(&msg_hash).unwrap();
8299
let randomness = secp256k1::Fq::one();
83-
let (sig_r, sig_s) = sign(randomness, sk, msg_hash);
100+
let (sig_r, sig_s, v) = sign(randomness, sk, msg_hash);
84101

85102
SignData {
86-
signature: (sig_r, sig_s),
103+
signature: (sig_r, sig_s, v),
87104
pk,
88105
msg: msg.into(),
89106
msg_hash,
@@ -92,12 +109,12 @@ lazy_static! {
92109
}
93110

94111
impl Default for SignData {
112+
// Hardcoded valid signature corresponding to a hardcoded private key and
113+
// message hash generated from "nothing up my sleeve" values to make the
114+
// ECDSA chip pass the constraints, to be use for padding signature
115+
// verifications (where the constraints pass, but we don't care about the
116+
// message hash and public key).
95117
fn default() -> Self {
96-
// Hardcoded valid signature corresponding to a hardcoded private key and
97-
// message hash generated from "nothing up my sleeve" values to make the
98-
// ECDSA chip pass the constraints, to be use for padding signature
99-
// verifications (where the constraints pass, but we don't care about the
100-
// message hash and public key).
101118
SIGN_DATA_DEFAULT.clone()
102119
}
103120
}

integration-tests/tests/mainnet.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use zkevm_circuits::{
1515
rlp_circuit_fsm::RlpCircuit,
1616
state_circuit::StateCircuit,
1717
super_circuit::SuperCircuit,
18-
tx_circuit::TxCircuit,
18+
tx_circuit::TestTxCircuit as TxCircuit,
1919
util::{Challenges, SubCircuit},
2020
witness,
2121
witness::Transaction,

mock/src/transaction.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use ethers_core::{
1212
use ethers_signers::{LocalWallet, Signer};
1313
use lazy_static::lazy_static;
1414
use rand::SeedableRng;
15-
use rand_chacha::ChaCha20Rng;
15+
use rand_chacha::{rand_core::OsRng, ChaCha20Rng};
1616

1717
lazy_static! {
1818
/// Collection of correctly hashed and signed Transactions which can be used to test circuits or opcodes that have to check integrity of the Tx itself.
@@ -171,7 +171,8 @@ impl Default for MockTransaction {
171171
block_hash: Hash::zero(),
172172
block_number: U64::zero(),
173173
transaction_index: U64::zero(),
174-
from: AddrOrWallet::Addr(MOCK_ACCOUNTS[0]),
174+
//from: AddrOrWallet::Addr(MOCK_ACCOUNTS[0]),
175+
from: AddrOrWallet::random(&mut OsRng),
175176
to: None,
176177
value: Word::zero(),
177178
gas_price: *MOCK_GASPRICE,

0 commit comments

Comments
 (0)