@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.
- package/dest/acvm/oracle/oracle.d.ts +3 -2
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +17 -6
- package/dest/acvm/oracle/typed_oracle.d.ts +3 -2
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +8 -5
- package/dest/avm/avm_gas.d.ts.map +1 -1
- package/dest/avm/avm_gas.js +3 -1
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_memory_types.js +2 -4
- package/dest/avm/fixtures/index.d.ts +4 -1
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +11 -6
- package/dest/avm/journal/journal.d.ts +3 -3
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +18 -13
- package/dest/avm/journal/trace.d.ts +3 -3
- package/dest/avm/journal/trace.d.ts.map +1 -1
- package/dest/avm/journal/trace.js +7 -6
- package/dest/avm/journal/trace_types.d.ts +4 -0
- package/dest/avm/journal/trace_types.d.ts.map +1 -1
- package/dest/avm/journal/trace_types.js +1 -5
- package/dest/avm/opcodes/accrued_substate.d.ts +2 -2
- package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
- package/dest/avm/opcodes/accrued_substate.js +36 -21
- package/dest/avm/opcodes/arithmetic.d.ts.map +1 -1
- package/dest/avm/opcodes/arithmetic.js +12 -9
- package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
- package/dest/avm/opcodes/bitwise.js +11 -8
- package/dest/avm/opcodes/comparators.d.ts.map +1 -1
- package/dest/avm/opcodes/comparators.js +7 -5
- package/dest/avm/opcodes/contract.d.ts.map +1 -1
- package/dest/avm/opcodes/contract.js +20 -24
- package/dest/avm/opcodes/control_flow.d.ts.map +1 -1
- package/dest/avm/opcodes/control_flow.js +4 -2
- package/dest/avm/opcodes/ec_add.d.ts +19 -0
- package/dest/avm/opcodes/ec_add.d.ts.map +1 -0
- package/dest/avm/opcodes/ec_add.js +78 -0
- package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
- package/dest/avm/opcodes/external_calls.js +8 -3
- package/dest/avm/opcodes/hashing.d.ts.map +1 -1
- package/dest/avm/opcodes/hashing.js +10 -2
- package/dest/avm/opcodes/instruction_impl.d.ts.map +1 -1
- package/dest/avm/opcodes/instruction_impl.js +4 -2
- package/dest/avm/opcodes/memory.d.ts +1 -1
- package/dest/avm/opcodes/memory.d.ts.map +1 -1
- package/dest/avm/opcodes/memory.js +14 -12
- package/dest/avm/opcodes/multi_scalar_mul.d.ts +16 -0
- package/dest/avm/opcodes/multi_scalar_mul.d.ts.map +1 -0
- package/dest/avm/opcodes/multi_scalar_mul.js +95 -0
- package/dest/avm/opcodes/storage.d.ts +1 -1
- package/dest/avm/opcodes/storage.d.ts.map +1 -1
- package/dest/avm/opcodes/storage.js +11 -8
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/bytecode_serialization.js +5 -1
- package/dest/avm/serialization/instruction_serialization.d.ts +3 -1
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +4 -2
- package/dest/client/client_execution_context.d.ts +15 -3
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +31 -17
- package/dest/client/simulator.d.ts +2 -1
- package/dest/client/simulator.d.ts.map +1 -1
- package/dest/client/simulator.js +17 -9
- package/dest/public/abstract_phase_manager.d.ts +10 -11
- package/dest/public/abstract_phase_manager.d.ts.map +1 -1
- package/dest/public/abstract_phase_manager.js +75 -53
- package/dest/public/app_logic_phase_manager.d.ts +3 -3
- package/dest/public/app_logic_phase_manager.d.ts.map +1 -1
- package/dest/public/app_logic_phase_manager.js +3 -3
- package/dest/public/db_interfaces.d.ts +1 -0
- package/dest/public/db_interfaces.d.ts.map +1 -1
- package/dest/public/execution.d.ts +1 -15
- package/dest/public/execution.d.ts.map +1 -1
- package/dest/public/execution.js +1 -51
- package/dest/public/executor.d.ts +1 -1
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +15 -8
- package/dest/public/hints_builder.d.ts +1 -1
- package/dest/public/index.d.ts +6 -6
- package/dest/public/index.d.ts.map +1 -1
- package/dest/public/index.js +7 -7
- package/dest/public/phase_manager_factory.d.ts +3 -3
- package/dest/public/phase_manager_factory.d.ts.map +1 -1
- package/dest/public/phase_manager_factory.js +5 -5
- package/dest/public/public_db_sources.d.ts +3 -1
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +54 -8
- package/dest/public/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor.js +4 -5
- package/dest/public/setup_phase_manager.d.ts +3 -3
- package/dest/public/setup_phase_manager.d.ts.map +1 -1
- package/dest/public/setup_phase_manager.js +3 -3
- package/dest/public/tail_phase_manager.d.ts +3 -3
- package/dest/public/tail_phase_manager.d.ts.map +1 -1
- package/dest/public/tail_phase_manager.js +3 -3
- package/dest/public/teardown_phase_manager.d.ts +3 -3
- package/dest/public/teardown_phase_manager.d.ts.map +1 -1
- package/dest/public/teardown_phase_manager.js +3 -3
- package/dest/public/transitional_adaptors.d.ts +1 -1
- package/dest/public/transitional_adaptors.d.ts.map +1 -1
- package/dest/public/transitional_adaptors.js +9 -3
- package/package.json +8 -8
- package/src/acvm/oracle/oracle.ts +35 -6
- package/src/acvm/oracle/typed_oracle.ts +20 -4
- package/src/avm/avm_gas.ts +2 -0
- package/src/avm/avm_memory_types.ts +1 -3
- package/src/avm/fixtures/index.ts +12 -8
- package/src/avm/journal/journal.ts +26 -21
- package/src/avm/journal/trace.ts +8 -11
- package/src/avm/journal/trace_types.ts +3 -0
- package/src/avm/opcodes/accrued_substate.ts +53 -20
- package/src/avm/opcodes/arithmetic.ts +17 -8
- package/src/avm/opcodes/bitwise.ts +13 -8
- package/src/avm/opcodes/comparators.ts +9 -4
- package/src/avm/opcodes/contract.ts +22 -26
- package/src/avm/opcodes/control_flow.ts +3 -1
- package/src/avm/opcodes/ec_add.ts +92 -0
- package/src/avm/opcodes/external_calls.ts +8 -2
- package/src/avm/opcodes/hashing.ts +11 -1
- package/src/avm/opcodes/instruction_impl.ts +4 -1
- package/src/avm/opcodes/memory.ts +18 -11
- package/src/avm/opcodes/multi_scalar_mul.ts +114 -0
- package/src/avm/opcodes/storage.ts +10 -10
- package/src/avm/serialization/bytecode_serialization.ts +4 -0
- package/src/avm/serialization/instruction_serialization.ts +2 -0
- package/src/client/client_execution_context.ts +46 -18
- package/src/client/simulator.ts +18 -8
- package/src/public/abstract_phase_manager.ts +77 -59
- package/src/public/app_logic_phase_manager.ts +2 -2
- package/src/public/db_interfaces.ts +2 -0
- package/src/public/execution.ts +0 -69
- package/src/public/executor.ts +17 -8
- package/src/public/index.ts +6 -12
- package/src/public/phase_manager_factory.ts +6 -6
- package/src/public/public_db_sources.ts +62 -7
- package/src/public/public_processor.ts +4 -7
- package/src/public/setup_phase_manager.ts +2 -2
- package/src/public/tail_phase_manager.ts +2 -2
- package/src/public/teardown_phase_manager.ts +2 -2
- package/src/public/transitional_adaptors.ts +42 -2
- package/dest/public/utils.d.ts +0 -8
- package/dest/public/utils.d.ts.map +0 -1
- package/dest/public/utils.js +0 -38
- 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
|
-
|
|
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
|
|
118
|
-
|
|
119
|
-
|
|
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(
|
|
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,
|
|
134
|
-
super(indirect, dstTag,
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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*/
|
|
35
|
-
super(indirect, srcOffset,
|
|
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 [
|
|
76
|
-
|
|
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(
|
|
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(
|
|
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
|
]);
|
|
@@ -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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
340
|
+
public override emitEncryptedEventLog(
|
|
337
341
|
contractAddress: AztecAddress,
|
|
338
342
|
randomness: Fr,
|
|
339
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
652
|
-
const
|
|
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;
|