@aztec/simulator 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dest/acvm/oracle/oracle.d.ts +3 -2
  2. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  3. package/dest/acvm/oracle/oracle.js +17 -6
  4. package/dest/acvm/oracle/typed_oracle.d.ts +3 -2
  5. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/typed_oracle.js +8 -5
  7. package/dest/avm/avm_gas.d.ts.map +1 -1
  8. package/dest/avm/avm_gas.js +3 -1
  9. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  10. package/dest/avm/avm_memory_types.js +2 -4
  11. package/dest/avm/fixtures/index.d.ts +4 -1
  12. package/dest/avm/fixtures/index.d.ts.map +1 -1
  13. package/dest/avm/fixtures/index.js +11 -6
  14. package/dest/avm/journal/journal.d.ts +3 -3
  15. package/dest/avm/journal/journal.d.ts.map +1 -1
  16. package/dest/avm/journal/journal.js +18 -13
  17. package/dest/avm/journal/trace.d.ts +3 -3
  18. package/dest/avm/journal/trace.d.ts.map +1 -1
  19. package/dest/avm/journal/trace.js +7 -6
  20. package/dest/avm/journal/trace_types.d.ts +4 -0
  21. package/dest/avm/journal/trace_types.d.ts.map +1 -1
  22. package/dest/avm/journal/trace_types.js +1 -5
  23. package/dest/avm/opcodes/accrued_substate.d.ts +2 -2
  24. package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
  25. package/dest/avm/opcodes/accrued_substate.js +36 -21
  26. package/dest/avm/opcodes/arithmetic.d.ts.map +1 -1
  27. package/dest/avm/opcodes/arithmetic.js +12 -9
  28. package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
  29. package/dest/avm/opcodes/bitwise.js +11 -8
  30. package/dest/avm/opcodes/comparators.d.ts.map +1 -1
  31. package/dest/avm/opcodes/comparators.js +7 -5
  32. package/dest/avm/opcodes/contract.d.ts.map +1 -1
  33. package/dest/avm/opcodes/contract.js +20 -24
  34. package/dest/avm/opcodes/control_flow.d.ts.map +1 -1
  35. package/dest/avm/opcodes/control_flow.js +4 -2
  36. package/dest/avm/opcodes/ec_add.d.ts +19 -0
  37. package/dest/avm/opcodes/ec_add.d.ts.map +1 -0
  38. package/dest/avm/opcodes/ec_add.js +78 -0
  39. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  40. package/dest/avm/opcodes/external_calls.js +8 -3
  41. package/dest/avm/opcodes/hashing.d.ts.map +1 -1
  42. package/dest/avm/opcodes/hashing.js +10 -2
  43. package/dest/avm/opcodes/instruction_impl.d.ts.map +1 -1
  44. package/dest/avm/opcodes/instruction_impl.js +4 -2
  45. package/dest/avm/opcodes/memory.d.ts +1 -1
  46. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  47. package/dest/avm/opcodes/memory.js +14 -12
  48. package/dest/avm/opcodes/multi_scalar_mul.d.ts +16 -0
  49. package/dest/avm/opcodes/multi_scalar_mul.d.ts.map +1 -0
  50. package/dest/avm/opcodes/multi_scalar_mul.js +95 -0
  51. package/dest/avm/opcodes/storage.d.ts +1 -1
  52. package/dest/avm/opcodes/storage.d.ts.map +1 -1
  53. package/dest/avm/opcodes/storage.js +11 -8
  54. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  55. package/dest/avm/serialization/bytecode_serialization.js +5 -1
  56. package/dest/avm/serialization/instruction_serialization.d.ts +3 -1
  57. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  58. package/dest/avm/serialization/instruction_serialization.js +4 -2
  59. package/dest/client/client_execution_context.d.ts +15 -3
  60. package/dest/client/client_execution_context.d.ts.map +1 -1
  61. package/dest/client/client_execution_context.js +31 -17
  62. package/dest/client/simulator.d.ts +2 -1
  63. package/dest/client/simulator.d.ts.map +1 -1
  64. package/dest/client/simulator.js +17 -9
  65. package/dest/public/abstract_phase_manager.d.ts +10 -11
  66. package/dest/public/abstract_phase_manager.d.ts.map +1 -1
  67. package/dest/public/abstract_phase_manager.js +75 -53
  68. package/dest/public/app_logic_phase_manager.d.ts +3 -3
  69. package/dest/public/app_logic_phase_manager.d.ts.map +1 -1
  70. package/dest/public/app_logic_phase_manager.js +3 -3
  71. package/dest/public/db_interfaces.d.ts +1 -0
  72. package/dest/public/db_interfaces.d.ts.map +1 -1
  73. package/dest/public/execution.d.ts +1 -15
  74. package/dest/public/execution.d.ts.map +1 -1
  75. package/dest/public/execution.js +1 -51
  76. package/dest/public/executor.d.ts +1 -1
  77. package/dest/public/executor.d.ts.map +1 -1
  78. package/dest/public/executor.js +15 -8
  79. package/dest/public/hints_builder.d.ts +1 -1
  80. package/dest/public/index.d.ts +6 -6
  81. package/dest/public/index.d.ts.map +1 -1
  82. package/dest/public/index.js +7 -7
  83. package/dest/public/phase_manager_factory.d.ts +3 -3
  84. package/dest/public/phase_manager_factory.d.ts.map +1 -1
  85. package/dest/public/phase_manager_factory.js +5 -5
  86. package/dest/public/public_db_sources.d.ts +3 -1
  87. package/dest/public/public_db_sources.d.ts.map +1 -1
  88. package/dest/public/public_db_sources.js +54 -8
  89. package/dest/public/public_processor.d.ts.map +1 -1
  90. package/dest/public/public_processor.js +4 -5
  91. package/dest/public/setup_phase_manager.d.ts +3 -3
  92. package/dest/public/setup_phase_manager.d.ts.map +1 -1
  93. package/dest/public/setup_phase_manager.js +3 -3
  94. package/dest/public/tail_phase_manager.d.ts +3 -3
  95. package/dest/public/tail_phase_manager.d.ts.map +1 -1
  96. package/dest/public/tail_phase_manager.js +3 -3
  97. package/dest/public/teardown_phase_manager.d.ts +3 -3
  98. package/dest/public/teardown_phase_manager.d.ts.map +1 -1
  99. package/dest/public/teardown_phase_manager.js +3 -3
  100. package/dest/public/transitional_adaptors.d.ts +1 -1
  101. package/dest/public/transitional_adaptors.d.ts.map +1 -1
  102. package/dest/public/transitional_adaptors.js +9 -3
  103. package/package.json +8 -8
  104. package/src/acvm/oracle/oracle.ts +35 -6
  105. package/src/acvm/oracle/typed_oracle.ts +20 -4
  106. package/src/avm/avm_gas.ts +2 -0
  107. package/src/avm/avm_memory_types.ts +1 -3
  108. package/src/avm/fixtures/index.ts +12 -8
  109. package/src/avm/journal/journal.ts +26 -21
  110. package/src/avm/journal/trace.ts +8 -11
  111. package/src/avm/journal/trace_types.ts +3 -0
  112. package/src/avm/opcodes/accrued_substate.ts +53 -20
  113. package/src/avm/opcodes/arithmetic.ts +17 -8
  114. package/src/avm/opcodes/bitwise.ts +13 -8
  115. package/src/avm/opcodes/comparators.ts +9 -4
  116. package/src/avm/opcodes/contract.ts +22 -26
  117. package/src/avm/opcodes/control_flow.ts +3 -1
  118. package/src/avm/opcodes/ec_add.ts +92 -0
  119. package/src/avm/opcodes/external_calls.ts +8 -2
  120. package/src/avm/opcodes/hashing.ts +11 -1
  121. package/src/avm/opcodes/instruction_impl.ts +4 -1
  122. package/src/avm/opcodes/memory.ts +18 -11
  123. package/src/avm/opcodes/multi_scalar_mul.ts +114 -0
  124. package/src/avm/opcodes/storage.ts +10 -10
  125. package/src/avm/serialization/bytecode_serialization.ts +4 -0
  126. package/src/avm/serialization/instruction_serialization.ts +2 -0
  127. package/src/client/client_execution_context.ts +46 -18
  128. package/src/client/simulator.ts +18 -8
  129. package/src/public/abstract_phase_manager.ts +77 -59
  130. package/src/public/app_logic_phase_manager.ts +2 -2
  131. package/src/public/db_interfaces.ts +2 -0
  132. package/src/public/execution.ts +0 -69
  133. package/src/public/executor.ts +17 -8
  134. package/src/public/index.ts +6 -12
  135. package/src/public/phase_manager_factory.ts +6 -6
  136. package/src/public/public_db_sources.ts +62 -7
  137. package/src/public/public_processor.ts +4 -7
  138. package/src/public/setup_phase_manager.ts +2 -2
  139. package/src/public/tail_phase_manager.ts +2 -2
  140. package/src/public/teardown_phase_manager.ts +2 -2
  141. package/src/public/transitional_adaptors.ts +42 -2
  142. package/dest/public/utils.d.ts +0 -8
  143. package/dest/public/utils.d.ts.map +0 -1
  144. package/dest/public/utils.js +0 -38
  145. package/src/public/utils.ts +0 -39
@@ -0,0 +1,92 @@
1
+ import { Grumpkin } from '@aztec/circuits.js/barretenberg';
2
+ import { Point } from '@aztec/foundation/fields';
3
+
4
+ import { type AvmContext } from '../avm_context.js';
5
+ import { Field } from '../avm_memory_types.js';
6
+ import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
7
+ import { Addressing } from './addressing_mode.js';
8
+ import { Instruction } from './instruction.js';
9
+
10
+ export class EcAdd extends Instruction {
11
+ static type: string = 'ECADD';
12
+ static readonly opcode = Opcode.ECADD;
13
+
14
+ // Informs (de)serialization. See Instruction.deserialize.
15
+ static readonly wireFormat: OperandType[] = [
16
+ OperandType.UINT8, // reserved
17
+ OperandType.UINT8, // indirect
18
+ OperandType.UINT32, // p1X
19
+ OperandType.UINT32, // p1Y
20
+ OperandType.UINT32, // p1IsInfinite
21
+ OperandType.UINT32, // p2X
22
+ OperandType.UINT32, // p2Y
23
+ OperandType.UINT32, // p2IsInfinite
24
+ OperandType.UINT32, // dst
25
+ ];
26
+
27
+ constructor(
28
+ private indirect: number,
29
+ private p1XOffset: number,
30
+ private p1YOffset: number,
31
+ private p1IsInfiniteOffset: number,
32
+ private p2XOffset: number,
33
+ private p2YOffset: number,
34
+ private p2IsInfiniteOffset: number,
35
+ private dstOffset: number,
36
+ ) {
37
+ super();
38
+ }
39
+
40
+ public async execute(context: AvmContext): Promise<void> {
41
+ const memoryOperations = { reads: 6, writes: 3, indirect: this.indirect };
42
+ const memory = context.machineState.memory.track(this.type);
43
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
44
+
45
+ const [p1XOffset, p1YOffset, p1IsInfiniteOffset, p2XOffset, p2YOffset, p2IsInfiniteOffset, dstOffset] =
46
+ Addressing.fromWire(this.indirect).resolve(
47
+ [
48
+ this.p1XOffset,
49
+ this.p1YOffset,
50
+ this.p1IsInfiniteOffset,
51
+ this.p2XOffset,
52
+ this.p2YOffset,
53
+ this.p2IsInfiniteOffset,
54
+ this.dstOffset,
55
+ ],
56
+ memory,
57
+ );
58
+
59
+ const p1X = memory.get(p1XOffset);
60
+ const p1Y = memory.get(p1YOffset);
61
+ const p1IsInfinite = memory.get(p1IsInfiniteOffset).toNumber() === 1;
62
+ const p1 = new Point(p1X.toFr(), p1Y.toFr());
63
+ if (!p1.isOnGrumpkin()) {
64
+ throw new Error(`Point1 is not on the curve`);
65
+ }
66
+
67
+ const p2X = memory.get(p2XOffset);
68
+ const p2Y = memory.get(p2YOffset);
69
+ // unused. Point doesn't store this information
70
+ const p2IsInfinite = memory.get(p2IsInfiniteOffset).toNumber() === 1;
71
+ const p2 = new Point(p2X.toFr(), p2Y.toFr());
72
+ if (!p2.isOnGrumpkin()) {
73
+ throw new Error(`Point1 is not on the curve`);
74
+ }
75
+
76
+ const grumpkin = new Grumpkin();
77
+ let dest = grumpkin.add(p1, p2);
78
+ // Temporary,
79
+ if (p1IsInfinite) {
80
+ dest = p2;
81
+ } else if (p2IsInfinite) {
82
+ dest = p1;
83
+ }
84
+ memory.set(dstOffset, new Field(dest.x));
85
+ memory.set(dstOffset + 1, new Field(dest.y));
86
+ // Check representation of infinity for grumpkin
87
+ memory.set(dstOffset + 2, new Field(dest.equals(Point.ZERO) ? 1 : 0));
88
+
89
+ memory.assert(memoryOperations);
90
+ context.machineState.incrementPc();
91
+ }
92
+ }
@@ -4,7 +4,7 @@ import { padArrayEnd } from '@aztec/foundation/collection';
4
4
  import { convertAvmResultsToPxResult, createPublicExecution } from '../../public/transitional_adaptors.js';
5
5
  import type { AvmContext } from '../avm_context.js';
6
6
  import { gasLeftToGas } from '../avm_gas.js';
7
- import { Field, Uint8 } from '../avm_memory_types.js';
7
+ import { Field, TypeTag, Uint8 } from '../avm_memory_types.js';
8
8
  import { type AvmContractCallResults } from '../avm_message_call_result.js';
9
9
  import { AvmSimulator } from '../avm_simulator.js';
10
10
  import { RethrownError } from '../errors.js';
@@ -53,9 +53,15 @@ abstract class ExternalCall extends Instruction {
53
53
  [this.gasOffset, this.addrOffset, this.argsOffset, this.argsSizeOffset, this.retOffset, this.successOffset],
54
54
  memory,
55
55
  );
56
+ memory.checkTags(TypeTag.FIELD, gasOffset, gasOffset + 1);
57
+ memory.checkTag(TypeTag.FIELD, addrOffset);
58
+ memory.checkTag(TypeTag.UINT32, argsSizeOffset);
59
+ memory.checkTag(TypeTag.FIELD, this.functionSelectorOffset);
56
60
 
57
- const callAddress = memory.getAs<Field>(addrOffset);
58
61
  const calldataSize = memory.get(argsSizeOffset).toNumber();
62
+ memory.checkTagsRange(TypeTag.FIELD, argsOffset, calldataSize);
63
+
64
+ const callAddress = memory.getAs<Field>(addrOffset);
59
65
  const calldata = memory.getSlice(argsOffset, calldataSize).map(f => f.toFr());
60
66
  const functionSelector = memory.getAs<Field>(this.functionSelectorOffset).toFr();
61
67
  // If we are already in a static call, we propagate the environment.
@@ -1,7 +1,7 @@
1
1
  import { keccak256, pedersenHash, poseidon2Permutation, sha256 } from '@aztec/foundation/crypto';
2
2
 
3
3
  import { type AvmContext } from '../avm_context.js';
4
- import { Field, Uint8 } from '../avm_memory_types.js';
4
+ import { Field, TypeTag, Uint8 } from '../avm_memory_types.js';
5
5
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
6
6
  import { Addressing } from './addressing_mode.js';
7
7
  import { Instruction } from './instruction.js';
@@ -32,6 +32,7 @@ export class Poseidon2 extends Instruction {
32
32
  [this.inputStateOffset, this.outputStateOffset],
33
33
  memory,
34
34
  );
35
+ memory.checkTagsRange(TypeTag.FIELD, inputOffset, Poseidon2.stateSize);
35
36
 
36
37
  const inputState = memory.getSlice(inputOffset, Poseidon2.stateSize);
37
38
  const outputState = poseidon2Permutation(inputState);
@@ -74,10 +75,13 @@ export class Keccak extends Instruction {
74
75
  [this.dstOffset, this.messageOffset, this.messageSizeOffset],
75
76
  memory,
76
77
  );
78
+ memory.checkTag(TypeTag.UINT32, messageSizeOffset);
77
79
  const messageSize = memory.get(messageSizeOffset).toNumber();
78
80
  const memoryOperations = { reads: messageSize + 1, writes: 32, indirect: this.indirect };
79
81
  context.machineState.consumeGas(this.gasCost(memoryOperations));
80
82
 
83
+ memory.checkTagsRange(TypeTag.UINT8, messageOffset, messageSize);
84
+
81
85
  const messageData = Buffer.concat(memory.getSlice(messageOffset, messageSize).map(word => word.toBuffer()));
82
86
  const hashBuffer = keccak256(messageData);
83
87
 
@@ -119,9 +123,11 @@ export class Sha256 extends Instruction {
119
123
  [this.dstOffset, this.messageOffset, this.messageSizeOffset],
120
124
  memory,
121
125
  );
126
+ memory.checkTag(TypeTag.UINT32, messageSizeOffset);
122
127
  const messageSize = memory.get(messageSizeOffset).toNumber();
123
128
  const memoryOperations = { reads: messageSize + 1, writes: 32, indirect: this.indirect };
124
129
  context.machineState.consumeGas(this.gasCost(memoryOperations));
130
+ memory.checkTagsRange(TypeTag.UINT8, messageOffset, messageSize);
125
131
 
126
132
  const messageData = Buffer.concat(memory.getSlice(messageOffset, messageSize).map(word => word.toBuffer()));
127
133
  const hashBuffer = sha256(messageData);
@@ -168,12 +174,16 @@ export class Pedersen extends Instruction {
168
174
 
169
175
  // We hash a set of field elements
170
176
  const genIndex = Number(memory.get(genIndexOffset).toBigInt());
177
+ memory.checkTag(TypeTag.UINT32, genIndexOffset);
171
178
  const messageSize = Number(memory.get(messageSizeOffset).toBigInt());
179
+ memory.checkTag(TypeTag.UINT32, messageSizeOffset);
172
180
  const hashData = memory.getSlice(messageOffset, messageSize);
173
181
 
174
182
  const memoryOperations = { reads: messageSize + 2, writes: 1, indirect: this.indirect };
175
183
  context.machineState.consumeGas(this.gasCost(memoryOperations));
176
184
 
185
+ memory.checkTagsRange(TypeTag.FIELD, messageOffset, messageSize);
186
+
177
187
  // No domain sep for now
178
188
  const hash = pedersenHash(hashData, genIndex);
179
189
  memory.set(dstOffset, new Field(hash));
@@ -1,6 +1,7 @@
1
1
  import { type AvmContext } from '../avm_context.js';
2
2
  import { type MemoryValue } from '../avm_memory_types.js';
3
3
  import { OperandType } from '../serialization/instruction_serialization.js';
4
+ import { Addressing } from './addressing_mode.js';
4
5
  import { Instruction } from './instruction.js';
5
6
 
6
7
  /** Wire format that informs deserialization for instructions with two operands. */
@@ -72,7 +73,9 @@ export abstract class GetterInstruction extends Instruction {
72
73
  const memory = context.machineState.memory.track(this.type);
73
74
  context.machineState.consumeGas(this.gasCost(memoryOperations));
74
75
 
75
- memory.set(this.dstOffset, this.getValue(context));
76
+ const [dstOffset] = Addressing.fromWire(this.indirect).resolve([this.dstOffset], memory);
77
+
78
+ memory.set(dstOffset, this.getValue(context));
76
79
 
77
80
  memory.assert(memoryOperations);
78
81
  context.machineState.incrementPc();
@@ -114,12 +114,17 @@ export class CMov extends Instruction {
114
114
  const memory = context.machineState.memory.track(this.type);
115
115
  context.machineState.consumeGas(this.gasCost(memoryOperations));
116
116
 
117
- const a = memory.get(this.aOffset);
118
- const b = memory.get(this.bOffset);
119
- const cond = memory.get(this.condOffset);
117
+ const [aOffset, bOffset, condOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve(
118
+ [this.aOffset, this.bOffset, this.condOffset, this.dstOffset],
119
+ memory,
120
+ );
121
+
122
+ const a = memory.get(aOffset);
123
+ const b = memory.get(bOffset);
124
+ const cond = memory.get(condOffset);
120
125
 
121
126
  // TODO: reconsider toBigInt() here
122
- memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b);
127
+ memory.set(dstOffset, cond.toBigInt() > 0 ? a : b);
123
128
 
124
129
  memory.assert(memoryOperations);
125
130
  context.machineState.incrementPc();
@@ -130,8 +135,8 @@ export class Cast extends TwoOperandInstruction {
130
135
  static readonly type: string = 'CAST';
131
136
  static readonly opcode = Opcode.CAST;
132
137
 
133
- constructor(indirect: number, dstTag: number, aOffset: number, dstOffset: number) {
134
- super(indirect, dstTag, aOffset, dstOffset);
138
+ constructor(indirect: number, dstTag: number, srcOffset: number, dstOffset: number) {
139
+ super(indirect, dstTag, srcOffset, dstOffset);
135
140
  }
136
141
 
137
142
  public async execute(context: AvmContext): Promise<void> {
@@ -139,13 +144,14 @@ export class Cast extends TwoOperandInstruction {
139
144
  const memory = context.machineState.memory.track(this.type);
140
145
  context.machineState.consumeGas(this.gasCost(memoryOperations));
141
146
 
142
- const a = memory.get(this.aOffset);
147
+ const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.aOffset, this.dstOffset], memory);
148
+
149
+ const a = memory.get(srcOffset);
143
150
 
144
- // TODO: consider not using toBigInt()
145
151
  const casted =
146
152
  this.inTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.inTag);
147
153
 
148
- memory.set(this.dstOffset, casted);
154
+ memory.set(dstOffset, casted);
149
155
 
150
156
  memory.assert(memoryOperations);
151
157
  context.machineState.incrementPc();
@@ -204,10 +210,11 @@ export class CalldataCopy extends Instruction {
204
210
  const memory = context.machineState.memory.track(this.type);
205
211
  context.machineState.consumeGas(this.gasCost(memoryOperations));
206
212
 
207
- const [dstOffset] = Addressing.fromWire(this.indirect).resolve([this.dstOffset], memory);
213
+ // We don't need to check tags here because: (1) the calldata is NOT in memory, and (2) we are the ones writing to destination.
214
+ const [cdOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.cdOffset, this.dstOffset], memory);
208
215
 
209
216
  const transformedData = context.environment.calldata
210
- .slice(this.cdOffset, this.cdOffset + this.copySize)
217
+ .slice(cdOffset, cdOffset + this.copySize)
211
218
  .map(f => new Field(f));
212
219
 
213
220
  memory.setSlice(dstOffset, transformedData);
@@ -0,0 +1,114 @@
1
+ import { Fq, Point } from '@aztec/circuits.js';
2
+ import { Grumpkin } from '@aztec/circuits.js/barretenberg';
3
+
4
+ import { strict as assert } from 'assert';
5
+
6
+ import { type AvmContext } from '../avm_context.js';
7
+ import { Field, TypeTag } from '../avm_memory_types.js';
8
+ import { InstructionExecutionError } from '../errors.js';
9
+ import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
10
+ import { Addressing } from './addressing_mode.js';
11
+ import { Instruction } from './instruction.js';
12
+
13
+ export class MultiScalarMul extends Instruction {
14
+ static type: string = 'MultiScalarMul';
15
+ static readonly opcode: Opcode = Opcode.MSM;
16
+
17
+ // Informs (de)serialization. See Instruction.deserialize.
18
+ static readonly wireFormat: OperandType[] = [
19
+ OperandType.UINT8 /* opcode */,
20
+ OperandType.UINT8 /* indirect */,
21
+ OperandType.UINT32 /* points vector offset */,
22
+ OperandType.UINT32 /* scalars vector offset */,
23
+ OperandType.UINT32 /* output offset (fixed triplet) */,
24
+ OperandType.UINT32 /* points length offset */,
25
+ ];
26
+
27
+ constructor(
28
+ private indirect: number,
29
+ private pointsOffset: number,
30
+ private scalarsOffset: number,
31
+ private outputOffset: number,
32
+ private pointsLengthOffset: number,
33
+ ) {
34
+ super();
35
+ }
36
+
37
+ public async execute(context: AvmContext): Promise<void> {
38
+ const memory = context.machineState.memory.track(this.type);
39
+ // Resolve indirects
40
+ const [pointsOffset, scalarsOffset, outputOffset] = Addressing.fromWire(this.indirect).resolve(
41
+ [this.pointsOffset, this.scalarsOffset, this.outputOffset],
42
+ memory,
43
+ );
44
+
45
+ // Length of the points vector should be U32
46
+ memory.checkTag(TypeTag.UINT32, this.pointsLengthOffset);
47
+ // Get the size of the unrolled (x, y , inf) points vector
48
+ const pointsReadLength = memory.get(this.pointsLengthOffset).toNumber();
49
+ assert(pointsReadLength % 3 === 0, 'Points vector offset should be a multiple of 3');
50
+ // Divide by 3 since each point is represented as a triplet to get the number of points
51
+ const numPoints = pointsReadLength / 3;
52
+ // The tag for each triplet will be (Field, Field, Uint8)
53
+ for (let i = 0; i < numPoints; i++) {
54
+ const offset = pointsOffset + i * 3;
55
+ // Check (Field, Field)
56
+ memory.checkTagsRange(TypeTag.FIELD, offset, 2);
57
+ // Check Uint8 (inf flag)
58
+ memory.checkTag(TypeTag.UINT8, offset + 2);
59
+ }
60
+ // Get the unrolled (x, y, inf) representing the points
61
+ const pointsVector = memory.getSlice(pointsOffset, pointsReadLength);
62
+
63
+ // The size of the scalars vector is twice the NUMBER of points because of the scalar limb decomposition
64
+ const scalarReadLength = numPoints * 2;
65
+ // Consume gas prior to performing work
66
+ const memoryOperations = {
67
+ reads: 1 + pointsReadLength + scalarReadLength /* points and scalars */,
68
+ writes: 3 /* output triplet */,
69
+ indirect: this.indirect,
70
+ };
71
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
72
+ // Get the unrolled scalar (lo & hi) representing the scalars
73
+ const scalarsVector = memory.getSlice(scalarsOffset, scalarReadLength);
74
+ memory.checkTagsRange(TypeTag.FIELD, scalarsOffset, scalarReadLength);
75
+
76
+ // Now we need to reconstruct the points and scalars into something we can operate on.
77
+ const grumpkinPoints: Point[] = [];
78
+ for (let i = 0; i < numPoints; i++) {
79
+ const p: Point = new Point(pointsVector[3 * i].toFr(), pointsVector[3 * i + 1].toFr());
80
+ // Include this later when we have a standard for representing infinity
81
+ // const isInf = pointsVector[i + 2].toBoolean();
82
+
83
+ if (!p.isOnGrumpkin()) {
84
+ throw new InstructionExecutionError(`Point ${p.toString()} is not on the curve.`);
85
+ }
86
+ grumpkinPoints.push(p);
87
+ }
88
+ // The scalars are read from memory as Fr elements, which are limbs of Fq elements
89
+ // So we need to reconstruct them before performing the scalar multiplications
90
+ const scalarFqVector: Fq[] = [];
91
+ for (let i = 0; i < numPoints; i++) {
92
+ const scalarLo = scalarsVector[2 * i].toFr();
93
+ const scalarHi = scalarsVector[2 * i + 1].toFr();
94
+ const fqScalar = Fq.fromHighLow(scalarHi, scalarLo);
95
+ scalarFqVector.push(fqScalar);
96
+ }
97
+ // TODO: Is there an efficient MSM implementation in ts that we can replace this by?
98
+ const grumpkin = new Grumpkin();
99
+ // Zip the points and scalars into pairs
100
+ const [firstBaseScalarPair, ...rest]: Array<[Point, Fq]> = grumpkinPoints.map((p, idx) => [p, scalarFqVector[idx]]);
101
+ // Fold the points and scalars into a single point
102
+ // We have to ensure get the first point, since the identity element (point at infinity) isn't quite working in ts
103
+ const outputPoint = rest.reduce(
104
+ (acc, curr) => grumpkin.add(acc, grumpkin.mul(curr[0], curr[1])),
105
+ grumpkin.mul(firstBaseScalarPair[0], firstBaseScalarPair[1]),
106
+ );
107
+ const output = outputPoint.toFieldsWithInf().map(f => new Field(f));
108
+
109
+ memory.setSlice(outputOffset, output);
110
+
111
+ memory.assert(memoryOperations);
112
+ context.machineState.incrementPc();
113
+ }
114
+ }
@@ -1,7 +1,7 @@
1
1
  import { Fr } from '@aztec/foundation/fields';
2
2
 
3
3
  import type { AvmContext } from '../avm_context.js';
4
- import { Field } from '../avm_memory_types.js';
4
+ import { Field, TypeTag } from '../avm_memory_types.js';
5
5
  import { StaticCallAlterationError } from '../errors.js';
6
6
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
7
7
  import { Addressing } from './addressing_mode.js';
@@ -31,8 +31,8 @@ export class SStore extends BaseStorageInstruction {
31
31
  static readonly type: string = 'SSTORE';
32
32
  static readonly opcode = Opcode.SSTORE;
33
33
 
34
- constructor(indirect: number, srcOffset: number, /*temporary*/ srcSize: number, slotOffset: number) {
35
- super(indirect, srcOffset, srcSize, slotOffset);
34
+ constructor(indirect: number, srcOffset: number, /*temporary*/ size: number, slotOffset: number) {
35
+ super(indirect, srcOffset, size, slotOffset);
36
36
  }
37
37
 
38
38
  public async execute(context: AvmContext): Promise<void> {
@@ -45,6 +45,8 @@ export class SStore extends BaseStorageInstruction {
45
45
  context.machineState.consumeGas(this.gasCost(memoryOperations));
46
46
 
47
47
  const [srcOffset, slotOffset] = Addressing.fromWire(this.indirect).resolve([this.aOffset, this.bOffset], memory);
48
+ memory.checkTag(TypeTag.FIELD, slotOffset);
49
+ memory.checkTagsRange(TypeTag.FIELD, srcOffset, this.size);
48
50
 
49
51
  const slot = memory.get(slotOffset).toFr();
50
52
  const data = memory.getSlice(srcOffset, this.size).map(field => field.toFr());
@@ -72,21 +74,19 @@ export class SLoad extends BaseStorageInstruction {
72
74
  const memory = context.machineState.memory.track(this.type);
73
75
  context.machineState.consumeGas(this.gasCost(memoryOperations));
74
76
 
75
- const [aOffset, size, bOffset] = Addressing.fromWire(this.indirect).resolve(
76
- [this.aOffset, this.size, this.bOffset],
77
- memory,
78
- );
77
+ const [slotOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.aOffset, this.bOffset], memory);
78
+ memory.checkTag(TypeTag.FIELD, slotOffset);
79
79
 
80
- const slot = memory.get(aOffset);
80
+ const slot = memory.get(slotOffset);
81
81
 
82
82
  // Write each read value from storage into memory
83
- for (let i = 0; i < size; i++) {
83
+ for (let i = 0; i < this.size; i++) {
84
84
  const data: Fr = await context.persistableState.readStorage(
85
85
  context.environment.storageAddress,
86
86
  new Fr(slot.toBigInt() + BigInt(i)),
87
87
  );
88
88
 
89
- memory.set(bOffset + i, new Field(data));
89
+ memory.set(dstOffset + i, new Field(data));
90
90
  }
91
91
 
92
92
  context.machineState.incrementPc();
@@ -1,4 +1,5 @@
1
1
  import { DAGasLeft, L2GasLeft } from '../opcodes/context_getters.js';
2
+ import { EcAdd } from '../opcodes/ec_add.js';
2
3
  import { Keccak, Pedersen, Poseidon2, Sha256 } from '../opcodes/hashing.js';
3
4
  import type { Instruction } from '../opcodes/index.js';
4
5
  import {
@@ -52,6 +53,7 @@ import {
52
53
  Version,
53
54
  Xor,
54
55
  } from '../opcodes/index.js';
56
+ import { MultiScalarMul } from '../opcodes/multi_scalar_mul.js';
55
57
  import { BufferCursor } from './buffer_cursor.js';
56
58
  import { Opcode } from './instruction_serialization.js';
57
59
 
@@ -137,10 +139,12 @@ const INSTRUCTION_SET = () =>
137
139
  [DebugLog.opcode, DebugLog],
138
140
 
139
141
  // Gadgets
142
+ [EcAdd.opcode, EcAdd],
140
143
  [Keccak.opcode, Keccak],
141
144
  [Poseidon2.opcode, Poseidon2],
142
145
  [Sha256.opcode, Sha256],
143
146
  [Pedersen.opcode, Pedersen],
147
+ [MultiScalarMul.opcode, MultiScalarMul],
144
148
  // Conversions
145
149
  [ToRadixLE.opcode, ToRadixLE],
146
150
  ]);
@@ -76,6 +76,8 @@ export enum Opcode {
76
76
  POSEIDON2,
77
77
  SHA256, // temp - may be removed, but alot of contracts rely on it
78
78
  PEDERSEN, // temp - may be removed, but alot of contracts rely on it
79
+ ECADD,
80
+ MSM,
79
81
  // Conversion
80
82
  TORADIXLE,
81
83
  }
@@ -3,10 +3,12 @@ import {
3
3
  type AztecNode,
4
4
  EncryptedL2Log,
5
5
  EncryptedL2NoteLog,
6
+ Event,
7
+ L1EventPayload,
6
8
  L1NotePayload,
7
9
  Note,
8
10
  type NoteStatus,
9
- TaggedNote,
11
+ TaggedLog,
10
12
  type UnencryptedL2Log,
11
13
  } from '@aztec/circuit-types';
12
14
  import {
@@ -19,7 +21,7 @@ import {
19
21
  type TxContext,
20
22
  } from '@aztec/circuits.js';
21
23
  import { Aes128 } from '@aztec/circuits.js/barretenberg';
22
- import { computePublicDataTreeLeafSlot, computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash';
24
+ import { computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash';
23
25
  import { type FunctionAbi, type FunctionArtifact, countArgumentsSize } from '@aztec/foundation/abi';
24
26
  import { AztecAddress } from '@aztec/foundation/aztec-address';
25
27
  import { pedersenHash } from '@aztec/foundation/crypto';
@@ -330,13 +332,15 @@ export class ClientExecutionContext extends ViewDataOracle {
330
332
 
331
333
  /**
332
334
  * Emit encrypted data
333
- * @param encryptedNote - The encrypted data.
335
+ * @param contractAddress - The contract emitting the encrypted event.
336
+ * @param randomness - A value used to mask the contract address we are siloing with.
337
+ * @param encryptedEvent - The encrypted event data.
334
338
  * @param counter - The effects counter.
335
339
  */
336
- public override emitEncryptedLog(
340
+ public override emitEncryptedEventLog(
337
341
  contractAddress: AztecAddress,
338
342
  randomness: Fr,
339
- encryptedData: Buffer,
343
+ encryptedEvent: Buffer,
340
344
  counter: number,
341
345
  ) {
342
346
  // In some cases, we actually want to reveal the contract address we are siloing with:
@@ -345,7 +349,7 @@ export class ClientExecutionContext extends ViewDataOracle {
345
349
  const maskedContractAddress = randomness.isZero()
346
350
  ? contractAddress.toField()
347
351
  : pedersenHash([contractAddress, randomness], 0);
348
- const encryptedLog = new CountedLog(new EncryptedL2Log(encryptedData, maskedContractAddress), counter);
352
+ const encryptedLog = new CountedLog(new EncryptedL2Log(encryptedEvent, maskedContractAddress), counter);
349
353
  this.encryptedLogs.push(encryptedLog);
350
354
  }
351
355
 
@@ -360,6 +364,34 @@ export class ClientExecutionContext extends ViewDataOracle {
360
364
  this.noteEncryptedLogs.push(encryptedLog);
361
365
  }
362
366
 
367
+ /**
368
+ * Encrypt an event
369
+ * @param contractAddress - The contract emitting the encrypted event.
370
+ * @param randomness - A value used to mask the contract address we are siloing with.
371
+ * @param eventTypeId - The type ID of the event (function selector).
372
+ * @param ovKeys - The outgoing viewing keys to use to encrypt.
373
+ * @param ivpkM - The master incoming viewing public key.
374
+ * @param preimage - The event preimage.
375
+ */
376
+ public override computeEncryptedEventLog(
377
+ contractAddress: AztecAddress,
378
+ randomness: Fr,
379
+ eventTypeId: Fr,
380
+ ovKeys: KeyValidationRequest,
381
+ ivpkM: Point,
382
+ preimage: Fr[],
383
+ ) {
384
+ const event = new Event(preimage);
385
+ const l1EventPayload = new L1EventPayload(event, contractAddress, randomness, eventTypeId);
386
+ const taggedEvent = new TaggedLog(l1EventPayload);
387
+
388
+ const ephSk = GrumpkinScalar.random();
389
+
390
+ const recipient = AztecAddress.random();
391
+
392
+ return taggedEvent.encrypt(ephSk, recipient, ivpkM, ovKeys);
393
+ }
394
+
363
395
  /**
364
396
  * Encrypt a note
365
397
  * @param contractAddress - The contract address of the note.
@@ -369,7 +401,7 @@ export class ClientExecutionContext extends ViewDataOracle {
369
401
  * @param ivpkM - The master incoming viewing public key.
370
402
  * @param preimage - The note preimage.
371
403
  */
372
- public override computeEncryptedLog(
404
+ public override computeEncryptedNoteLog(
373
405
  contractAddress: AztecAddress,
374
406
  storageSlot: Fr,
375
407
  noteTypeId: Fr,
@@ -379,10 +411,13 @@ export class ClientExecutionContext extends ViewDataOracle {
379
411
  ) {
380
412
  const note = new Note(preimage);
381
413
  const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId);
382
- const taggedNote = new TaggedNote(l1NotePayload);
414
+ const taggedNote = new TaggedLog(l1NotePayload);
383
415
 
384
416
  const ephSk = GrumpkinScalar.random();
385
417
 
418
+ // @todo This should be populated properly.
419
+ // Note that this encryption function SHOULD not be used, but is currently used
420
+ // as oracle for encrypted event logs.
386
421
  const recipient = AztecAddress.random();
387
422
 
388
423
  return taggedNote.encrypt(ephSk, recipient, ivpkM, ovKeys);
@@ -641,20 +676,13 @@ export class ClientExecutionContext extends ViewDataOracle {
641
676
  * @param numberOfElements - Number of elements to read from the starting storage slot.
642
677
  */
643
678
  public override async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise<Fr[]> {
644
- // TODO(#4320): This is a hack to work around not having directly access to the public data tree but
645
- // still having access to the witnesses
646
- const bn = await this.db.getBlockNumber();
647
-
648
679
  const values = [];
649
680
  for (let i = 0n; i < numberOfElements; i++) {
650
681
  const storageSlot = new Fr(startStorageSlot.value + i);
651
- const leafSlot = computePublicDataTreeLeafSlot(this.callContext.storageContractAddress, storageSlot);
652
- const witness = await this.db.getPublicDataTreeWitness(bn, leafSlot);
653
- if (!witness) {
654
- throw new Error(`No witness for slot ${storageSlot.toString()}`);
655
- }
656
- const value = witness.leafPreimage.value;
682
+
683
+ const value = await this.aztecNode.getPublicStorageAt(this.callContext.storageContractAddress, storageSlot);
657
684
  this.log.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`);
685
+
658
686
  values.push(value);
659
687
  }
660
688
  return values;