Skip to content

feat(ecc): add EC_MUL and SETUP_EC_MUL opcodes - #3103

Closed
mansur20478 wants to merge 114 commits into
develop-v2.1.0from
feat/ecmul-opcode
Closed

mansur20478 wants to merge 114 commits into
develop-v2.1.0from
feat/ecmul-opcode

Conversation

@mansur20478

@mansur20478 mansur20478 commented Aug 4, 2026

Copy link
Copy Markdown

This PR adds ecmul opcode:

EC_MUL rd, rs1, rs2          // [rd] ← scalar_at(rs2) · point_at(rs1)
SETUP_EC_MUL rd, rs1, rs2    // rs1 → (modulus, a); run once per curve before any EC_MUL

The scalar must be odd, nonzero, and below the subgroup order.

The guest-side mul_scalar wrappers handle even scalar case scenarios. SETUP_EC_MUL follows the same pattern as the other Weierstrass setups: its point operand carries (modulus, a), which the chip pins against its configuration.

The reason for choosing odd scalars is so that the scalar can be represented as 2B + 1. The base point must be a non-identity point on the curve, in the prime-order subgroup, and the subgroup order must satisfy n ≡ 1 (mod 4).


EC_MUL follows this formula where σᵢ = 2bᵢ − 1:

R = P
for i in (0..256).rev():
    R = 2R + σᵢ·P,      σᵢ ∈ {+1, −1}
// becomes R = 2²⁵⁶·P + ( Σᵢ σᵢ·2ⁱ )·P at the end

The formula above works because:

2²⁵⁶ + Σᵢ σᵢ·2ⁱ  = 2²⁵⁶ + Σᵢ (2bᵢ − 1)·2ⁱ =  2²⁵⁶ + 2·Σᵢ bᵢ·2ⁱ − Σᵢ 2ⁱ  =  2²⁵⁶ + 2B − (2²⁵⁶ − 1) = 2B + 1

As result, one instruction spans 128 trace rows (we pack two steps per row and fuse the IO row
with the last row).

To bind the digits to the actual operand, the header threads a bit accumulator for B across the
rows. The final row reconstructs 2B + 1 and checks it byte-by-byte against the scalar read from
memory.

The row layout is header / field expression / IO. The point accumulator is threaded between rows
by transition constraint rather than through memory, and all memory traffic sits on the final row:
one point read, one scalar read, one point write per instruction.

On secp256k1 the chip is 1711 columns wide, 128 rows, 219k cells per scalar multiplication.

On the guest side, msm_via_ec_mul computes MSM as one EC_MUL per base, and
IntrinsicCurve::msm for all four curves routes through it. An ECDSA verify/recover is now two
EC_MULs and one add. The generic Pippenger msm stays for G2 and external callers or custom defined.


Resolves INT-8898, INT-9082

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Comment thread extensions/algebra/rvr/ffi/modular/c/rvr_ext_bls12_381.c Outdated
@shuklaayush
shuklaayush requested a review from GunaDD August 11, 2026 10:15
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Comment thread extensions/algebra/rvr/ffi/modular/c/rvr_ext_secp256k1.c
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor
group app.proof_time_ms app.cycles leaf.proof_time_ms
fibonacci 468 4,000,051 232
keccak 7,510 14,365,133 1,548
sha2_bench 4,166 11,167,961 517
regex 657 4,090,656 216
ecrecover 196 112,210 199
pairing 236 592,827 197
kitchen_sink 2,024 1,979,971 525

Note: cells_used metrics omitted because CUDA tracegen does not expose unpadded trace heights.

Commit: f1e0ecf

Benchmark Workflow

`openvm_pairing::bls12_381` is behind the `bls12_381` feature, so building
the examples with default features failed to resolve the import. Add the
`required-features` entry the other bls examples already carry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor
group app.proof_time_ms app.cycles leaf.proof_time_ms
fibonacci 472 4,000,051 231
keccak 7,457 14,365,133 1,541
sha2_bench 4,111 11,167,961 516
regex 673 4,090,656 215
ecrecover 196 112,210 200
pairing 237 592,827 197
kitchen_sink 2,018 1,979,971 520

Note: cells_used metrics omitted because CUDA tracegen does not expose unpadded trace heights.

Commit: c859c16

Benchmark Workflow

@github-actions

Copy link
Copy Markdown
Contributor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@github-actions

Copy link
Copy Markdown
Contributor
group app.proof_time_ms app.cycles leaf.proof_time_ms
fibonacci 461 4,000,051 228
keccak 7,334 14,365,133 1,519
sha2_bench 4,157 11,167,961 528
regex 668 4,090,656 215
ecrecover 196 112,210 199
pairing 237 592,827 196
kitchen_sink 2,033 1,979,971 527

Note: cells_used metrics omitted because CUDA tracegen does not expose unpadded trace heights.

Commit: 4926b40

Benchmark Workflow

@github-actions

Copy link
Copy Markdown
Contributor
group app.proof_time_ms app.cycles leaf.proof_time_ms
fibonacci 462 4,000,051 232
keccak 7,399 14,365,133 1,508
sha2_bench 4,175 11,167,961 522
regex 649 4,090,656 210
ecrecover 196 112,210 199
pairing 235 592,827 197
kitchen_sink 2,019 1,979,971 520

Note: cells_used metrics omitted because CUDA tracegen does not expose unpadded trace heights.

Commit: 6b08efd

Benchmark Workflow

@github-actions

Copy link
Copy Markdown
Contributor
group app.proof_time_ms app.cycles leaf.proof_time_ms
fibonacci 464 4,000,051 229
keccak 7,313 14,365,133 1,506
sha2_bench 4,166 11,167,961 525
regex 665 4,090,656 214
ecrecover 197 112,210 197
pairing 235 592,827 196
kitchen_sink 2,039 1,979,971 527

Note: cells_used metrics omitted because CUDA tracegen does not expose unpadded trace heights.

Commit: 989fac1

Benchmark Workflow

@shuklaayush
shuklaayush marked this pull request as draft August 20, 2026 20:02
@shuklaayush

Copy link
Copy Markdown
Collaborator

unfortunately this causes a blowup in recursion time so i'm going to close it for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants