From d92990022370ef3aab1b481ce104f7d26bbf3ea9 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 6 May 2026 14:14:24 +0000 Subject: [PATCH 1/6] fix: fully remove inf memory write --- barretenberg/cpp/pil/vm2/ecc_mem.pil | 2 ++ 1 file changed, 2 insertions(+) diff --git a/barretenberg/cpp/pil/vm2/ecc_mem.pil b/barretenberg/cpp/pil/vm2/ecc_mem.pil index e7d6d41e01e1..be54cb05d2e4 100644 --- a/barretenberg/cpp/pil/vm2/ecc_mem.pil +++ b/barretenberg/cpp/pil/vm2/ecc_mem.pil @@ -4,6 +4,8 @@ include "gt.pil"; include "memory.pil"; include "precomputed.pil"; +// TODO(#AVM-266): documentation + /** * This handles the memory writes when the ECADD opcode is executed by user code. * Given two points, P & Q, this trace constrains that both exist on the Grumpkin curve From ecde0c5b3aa51cb787e2d0cb44f2e2ae58c11364 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Thu, 7 May 2026 12:41:07 +0000 Subject: [PATCH 2/6] chore: docs, clear out old todos --- barretenberg/cpp/pil/vm2/ecc_mem.pil | 2 -- 1 file changed, 2 deletions(-) diff --git a/barretenberg/cpp/pil/vm2/ecc_mem.pil b/barretenberg/cpp/pil/vm2/ecc_mem.pil index be54cb05d2e4..e7d6d41e01e1 100644 --- a/barretenberg/cpp/pil/vm2/ecc_mem.pil +++ b/barretenberg/cpp/pil/vm2/ecc_mem.pil @@ -4,8 +4,6 @@ include "gt.pil"; include "memory.pil"; include "precomputed.pil"; -// TODO(#AVM-266): documentation - /** * This handles the memory writes when the ECADD opcode is executed by user code. * Given two points, P & Q, this trace constrains that both exist on the Grumpkin curve From 492aa7f64f917af89ab7293ea838c2818c4b3e05 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Thu, 7 May 2026 10:21:31 +0000 Subject: [PATCH 3/6] feat: first pass, change ECADD opcode sig in transpiler, y-p --- avm-transpiler/src/procedures/compiler.rs | 2 - avm-transpiler/src/procedures/msm.rs | 19 +- avm-transpiler/src/transpile.rs | 10 +- .../docs/avm/avm-isa-quick-reference.md | 6 +- .../src/public/avm/opcodes/ec_add.test.ts | 266 ++++++------------ .../src/public/avm/opcodes/ec_add.ts | 33 +-- .../src/public/fixtures/opcode_spammer.ts | 12 +- 7 files changed, 110 insertions(+), 238 deletions(-) diff --git a/avm-transpiler/src/procedures/compiler.rs b/avm-transpiler/src/procedures/compiler.rs index c3789e48b221..8cd6091881d6 100644 --- a/avm-transpiler/src/procedures/compiler.rs +++ b/avm-transpiler/src/procedures/compiler.rs @@ -233,10 +233,8 @@ fn compile_opcode( Mnemonic::ECADD => { collector.memory_address_operand()?; // p1 x collector.memory_address_operand()?; // p1 y - collector.memory_address_operand()?; // p1 is_infinite collector.memory_address_operand()?; // p2 x collector.memory_address_operand()?; // p2 y - collector.memory_address_operand()?; // p2 is_infinite collector.memory_address_operand()?; // result let collection = collector.finish()?; result.add_instruction( diff --git a/avm-transpiler/src/procedures/msm.rs b/avm-transpiler/src/procedures/msm.rs index f00f3504bb46..4900bd52fe20 100644 --- a/avm-transpiler/src/procedures/msm.rs +++ b/avm-transpiler/src/procedures/msm.rs @@ -1,15 +1,13 @@ pub(crate) const MSM_ASSEMBLY: &str = " ; We are passed three pointers and one usize. - ; d0 points to the points. Points are represented by (x: Field, y: Field, is_infinite: bool) + ; d0 points to the points. Points are represented by (x: Field, y: Field). ; d1 points to the scalars. Scalars are represented by (lo: Field, hi: Field) both range checked to 128 bits. ; d2 contains the number of points. ; d3 points to the result. The result is a point. ADD d3, /*the reserved register 'one_usize'*/ $2, d4; Compute the pointer to the result y. - ADD d4, $2, d5; Compute the pointer to the result is_infinite ; Initialize the msm result: point at infinity SET i3, 0 ff SET i4, 0 ff - SET i5, 1 u1 ; Loop globals SET d6, 0 u32; Initialize the outer loop variable, ranging from 0 to the number of points SET d8, 0 ff; Initialize a 0 FF @@ -51,16 +49,13 @@ FIND_MSB_BODY: JUMPI i19, FIND_MSB_END; Check if the current bit is one JUMP FIND_MSB_BODY ; Now we have the pointer of the MSB in d19 - ; Now store the result of the scalar multiplication in d22, d23, d24 + ; Now store the result of the scalar multiplication in d22, d23 FIND_MSB_END: MOV i16, d22; x ADD d16, $2, d25; pointer to y MOV i25, d23; y - ADD d25, $2, d25; pointer to is_infinite - MOV i25, d24; is_infinite - ; Also store the original point in d25, d26, d27 + ; Also store the original point in d25, d26 MOV d22, d25; x MOV d23, d26; y - MOV d24, d27; is_infinite ; Now we need to do the inner loop, that will do double then add ; We need to iterate from the pointer of the MSB + 1 to the end pointer (d21) @@ -68,18 +63,18 @@ FIND_MSB_END: MOV i16, d22; x INNER_HEAD: LT d19, d21, d28; Check if we are done with the loop JUMPI d28, INNER_BODY JUMP INNER_END -INNER_BODY: ECADD d22, d23, d24, d22, d23, d24, /*not indirect, so the result is stored in d22, d23, d24*/ d22; Double the current result. +INNER_BODY: ECADD d22, d23, d22, d23, /*not indirect, so the result is stored in d22, d23*/ d22; Double the current result. EQ i19, d12, d28; Check if the current bit is zero JUMPI d28, INNER_INC; If the current bit is zero, continue - ECADD d25, d26, d27, d22, d23, d24, /*not indirect, so the result is stored in d22, d23, d24*/ d22; Add the original point to the result + ECADD d25, d26, d22, d23, /*not indirect, so the result is stored in d22, d23*/ d22; Add the original point to the result INNER_INC: ADD d19, $2, d19; Increment the pointer JUMP INNER_HEAD ; After the inner loop we have computed the scalar multiplication. Add it to the msm result -INNER_END: ECADD i3, i4, i5, d22, d23, d24, i3; Add the result to the msm result +INNER_END: ECADD i3, i4, d22, d23, i3; Add the result to the msm result OUTER_INC: ADD d6, $2, d6; Increment the outer loop variable JUMP OUTER_HEAD - ; After the outer loop we have computed the msm. We can return since we wrote the result in i3, i4, i5 + ; After the outer loop we have computed the msm. We can return since we wrote the result in i3, i4 OUTER_END: INTERNALRETURN "; diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 40e3a6f0bb4f..de0b15c8f49f 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -1280,32 +1280,28 @@ fn handle_black_box_function( BlackBoxOp::EmbeddedCurveAdd { input1_x: p1_x_offset, input1_y: p1_y_offset, - input1_infinite: p1_infinite_offset, + input1_infinite: _, input2_x: p2_x_offset, input2_y: p2_y_offset, - input2_infinite: p2_infinite_offset, + input2_infinite: _, result, } => avm_instrs.push(AvmInstruction { opcode: AvmOpcode::ECADD, - // The result (SIXTH operand) is indirect (addressing mode). + // The result (FOURTH operand) is indirect (addressing mode). addressing_mode: Some( AddressingModeBuilder::default() .direct_operand(p1_x_offset) .direct_operand(p1_y_offset) - .direct_operand(p1_infinite_offset) .direct_operand(p2_x_offset) .direct_operand(p2_y_offset) - .direct_operand(p2_infinite_offset) .indirect_operand(&result.pointer) .build(), ), operands: vec![ AvmOperand::U16 { value: p1_x_offset.to_u32() as u16 }, AvmOperand::U16 { value: p1_y_offset.to_u32() as u16 }, - AvmOperand::U16 { value: p1_infinite_offset.to_u32() as u16 }, AvmOperand::U16 { value: p2_x_offset.to_u32() as u16 }, AvmOperand::U16 { value: p2_y_offset.to_u32() as u16 }, - AvmOperand::U16 { value: p2_infinite_offset.to_u32() as u16 }, AvmOperand::U16 { value: result.pointer.to_u32() as u16 }, ], ..Default::default() diff --git a/yarn-project/simulator/docs/avm/avm-isa-quick-reference.md b/yarn-project/simulator/docs/avm/avm-isa-quick-reference.md index f736c83c867d..6519f4aab32f 100644 --- a/yarn-project/simulator/docs/avm/avm-isa-quick-reference.md +++ b/yarn-project/simulator/docs/avm/avm-isa-quick-reference.md @@ -250,9 +250,9 @@ Click on an opcode name to view its detailed documentation. * **[đź”—ECADD](opcodes/ecadd.md)**: Grumpkin elliptic curve addition * Opcode `0x42` ```javascript - M[dstOffset:dstOffset+3] = grumpkinAdd( - /*point1=*/{x: M[p1XOffset], y: M[p1YOffset], isInfinite: M[p1IsInfiniteOffset]}, - /*point2=*/{x: M[p2XOffset], y: M[p2YOffset], isInfinite: M[p2IsInfiniteOffset]} + M[dstOffset:dstOffset+1] = grumpkinAdd( + /*point1=*/{x: M[p1XOffset], y: M[p1YOffset]}, + /*point2=*/{x: M[p2XOffset], y: M[p2YOffset]} ) ``` * **[đź”—TORADIXBE](opcodes/toradixbe.md)**: Convert to radix (big-endian) diff --git a/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts b/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts index 8e144efaed91..b7ff58247dde 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts @@ -24,20 +24,16 @@ describe('EC Instructions', () => { ...Buffer.from('1234', 'hex'), // indirect ...Buffer.from('1235', 'hex'), // p1x ...Buffer.from('1236', 'hex'), // p1y - ...Buffer.from('0000', 'hex'), // p1IsInfinite ...Buffer.from('1237', 'hex'), // p2x ...Buffer.from('1238', 'hex'), // p2y - ...Buffer.from('0001', 'hex'), // p2IsInfinite ...Buffer.from('1239', 'hex'), // dstOffset ]); const inst = new EcAdd( /*addressing_mode=*/ 0x1234, /*p1X=*/ 0x1235, /*p1Y=*/ 0x1236, - /*p1IsInfinite=*/ 0, /*p2X=*/ 0x1237, /*p2Y=*/ 0x1238, - /*p2IsInfinite=*/ 1, /*dstOffset=*/ 0x1239, ); @@ -48,41 +44,28 @@ describe('EC Instructions', () => { it(`Should double correctly`, async () => { const x = new Field(Grumpkin.generator.x); const y = new Field(Grumpkin.generator.y); - const zero = new Uint1(0); context.machineState.memory.set(0, x); context.machineState.memory.set(1, y); - context.machineState.memory.set(2, zero); - context.machineState.memory.set(3, x); - context.machineState.memory.set(4, y); - context.machineState.memory.set(5, zero); - // context.machineState.memory.set(6, new Uint32(6)); - - await new EcAdd( - /*addressing_mode=*/ 0, - /*p1X=*/ 0, - /*p1Y=*/ 1, - /*p1IsInfinite=*/ 2, - /*p2X=*/ 3, - /*p2Y=*/ 4, - /*p2IsInfinite=*/ 5, - /*dstOffset=*/ 6, - ).execute(context); - - const pIsInfinite = context.machineState.memory.get(8).toNumber() === 1; + context.machineState.memory.set(2, x); + context.machineState.memory.set(3, y); + // context.machineState.memory.set(4, new Uint32(4)); + + await new EcAdd(/*addressing_mode=*/ 0, /*p1X=*/ 0, /*p1Y=*/ 1, /*p2X=*/ 2, /*p2Y=*/ 3, /*dstOffset=*/ 4).execute( + context, + ); + const actual = new Point( - context.machineState.memory.get(6).toFr(), - context.machineState.memory.get(7).toFr(), - pIsInfinite, + context.machineState.memory.get(4).toFr(), + context.machineState.memory.get(5).toFr(), + false, ); const expected = await Grumpkin.add(Grumpkin.generator, Grumpkin.generator); expect(actual).toEqual(expected); - expect(context.machineState.memory.get(8).toFr().equals(Fr.ZERO)).toBe(true); }); it('Should add correctly', async () => { const G2 = await Grumpkin.add(Grumpkin.generator, Grumpkin.generator); - const zero = new Uint1(0); const x1 = new Field(Grumpkin.generator.x); const y1 = new Field(Grumpkin.generator.y); @@ -91,36 +74,25 @@ describe('EC Instructions', () => { context.machineState.memory.set(0, x1); context.machineState.memory.set(1, y1); - context.machineState.memory.set(2, zero); - context.machineState.memory.set(3, x2); - context.machineState.memory.set(4, y2); - context.machineState.memory.set(5, zero); - context.machineState.memory.set(6, new Uint32(6)); - - await new EcAdd( - /*addressing_mode=*/ 0, - /*p1X=*/ 0, - /*p1Y=*/ 1, - /*p1IsInfinite=*/ 2, - /*p2X=*/ 3, - /*p2Y=*/ 4, - /*p2IsInfinite=*/ 5, - /*dstOffset=*/ 6, - ).execute(context); + context.machineState.memory.set(2, x2); + context.machineState.memory.set(3, y2); + context.machineState.memory.set(4, new Uint32(4)); + + await new EcAdd(/*addressing_mode=*/ 0, /*p1X=*/ 0, /*p1Y=*/ 1, /*p2X=*/ 2, /*p2Y=*/ 3, /*dstOffset=*/ 4).execute( + context, + ); const actual = new Point( - context.machineState.memory.get(6).toFr(), - context.machineState.memory.get(7).toFr(), + context.machineState.memory.get(4).toFr(), + context.machineState.memory.get(5).toFr(), false, ); const G3 = await Grumpkin.add(Grumpkin.generator, G2); expect(actual).toEqual(G3); - expect(context.machineState.memory.get(8).toFr().equals(Fr.ZERO)).toBe(true); }); it('Should add correctly with rhs being infinity', async () => { - const zero = new Uint1(0); - const one = new Uint1(1); + const zero = new Field(0); const x = new Field(Grumpkin.generator.x); const y = new Field(Grumpkin.generator.y); @@ -128,103 +100,67 @@ describe('EC Instructions', () => { // Point 1 is not infinity context.machineState.memory.set(0, x); context.machineState.memory.set(1, y); - context.machineState.memory.set(2, zero); // Point 2 is infinity - context.machineState.memory.set(3, x); - context.machineState.memory.set(4, y); - context.machineState.memory.set(5, one); - context.machineState.memory.set(6, new Uint32(6)); - - await new EcAdd( - /*addressing_mode=*/ 0, - /*p1X=*/ 0, - /*p1Y=*/ 1, - /*p1IsInfinite=*/ 2, - /*p2X=*/ 3, - /*p2Y=*/ 4, - /*p2IsInfinite=*/ 5, - /*dstOffset=*/ 6, - ).execute(context); - - expect([ - context.machineState.memory.get(6).toFr(), - context.machineState.memory.get(7).toFr(), - context.machineState.memory.get(8).toNumber(), - ]).toEqual([x.toFr(), y.toFr(), 0]); + context.machineState.memory.set(2, zero); + context.machineState.memory.set(3, zero); + context.machineState.memory.set(4, new Uint32(4)); + + await new EcAdd(/*addressing_mode=*/ 0, /*p1X=*/ 0, /*p1Y=*/ 1, /*p2X=*/ 2, /*p2Y=*/ 3, /*dstOffset=*/ 4).execute( + context, + ); + + expect([context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr()]).toEqual([ + x.toFr(), + y.toFr(), + ]); }); it('Should add correctly with lhs being infinity', async () => { - const zero = new Uint1(0); - const one = new Uint1(1); + const zero = new Field(0); const x = new Field(Grumpkin.generator.x); const y = new Field(Grumpkin.generator.y); // Point 1 is infinity - context.machineState.memory.set(0, x); - context.machineState.memory.set(1, y); - context.machineState.memory.set(2, one); + context.machineState.memory.set(0, zero); + context.machineState.memory.set(1, zero); // Point 2 is not infinity - context.machineState.memory.set(3, x); - context.machineState.memory.set(4, y); - context.machineState.memory.set(5, zero); - context.machineState.memory.set(6, new Uint32(6)); - - await new EcAdd( - /*addressing_mode=*/ 0, - /*p1X=*/ 0, - /*p1Y=*/ 1, - /*p1IsInfinite=*/ 2, - /*p2X=*/ 3, - /*p2Y=*/ 4, - /*p2IsInfinite=*/ 5, - /*dstOffset=*/ 6, - ).execute(context); - - expect([ - context.machineState.memory.get(6).toFr(), - context.machineState.memory.get(7).toFr(), - context.machineState.memory.get(8).toNumber(), - ]).toEqual([x.toFr(), y.toFr(), 0]); + context.machineState.memory.set(2, x); + context.machineState.memory.set(3, y); + context.machineState.memory.set(4, new Uint32(4)); + + await new EcAdd(/*addressing_mode=*/ 0, /*p1X=*/ 0, /*p1Y=*/ 1, /*p2X=*/ 2, /*p2Y=*/ 3, /*dstOffset=*/ 4).execute( + context, + ); + + expect([context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr()]).toEqual([ + x.toFr(), + y.toFr(), + ]); }); it('Should add correctly with both being infinity', async () => { - const one = new Uint1(1); - - const x = new Field(Grumpkin.generator.x); - const y = new Field(Grumpkin.generator.y); + const zero = new Field(0); // Point 1 is infinity - context.machineState.memory.set(0, x); - context.machineState.memory.set(1, y); - context.machineState.memory.set(2, one); + context.machineState.memory.set(0, zero); + context.machineState.memory.set(1, zero); // Point 2 is infinity - context.machineState.memory.set(3, x); - context.machineState.memory.set(4, y); - context.machineState.memory.set(5, one); - context.machineState.memory.set(6, new Uint32(6)); - - await new EcAdd( - /*addressing_mode=*/ 0, - /*p1X=*/ 0, - /*p1Y=*/ 1, - /*p1IsInfinite=*/ 2, - /*p2X=*/ 3, - /*p2Y=*/ 4, - /*p2IsInfinite=*/ 5, - /*dstOffset=*/ 6, - ).execute(context); - - expect([ - context.machineState.memory.get(6).toFr(), - context.machineState.memory.get(7).toFr(), - context.machineState.memory.get(8).toNumber(), - ]).toEqual([Fr.ZERO, Fr.ZERO, 1]); + context.machineState.memory.set(2, zero); + context.machineState.memory.set(3, zero); + context.machineState.memory.set(4, new Uint32(4)); + + await new EcAdd(/*addressing_mode=*/ 0, /*p1X=*/ 0, /*p1Y=*/ 1, /*p2X=*/ 2, /*p2Y=*/ 3, /*dstOffset=*/ 4).execute( + context, + ); + + expect([context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr()]).toEqual([ + Fr.ZERO, + Fr.ZERO, + ]); }); it('Should add correctly with none infinity adding up to infinity', async () => { - const zero = new Uint1(0); - // Point 1 is a "random" point on the curve const x1 = new Field(2165030248772332382647339664685760681662697934905450801078761197378150920554n); const y1 = new Field(1518479793551399970960577643223827307749147426195887130444945641264602004320n); @@ -234,29 +170,19 @@ describe('EC Instructions', () => { context.machineState.memory.set(0, x1); context.machineState.memory.set(1, y1); - context.machineState.memory.set(2, zero); - context.machineState.memory.set(3, x2); - context.machineState.memory.set(4, y2); - context.machineState.memory.set(5, zero); - context.machineState.memory.set(6, new Uint32(6)); - - await new EcAdd( - /*addressing_mode=*/ 0, - /*p1X=*/ 0, - /*p1Y=*/ 1, - /*p1IsInfinite=*/ 2, - /*p2X=*/ 3, - /*p2Y=*/ 4, - /*p2IsInfinite=*/ 5, - /*dstOffset=*/ 6, - ).execute(context); - - expect([ - context.machineState.memory.get(6).toFr(), - context.machineState.memory.get(7).toFr(), - context.machineState.memory.get(8).toNumber(), - ]).toEqual([Fr.ZERO, Fr.ZERO, 1]); + context.machineState.memory.set(2, x2); + context.machineState.memory.set(3, y2); + context.machineState.memory.set(4, new Uint32(4)); + + await new EcAdd(/*addressing_mode=*/ 0, /*p1X=*/ 0, /*p1Y=*/ 1, /*p2X=*/ 2, /*p2Y=*/ 3, /*dstOffset=*/ 4).execute( + context, + ); + + expect([context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr()]).toEqual([ + Fr.ZERO, + Fr.ZERO, + ]); }); }); @@ -265,29 +191,16 @@ describe('EC Instructions', () => { const validPoint = await Point.random(); const p1xOffset = 0; const p1yOffset = 1; - const p1IsInfiniteOffset = 2; - const p2xOffset = 3; - const p2yOffset = 4; - const p2IsInfiniteOffset = 5; - const dstOffset = 6; + const p2xOffset = 2; + const p2yOffset = 3; + const dstOffset = 4; context.machineState.memory.set(p1xOffset, new Field(new Fr(1))); // p1x (point is invalid) context.machineState.memory.set(p1yOffset, new Field(new Fr(1))); // p1y (point is invalid) - context.machineState.memory.set(p1IsInfiniteOffset, new Uint1(0)); // p1IsInfinite context.machineState.memory.set(p2xOffset, new Field(validPoint.x)); // p2x context.machineState.memory.set(p2yOffset, new Field(validPoint.y)); // p2y - context.machineState.memory.set(p2IsInfiniteOffset, new Uint1(validPoint.isInfinite ? 1 : 0)); // p2IsInfinite await expect( - new EcAdd( - /*addressing_mode=*/ 0, - p1xOffset, - p1yOffset, - p1IsInfiniteOffset, - p2xOffset, - p2yOffset, - p2IsInfiniteOffset, - dstOffset, - ).execute(context), + new EcAdd(/*addressing_mode=*/ 0, p1xOffset, p1yOffset, p2xOffset, p2yOffset, dstOffset).execute(context), ).rejects.toThrow(EcAddPointNotOnCurveError); }); @@ -295,29 +208,16 @@ describe('EC Instructions', () => { const validPoint = await Point.random(); const p1xOffset = 0; const p1yOffset = 1; - const p1IsInfiniteOffset = 2; - const p2xOffset = 3; - const p2yOffset = 4; - const p2IsInfiniteOffset = 5; - const dstOffset = 6; + const p2xOffset = 2; + const p2yOffset = 3; + const dstOffset = 4; context.machineState.memory.set(p1xOffset, new Field(validPoint.x)); // p1x context.machineState.memory.set(p1yOffset, new Field(validPoint.y)); // p1y - context.machineState.memory.set(p1IsInfiniteOffset, new Uint1(validPoint.isInfinite ? 1 : 0)); // p1IsInfinite context.machineState.memory.set(p2xOffset, new Field(new Fr(1))); // p2x (point is invalid) context.machineState.memory.set(p2yOffset, new Field(new Fr(1))); // p2y (point is invalid) - context.machineState.memory.set(p2IsInfiniteOffset, new Uint1(0)); // p2IsInfinite await expect( - new EcAdd( - /*addressing_mode=*/ 0, - p1xOffset, - p1yOffset, - p1IsInfiniteOffset, - p2xOffset, - p2yOffset, - p2IsInfiniteOffset, - dstOffset, - ).execute(context), + new EcAdd(/*addressing_mode=*/ 0, p1xOffset, p1yOffset, p2xOffset, p2yOffset, dstOffset).execute(context), ).rejects.toThrow(EcAddPointNotOnCurveError); }); }); diff --git a/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts b/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts index 19e3c1adc6ca..83438f797144 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts @@ -18,10 +18,8 @@ export class EcAdd extends Instruction { OperandType.UINT16, // indirect OperandType.UINT16, // p1X OperandType.UINT16, // p1Y - OperandType.UINT16, // p1IsInfinite OperandType.UINT16, // p2X OperandType.UINT16, // p2Y - OperandType.UINT16, // p2IsInfinite OperandType.UINT16, // dst ]; @@ -29,10 +27,8 @@ export class EcAdd extends Instruction { private addressingMode: number, private p1XOffset: number, private p1YOffset: number, - private p1IsInfiniteOffset: number, private p2XOffset: number, private p2YOffset: number, - private p2IsInfiniteOffset: number, private dstOffset: number, ) { super(); @@ -46,34 +42,27 @@ export class EcAdd extends Instruction { this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()), ); - const operands = [ - this.p1XOffset, - this.p1YOffset, - this.p1IsInfiniteOffset, - this.p2XOffset, - this.p2YOffset, - this.p2IsInfiniteOffset, - this.dstOffset, - ]; - const [p1XOffset, p1YOffset, p1IsInfiniteOffset, p2XOffset, p2YOffset, p2IsInfiniteOffset, dstOffset] = - addressing.resolve(operands, memory); + const operands = [this.p1XOffset, this.p1YOffset, this.p2XOffset, this.p2YOffset, this.dstOffset]; + const [p1XOffset, p1YOffset, p2XOffset, p2YOffset, dstOffset] = addressing.resolve(operands, memory); memory.checkTags(TypeTag.FIELD, p1XOffset, p1YOffset, p2XOffset, p2YOffset); - memory.checkTags(TypeTag.UINT1, p1IsInfiniteOffset, p2IsInfiniteOffset); const p1X = memory.get(p1XOffset); const p1Y = memory.get(p1YOffset); - const p1IsInfinite = memory.get(p1IsInfiniteOffset).toNumber() === 1; - const p1 = new Point(p1X.toFr(), p1Y.toFr(), p1IsInfinite); + const p1XFr = p1X.toFr(); + const p1YFr = p1Y.toFr(); + const p1IsInfinite = p1XFr.isZero() && p1YFr.isZero(); + const p1 = new Point(p1XFr, p1YFr, p1IsInfinite); if (!p1.isOnGrumpkin()) { throw new EcAddPointNotOnCurveError(/*pointIndex=*/ 1, p1); } const p2X = memory.get(p2XOffset); const p2Y = memory.get(p2YOffset); - // unused. Point doesn't store this information - const p2IsInfinite = memory.get(p2IsInfiniteOffset).toNumber() === 1; - const p2 = new Point(p2X.toFr(), p2Y.toFr(), p2IsInfinite); + const p2XFr = p2X.toFr(); + const p2YFr = p2Y.toFr(); + const p2IsInfinite = p2XFr.isZero() && p2YFr.isZero(); + const p2 = new Point(p2XFr, p2YFr, p2IsInfinite); if (!p2.isOnGrumpkin()) { throw new EcAddPointNotOnCurveError(/*pointIndex=*/ 2, p2); } @@ -99,7 +88,5 @@ export class EcAdd extends Instruction { // Important to use setSlice() and not set() in the two following statements as // this checks that the offsets lie within memory range. memory.setSlice(dstOffset, [new Field(dest.x), new Field(dest.y)]); - // Check representation of infinity for grumpkin - memory.setSlice(dstOffset + 2, [new Uint1(dest.equals(Point.ZERO) ? 1 : 0)]); } } diff --git a/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts b/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts index 88e9a1663eb3..dd4322029d81 100644 --- a/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts +++ b/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts @@ -1339,20 +1339,16 @@ export const SPAM_CONFIGS: Partial> = { setup: [ { offset: 0, value: new Field(Grumpkin.generator.x) }, // p1X = G.x { offset: 1, value: new Field(Grumpkin.generator.y) }, // p1Y = G.y - { offset: 2, value: new Uint1(0n) }, // p1IsInfinite = false - { offset: 3, value: new Field(Grumpkin.generator.x) }, // p2X = G.x - { offset: 4, value: new Field(Grumpkin.generator.y) }, // p2Y = G.y - { offset: 5, value: new Uint1(0n) }, // p2IsInfinite = false + { offset: 2, value: new Field(Grumpkin.generator.x) }, // p2X = G.x + { offset: 3, value: new Field(Grumpkin.generator.y) }, // p2Y = G.y ], targetInstructions: () => [ new EcAdd( /*addressing_mode=*/ 0, /*p1XOffset=*/ 0, /*p1YOffset=*/ 1, - /*p1IsInfiniteOffset=*/ 2, - /*p2XOffset=*/ 3, - /*p2YOffset=*/ 4, - /*p2IsInfiniteOffset=*/ 5, + /*p2XOffset=*/ 2, + /*p2YOffset=*/ 3, /*dstOffset=*/ 0, ), ], From faf94ad096f79167d061dce3b44a6d333ccfa2b6 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 8 May 2026 10:38:14 +0000 Subject: [PATCH 4/6] chore: update gas cost for ECADD --- barretenberg/cpp/src/barretenberg/aztec/aztec_constants.hpp | 2 +- .../noir-protocol-circuits/crates/types/src/constants.nr | 2 +- yarn-project/constants/src/constants.gen.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/aztec/aztec_constants.hpp b/barretenberg/cpp/src/barretenberg/aztec/aztec_constants.hpp index 070d5bbc80ef..5b2c97b22806 100644 --- a/barretenberg/cpp/src/barretenberg/aztec/aztec_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec/aztec_constants.hpp @@ -233,7 +233,7 @@ #define AVM_POSEIDON2_BASE_L2_GAS 360 #define AVM_SHA256COMPRESSION_BASE_L2_GAS 12288 #define AVM_KECCAKF1600_BASE_L2_GAS 58176 -#define AVM_ECADD_BASE_L2_GAS 270 +#define AVM_ECADD_BASE_L2_GAS 180 #define AVM_TORADIXBE_BASE_L2_GAS 24 #define AVM_CALLDATACOPY_DYN_L2_GAS 3 #define AVM_RETURNDATACOPY_DYN_L2_GAS 3 diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 87b7b82eaf5e..441196f366bd 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -1220,7 +1220,7 @@ pub global AVM_DEBUGLOG_BASE_L2_GAS: u32 = 9; pub global AVM_POSEIDON2_BASE_L2_GAS: u32 = 24 * 15; // SLOW_SIM_MUL = 15 pub global AVM_SHA256COMPRESSION_BASE_L2_GAS: u32 = 12288; pub global AVM_KECCAKF1600_BASE_L2_GAS: u32 = 58176; -pub global AVM_ECADD_BASE_L2_GAS: u32 = 27 * 10; // SLOW_SIM_MUL = 10 +pub global AVM_ECADD_BASE_L2_GAS: u32 = 18 * 10; // SLOW_SIM_MUL = 10 pub global AVM_TORADIXBE_BASE_L2_GAS: u32 = 24; // Dynamic L2 GAS diff --git a/yarn-project/constants/src/constants.gen.ts b/yarn-project/constants/src/constants.gen.ts index 99e5da0c2bc4..72d5e20ca2d6 100644 --- a/yarn-project/constants/src/constants.gen.ts +++ b/yarn-project/constants/src/constants.gen.ts @@ -451,7 +451,7 @@ export const AVM_DEBUGLOG_BASE_L2_GAS = 9; export const AVM_POSEIDON2_BASE_L2_GAS = 360; export const AVM_SHA256COMPRESSION_BASE_L2_GAS = 12288; export const AVM_KECCAKF1600_BASE_L2_GAS = 58176; -export const AVM_ECADD_BASE_L2_GAS = 270; +export const AVM_ECADD_BASE_L2_GAS = 180; export const AVM_TORADIXBE_BASE_L2_GAS = 24; export const AVM_CALLDATACOPY_DYN_L2_GAS = 3; export const AVM_RETURNDATACOPY_DYN_L2_GAS = 3; @@ -497,6 +497,7 @@ export const GRUMPKIN_ONE_Y = 17631683881184975370165255887551781615748388533673 export const DEFAULT_MAX_DEBUG_LOG_MEMORY_READS = 125000; export enum DomainSeparator { NOTE_HASH = 116501019, + PARTIAL_NOTE_COMMITMENT = 568912195, SILOED_NOTE_HASH = 3361878420, UNIQUE_NOTE_HASH = 226850429, NOTE_HASH_NONCE = 1721808740, From fe3d77e98c15469223679f4525a4a0f0883c7776 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 8 May 2026 14:14:55 +0000 Subject: [PATCH 5/6] chore: update vk after updating gas values --- .../cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp index ce806adf3b29..77d2b59d0272 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp @@ -17,7 +17,7 @@ class AvmHardCodedVKAndHash { using FF = bb::curve::BN254::ScalarField; // Precomputed VK hash (hash of all commitments below). - static FF vk_hash() { return FF(uint256_t("0x0f0714f53e7fcf7ffb15cfb22b7a1614c65f01742706b0ca20eb80454eaf1e48")); } + static FF vk_hash() { return FF(uint256_t("0x00b6d67db723a570d7686fbcb5f3c4c39945378222f37e86fa9f511af4c036b5")); } static constexpr std::array get_all() { @@ -71,9 +71,9 @@ class AvmHardCodedVKAndHash { uint256_t( "0x090dda25e7d64ab5cabe09fd80fbb731af2a98de7a608157dc10394b4fc022a4")), // precomputed_exec_opcode_dynamic_l2_gas Commitment( - uint256_t("0x26086b5fb31a24f236f0441d5b922b94ca141e861b9cc640184681c518cd68d3"), + uint256_t("0x1fbccee2ff656d845414c1a520adde56aa3625e29b6fff377044986493023e6d"), uint256_t( - "0x0bab134bb4e25ff33584c1094847e762ce6573054bae27715d0e4eb2b7278d80")), // precomputed_exec_opcode_opcode_gas + "0x05c88802d3174f1c7b3c9aa1abf4754ebdaf6409d1aaf1dfa3f551da1c10fa93")), // precomputed_exec_opcode_opcode_gas Commitment( uint256_t("0x296def9415d1c96b4d8ab91df5f59ad8522a726f98461b1ab5c4d4c5b22471a4"), uint256_t( From 5ab28e8d5dfb11bea67fdfd6193ae27ffabfc0bc Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 8 May 2026 15:10:35 +0000 Subject: [PATCH 6/6] chore: ts linting --- yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts | 2 +- yarn-project/simulator/src/public/avm/opcodes/ec_add.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts b/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts index b7ff58247dde..ec05d289e2d7 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts @@ -5,7 +5,7 @@ import { Point } from '@aztec/foundation/curves/grumpkin'; import { beforeEach } from '@jest/globals'; import type { AvmContext } from '../avm_context.js'; -import { Field, Uint1, Uint32 } from '../avm_memory_types.js'; +import { Field, Uint32 } from '../avm_memory_types.js'; import { EcAddPointNotOnCurveError } from '../errors.js'; import { initContext } from '../fixtures/initializers.js'; import { EcAdd } from './ec_add.js'; diff --git a/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts b/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts index 83438f797144..9fe17d84128c 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts @@ -2,7 +2,7 @@ import { Grumpkin } from '@aztec/foundation/crypto/grumpkin'; import { Point } from '@aztec/foundation/curves/grumpkin'; import type { AvmContext } from '../avm_context.js'; -import { Field, TypeTag, Uint1 } from '../avm_memory_types.js'; +import { Field, TypeTag } from '../avm_memory_types.js'; import { EcAddPointNotOnCurveError } from '../errors.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Addressing } from './addressing_mode.js';