@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.
Files changed (107) hide show
  1. package/dest/avm/avm_memory_types.d.ts +3 -1
  2. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  3. package/dest/avm/avm_memory_types.js +22 -24
  4. package/dest/avm/avm_simulator.d.ts +5 -1
  5. package/dest/avm/avm_simulator.d.ts.map +1 -1
  6. package/dest/avm/avm_simulator.js +16 -9
  7. package/dest/avm/avm_tree.d.ts +56 -7
  8. package/dest/avm/avm_tree.d.ts.map +1 -1
  9. package/dest/avm/avm_tree.js +155 -82
  10. package/dest/avm/errors.d.ts +19 -0
  11. package/dest/avm/errors.d.ts.map +1 -1
  12. package/dest/avm/errors.js +29 -1
  13. package/dest/avm/journal/journal.d.ts +8 -7
  14. package/dest/avm/journal/journal.d.ts.map +1 -1
  15. package/dest/avm/journal/journal.js +47 -29
  16. package/dest/avm/journal/nullifiers.d.ts +11 -58
  17. package/dest/avm/journal/nullifiers.d.ts.map +1 -1
  18. package/dest/avm/journal/nullifiers.js +27 -107
  19. package/dest/avm/opcodes/contract.d.ts +2 -2
  20. package/dest/avm/opcodes/contract.d.ts.map +1 -1
  21. package/dest/avm/opcodes/contract.js +4 -4
  22. package/dest/avm/opcodes/control_flow.d.ts +2 -2
  23. package/dest/avm/opcodes/control_flow.d.ts.map +1 -1
  24. package/dest/avm/opcodes/control_flow.js +4 -4
  25. package/dest/avm/opcodes/environment_getters.d.ts +2 -2
  26. package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
  27. package/dest/avm/opcodes/environment_getters.js +4 -4
  28. package/dest/avm/opcodes/instruction.d.ts +1 -1
  29. package/dest/avm/opcodes/instruction.d.ts.map +1 -1
  30. package/dest/avm/opcodes/instruction.js +1 -1
  31. package/dest/avm/opcodes/memory.d.ts +4 -4
  32. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  33. package/dest/avm/opcodes/memory.js +17 -13
  34. package/dest/avm/opcodes/misc.d.ts +2 -2
  35. package/dest/avm/opcodes/misc.d.ts.map +1 -1
  36. package/dest/avm/opcodes/misc.js +4 -4
  37. package/dest/avm/serialization/buffer_cursor.d.ts +2 -0
  38. package/dest/avm/serialization/buffer_cursor.d.ts.map +1 -1
  39. package/dest/avm/serialization/buffer_cursor.js +8 -3
  40. package/dest/avm/serialization/bytecode_serialization.d.ts +1 -0
  41. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  42. package/dest/avm/serialization/bytecode_serialization.js +27 -13
  43. package/dest/avm/serialization/instruction_serialization.d.ts +1 -0
  44. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  45. package/dest/avm/serialization/instruction_serialization.js +9 -6
  46. package/dest/avm/test_utils.d.ts.map +1 -1
  47. package/dest/avm/test_utils.js +3 -2
  48. package/dest/common/errors.d.ts.map +1 -1
  49. package/dest/common/errors.js +3 -2
  50. package/dest/public/dual_side_effect_trace.d.ts +2 -2
  51. package/dest/public/dual_side_effect_trace.d.ts.map +1 -1
  52. package/dest/public/dual_side_effect_trace.js +7 -7
  53. package/dest/public/enqueued_call_side_effect_trace.d.ts +8 -23
  54. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
  55. package/dest/public/enqueued_call_side_effect_trace.js +31 -92
  56. package/dest/public/executor_metrics.d.ts +4 -2
  57. package/dest/public/executor_metrics.d.ts.map +1 -1
  58. package/dest/public/executor_metrics.js +20 -3
  59. package/dest/public/fixtures/index.d.ts.map +1 -1
  60. package/dest/public/fixtures/index.js +66 -35
  61. package/dest/public/public_db_sources.d.ts +3 -1
  62. package/dest/public/public_db_sources.d.ts.map +1 -1
  63. package/dest/public/public_db_sources.js +26 -11
  64. package/dest/public/public_processor.d.ts.map +1 -1
  65. package/dest/public/public_processor.js +12 -5
  66. package/dest/public/public_tx_context.d.ts +5 -6
  67. package/dest/public/public_tx_context.d.ts.map +1 -1
  68. package/dest/public/public_tx_context.js +19 -17
  69. package/dest/public/public_tx_simulator.d.ts +13 -2
  70. package/dest/public/public_tx_simulator.d.ts.map +1 -1
  71. package/dest/public/public_tx_simulator.js +263 -217
  72. package/dest/public/side_effect_trace.d.ts +2 -2
  73. package/dest/public/side_effect_trace.d.ts.map +1 -1
  74. package/dest/public/side_effect_trace.js +8 -6
  75. package/dest/public/side_effect_trace_interface.d.ts +2 -2
  76. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  77. package/dest/public/transitional_adapters.d.ts.map +1 -1
  78. package/dest/public/transitional_adapters.js +2 -6
  79. package/package.json +9 -9
  80. package/src/avm/avm_memory_types.ts +26 -24
  81. package/src/avm/avm_simulator.ts +20 -13
  82. package/src/avm/avm_tree.ts +196 -93
  83. package/src/avm/errors.ts +31 -0
  84. package/src/avm/journal/journal.ts +61 -47
  85. package/src/avm/journal/nullifiers.ts +29 -121
  86. package/src/avm/opcodes/contract.ts +2 -2
  87. package/src/avm/opcodes/control_flow.ts +2 -2
  88. package/src/avm/opcodes/environment_getters.ts +2 -2
  89. package/src/avm/opcodes/instruction.ts +1 -1
  90. package/src/avm/opcodes/memory.ts +15 -10
  91. package/src/avm/opcodes/misc.ts +2 -2
  92. package/src/avm/serialization/buffer_cursor.ts +9 -3
  93. package/src/avm/serialization/bytecode_serialization.ts +29 -13
  94. package/src/avm/serialization/instruction_serialization.ts +12 -6
  95. package/src/avm/test_utils.ts +9 -1
  96. package/src/common/errors.ts +2 -1
  97. package/src/public/dual_side_effect_trace.ts +6 -30
  98. package/src/public/enqueued_call_side_effect_trace.ts +35 -154
  99. package/src/public/executor_metrics.ts +23 -1
  100. package/src/public/fixtures/index.ts +97 -43
  101. package/src/public/public_db_sources.ts +29 -15
  102. package/src/public/public_processor.ts +11 -4
  103. package/src/public/public_tx_context.ts +21 -23
  104. package/src/public/public_tx_simulator.ts +45 -8
  105. package/src/public/side_effect_trace.ts +7 -9
  106. package/src/public/side_effect_trace_interface.ts +2 -4
  107. 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
- /** Cached nullifiers. */
17
- private readonly cache: NullifierCache = new NullifierCache(),
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: Fr[]) {
26
- const cache = new NullifierCache(pendingSiloedNullifiers);
27
- return new NullifierManager(hostNullifiers, cache);
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 NullifierCache(), this);
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 contractAddress - the address of the contract whose storage is being read from
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(contractAddress: AztecAddress, nullifier: Fr): boolean {
44
+ private checkExistsHereOrParent(siloedNullifier: Fr): boolean {
45
45
  // First check this cache
46
- let existsAsPending = this.cache.exists(contractAddress, nullifier);
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(contractAddress, nullifier);
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 contractAddress - the address of the contract whose storage is being read from
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
- contractAddress: AztecAddress,
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(contractAddress, nullifier);
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(siloNullifier(contractAddress, nullifier));
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 contractAddress - the address of the contract that the nullifier is associated with
90
- * @param nullifier - the nullifier to stage
87
+ * @param siloedNullifier - the nullifier to stage
91
88
  */
92
- public async append(contractAddress: AztecAddress, nullifier: Fr) {
93
- const [exists, ,] = await this.checkExists(contractAddress, nullifier);
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.append(contractAddress, nullifier);
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
- this.cache.acceptAndMerge(incomingNullifiers.cache);
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 loc: number, private condOffset: number) {
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 varEnum: number, private dstOffset: number) {
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
- public gasCost(dynMultiplier: number = 0): Gas {
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 dstTag: number, private srcOffset: number, private dstOffset: number) {
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);
@@ -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
- assert(n < this._buffer.length);
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 Error(`pc ${pc} is out of bounds for bytecode of length ${bytecode.length}`);
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
- const instructionDeserializerOrUndef = instructionSet.get(opcode);
189
- if (instructionDeserializerOrUndef === undefined) {
190
- throw new Error(`Opcode ${Opcode[opcode]} (0x${opcode.toString(16)}) not implemented`);
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.bufferAtPosition()));
172
+ argValues.push(reader.call(cursor.buffer(), cursor.position()));
167
173
  cursor.advance(sizeBytes);
168
174
  }
169
175
 
@@ -1,4 +1,9 @@
1
- import { type ContractClassPublic, type ContractInstanceWithAddress, Fr } from '@aztec/circuits.js';
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) {
@@ -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 JSON.stringify(decoded);
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
- contractAddress: AztecAddress,
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
- contractAddress,
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
- contractAddress: AztecAddress,
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
- contractAddress,
140
- nullifier,
116
+ siloedNullifier,
141
117
  lowLeafPreimage,
142
118
  lowLeafIndex,
143
119
  lowLeafPath,