Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/pr_main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
path: |
executor/program_artifacts/rust
executor/shared_target
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'crypto/ethrex-crypto/**', 'Makefile') }}
restore-keys: |
rust-elf-artifacts-

Expand Down Expand Up @@ -213,7 +213,7 @@ jobs:
path: |
executor/program_artifacts/rust
executor/shared_target
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'crypto/ethrex-crypto/**', 'Makefile') }}
restore-keys: |
rust-elf-artifacts-

Expand Down Expand Up @@ -329,7 +329,7 @@ jobs:
path: |
executor/program_artifacts/rust
executor/shared_target
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'crypto/ethrex-crypto/**', 'Makefile') }}
restore-keys: |
rust-elf-artifacts-

Expand Down Expand Up @@ -409,7 +409,7 @@ jobs:
path: |
executor/program_artifacts/rust
executor/shared_target
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'crypto/ethrex-crypto/**', 'Makefile') }}
restore-keys: |
rust-elf-artifacts-

Expand Down Expand Up @@ -493,7 +493,7 @@ jobs:
path: |
executor/program_artifacts/rust
executor/shared_target
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'crypto/ethrex-crypto/**', 'Makefile') }}
restore-keys: |
rust-elf-artifacts-

Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

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

17 changes: 13 additions & 4 deletions bin/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ fn cmd_execute(
// below (the flamegraph path drives execution inside the executor and does
// not expose per-log data). `None` means "not counted", so the accel lines
// are omitted rather than printed as misleading zeros.
let mut accel_counts: Option<(u64, u64)> = None;
let mut accel_counts: Option<(u64, u64, u64)> = None;

let cycle_count = if let Some(ref output_path) = flamegraph.path {
// Shared execute+flamegraph path (executor::flamegraph) instead of
Expand Down Expand Up @@ -480,6 +480,7 @@ fn cmd_execute(
let mut cycle_count: u64 = 0;
let mut keccak_calls: u64 = 0;
let mut ecsm_calls: u64 = 0;
let mut ecsm_lincomb2_calls: u64 = 0;
// Reused per chunk: `(current_pc, a7)` for logs whose a7 matches an
// accelerator syscall number. This is a cheap superset — a non-ECALL
// instruction can hold the same value in src1 — that `accelerator_of`
Expand Down Expand Up @@ -512,6 +513,7 @@ fn cmd_execute(
match accelerator_of(executor.instructions.get(pc), a7) {
Some(Accelerator::Keccak) => keccak_calls += 1,
Some(Accelerator::Ecsm) => ecsm_calls += 1,
Some(Accelerator::EcsmLincomb2) => ecsm_lincomb2_calls += 1,
None => {}
}
}
Expand All @@ -526,16 +528,17 @@ fn cmd_execute(
}

if cycles {
accel_counts = Some((keccak_calls, ecsm_calls));
accel_counts = Some((keccak_calls, ecsm_calls, ecsm_lincomb2_calls));
}
cycle_count
};

if cycles {
println!("Cycles: {}", cycle_count);
if let Some((keccak_calls, ecsm_calls)) = accel_counts {
if let Some((keccak_calls, ecsm_calls, ecsm_lincomb2_calls)) = accel_counts {
println!("Keccak calls: {}", keccak_calls);
println!("Ecsm calls: {}", ecsm_calls);
println!("Ecsm lincomb2 calls: {}", ecsm_lincomb2_calls);
}
}

Expand Down Expand Up @@ -1097,7 +1100,9 @@ mod tests {
// non-ECALL whose src1 collides with an accelerator number, and a cache miss.
#[test]
fn accelerator_of_mirrors_prover_classification() {
use executor::vm::instruction::execution::{ECSM_SYSCALL_NUMBER, KECCAK_SYSCALL_NUMBER};
use executor::vm::instruction::execution::{
ECSM_LINCOMB2_SYSCALL_NUMBER, ECSM_SYSCALL_NUMBER, KECCAK_SYSCALL_NUMBER,
};

let ecall = Instruction::EcallEbreak;

Expand All @@ -1109,6 +1114,10 @@ mod tests {
accelerator_of(Some(&ecall), ECSM_SYSCALL_NUMBER),
Some(Accelerator::Ecsm)
);
assert_eq!(
accelerator_of(Some(&ecall), ECSM_LINCOMB2_SYSCALL_NUMBER),
Some(Accelerator::EcsmLincomb2)
);

// Non-accelerator syscalls (Commit=64, Halt=93) count as neither.
assert_eq!(
Expand Down
5 changes: 5 additions & 0 deletions crypto/ecsm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ num-traits = "0.2.19"
# constraint system). Used for executor scalar multiplication and for the projective
# double-and-add replay + batch inversion that builds ECDAS step witnesses efficiently.
k256 = { version = "0.13", default-features = false, features = ["arithmetic", "expose-field"] }

[dev-dependencies]
# Test-only: re-derives the NUMS blinding point T₀ (SHA-256 try-and-increment)
# to prove the pinned constant in `witness.rs` is reproducible from its tag.
sha2 = { version = "0.10", default-features = false }
4 changes: 2 additions & 2 deletions crypto/ecsm/src/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ fn biguint_from_fe(f: &FieldElement) -> BigUint {
BigUint::from_bytes_be(&f.to_bytes())
}

fn to_k256_affine(a: &AffinePoint) -> K256Affine {
pub(crate) fn to_k256_affine(a: &AffinePoint) -> K256Affine {
let ep = EncodedPoint::from_affine_coordinates(&be32(&a.x).into(), &be32(&a.y).into(), false);
Option::from(K256Affine::from_encoded_point(&ep)).expect("ECSM: point must be on the curve")
}

fn from_k256_affine(p: &K256Affine) -> AffinePoint {
pub(crate) fn from_k256_affine(p: &K256Affine) -> AffinePoint {
let ep = p.to_encoded_point(false);
AffinePoint {
x: BigUint::from_bytes_be(ep.x().expect("ECSM: affine point has x")),
Expand Down
1 change: 1 addition & 0 deletions crypto/ecsm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//! Curve: secp256k1, `y^2 = x^3 + 7 mod p`, `p = 2^256 - 2^32 - 977`, order `N`.

pub mod curve;
pub mod lincomb2_table;
pub mod witness;

#[cfg(test)]
Expand Down
76 changes: 76 additions & 0 deletions crypto/ecsm/src/lincomb2_table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//! The lincomb2 NUMS correction constants: `−2^i·T₀` for `i ∈ [0, 256]`.
//!
//! The lincomb2 joint chain seeds its accumulator with the nothing-up-my-sleeve
//! point `T₀` (see [`crate::witness::t0`] and
//! `thoughts/ec-recover-opt/lincomb2/T0.md`), so after `len` doublings the
//! accumulator carries a blind of `2^len·T₀`. The chain's final row —
//! [`crate::witness::JointSel::Correction`] — strips it by *adding*
//! `−2^len·T₀`.
//!
//! # Sign convention: these are the NEGATED points
//!
//! Entry `i` is `−2^i·T₀`, i.e. `(x(2^i·T₀), p − y(2^i·T₀))`, **not** `2^i·T₀`.
//! That is exactly the addend `lincomb2_witness` writes into the correction
//! row's `(x_g, y_g)` columns (`witness.rs`: `neg_tpow` is built by negating
//! `tpow`'s `y`, and is passed as `joint_row`'s `addend`), so a chip that wires
//! a lookup of this table straight into its addend columns needs no modular
//! negation at all. `crate::tests::lincomb2_table_tests` asserts the match
//! against a real witness rather than restating it.
//!
//! Note that only `y` differs between the two conventions — `x(−P) = x(P)` —
//! so the `x` half of an entry serves either reading.
//!
//! # Why 257 entries
//!
//! `len = max(bits(u1), bits(u2))` for scalars in `[1, N)`, so the reachable
//! range is `[1, 256]`. Entry `0` (`−T₀` itself) is defined anyway: it is the
//! anchor of the doubling recurrence and costs nothing.

use num_bigint::BigUint;

use k256::elliptic_curve::group::Curve as _;
use k256::{AffinePoint as K256Affine, ProjectivePoint};

use crate::curve::{AffinePoint, from_k256_affine, to_k256_affine};
use crate::p;
use crate::witness::t0;

/// Number of defined entries: `i ∈ [0, 256]`.
pub const NEG_T0_POW2_ROWS: usize = 257;

/// `−2^i·T₀` for every `i ∈ [0, 256]`, indexed by `i`.
///
/// Derived from [`crate::witness::t0`] by repeated doubling in `k256`
/// projective coordinates (one `batch_normalize` for the whole chain), then
/// negating each `y`. Deterministic and reproducible: the only input is the
/// pinned `T₀`.
///
/// See the module header for the sign convention — these are the *negated*
/// points, matching the correction row's addend.
pub fn neg_t0_pow2_points() -> Vec<AffinePoint> {
let mut proj = ProjectivePoint::from(to_k256_affine(&t0()));
let mut chain = Vec::with_capacity(NEG_T0_POW2_ROWS);
for _ in 0..NEG_T0_POW2_ROWS {
chain.push(proj);
proj = proj.double();
}

let mut affine = vec![K256Affine::IDENTITY; NEG_T0_POW2_ROWS];
ProjectivePoint::batch_normalize(&chain, &mut affine);

let modulus = p();
affine
.iter()
.map(|a| {
let pt = from_k256_affine(a);
// `T₀` generates the prime-order group, so no multiple of it is the
// identity or a 2-torsion point: `y` is never 0 and `p - y` is a
// canonical nonzero field element.
debug_assert!(pt.y != BigUint::from(0u8), "2^i·T0 has y = 0");
AffinePoint {
x: pt.x,
y: &modulus - &pt.y,
}
})
.collect()
}
Loading