Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
feat/#1665 Precompile ECRECOVER (#1720)
Browse files Browse the repository at this point in the history
closed #1665 

This PR was ported from 
- scroll-tech#529
- scroll-tech#930

which manly includes,
1. the main logic in ecrecover gadget (`ecrecover.rs`)
2. signature verifcation circuiit (`sig_circuit.rs`). What I did is to
change rlc to word lo/hi.
3. a new table, `sig_table.rs`
4. ecc circuit (`ecc_circuit.rs`). It's not be used in `ecRecover`, but
it was implemented in Scroll's PR. If I removed ecc_circuit, it would be
inconvienent for people porting `ecAdd`, `ecMul` or `ecPairing`. That's
why I keep it here.
5. dependencies update, using `halo2lib` (includes `halo2-base` and
`halo2-ecc`)

---------

Co-authored-by: Rohit Narurkar <[email protected]>
Co-authored-by: Zhang Zhuo <[email protected]>
  • Loading branch information
3 people authored Feb 19, 2024
1 parent db0d403 commit e19d163
Show file tree
Hide file tree
Showing 48 changed files with 5,143 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --release --all --all-features --exclude integration-tests --exclude circuit-benchmarks
args: --verbose --release --all --all-features --exclude integration-tests --exclude circuit-benchmarks -- --test-threads 24
- name: Run testool internal tests
uses: actions-rs/cargo@v1
with:
Expand Down
49 changes: 49 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ members = [
[patch.crates-io]
halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" }

[patch."https://github.com/scroll-tech/halo2.git"]
halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" }

[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"]
halo2curves = { version = "0.1.0", features = ["derive_serde"] }

# Definition of benchmarks profile to use.
[profile.bench]
opt-level = 3
Expand Down
2 changes: 2 additions & 0 deletions bus-mapping/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.gi
itertools = "0.10"
lazy_static = "1.4"
log = "0.4.14"
num = "0.4"
rand = { version = "0.8", optional = true }
serde = {version = "1.0.130", features = ["derive"] }
serde_json = "1.0.66"
strum = "0.24"
Expand Down
8 changes: 8 additions & 0 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use eth_types::{
use ethers_providers::JsonRpcClient;
pub use execution::{
CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash,
PrecompileEvent, PrecompileEvents, N_BYTES_PER_PAIR, N_PAIRING_PER_OP,
};
pub use input_state_ref::CircuitInputStateRef;
use itertools::Itertools;
Expand Down Expand Up @@ -112,6 +113,11 @@ pub struct FixedCParams {
/// calculated, so the same circuit will not be able to prove different
/// witnesses.
pub max_keccak_rows: usize,
/// This number indicate what 100% usage means, for example if we can support up to 2
/// ecPairing inside circuit, and max_vertical_circuit_rows is set to 1_000_000,
/// then if there is 1 ecPairing in the input, we will return 500_000 as the "row usage"
/// for the ec circuit.
pub max_vertical_circuit_rows: usize,
}

/// Unset Circuits Parameters
Expand Down Expand Up @@ -153,6 +159,7 @@ impl Default for FixedCParams {
max_bytecode: 512,
max_evm_rows: 0,
max_keccak_rows: 0,
max_vertical_circuit_rows: 0,
}
}
}
Expand Down Expand Up @@ -497,6 +504,7 @@ impl CircuitInputBuilder<DynamicCParams> {
max_bytecode,
max_evm_rows,
max_keccak_rows,
max_vertical_circuit_rows: 0,
}
};
let mut cib = CircuitInputBuilder::<FixedCParams> {
Expand Down
11 changes: 10 additions & 1 deletion bus-mapping/src/circuit_input_builder/block.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Block-related utility module

use super::{
execution::ExecState, transaction::Transaction, CopyEvent, ExecStep, ExpEvent, Withdrawal,
execution::ExecState, transaction::Transaction, CopyEvent, ExecStep, ExpEvent, PrecompileEvent,
PrecompileEvents, Withdrawal,
};
use crate::{
operation::{OperationContainer, RWCounter},
Expand Down Expand Up @@ -87,6 +88,8 @@ pub struct Block {
pub sha3_inputs: Vec<Vec<u8>>,
/// Exponentiation events in the block.
pub exp_events: Vec<ExpEvent>,
/// IO to/from the precompiled contract calls.
pub precompile_events: PrecompileEvents,
/// Original block from geth
pub eth_block: eth_types::Block<eth_types::Transaction>,
}
Expand Down Expand Up @@ -145,6 +148,7 @@ impl Block {
copy_events: Vec::new(),
exp_events: Vec::new(),
sha3_inputs: Vec::new(),
precompile_events: PrecompileEvents { events: Vec::new() },
eth_block: eth_block.clone(),
})
}
Expand Down Expand Up @@ -191,4 +195,9 @@ impl Block {
pub fn add_exp_event(&mut self, event: ExpEvent) {
self.exp_events.push(event);
}

/// Push a precompile event to the block.
pub fn add_precompile_event(&mut self, event: PrecompileEvent) {
self.precompile_events.events.push(event);
}
}
48 changes: 46 additions & 2 deletions bus-mapping/src/circuit_input_builder/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::{
error::{ExecError, OogError},
exec_trace::OperationRef,
operation::RWCounter,
precompile::PrecompileCalls,
precompile::{PrecompileAuxData, PrecompileCalls},
};
use eth_types::{evm_types::OpcodeId, GethExecStep, Word, H256};
use eth_types::{evm_types::OpcodeId, sign_types::SignData, GethExecStep, Word, H256};
use gadgets::impl_expr;
use halo2_proofs::plonk::Expression;
use strum_macros::EnumIter;
Expand Down Expand Up @@ -49,6 +49,8 @@ pub struct ExecStep {
pub copy_rw_counter_delta: u64,
/// Error generated by this step
pub error: Option<ExecError>,
/// Optional auxiliary data that is attached to precompile call internal states.
pub aux_data: Option<PrecompileAuxData>,
}

impl ExecStep {
Expand Down Expand Up @@ -77,6 +79,7 @@ impl ExecStep {
bus_mapping_instance: Vec::new(),
copy_rw_counter_delta: 0,
error: None,
aux_data: None,
}
}

Expand Down Expand Up @@ -364,3 +367,44 @@ impl Default for ExpEvent {
}
}
}

/// I/Os from all precompiled contract calls in a block.
#[derive(Clone, Debug, Default)]
pub struct PrecompileEvents {
/// All events.
pub events: Vec<PrecompileEvent>,
}

impl PrecompileEvents {
/// Get all ecrecover events.
pub fn get_ecrecover_events(&self) -> Vec<SignData> {
self.events
.iter()
.map(|e| {
let PrecompileEvent::Ecrecover(sign_data) = e;
sign_data
})
.cloned()
.collect()
}
}

/// I/O from a precompiled contract call.
#[derive(Clone, Debug)]
pub enum PrecompileEvent {
/// Represents the I/O from Ecrecover call.
Ecrecover(SignData),
}

impl Default for PrecompileEvent {
fn default() -> Self {
Self::Ecrecover(SignData::default())
}
}

/// The number of pairing inputs per pairing operation. If the inputs provided to the precompile
/// call are < 4, we append (G1::infinity, G2::generator) until we have the required no. of inputs.
pub const N_PAIRING_PER_OP: usize = 4;

/// The number of bytes taken to represent a pair (G1, G2).
pub const N_BYTES_PER_PAIR: usize = 192;
8 changes: 6 additions & 2 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use super::{
get_call_memory_offset_length, get_create_init_code, Block, BlockContext, Call, CallContext,
CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, Transaction,
CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, PrecompileEvent, Transaction,
TransactionContext,
};
use crate::{
Expand Down Expand Up @@ -1404,6 +1404,11 @@ impl<'a> CircuitInputStateRef<'a> {
self.block.add_exp_event(event)
}

/// Push an event representing auxiliary data for a precompile call to the state.
pub fn push_precompile_event(&mut self, event: PrecompileEvent) {
self.block.add_precompile_event(event)
}

pub(crate) fn get_step_err(
&self,
step: &GethExecStep,
Expand Down Expand Up @@ -1614,7 +1619,6 @@ impl<'a> CircuitInputStateRef<'a> {
PrecompileCalls::Sha256
| PrecompileCalls::Ripemd160
| PrecompileCalls::Blake2F
| PrecompileCalls::ECRecover
| PrecompileCalls::Bn128Add
| PrecompileCalls::Bn128Mul
| PrecompileCalls::Bn128Pairing
Expand Down
12 changes: 11 additions & 1 deletion bus-mapping/src/circuit_input_builder/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,24 @@ pub struct Transaction {
/// The transaction id
pub id: u64,
/// The raw transaction fields
tx: geth_types::Transaction,
pub tx: geth_types::Transaction,
/// Calls made in the transaction
pub(crate) calls: Vec<Call>,
/// Execution steps
steps: Vec<ExecStep>,
}

impl Transaction {
/// Create a dummy Transaction with zero values
pub fn dummy() -> Self {
Self {
id: 0,
calls: Vec::new(),
steps: Vec::new(),
tx: geth_types::Transaction::dummy(),
}
}

/// Create a new Self.
pub fn new(
id: u64,
Expand Down
24 changes: 18 additions & 6 deletions bus-mapping/src/evm/opcodes/callop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {

// insert a copy event (input) for this step and generate memory op
let rw_counter_start = state.block_ctx.rwc;
if call.call_data_length > 0 {
let input_bytes = if call.call_data_length > 0 {
let n_input_bytes = if let Some(input_len) = precompile_call.input_len() {
min(input_len, call.call_data_length as usize)
} else {
Expand Down Expand Up @@ -390,11 +390,14 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
bytes: input_bytes.iter().map(|s| (*s, false)).collect(),
},
);
}
Some(input_bytes)
} else {
None
};

// write the result in the callee's memory
let rw_counter_start = state.block_ctx.rwc;
if call.is_success && !result.is_empty() {
let output_bytes = if call.is_success && !result.is_empty() {
let (output_bytes, _prev_bytes) = state
.gen_copy_steps_for_precompile_callee_memory(&mut exec_step, &result)?;

Expand All @@ -413,11 +416,14 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
bytes: output_bytes.iter().map(|s| (*s, false)).collect(),
},
);
}
Some(output_bytes)
} else {
None
};

// insert another copy event (output) for this step.
let rw_counter_start = state.block_ctx.rwc;
if call.is_success && length > 0 {
let return_bytes = if call.is_success && length > 0 {
let return_bytes = state.gen_copy_steps_for_precompile_returndata(
&mut exec_step,
call.return_data_offset,
Expand All @@ -439,7 +445,10 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
bytes: return_bytes.iter().map(|s| (*s, false)).collect(),
},
);
}
Some(return_bytes)
} else {
None
};

if has_oog_err {
let mut oog_step = ErrorOOGPrecompile::gen_associated_ops(
Expand All @@ -462,6 +471,9 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
geth_steps[1].clone(),
call.clone(),
precompile_call,
&input_bytes.unwrap_or_default(),
&output_bytes.unwrap_or_default(),
&return_bytes.unwrap_or_default(),
)?;

// Set gas left and gas cost for precompile step.
Expand Down
Loading

0 comments on commit e19d163

Please sign in to comment.