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

Commit

Permalink
wip: preliminary work sig circuit/lookp
Browse files Browse the repository at this point in the history
  • Loading branch information
roynalnaruto committed Sep 11, 2023
1 parent 83b4d3b commit 1b2a410
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 121 deletions.
33 changes: 15 additions & 18 deletions zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,6 @@ impl<F: Field> ExecutionGadget<F> for EcrecoverGadget<F> {
cb.keccak_rlc(sig_s.clone().map(|x| x.expr())),
);

cb.condition(recovered.expr(), |cb| {
// if address was recovered, the sig_v (recovery ID) was correct.
cb.require_zero(
"sig_v == 27 or 28",
(sig_v_keccak_rlc.expr() - 27.expr()) * (sig_v_keccak_rlc.expr() - 28.expr()),
);

// lookup to the sign_verify table
// || v | r | s | msg_hash | recovered_addr ||
cb.sig_table_lookup(
cb.word_rlc(msg_hash.clone().map(|x| x.expr())),
sig_v_keccak_rlc.expr() - 27.expr(),
cb.word_rlc(sig_r.clone().map(|x| x.expr())),
cb.word_rlc(sig_s.clone().map(|x| x.expr())),
from_bytes::expr(&recovered_addr_keccak_rlc.cells),
);
});

let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] =
[
CallContextFieldTag::IsSuccess,
Expand All @@ -122,6 +104,21 @@ impl<F: Field> ExecutionGadget<F> for EcrecoverGadget<F> {
cb.curr.state.gas_left.expr(),
);

// lookup to the sign_verify table
// || v | r | s | msg_hash | recovered_addr ||
cb.sig_table_lookup(
cb.word_rlc(msg_hash.clone().map(|x| x.expr())),
sig_v_keccak_rlc.expr() - 27.expr(),
cb.word_rlc(sig_r.clone().map(|x| x.expr())),
cb.word_rlc(sig_s.clone().map(|x| x.expr())),
select::expr(
recovered.expr(),
from_bytes::expr(&recovered_addr_keccak_rlc.cells),
0.expr(),
),
recovered.expr(),
);

cb.precompile_info_lookup(
cb.execution_state().as_u64().expr(),
callee_address.expr(),
Expand Down
3 changes: 3 additions & 0 deletions zkevm-circuits/src/evm_circuit/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ pub(crate) enum Lookup<F> {
sig_r_rlc: Expression<F>,
sig_s_rlc: Expression<F>,
recovered_addr: Expression<F>,
is_valid: Expression<F>,
},
ModExpTable {
base_limbs: [Expression<F>; 3],
Expand Down Expand Up @@ -481,13 +482,15 @@ impl<F: Field> Lookup<F> {
sig_r_rlc,
sig_s_rlc,
recovered_addr,
is_valid,
} => vec![
1.expr(), // q_enable
msg_hash_rlc.clone(),
sig_v.clone(),
sig_r_rlc.clone(),
sig_s_rlc.clone(),
recovered_addr.clone(),
is_valid.clone(),
],
Self::ModExpTable {
base_limbs,
Expand Down
2 changes: 2 additions & 0 deletions zkevm-circuits/src/evm_circuit/util/constraint_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> {
sig_r_rlc: Expression<F>,
sig_s_rlc: Expression<F>,
recovered_addr: Expression<F>,
is_valid: Expression<F>,
) {
self.add_lookup(
"sig table",
Expand All @@ -1388,6 +1389,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> {
sig_r_rlc: sig_r_rlc.expr(),
sig_s_rlc: sig_s_rlc.expr(),
recovered_addr: recovered_addr.expr(),
is_valid: is_valid.expr(),
},
);
}
Expand Down
90 changes: 37 additions & 53 deletions zkevm-circuits/src/sig_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ impl<F: Field> SigCircuit<F> {
ecdsa_chip: &FpChip<F>,
sign_data: &SignData,
) -> Result<AssignedECDSA<F, FpChip<F>>, Error> {
let gate = ecdsa_chip.gate();

log::trace!("start ecdsa assignment");
let SignData {
signature,
Expand All @@ -340,15 +342,12 @@ impl<F: Field> SigCircuit<F> {
msg_hash,
} = sign_data;
let (sig_r, sig_s, v) = signature;
log::trace!("v : {:?}", v);
log::trace!("sig_r : {:?}", sig_r);
log::trace!("sig_s : {:?}", sig_s);
log::trace!("msg_hash : {:?}", msg_hash);

// build ecc chip from Fp chip
let ecc_chip = EccChip::<F, FpChip<F>>::construct(ecdsa_chip.clone());
let pk_assigned = ecc_chip.load_private(ctx, (Value::known(pk.x), Value::known(pk.y)));
ecc_chip.assert_is_on_curve::<Secp256k1Affine>(ctx, &pk_assigned);
let pk_is_valid = ecc_chip.is_on_curve_or_infinity::<Secp256k1Affine>(ctx, &pk_assigned);
gate.assert_is_const(ctx, &pk_is_valid, F::one());

// build Fq chip from Fp chip
let fq_chip = FqChip::construct(ecdsa_chip.range.clone(), 88, 3, modulus::<Fq>());
Expand All @@ -358,8 +357,6 @@ impl<F: Field> SigCircuit<F> {
fq_chip.load_private(ctx, FqChip::<F>::fe_to_witness(&Value::known(*sig_s)));
let msg_hash =
fq_chip.load_private(ctx, FqChip::<F>::fe_to_witness(&Value::known(*msg_hash)));
//log::trace!("integer_r : {:?}", integer_r);
//log::trace!("integer_s : {:?}", integer_s);

// returns the verification result of ecdsa signature
//
Expand All @@ -375,7 +372,6 @@ impl<F: Field> SigCircuit<F> {
4,
4,
);
log::trace!("ECDSA res : {:?}", sig_is_valid);

// =======================================
// constrains v == y.is_oddness()
Expand All @@ -388,8 +384,6 @@ impl<F: Field> SigCircuit<F> {
// - tmp is also < 88 bits (this is crucial otherwise tmp may wrap around and break
// soundness)

let gate = ecdsa_chip.gate();

let assigned_y_is_odd = gate.load_witness(ctx, Value::known(F::from(*v as u64)));
gate.assert_bit(ctx, assigned_y_is_odd);

Expand Down Expand Up @@ -585,13 +579,12 @@ impl<F: Field> SigCircuit<F> {
.iter()
.map(|&x| QuantumCell::Witness(Value::known(F::from_u128(x as u128))))
.collect_vec();

let pk_y_le = sign_data
.pk
.y
.to_bytes()
.iter()
.map(|&x| QuantumCell::Witness(Value::known(F::from_u128(x as u128))))
.map(|&y| QuantumCell::Witness(Value::known(F::from_u128(y as u128))))
.collect_vec();
let pk_assigned = ecc_chip.load_private(
ctx,
Expand All @@ -606,7 +599,6 @@ impl<F: Field> SigCircuit<F> {
&powers_of_256_cells,
&None,
)?;

self.assert_crt_int_byte_repr(
ctx,
&ecdsa_chip.range,
Expand Down Expand Up @@ -770,13 +762,10 @@ impl<F: Field> SigCircuit<F> {
// ================================================
// step 1: assert the signature is valid in circuit
// ================================================

let assigned_ecdsas = signatures
.iter()
.chain(
std::iter::repeat(&SignData::default())
.take(self.max_verif - signatures.len()),
)
.chain(std::iter::repeat(&SignData::default()))
.take(self.max_verif)
.map(|sign_data| self.assign_ecdsa(&mut ctx, ecdsa_chip, sign_data))
.collect::<Result<Vec<AssignedECDSA<F, FpChip<F>>>, Error>>()?;

Expand All @@ -785,10 +774,8 @@ impl<F: Field> SigCircuit<F> {
// ================================================
let sign_data_decomposed = signatures
.iter()
.chain(
std::iter::repeat(&SignData::default())
.take(self.max_verif - signatures.len()),
)
.chain(std::iter::repeat(&SignData::default()))
.take(self.max_verif)
.zip_eq(assigned_ecdsas.iter())
.map(|(sign_data, assigned_ecdsa)| {
self.sign_data_decomposition(
Expand All @@ -814,37 +801,37 @@ impl<F: Field> SigCircuit<F> {
// ================================================
// step 3: compute RLC of keys and messages
// ================================================
let assigned_keccak_values_and_sigs =
signatures
.iter()
.chain(
std::iter::repeat(&SignData::default())
.take(self.max_verif - signatures.len()),
)
.zip_eq(assigned_ecdsas.iter())
.zip_eq(sign_data_decomposed.iter())
.map(|((sign_data, assigned_ecdsa), sign_data_decomp)| {
self.assign_sig_verify(
&mut ctx,
&ecdsa_chip.range,
sign_data,
sign_data_decomp,
challenges,
assigned_ecdsa,
)
})
.collect::<Result<
Vec<([AssignedValue<F>; 3], AssignedSignatureVerify<F>)>,
Error,
>>()?;
let (assigned_keccak_values, assigned_sig_values): (
Vec<[AssignedValue<F>; 3]>,
Vec<AssignedSignatureVerify<F>>,
) = signatures
.iter()
.chain(std::iter::repeat(&SignData::default()))
.take(self.max_verif)
.zip_eq(assigned_ecdsas.iter())
.zip_eq(sign_data_decomposed.iter())
.map(|((sign_data, assigned_ecdsa), sign_data_decomp)| {
self.assign_sig_verify(
&mut ctx,
&ecdsa_chip.range,
sign_data,
sign_data_decomp,
challenges,
assigned_ecdsa,
)
})
.collect::<Result<
Vec<([AssignedValue<F>; 3], AssignedSignatureVerify<F>)>,
Error,
>>()?
.into_iter()
.unzip();

// ================================================
// step 4: deferred keccak checks
// ================================================
for (i, [is_address_zero, pk_rlc, pk_hash_rlc]) in assigned_keccak_values_and_sigs
.iter()
.map(|a| &a.0)
.enumerate()
for (i, [is_address_zero, pk_rlc, pk_hash_rlc]) in
assigned_keccak_values.iter().enumerate()
{
let offset = i * 3;
self.enable_keccak_lookup(
Expand All @@ -865,10 +852,7 @@ impl<F: Field> SigCircuit<F> {
log::info!("total number of lookup cells: {}", lookup_cells);

ctx.print_stats(&["ECDSA context"]);
Ok(assigned_keccak_values_and_sigs
.iter()
.map(|a| a.1.clone())
.collect())
Ok(assigned_sig_values)
},
)?;

Expand Down
51 changes: 30 additions & 21 deletions zkevm-circuits/src/sig_circuit/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ where
);
let n = scalar_chip.load_constant(ctx, scalar_chip.p.to_biguint().unwrap());

// check whether the pubkey is (0, 0), i.e. in the case of ecrecover, no pubkey could be
// recovered.
let is_pubkey_not_zero = {
let is_pubkey_x_zero = ecc_chip.field_chip().is_zero(ctx, &pubkey.x);
let is_pubkey_y_zero = ecc_chip.field_chip().is_zero(ctx, &pubkey.y);
let is_pubkey_zero = ecc_chip.field_chip().range().gate().and(
ctx,
Existing(is_pubkey_x_zero),
Existing(is_pubkey_y_zero),
);
ecc_chip
.field_chip()
.range()
.gate()
.not(ctx, Existing(is_pubkey_zero))
};

// check r,s are in [1, n - 1]
let r_valid = scalar_chip.is_soft_nonzero(ctx, r);
let s_valid = scalar_chip.is_soft_nonzero(ctx, s);
Expand Down Expand Up @@ -138,27 +155,19 @@ where
);

// check (r in [1, n - 1]) and (s in [1, n - 1]) and (u1_mul != - u2_mul) and (r == x1 mod n)
let res1 = base_chip
.range
.gate()
.and(ctx, Existing(r_valid), Existing(s_valid));
let res2 = base_chip
.range
.gate()
.and(ctx, Existing(res1), Existing(u1_small));
let res3 = base_chip
.range
.gate()
.and(ctx, Existing(res2), Existing(u2_small));
let res4 = base_chip
.range
.gate()
.and(ctx, Existing(res3), Existing(u1_u2_not_eq));
let res5 = base_chip
.range
.gate()
.and(ctx, Existing(res4), Existing(equal_check));
(res5, sum.y)
let res = base_chip.range().gate().and_many(
ctx,
vec![
Existing(r_valid),
Existing(s_valid),
Existing(u1_small),
Existing(u2_small),
Existing(u1_u2_not_eq),
Existing(equal_check),
Existing(is_pubkey_not_zero),
],
);
(res, sum.y)
}

fn scalar_field_element_is_one<F: PrimeField, SF: PrimeField>(
Expand Down
Loading

0 comments on commit 1b2a410

Please sign in to comment.