@aztec/simulator 0.64.0 → 0.65.1
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/avm/avm_memory_types.d.ts +3 -1
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_memory_types.js +22 -24
- package/dest/avm/avm_simulator.d.ts +5 -1
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +16 -9
- package/dest/avm/avm_tree.d.ts +56 -7
- package/dest/avm/avm_tree.d.ts.map +1 -1
- package/dest/avm/avm_tree.js +155 -82
- package/dest/avm/errors.d.ts +19 -0
- package/dest/avm/errors.d.ts.map +1 -1
- package/dest/avm/errors.js +29 -1
- package/dest/avm/journal/journal.d.ts +8 -7
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +47 -29
- package/dest/avm/journal/nullifiers.d.ts +11 -58
- package/dest/avm/journal/nullifiers.d.ts.map +1 -1
- package/dest/avm/journal/nullifiers.js +27 -107
- package/dest/avm/opcodes/contract.d.ts +2 -2
- package/dest/avm/opcodes/contract.d.ts.map +1 -1
- package/dest/avm/opcodes/contract.js +4 -4
- package/dest/avm/opcodes/control_flow.d.ts +2 -2
- package/dest/avm/opcodes/control_flow.d.ts.map +1 -1
- package/dest/avm/opcodes/control_flow.js +4 -4
- package/dest/avm/opcodes/environment_getters.d.ts +2 -2
- package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
- package/dest/avm/opcodes/environment_getters.js +4 -4
- package/dest/avm/opcodes/instruction.d.ts +1 -1
- package/dest/avm/opcodes/instruction.d.ts.map +1 -1
- package/dest/avm/opcodes/instruction.js +1 -1
- package/dest/avm/opcodes/memory.d.ts +4 -4
- package/dest/avm/opcodes/memory.d.ts.map +1 -1
- package/dest/avm/opcodes/memory.js +17 -13
- package/dest/avm/opcodes/misc.d.ts +2 -2
- package/dest/avm/opcodes/misc.d.ts.map +1 -1
- package/dest/avm/opcodes/misc.js +4 -4
- package/dest/avm/serialization/buffer_cursor.d.ts +2 -0
- package/dest/avm/serialization/buffer_cursor.d.ts.map +1 -1
- package/dest/avm/serialization/buffer_cursor.js +8 -3
- package/dest/avm/serialization/bytecode_serialization.d.ts +1 -0
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/bytecode_serialization.js +27 -13
- package/dest/avm/serialization/instruction_serialization.d.ts +1 -0
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +9 -6
- package/dest/avm/test_utils.d.ts.map +1 -1
- package/dest/avm/test_utils.js +3 -2
- package/dest/common/errors.d.ts.map +1 -1
- package/dest/common/errors.js +3 -2
- package/dest/public/dual_side_effect_trace.d.ts +2 -2
- package/dest/public/dual_side_effect_trace.d.ts.map +1 -1
- package/dest/public/dual_side_effect_trace.js +7 -7
- package/dest/public/enqueued_call_side_effect_trace.d.ts +8 -23
- package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
- package/dest/public/enqueued_call_side_effect_trace.js +31 -92
- package/dest/public/executor_metrics.d.ts +4 -2
- package/dest/public/executor_metrics.d.ts.map +1 -1
- package/dest/public/executor_metrics.js +20 -3
- package/dest/public/fixtures/index.d.ts.map +1 -1
- package/dest/public/fixtures/index.js +66 -35
- 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 +26 -11
- package/dest/public/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor.js +12 -5
- package/dest/public/public_tx_context.d.ts +5 -6
- package/dest/public/public_tx_context.d.ts.map +1 -1
- package/dest/public/public_tx_context.js +19 -17
- package/dest/public/public_tx_simulator.d.ts +13 -2
- package/dest/public/public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator.js +263 -217
- package/dest/public/side_effect_trace.d.ts +2 -2
- package/dest/public/side_effect_trace.d.ts.map +1 -1
- package/dest/public/side_effect_trace.js +8 -6
- package/dest/public/side_effect_trace_interface.d.ts +2 -2
- package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
- package/dest/public/transitional_adapters.d.ts.map +1 -1
- package/dest/public/transitional_adapters.js +2 -6
- package/package.json +9 -9
- package/src/avm/avm_memory_types.ts +26 -24
- package/src/avm/avm_simulator.ts +20 -13
- package/src/avm/avm_tree.ts +196 -93
- package/src/avm/errors.ts +31 -0
- package/src/avm/journal/journal.ts +61 -47
- package/src/avm/journal/nullifiers.ts +29 -121
- package/src/avm/opcodes/contract.ts +2 -2
- package/src/avm/opcodes/control_flow.ts +2 -2
- package/src/avm/opcodes/environment_getters.ts +2 -2
- package/src/avm/opcodes/instruction.ts +1 -1
- package/src/avm/opcodes/memory.ts +15 -10
- package/src/avm/opcodes/misc.ts +2 -2
- package/src/avm/serialization/buffer_cursor.ts +9 -3
- package/src/avm/serialization/bytecode_serialization.ts +29 -13
- package/src/avm/serialization/instruction_serialization.ts +12 -6
- package/src/avm/test_utils.ts +9 -1
- package/src/common/errors.ts +2 -1
- package/src/public/dual_side_effect_trace.ts +6 -30
- package/src/public/enqueued_call_side_effect_trace.ts +35 -154
- package/src/public/executor_metrics.ts +23 -1
- package/src/public/fixtures/index.ts +97 -43
- package/src/public/public_db_sources.ts +29 -15
- package/src/public/public_processor.ts +11 -4
- package/src/public/public_tx_context.ts +21 -23
- package/src/public/public_tx_simulator.ts +45 -8
- package/src/public/side_effect_trace.ts +7 -9
- package/src/public/side_effect_trace_interface.ts +2 -4
- package/src/public/transitional_adapters.ts +0 -11
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import { type AztecAddress } from '@aztec/circuits.js';
|
|
2
|
-
import { siloNullifier } from '@aztec/circuits.js/hash';
|
|
3
1
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
2
|
|
|
5
3
|
import type { CommitmentsDB } from '../../index.js';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* A class to manage new nullifier staging and existence checks during a contract call's AVM simulation.
|
|
9
|
-
* Maintains a nullifier cache, and ensures that existence checks fall back to the correct source.
|
|
7
|
+
* Maintains a siloed nullifier cache, and ensures that existence checks fall back to the correct source.
|
|
10
8
|
* When a contract call completes, its cached nullifier set can be merged into its parent's.
|
|
11
9
|
*/
|
|
12
10
|
export class NullifierManager {
|
|
13
11
|
constructor(
|
|
14
12
|
/** Reference to node storage. Checked on parent cache-miss. */
|
|
15
13
|
private readonly hostNullifiers: CommitmentsDB,
|
|
16
|
-
/**
|
|
17
|
-
private
|
|
14
|
+
/** Cache of siloed nullifiers. */
|
|
15
|
+
private cache: Set<bigint> = new Set(),
|
|
18
16
|
/** Parent nullifier manager to fall back on */
|
|
19
17
|
private readonly parent?: NullifierManager,
|
|
20
18
|
) {}
|
|
@@ -22,32 +20,34 @@ export class NullifierManager {
|
|
|
22
20
|
/**
|
|
23
21
|
* Create a new nullifiers manager with some preloaded pending siloed nullifiers
|
|
24
22
|
*/
|
|
25
|
-
public static newWithPendingSiloedNullifiers(hostNullifiers: CommitmentsDB, pendingSiloedNullifiers
|
|
26
|
-
const
|
|
27
|
-
|
|
23
|
+
public static newWithPendingSiloedNullifiers(hostNullifiers: CommitmentsDB, pendingSiloedNullifiers?: Fr[]) {
|
|
24
|
+
const cachedSiloedNullifiers = new Set<bigint>();
|
|
25
|
+
if (pendingSiloedNullifiers !== undefined) {
|
|
26
|
+
pendingSiloedNullifiers.forEach(nullifier => cachedSiloedNullifiers.add(nullifier.toBigInt()));
|
|
27
|
+
}
|
|
28
|
+
return new NullifierManager(hostNullifiers, cachedSiloedNullifiers);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Create a new nullifiers manager forked from this one
|
|
32
33
|
*/
|
|
33
34
|
public fork() {
|
|
34
|
-
return new NullifierManager(this.hostNullifiers, new
|
|
35
|
+
return new NullifierManager(this.hostNullifiers, new Set(), this);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Get a nullifier's existence in this' cache or parent's (recursively).
|
|
39
40
|
* DOES NOT CHECK HOST STORAGE!
|
|
40
|
-
* @param
|
|
41
|
-
* @param nullifier - the nullifier to check for
|
|
41
|
+
* @param siloedNullifier - the nullifier to check for
|
|
42
42
|
* @returns exists: whether the nullifier exists in cache here or in parent's
|
|
43
43
|
*/
|
|
44
|
-
private checkExistsHereOrParent(
|
|
44
|
+
private checkExistsHereOrParent(siloedNullifier: Fr): boolean {
|
|
45
45
|
// First check this cache
|
|
46
|
-
let existsAsPending = this.cache.
|
|
46
|
+
let existsAsPending = this.cache.has(siloedNullifier.toBigInt());
|
|
47
47
|
// Then try parent's nullifier cache
|
|
48
48
|
if (!existsAsPending && this.parent) {
|
|
49
49
|
// Note: this will recurse to grandparent/etc until a cache-hit is encountered.
|
|
50
|
-
existsAsPending = this.parent.checkExistsHereOrParent(
|
|
50
|
+
existsAsPending = this.parent.checkExistsHereOrParent(siloedNullifier);
|
|
51
51
|
}
|
|
52
52
|
return existsAsPending;
|
|
53
53
|
}
|
|
@@ -59,24 +59,22 @@ export class NullifierManager {
|
|
|
59
59
|
* 3. Fall back to the host state.
|
|
60
60
|
* 4. Not found! Nullifier does not exist.
|
|
61
61
|
*
|
|
62
|
-
* @param
|
|
63
|
-
* @param nullifier - the nullifier to check for
|
|
62
|
+
* @param siloedNullifier - the nullifier to check for
|
|
64
63
|
* @returns exists: whether the nullifier exists at all,
|
|
65
64
|
* isPending: whether the nullifier was found in a cache,
|
|
66
65
|
* leafIndex: the nullifier's leaf index if it exists and is not pending (comes from host state).
|
|
67
66
|
*/
|
|
68
67
|
public async checkExists(
|
|
69
|
-
|
|
70
|
-
nullifier: Fr,
|
|
68
|
+
siloedNullifier: Fr,
|
|
71
69
|
): Promise<[/*exists=*/ boolean, /*isPending=*/ boolean, /*leafIndex=*/ Fr]> {
|
|
72
70
|
// Check this cache and parent's (recursively)
|
|
73
|
-
const existsAsPending = this.checkExistsHereOrParent(
|
|
71
|
+
const existsAsPending = this.checkExistsHereOrParent(siloedNullifier);
|
|
74
72
|
// Finally try the host's Aztec state (a trip to the database)
|
|
75
73
|
// If the value is found in the database, it will be associated with a leaf index!
|
|
76
74
|
let leafIndex: bigint | undefined = undefined;
|
|
77
75
|
if (!existsAsPending) {
|
|
78
76
|
// silo the nullifier before checking for its existence in the host
|
|
79
|
-
leafIndex = await this.hostNullifiers.getNullifierIndex(
|
|
77
|
+
leafIndex = await this.hostNullifiers.getNullifierIndex(siloedNullifier);
|
|
80
78
|
}
|
|
81
79
|
const exists = existsAsPending || leafIndex !== undefined;
|
|
82
80
|
leafIndex = leafIndex === undefined ? BigInt(0) : leafIndex;
|
|
@@ -86,17 +84,14 @@ export class NullifierManager {
|
|
|
86
84
|
/**
|
|
87
85
|
* Stage a new nullifier (append it to the cache).
|
|
88
86
|
*
|
|
89
|
-
* @param
|
|
90
|
-
* @param nullifier - the nullifier to stage
|
|
87
|
+
* @param siloedNullifier - the nullifier to stage
|
|
91
88
|
*/
|
|
92
|
-
public async append(
|
|
93
|
-
const [exists, ,] = await this.checkExists(
|
|
89
|
+
public async append(siloedNullifier: Fr) {
|
|
90
|
+
const [exists, ,] = await this.checkExists(siloedNullifier);
|
|
94
91
|
if (exists) {
|
|
95
|
-
throw new NullifierCollisionError(
|
|
96
|
-
`Nullifier ${nullifier} at contract ${contractAddress} already exists in parent cache or host.`,
|
|
97
|
-
);
|
|
92
|
+
throw new NullifierCollisionError(`Siloed nullifier ${siloedNullifier} already exists in parent cache or host.`);
|
|
98
93
|
}
|
|
99
|
-
this.cache.
|
|
94
|
+
this.cache.add(siloedNullifier.toBigInt());
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
/**
|
|
@@ -105,101 +100,14 @@ export class NullifierManager {
|
|
|
105
100
|
* @param incomingNullifiers - the incoming cached nullifiers to merge into this instance's
|
|
106
101
|
*/
|
|
107
102
|
public acceptAndMerge(incomingNullifiers: NullifierManager) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
* A class to cache nullifiers created during a contract call's AVM simulation.
|
|
114
|
-
* "append" updates a map, "exists" checks that map.
|
|
115
|
-
* An instance of this class can merge another instance's cached nullifiers into its own.
|
|
116
|
-
*/
|
|
117
|
-
export class NullifierCache {
|
|
118
|
-
/**
|
|
119
|
-
* Map for staging nullifiers.
|
|
120
|
-
* One inner-set per contract storage address,
|
|
121
|
-
* each entry being a nullifier.
|
|
122
|
-
*/
|
|
123
|
-
private cachePerContract: Map<bigint, Set<bigint>> = new Map();
|
|
124
|
-
private siloedNullifiers: Set<bigint> = new Set();
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* @parem siloedNullifierFrs: optional list of pending siloed nullifiers to initialize this cache with
|
|
128
|
-
*/
|
|
129
|
-
constructor(siloedNullifierFrs?: Fr[]) {
|
|
130
|
-
if (siloedNullifierFrs !== undefined) {
|
|
131
|
-
siloedNullifierFrs.forEach(nullifier => this.siloedNullifiers.add(nullifier.toBigInt()));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Check whether a nullifier exists in the cache.
|
|
137
|
-
*
|
|
138
|
-
* @param contractAddress - the address of the contract that the nullifier is associated with
|
|
139
|
-
* @param nullifier - the nullifier to check existence of
|
|
140
|
-
* @returns whether the nullifier is found in the cache
|
|
141
|
-
*/
|
|
142
|
-
public exists(contractAddress: AztecAddress, nullifier: Fr): boolean {
|
|
143
|
-
const exists =
|
|
144
|
-
this.cachePerContract.get(contractAddress.toBigInt())?.has(nullifier.toBigInt()) ||
|
|
145
|
-
this.siloedNullifiers.has(siloNullifier(contractAddress, nullifier).toBigInt());
|
|
146
|
-
return !!exists;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Stage a new nullifier (append it to the cache).
|
|
151
|
-
*
|
|
152
|
-
* @param contractAddress - the address of the contract that the nullifier is associated with
|
|
153
|
-
* @param nullifier - the nullifier to stage
|
|
154
|
-
*/
|
|
155
|
-
public append(contractAddress: AztecAddress, nullifier: Fr) {
|
|
156
|
-
if (this.exists(contractAddress, nullifier)) {
|
|
157
|
-
throw new NullifierCollisionError(
|
|
158
|
-
`Nullifier ${nullifier} at contract ${contractAddress} already exists in cache.`,
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let nullifiersForContract = this.cachePerContract.get(contractAddress.toBigInt());
|
|
163
|
-
// If this contract's nullifier set has no cached nullifiers, create a new Set to store them
|
|
164
|
-
if (!nullifiersForContract) {
|
|
165
|
-
nullifiersForContract = new Set();
|
|
166
|
-
this.cachePerContract.set(contractAddress.toBigInt(), nullifiersForContract);
|
|
167
|
-
}
|
|
168
|
-
nullifiersForContract.add(nullifier.toBigInt());
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Merge another cache's nullifiers into this instance's.
|
|
173
|
-
*
|
|
174
|
-
* Cached nullifiers in "incoming" must not collide with any present in "this".
|
|
175
|
-
*
|
|
176
|
-
* In practice, "this" is a parent call's pending nullifiers, and "incoming" is a nested call's.
|
|
177
|
-
*
|
|
178
|
-
* @param incomingNullifiers - the incoming cached nullifiers to merge into this instance's
|
|
179
|
-
*/
|
|
180
|
-
public acceptAndMerge(incomingNullifiers: NullifierCache) {
|
|
181
|
-
// Merge siloed nullifiers.
|
|
182
|
-
this.siloedNullifiers = new Set([...this.siloedNullifiers, ...incomingNullifiers.siloedNullifiers]);
|
|
183
|
-
// Iterate over all contracts with staged writes in the child.
|
|
184
|
-
for (const [incomingAddress, incomingCacheAtContract] of incomingNullifiers.cachePerContract) {
|
|
185
|
-
const thisCacheAtContract = this.cachePerContract.get(incomingAddress);
|
|
186
|
-
if (!thisCacheAtContract) {
|
|
187
|
-
// This contract has no nullifiers cached here
|
|
188
|
-
// so just accept incoming cache as-is for this contract.
|
|
189
|
-
this.cachePerContract.set(incomingAddress, incomingCacheAtContract);
|
|
190
|
-
} else {
|
|
191
|
-
// "Incoming" and "this" both have cached nullifiers for this contract.
|
|
192
|
-
// Merge in incoming nullifiers, erroring if there are any duplicates.
|
|
193
|
-
for (const nullifier of incomingCacheAtContract) {
|
|
194
|
-
if (thisCacheAtContract.has(nullifier)) {
|
|
195
|
-
throw new NullifierCollisionError(
|
|
196
|
-
`Failed to accept child call's nullifiers. Nullifier ${nullifier} already exists at contract ${incomingAddress}.`,
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
thisCacheAtContract.add(nullifier);
|
|
200
|
-
}
|
|
103
|
+
for (const incomingNullifier of incomingNullifiers.cache) {
|
|
104
|
+
if (this.cache.has(incomingNullifier)) {
|
|
105
|
+
throw new NullifierCollisionError(
|
|
106
|
+
`Failed to merge in fork's cached nullifiers. Siloed nullifier ${incomingNullifier} already exists in parent cache.`,
|
|
107
|
+
);
|
|
201
108
|
}
|
|
202
109
|
}
|
|
110
|
+
this.cache = new Set([...this.cache, ...incomingNullifiers.cache]);
|
|
203
111
|
}
|
|
204
112
|
}
|
|
205
113
|
|
|
@@ -18,18 +18,18 @@ export class GetContractInstance extends Instruction {
|
|
|
18
18
|
static readonly wireFormat: OperandType[] = [
|
|
19
19
|
OperandType.UINT8, // opcode
|
|
20
20
|
OperandType.UINT8, // indirect bits
|
|
21
|
-
OperandType.UINT8, // member enum (immediate)
|
|
22
21
|
OperandType.UINT16, // addressOffset
|
|
23
22
|
OperandType.UINT16, // dstOffset
|
|
24
23
|
OperandType.UINT16, // existsOfsset
|
|
24
|
+
OperandType.UINT8, // member enum (immediate)
|
|
25
25
|
];
|
|
26
26
|
|
|
27
27
|
constructor(
|
|
28
28
|
private indirect: number,
|
|
29
|
-
private memberEnum: number,
|
|
30
29
|
private addressOffset: number,
|
|
31
30
|
private dstOffset: number,
|
|
32
31
|
private existsOffset: number,
|
|
32
|
+
private memberEnum: number,
|
|
33
33
|
) {
|
|
34
34
|
super();
|
|
35
35
|
}
|
|
@@ -36,11 +36,11 @@ export class JumpI extends Instruction {
|
|
|
36
36
|
static readonly wireFormat: OperandType[] = [
|
|
37
37
|
OperandType.UINT8,
|
|
38
38
|
OperandType.UINT8,
|
|
39
|
-
OperandType.UINT32,
|
|
40
39
|
OperandType.UINT16,
|
|
40
|
+
OperandType.UINT32,
|
|
41
41
|
];
|
|
42
42
|
|
|
43
|
-
constructor(private indirect: number, private
|
|
43
|
+
constructor(private indirect: number, private condOffset: number, private loc: number) {
|
|
44
44
|
super();
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -60,11 +60,11 @@ export class GetEnvVar extends Instruction {
|
|
|
60
60
|
static readonly wireFormat16: OperandType[] = [
|
|
61
61
|
OperandType.UINT8, // opcode
|
|
62
62
|
OperandType.UINT8, // indirect
|
|
63
|
-
OperandType.UINT8, // variable enum (immediate)
|
|
64
63
|
OperandType.UINT16, // dstOffset
|
|
64
|
+
OperandType.UINT8, // variable enum (immediate)
|
|
65
65
|
];
|
|
66
66
|
|
|
67
|
-
constructor(private indirect: number, private
|
|
67
|
+
constructor(private indirect: number, private dstOffset: number, private varEnum: number) {
|
|
68
68
|
super();
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -94,7 +94,7 @@ export abstract class Instruction {
|
|
|
94
94
|
* Computes gas cost for the instruction based on its base cost and memory operations.
|
|
95
95
|
* @returns Gas cost.
|
|
96
96
|
*/
|
|
97
|
-
|
|
97
|
+
protected gasCost(dynMultiplier: number = 0): Gas {
|
|
98
98
|
const baseGasCost = getBaseGasCost(this.opcode);
|
|
99
99
|
const dynGasCost = mulGas(getDynamicGasCost(this.opcode), dynMultiplier);
|
|
100
100
|
return sumGas(baseGasCost, dynGasCost);
|
|
@@ -12,63 +12,66 @@ export class Set extends Instruction {
|
|
|
12
12
|
public static readonly wireFormat8: OperandType[] = [
|
|
13
13
|
OperandType.UINT8, // opcode
|
|
14
14
|
OperandType.UINT8, // indirect
|
|
15
|
+
OperandType.UINT8, // dstOffset
|
|
15
16
|
OperandType.UINT8, // tag
|
|
16
17
|
OperandType.UINT8, // const (value)
|
|
17
|
-
OperandType.UINT8, // dstOffset
|
|
18
18
|
];
|
|
19
19
|
public static readonly wireFormat16: OperandType[] = [
|
|
20
20
|
OperandType.UINT8, // opcode
|
|
21
21
|
OperandType.UINT8, // indirect
|
|
22
|
+
OperandType.UINT16, // dstOffset
|
|
22
23
|
OperandType.UINT8, // tag
|
|
23
24
|
OperandType.UINT16, // const (value)
|
|
24
|
-
OperandType.UINT16, // dstOffset
|
|
25
25
|
];
|
|
26
26
|
public static readonly wireFormat32: OperandType[] = [
|
|
27
27
|
OperandType.UINT8, // opcode
|
|
28
28
|
OperandType.UINT8, // indirect
|
|
29
|
+
OperandType.UINT16, // dstOffset
|
|
29
30
|
OperandType.UINT8, // tag
|
|
30
31
|
OperandType.UINT32, // const (value)
|
|
31
|
-
OperandType.UINT16, // dstOffset
|
|
32
32
|
];
|
|
33
33
|
public static readonly wireFormat64: OperandType[] = [
|
|
34
34
|
OperandType.UINT8, // opcode
|
|
35
35
|
OperandType.UINT8, // indirect
|
|
36
|
+
OperandType.UINT16, // dstOffset
|
|
36
37
|
OperandType.UINT8, // tag
|
|
37
38
|
OperandType.UINT64, // const (value)
|
|
38
|
-
OperandType.UINT16, // dstOffset
|
|
39
39
|
];
|
|
40
40
|
public static readonly wireFormat128: OperandType[] = [
|
|
41
41
|
OperandType.UINT8, // opcode
|
|
42
42
|
OperandType.UINT8, // indirect
|
|
43
|
+
OperandType.UINT16, // dstOffset
|
|
43
44
|
OperandType.UINT8, // tag
|
|
44
45
|
OperandType.UINT128, // const (value)
|
|
45
|
-
OperandType.UINT16, // dstOffset
|
|
46
46
|
];
|
|
47
47
|
public static readonly wireFormatFF: OperandType[] = [
|
|
48
48
|
OperandType.UINT8, // opcode
|
|
49
49
|
OperandType.UINT8, // indirect
|
|
50
|
+
OperandType.UINT16, // dstOffset
|
|
50
51
|
OperandType.UINT8, // tag
|
|
51
52
|
OperandType.FF, // const (value)
|
|
52
|
-
OperandType.UINT16, // dstOffset
|
|
53
53
|
];
|
|
54
54
|
|
|
55
55
|
constructor(
|
|
56
56
|
private indirect: number,
|
|
57
|
+
private dstOffset: number,
|
|
57
58
|
private inTag: number,
|
|
58
59
|
private value: bigint | number,
|
|
59
|
-
private dstOffset: number,
|
|
60
60
|
) {
|
|
61
61
|
super();
|
|
62
|
+
TaggedMemory.checkIsValidTag(inTag);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
public async execute(context: AvmContext): Promise<void> {
|
|
66
|
+
// Constructor ensured that this.inTag is a valid tag
|
|
67
|
+
const res = TaggedMemory.buildFromTagTruncating(this.value, this.inTag);
|
|
68
|
+
|
|
65
69
|
const memory = context.machineState.memory.track(this.type);
|
|
66
70
|
context.machineState.consumeGas(this.gasCost());
|
|
67
71
|
|
|
68
72
|
const operands = [this.dstOffset];
|
|
69
73
|
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
70
74
|
const [dstOffset] = addressing.resolve(operands, memory);
|
|
71
|
-
const res = TaggedMemory.buildFromTagTruncating(this.value, this.inTag);
|
|
72
75
|
memory.set(dstOffset, res);
|
|
73
76
|
|
|
74
77
|
memory.assert({ writes: 1, addressing });
|
|
@@ -87,15 +90,16 @@ export class Cast extends Instruction {
|
|
|
87
90
|
OperandType.UINT8,
|
|
88
91
|
];
|
|
89
92
|
static readonly wireFormat16 = [
|
|
90
|
-
OperandType.UINT8,
|
|
91
93
|
OperandType.UINT8,
|
|
92
94
|
OperandType.UINT8,
|
|
93
95
|
OperandType.UINT16,
|
|
94
96
|
OperandType.UINT16,
|
|
97
|
+
OperandType.UINT8,
|
|
95
98
|
];
|
|
96
99
|
|
|
97
|
-
constructor(private indirect: number, private
|
|
100
|
+
constructor(private indirect: number, private srcOffset: number, private dstOffset: number, private dstTag: number) {
|
|
98
101
|
super();
|
|
102
|
+
TaggedMemory.checkIsValidTag(dstTag);
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
public async execute(context: AvmContext): Promise<void> {
|
|
@@ -107,6 +111,7 @@ export class Cast extends Instruction {
|
|
|
107
111
|
const [srcOffset, dstOffset] = addressing.resolve(operands, memory);
|
|
108
112
|
|
|
109
113
|
const a = memory.get(srcOffset);
|
|
114
|
+
// Constructor ensured that this.dstTag is a valid tag
|
|
110
115
|
const casted = TaggedMemory.buildFromTagTruncating(a.toBigInt(), this.dstTag);
|
|
111
116
|
|
|
112
117
|
memory.set(dstOffset, casted);
|
package/src/avm/opcodes/misc.ts
CHANGED
|
@@ -16,17 +16,17 @@ export class DebugLog extends Instruction {
|
|
|
16
16
|
OperandType.UINT8, // Opcode
|
|
17
17
|
OperandType.UINT8, // Indirect
|
|
18
18
|
OperandType.UINT16, // message memory address
|
|
19
|
-
OperandType.UINT16, // message size
|
|
20
19
|
OperandType.UINT16, // fields memory address
|
|
21
20
|
OperandType.UINT16, // fields size address
|
|
21
|
+
OperandType.UINT16, // message size
|
|
22
22
|
];
|
|
23
23
|
|
|
24
24
|
constructor(
|
|
25
25
|
private indirect: number,
|
|
26
26
|
private messageOffset: number,
|
|
27
|
-
private messageSize: number,
|
|
28
27
|
private fieldsOffset: number,
|
|
29
28
|
private fieldsSizeOffset: number,
|
|
29
|
+
private messageSize: number,
|
|
30
30
|
) {
|
|
31
31
|
super();
|
|
32
32
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { strict as assert } from 'assert';
|
|
2
|
-
|
|
3
1
|
/*
|
|
4
2
|
* A Buffer-like class that automatically advances the position.
|
|
5
3
|
*/
|
|
@@ -10,6 +8,10 @@ export class BufferCursor {
|
|
|
10
8
|
return this._position;
|
|
11
9
|
}
|
|
12
10
|
|
|
11
|
+
public buffer(): Buffer {
|
|
12
|
+
return this._buffer;
|
|
13
|
+
}
|
|
14
|
+
|
|
13
15
|
public eof(): boolean {
|
|
14
16
|
return this._position === this._buffer.length;
|
|
15
17
|
}
|
|
@@ -20,7 +22,11 @@ export class BufferCursor {
|
|
|
20
22
|
|
|
21
23
|
public advance(n: number): void {
|
|
22
24
|
this._position += n;
|
|
23
|
-
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public peekUint8(): number {
|
|
28
|
+
const ret = this._buffer.readUint8(this._position);
|
|
29
|
+
return ret;
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
public readUint8(): number {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AvmExecutionError, AvmParsingError, InvalidOpcodeError, InvalidProgramCounterError } from '../errors.js';
|
|
1
2
|
import {
|
|
2
3
|
Add,
|
|
3
4
|
And,
|
|
@@ -48,7 +49,7 @@ import {
|
|
|
48
49
|
} from '../opcodes/index.js';
|
|
49
50
|
import { MultiScalarMul } from '../opcodes/multi_scalar_mul.js';
|
|
50
51
|
import { BufferCursor } from './buffer_cursor.js';
|
|
51
|
-
import { Opcode } from './instruction_serialization.js';
|
|
52
|
+
import { MAX_OPCODE_VALUE, Opcode } from './instruction_serialization.js';
|
|
52
53
|
|
|
53
54
|
export type InstructionDeserializer = (buf: BufferCursor | Buffer) => Instruction;
|
|
54
55
|
|
|
@@ -63,7 +64,7 @@ export interface Deserializable {
|
|
|
63
64
|
export type InstructionSet = Map<Opcode, InstructionDeserializer>;
|
|
64
65
|
// TODO(4359): This is a function so that Call and StaticCall can be lazily resolved.
|
|
65
66
|
// This is a temporary solution until we solve the dependency cycle.
|
|
66
|
-
const INSTRUCTION_SET = () =>
|
|
67
|
+
export const INSTRUCTION_SET = () =>
|
|
67
68
|
new Map<Opcode, InstructionDeserializer>([
|
|
68
69
|
[Opcode.ADD_8, Add.as(Add.wireFormat8).deserialize],
|
|
69
70
|
[Opcode.ADD_16, Add.as(Add.wireFormat16).deserialize],
|
|
@@ -179,18 +180,33 @@ export function decodeInstructionFromBytecode(
|
|
|
179
180
|
instructionSet: InstructionSet = INSTRUCTION_SET(),
|
|
180
181
|
): [Instruction, number] {
|
|
181
182
|
if (pc >= bytecode.length) {
|
|
182
|
-
throw new
|
|
183
|
+
throw new InvalidProgramCounterError(pc, bytecode.length);
|
|
183
184
|
}
|
|
184
|
-
const cursor = new BufferCursor(bytecode, pc);
|
|
185
|
-
const startingPosition = cursor.position();
|
|
186
|
-
const opcode: Opcode = cursor.bufferAtPosition().readUint8(); // peek.
|
|
187
185
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
186
|
+
try {
|
|
187
|
+
const cursor = new BufferCursor(bytecode, pc);
|
|
188
|
+
const startingPosition = cursor.position();
|
|
189
|
+
const opcode: number = cursor.bufferAtPosition().readUint8(); // peek.
|
|
190
|
+
|
|
191
|
+
if (opcode > MAX_OPCODE_VALUE) {
|
|
192
|
+
throw new InvalidOpcodeError(
|
|
193
|
+
`Opcode ${opcode} (0x${opcode.toString(16)}) value is not in the range of valid opcodes.`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const instructionDeserializerOrUndef = instructionSet.get(opcode);
|
|
198
|
+
if (instructionDeserializerOrUndef === undefined) {
|
|
199
|
+
throw new InvalidOpcodeError(`Opcode ${Opcode[opcode]} (0x${opcode.toString(16)}) is not implemented`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const instructionDeserializer: InstructionDeserializer = instructionDeserializerOrUndef;
|
|
203
|
+
const instruction = instructionDeserializer(cursor);
|
|
204
|
+
return [instruction, cursor.position() - startingPosition];
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (error instanceof InvalidOpcodeError || error instanceof AvmExecutionError) {
|
|
207
|
+
throw error;
|
|
208
|
+
} else {
|
|
209
|
+
throw new AvmParsingError(`${error}`);
|
|
210
|
+
}
|
|
191
211
|
}
|
|
192
|
-
|
|
193
|
-
const instructionDeserializer: InstructionDeserializer = instructionDeserializerOrUndef;
|
|
194
|
-
const instruction = instructionDeserializer(cursor);
|
|
195
|
-
return [instruction, cursor.position() - startingPosition];
|
|
196
212
|
}
|
|
@@ -86,6 +86,12 @@ export enum Opcode {
|
|
|
86
86
|
TORADIXBE,
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
export const MAX_OPCODE_VALUE = Math.max(
|
|
90
|
+
...Object.values(Opcode)
|
|
91
|
+
.map(k => +k)
|
|
92
|
+
.filter(k => !isNaN(k)),
|
|
93
|
+
);
|
|
94
|
+
|
|
89
95
|
// Possible types for an instruction's operand in its wire format. (Keep in sync with CPP code.
|
|
90
96
|
// See vm/avm_trace/deserialization.cpp)
|
|
91
97
|
// Note that cpp code introduced an additional enum value TAG to express the instruction tag. In TS,
|
|
@@ -103,7 +109,7 @@ type OperandNativeType = number | bigint;
|
|
|
103
109
|
type OperandWriter = (value: any) => void;
|
|
104
110
|
|
|
105
111
|
// Specifies how to read and write each operand type.
|
|
106
|
-
const OPERAND_SPEC = new Map<OperandType, [number, () => OperandNativeType, OperandWriter]>([
|
|
112
|
+
const OPERAND_SPEC = new Map<OperandType, [number, (offset: number) => OperandNativeType, OperandWriter]>([
|
|
107
113
|
[OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]],
|
|
108
114
|
[OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]],
|
|
109
115
|
[OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]],
|
|
@@ -112,12 +118,12 @@ const OPERAND_SPEC = new Map<OperandType, [number, () => OperandNativeType, Oper
|
|
|
112
118
|
[OperandType.FF, [32, readBigInt254BE, writeBigInt254BE]],
|
|
113
119
|
]);
|
|
114
120
|
|
|
115
|
-
function readBigInt254BE(this: Buffer): bigint {
|
|
121
|
+
function readBigInt254BE(this: Buffer, offset: number): bigint {
|
|
116
122
|
const totalBytes = 32;
|
|
117
123
|
let ret: bigint = 0n;
|
|
118
124
|
for (let i = 0; i < totalBytes; ++i) {
|
|
119
125
|
ret <<= 8n;
|
|
120
|
-
ret |= BigInt(this.readUint8(i));
|
|
126
|
+
ret |= BigInt(this.readUint8(i + offset));
|
|
121
127
|
}
|
|
122
128
|
return ret;
|
|
123
129
|
}
|
|
@@ -130,12 +136,12 @@ function writeBigInt254BE(this: Buffer, value: bigint): void {
|
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
|
|
133
|
-
function readBigInt128BE(this: Buffer): bigint {
|
|
139
|
+
function readBigInt128BE(this: Buffer, offset: number): bigint {
|
|
134
140
|
const totalBytes = 16;
|
|
135
141
|
let ret: bigint = 0n;
|
|
136
142
|
for (let i = 0; i < totalBytes; ++i) {
|
|
137
143
|
ret <<= 8n;
|
|
138
|
-
ret |= BigInt(this.readUint8(i));
|
|
144
|
+
ret |= BigInt(this.readUint8(i + offset));
|
|
139
145
|
}
|
|
140
146
|
return ret;
|
|
141
147
|
}
|
|
@@ -163,7 +169,7 @@ export function deserialize(cursor: BufferCursor | Buffer, operands: OperandType
|
|
|
163
169
|
for (const op of operands) {
|
|
164
170
|
const opType = op;
|
|
165
171
|
const [sizeBytes, reader, _writer] = OPERAND_SPEC.get(opType)!;
|
|
166
|
-
argValues.push(reader.call(cursor.
|
|
172
|
+
argValues.push(reader.call(cursor.buffer(), cursor.position()));
|
|
167
173
|
cursor.advance(sizeBytes);
|
|
168
174
|
}
|
|
169
175
|
|
package/src/avm/test_utils.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ContractClassPublic,
|
|
3
|
+
type ContractInstanceWithAddress,
|
|
4
|
+
Fr,
|
|
5
|
+
computePublicBytecodeCommitment,
|
|
6
|
+
} from '@aztec/circuits.js';
|
|
2
7
|
|
|
3
8
|
import { type jest } from '@jest/globals';
|
|
4
9
|
import { mock } from 'jest-mock-extended';
|
|
@@ -8,6 +13,9 @@ import { type PublicSideEffectTraceInterface } from '../public/side_effect_trace
|
|
|
8
13
|
|
|
9
14
|
export function mockGetBytecode(worldStateDB: WorldStateDB, bytecode: Buffer) {
|
|
10
15
|
(worldStateDB as jest.Mocked<WorldStateDB>).getBytecode.mockResolvedValue(bytecode);
|
|
16
|
+
(worldStateDB as jest.Mocked<WorldStateDB>).getBytecodeCommitment.mockResolvedValue(
|
|
17
|
+
computePublicBytecodeCommitment(bytecode),
|
|
18
|
+
);
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
export function mockTraceFork(trace: PublicSideEffectTraceInterface, nestedTrace?: PublicSideEffectTraceInterface) {
|
package/src/common/errors.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '@aztec/circuit-types';
|
|
7
7
|
import { type Fr } from '@aztec/circuits.js';
|
|
8
8
|
import type { BrilligFunctionId, FunctionAbi, FunctionDebugMetadata, OpcodeLocation } from '@aztec/foundation/abi';
|
|
9
|
+
import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
9
10
|
|
|
10
11
|
import { type RawAssertionPayload } from '@noir-lang/acvm_js';
|
|
11
12
|
import { abiDecodeError } from '@noir-lang/noirc_abi';
|
|
@@ -153,7 +154,7 @@ export function resolveAssertionMessage(errorPayload: RawAssertionPayload, abi:
|
|
|
153
154
|
if (typeof decoded === 'string') {
|
|
154
155
|
return decoded;
|
|
155
156
|
} else {
|
|
156
|
-
return
|
|
157
|
+
return jsonStringify(decoded);
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
|
|
@@ -94,50 +94,26 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
public traceNullifierCheck(
|
|
97
|
-
|
|
98
|
-
nullifier: Fr,
|
|
97
|
+
siloedNullifier: Fr,
|
|
99
98
|
exists: boolean,
|
|
100
99
|
lowLeafPreimage: NullifierLeafPreimage,
|
|
101
100
|
lowLeafIndex: Fr,
|
|
102
101
|
lowLeafPath: Fr[],
|
|
103
102
|
) {
|
|
104
|
-
this.innerCallTrace.traceNullifierCheck(
|
|
105
|
-
|
|
106
|
-
nullifier,
|
|
107
|
-
exists,
|
|
108
|
-
lowLeafPreimage,
|
|
109
|
-
lowLeafIndex,
|
|
110
|
-
lowLeafPath,
|
|
111
|
-
);
|
|
112
|
-
this.enqueuedCallTrace.traceNullifierCheck(
|
|
113
|
-
contractAddress,
|
|
114
|
-
nullifier,
|
|
115
|
-
exists,
|
|
116
|
-
lowLeafPreimage,
|
|
117
|
-
lowLeafIndex,
|
|
118
|
-
lowLeafPath,
|
|
119
|
-
);
|
|
103
|
+
this.innerCallTrace.traceNullifierCheck(siloedNullifier, exists, lowLeafPreimage, lowLeafIndex, lowLeafPath);
|
|
104
|
+
this.enqueuedCallTrace.traceNullifierCheck(siloedNullifier, exists, lowLeafPreimage, lowLeafIndex, lowLeafPath);
|
|
120
105
|
}
|
|
121
106
|
|
|
122
107
|
public traceNewNullifier(
|
|
123
|
-
|
|
124
|
-
nullifier: Fr,
|
|
108
|
+
siloedNullifier: Fr,
|
|
125
109
|
lowLeafPreimage: NullifierLeafPreimage,
|
|
126
110
|
lowLeafIndex: Fr,
|
|
127
111
|
lowLeafPath: Fr[],
|
|
128
112
|
insertionPath: Fr[],
|
|
129
113
|
) {
|
|
130
|
-
this.innerCallTrace.traceNewNullifier(
|
|
131
|
-
contractAddress,
|
|
132
|
-
nullifier,
|
|
133
|
-
lowLeafPreimage,
|
|
134
|
-
lowLeafIndex,
|
|
135
|
-
lowLeafPath,
|
|
136
|
-
insertionPath,
|
|
137
|
-
);
|
|
114
|
+
this.innerCallTrace.traceNewNullifier(siloedNullifier, lowLeafPreimage, lowLeafIndex, lowLeafPath, insertionPath);
|
|
138
115
|
this.enqueuedCallTrace.traceNewNullifier(
|
|
139
|
-
|
|
140
|
-
nullifier,
|
|
116
|
+
siloedNullifier,
|
|
141
117
|
lowLeafPreimage,
|
|
142
118
|
lowLeafIndex,
|
|
143
119
|
lowLeafPath,
|