Skip to content

Conversation

@MirandaWood
Copy link
Contributor

@MirandaWood MirandaWood commented Jan 9, 2026

ECC Pre-Audit - Normalise infinities

Closes AVM-193

NOTE: The issue of no operation being set is now addressed in #19471 => this PR only normalises infs so they always have (0, 0) coordinates in ecc.pil. Though the circuit no longer fails, it does incorrectly set the predicates (i.e. double = 0 when doubling inf), which could be a footgun.

In pre-audit it was found that our C++ elliptic curve point class StandardAffinePoint accepts different representations of the infinity (O) point - this makes sense as in noir we use (0, 0) and in BB we use ((P+1)/2, 0). This would be fine if any ECC calculations in the AVM first checked is_inf and overrided any subsequent coordinate-based operations. But the ecc.pil trace sets whether we have a double or add operation based on coordinates without gating by any is_inf checks:

        bool x_match = p.x() == q.x();
        bool y_match = p.y() == q.y();

        bool double_predicate = (x_match && y_match);
        bool add_predicate = (!x_match && !y_match);
        // If x match but the y's don't, the result is the infinity point when adding;
        bool infinity_predicate = (x_match && !y_match);

Assuming (understandably!) that two infinity points would share the same coordinates, the above works fine, but we can input a BB-standard O as p and a Noir-standard O as q from as early as the simulation call without any errors/checks. This results in !x_match && y_match which means none of double_predicate, add_predicate, or infinity_predicate are true and the circuit falls over. See c9da1ea for repro of this.

Though there is no current flow that throws here, I think it's worth addressing as we can make the circuit fail with what should be valid inputs.

Fix

The current approach is to normalise any input infinity points (we already normalise resulting infinity points in ecc and scalar_mul) at the simulation stage, so events with any infs always have (0, 0) coordinates. This means:

  • No changes required to ecc.pil or the execution.pil dispatch
  • No changes to memory reads/writes
  • The ecc simulator sets x = 0, y = 0 if the point is_inf for standard add and scalar mul events
  • ecc_mem.pil now constrains that ecc.pil has x = 0, y = 0 if the point is_inf via the existing lookup (requires 4 more columns)

We could alternatively normalise the points in tracegen, but this means we don't use the values emitted in the events, which feels a bit gross. We could also add many gating constraints to ecc.pil to handle inf edge cases, but this would be expensive and defeats the point a bit of the trace assuming that the input points are on the curve and validated. So this approach felt the smoothest!

@AztecBot
Copy link
Collaborator

AztecBot commented Jan 12, 2026

Flakey Tests

🤖 says: This CI run detected 4 tests that failed, but were tolerated due to a .test_patterns.yml entry.

\033FLAKED\033 (8;;http://ci.aztec-labs.com/cee191aa406592bc�cee191aa406592bc8;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/e2e_epochs/epochs_invalidate_block.parallel.test.ts "committee member invalidates a block if proposer does not come through" (92s) (code: 1) group:e2e-p2p-epoch-flakes (\033MirandaWood\033: Revert "chore: regen")
\033FLAKED\033 (8;;http://ci.aztec-labs.com/c0266b165e134d65�c0266b165e134d658;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/e2e_epochs/epochs_invalidate_block.parallel.test.ts "proposer invalidates multiple blocks" (142s) (code: 1) group:e2e-p2p-epoch-flakes (\033MirandaWood\033: Revert "chore: regen")
\033FLAKED\033 (8;;http://ci.aztec-labs.com/43676add53066690�43676add530666908;;�): yarn-project/end-to-end/scripts/run_test.sh web3signer src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts (37s) (code: 1) (\033MirandaWood\033: Revert "chore: regen")
\033FLAKED\033 (8;;http://ci.aztec-labs.com/5c349af31baf6791�5c349af31baf67918;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/e2e_p2p/gossip_network.test.ts (388s) (code: 1) group:e2e-p2p-epoch-flakes (\033MirandaWood\033: Revert "chore: regen")

This reverts commit 6da1494.
Copy link
Contributor

@IlyasRidhuan IlyasRidhuan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@MirandaWood MirandaWood merged commit 5e6a1d8 into merge-train/avm Jan 14, 2026
8 checks passed
@MirandaWood MirandaWood deleted the mw/avm-ecc-inf-rep branch January 14, 2026 13:10
@AztecBot AztecBot mentioned this pull request Jan 14, 2026
github-merge-queue bot pushed a commit that referenced this pull request Jan 14, 2026
BEGIN_COMMIT_OVERRIDE
fix(avm): Fix relative addressing in fuzzer (#19550)
feat(avm): avm fuzzer bytecode mutation (#19378)
chore(avm): there is automatic conversion from uint128_t to FF
chore(avm): ECC pre-audit - normalise infinity points (#19462)
feat(bb-pilcom): single-component graph check (#19578)
feat(avm): contract class mutation (#19498)
chore: support uint128_t in uint256_t construction (#19581)
fix!: remove unused column in update_check.pil (#19557)
fix(avm)!: pre-audit review of context.pil (#19549)
fix(avm): Relax fuzzer memory manager asserts (#19591)
fix!: sha256.pil missing input propagation constraints (#19590)
END_COMMIT_OVERRIDE
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.

4 participants