@aztec/simulator 0.71.0 → 0.73.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/README.md +1 -1
- package/dest/acvm/oracle/oracle.d.ts +0 -1
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +1 -5
- package/dest/acvm/oracle/typed_oracle.d.ts +0 -1
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +1 -4
- package/dest/avm/avm_simulator.d.ts +0 -1
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +4 -19
- package/dest/avm/avm_tree.d.ts +9 -8
- package/dest/avm/avm_tree.d.ts.map +1 -1
- package/dest/avm/avm_tree.js +35 -30
- package/dest/avm/errors.d.ts +6 -0
- package/dest/avm/errors.d.ts.map +1 -1
- package/dest/avm/errors.js +10 -1
- package/dest/avm/fixtures/avm_simulation_tester.d.ts +21 -0
- package/dest/avm/fixtures/avm_simulation_tester.d.ts.map +1 -0
- package/dest/avm/fixtures/avm_simulation_tester.js +74 -0
- package/dest/avm/fixtures/base_avm_simulation_tester.d.ts +35 -0
- package/dest/avm/fixtures/base_avm_simulation_tester.d.ts.map +1 -0
- package/dest/avm/fixtures/base_avm_simulation_tester.js +76 -0
- package/dest/avm/fixtures/index.d.ts +6 -2
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +28 -14
- package/dest/avm/fixtures/simple_contract_data_source.d.ts +31 -0
- package/dest/avm/fixtures/simple_contract_data_source.d.ts.map +1 -0
- package/dest/avm/fixtures/simple_contract_data_source.js +75 -0
- package/dest/avm/journal/journal.d.ts +5 -6
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +28 -26
- package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
- package/dest/avm/opcodes/accrued_substate.js +4 -3
- package/dest/avm/opcodes/conversion.d.ts.map +1 -1
- package/dest/avm/opcodes/conversion.js +10 -7
- package/dest/avm/opcodes/ec_add.js +2 -2
- package/dest/avm/opcodes/hashing.js +2 -2
- package/dest/avm/opcodes/multi_scalar_mul.d.ts.map +1 -1
- package/dest/avm/opcodes/multi_scalar_mul.js +9 -7
- package/dest/avm/test_utils.d.ts +1 -1
- package/dest/avm/test_utils.d.ts.map +1 -1
- package/dest/avm/test_utils.js +4 -3
- package/dest/client/client_execution_context.d.ts +2 -6
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +23 -26
- package/dest/client/execution_note_cache.d.ts +3 -3
- package/dest/client/execution_note_cache.d.ts.map +1 -1
- package/dest/client/execution_note_cache.js +10 -10
- package/dest/client/pick_notes.js +5 -5
- package/dest/client/simulator.js +4 -4
- package/dest/client/view_data_oracle.js +2 -2
- package/dest/common/hashed_values_cache.d.ts +1 -1
- package/dest/common/hashed_values_cache.d.ts.map +1 -1
- package/dest/common/hashed_values_cache.js +5 -5
- package/dest/providers/acvm_wasm.d.ts +2 -0
- package/dest/providers/acvm_wasm.d.ts.map +1 -1
- package/dest/providers/acvm_wasm.js +15 -2
- package/dest/public/execution.d.ts +8 -26
- package/dest/public/execution.d.ts.map +1 -1
- package/dest/public/execution.js +3 -3
- package/dest/public/fee_payment.d.ts +2 -2
- package/dest/public/fee_payment.d.ts.map +1 -1
- package/dest/public/fee_payment.js +3 -3
- package/dest/public/fixtures/index.d.ts +2 -37
- package/dest/public/fixtures/index.d.ts.map +1 -1
- package/dest/public/fixtures/index.js +3 -247
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts +21 -0
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -0
- package/dest/public/fixtures/public_tx_simulation_tester.js +89 -0
- package/dest/public/fixtures/utils.d.ts +17 -0
- package/dest/public/fixtures/utils.d.ts.map +1 -0
- package/dest/public/fixtures/utils.js +66 -0
- package/dest/public/index.d.ts +1 -1
- package/dest/public/index.d.ts.map +1 -1
- package/dest/public/index.js +2 -2
- package/dest/public/public_db_sources.d.ts +2 -1
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +27 -21
- package/dest/public/public_processor.d.ts +4 -5
- package/dest/public/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor.js +28 -28
- package/dest/public/public_tx_context.d.ts +5 -5
- package/dest/public/public_tx_context.d.ts.map +1 -1
- package/dest/public/public_tx_context.js +44 -18
- package/dest/public/public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator.js +9 -12
- package/dest/public/{enqueued_call_side_effect_trace.d.ts → side_effect_trace.d.ts} +12 -15
- package/dest/public/side_effect_trace.d.ts.map +1 -0
- package/dest/public/side_effect_trace.js +350 -0
- package/dest/public/side_effect_trace_interface.d.ts +4 -5
- package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
- package/dest/test/utils.d.ts +1 -1
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +3 -3
- package/package.json +10 -10
- package/src/acvm/oracle/oracle.ts +0 -5
- package/src/acvm/oracle/typed_oracle.ts +0 -4
- package/src/avm/avm_simulator.ts +3 -27
- package/src/avm/avm_tree.ts +39 -37
- package/src/avm/errors.ts +10 -0
- package/src/avm/fixtures/avm_simulation_tester.ts +105 -0
- package/src/avm/fixtures/base_avm_simulation_tester.ts +104 -0
- package/src/avm/fixtures/index.ts +46 -17
- package/src/avm/fixtures/simple_contract_data_source.ts +98 -0
- package/src/avm/journal/journal.ts +28 -26
- package/src/avm/opcodes/accrued_substate.ts +3 -2
- package/src/avm/opcodes/conversion.ts +15 -6
- package/src/avm/opcodes/ec_add.ts +1 -1
- package/src/avm/opcodes/hashing.ts +1 -1
- package/src/avm/opcodes/multi_scalar_mul.ts +8 -6
- package/src/avm/test_utils.ts +3 -4
- package/src/client/client_execution_context.ts +29 -30
- package/src/client/execution_note_cache.ts +19 -14
- package/src/client/pick_notes.ts +4 -4
- package/src/client/simulator.ts +3 -3
- package/src/client/view_data_oracle.ts +1 -1
- package/src/common/hashed_values_cache.ts +4 -4
- package/src/providers/acvm_wasm.ts +13 -2
- package/src/public/execution.ts +10 -34
- package/src/public/fee_payment.ts +2 -2
- package/src/public/fixtures/index.ts +2 -381
- package/src/public/fixtures/public_tx_simulation_tester.ts +174 -0
- package/src/public/fixtures/utils.ts +110 -0
- package/src/public/index.ts +1 -1
- package/src/public/public_db_sources.ts +31 -20
- package/src/public/public_processor.ts +32 -30
- package/src/public/public_tx_context.ts +87 -28
- package/src/public/public_tx_simulator.ts +7 -14
- package/src/public/{enqueued_call_side_effect_trace.ts → side_effect_trace.ts} +29 -41
- package/src/public/side_effect_trace_interface.ts +4 -4
- package/src/test/utils.ts +2 -2
- package/dest/public/enqueued_call_side_effect_trace.d.ts.map +0 -1
- package/dest/public/enqueued_call_side_effect_trace.js +0 -357
- package/dest/public/transitional_adapters.d.ts +0 -4
- package/dest/public/transitional_adapters.d.ts.map +0 -1
- package/dest/public/transitional_adapters.js +0 -29
- package/src/public/transitional_adapters.ts +0 -113
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { MerkleTreeId, type MerkleTreeWriteOperations } from '@aztec/circuit-types';
|
|
2
|
+
import {
|
|
3
|
+
type ContractClassPublic,
|
|
4
|
+
type ContractInstanceWithAddress,
|
|
5
|
+
DEPLOYER_CONTRACT_ADDRESS,
|
|
6
|
+
PUBLIC_DISPATCH_SELECTOR,
|
|
7
|
+
PublicDataWrite,
|
|
8
|
+
computeInitializationHash,
|
|
9
|
+
} from '@aztec/circuits.js';
|
|
10
|
+
import { computePublicDataTreeLeafSlot, siloNullifier } from '@aztec/circuits.js/hash';
|
|
11
|
+
import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/circuits.js/testing';
|
|
12
|
+
import { type ContractArtifact, FunctionSelector } from '@aztec/foundation/abi';
|
|
13
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
14
|
+
import { type Fr } from '@aztec/foundation/fields';
|
|
15
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
16
|
+
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
17
|
+
|
|
18
|
+
import { computeFeePayerBalanceStorageSlot } from '../../server.js';
|
|
19
|
+
import { PUBLIC_DISPATCH_FN_NAME, getContractFunctionArtifact } from './index.js';
|
|
20
|
+
import { type SimpleContractDataSource } from './simple_contract_data_source.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* An abstract test class that enables tests of real apps in the AVM without requiring e2e tests.
|
|
24
|
+
* It enables this by letting us (1) perform pseudo-contract-deployments (and registrations)
|
|
25
|
+
* that trigger merkle tree operations and (2) maintain a contract data source to store
|
|
26
|
+
* and retrieve contract classes and instances.
|
|
27
|
+
*
|
|
28
|
+
* This class is meant to be extended when writing tests for a specific simulation interface.
|
|
29
|
+
* For example, has been extended for testing of the core AvmSimulator, and again for the PublicTxSimulator,
|
|
30
|
+
* both of which benefit from such pseudo-deployments by populating merkle trees and a contract data source
|
|
31
|
+
* with contract information.
|
|
32
|
+
*/
|
|
33
|
+
export abstract class BaseAvmSimulationTester {
|
|
34
|
+
public logger = createLogger('avm-simulation-tester');
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
public contractDataSource: SimpleContractDataSource,
|
|
38
|
+
public merkleTrees: MerkleTreeWriteOperations,
|
|
39
|
+
/* May want to skip contract deployment tree ops to test failed contract address nullifier checks on CALL */
|
|
40
|
+
private skipContractDeployments = false,
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
async setFeePayerBalance(feePayer: AztecAddress, balance: Fr) {
|
|
44
|
+
const feeJuiceAddress = ProtocolContractAddress.FeeJuice;
|
|
45
|
+
const balanceSlot = await computeFeePayerBalanceStorageSlot(feePayer);
|
|
46
|
+
await this.setPublicStorage(feeJuiceAddress, balanceSlot, balance);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async setPublicStorage(address: AztecAddress, slot: Fr, value: Fr) {
|
|
50
|
+
const leafSlot = await computePublicDataTreeLeafSlot(address, slot);
|
|
51
|
+
// get existing preimage
|
|
52
|
+
const publicDataWrite = new PublicDataWrite(leafSlot, value);
|
|
53
|
+
await this.merkleTrees.batchInsert(MerkleTreeId.PUBLIC_DATA_TREE, [publicDataWrite.toBuffer()], 0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Derive the contract class and instance with some seed.
|
|
58
|
+
* Add both to the contract data source along with the contract artifact.
|
|
59
|
+
*/
|
|
60
|
+
async registerAndDeployContract(
|
|
61
|
+
constructorArgs: any[],
|
|
62
|
+
deployer: AztecAddress,
|
|
63
|
+
contractArtifact: ContractArtifact,
|
|
64
|
+
seed = 0,
|
|
65
|
+
): Promise<ContractInstanceWithAddress> {
|
|
66
|
+
const bytecode = getContractFunctionArtifact(PUBLIC_DISPATCH_FN_NAME, contractArtifact)!.bytecode;
|
|
67
|
+
const contractClass = await makeContractClassPublic(
|
|
68
|
+
seed,
|
|
69
|
+
/*publicDispatchFunction=*/ { bytecode, selector: new FunctionSelector(PUBLIC_DISPATCH_SELECTOR) },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const constructorAbi = getContractFunctionArtifact('constructor', contractArtifact);
|
|
73
|
+
const initializationHash = await computeInitializationHash(constructorAbi, constructorArgs);
|
|
74
|
+
const contractInstance = await makeContractInstanceFromClassId(contractClass.id, seed, {
|
|
75
|
+
deployer,
|
|
76
|
+
initializationHash,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await this.addContractClass(contractClass, contractArtifact);
|
|
80
|
+
await this.addContractInstance(contractInstance);
|
|
81
|
+
return contractInstance;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getFirstContractInstance(): ContractInstanceWithAddress {
|
|
85
|
+
return this.contractDataSource.getFirstContractInstance();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
addContractClass(contractClass: ContractClassPublic, contractArtifact: ContractArtifact): Promise<void> {
|
|
89
|
+
this.logger.debug(`Adding contract class with Id ${contractClass.id}`);
|
|
90
|
+
this.contractDataSource.addContractArtifact(contractClass.id, contractArtifact);
|
|
91
|
+
return this.contractDataSource.addContractClass(contractClass);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async addContractInstance(contractInstance: ContractInstanceWithAddress) {
|
|
95
|
+
if (!this.skipContractDeployments) {
|
|
96
|
+
const contractAddressNullifier = await siloNullifier(
|
|
97
|
+
AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
|
|
98
|
+
contractInstance.address.toField(),
|
|
99
|
+
);
|
|
100
|
+
await this.merkleTrees.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0);
|
|
101
|
+
}
|
|
102
|
+
await this.contractDataSource.addContractInstance(contractInstance);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isNoirCallStackUnresolved } from '@aztec/circuit-types';
|
|
2
2
|
import { GasFees, GlobalVariables, MAX_L2_GAS_PER_TX_PUBLIC_PORTION } from '@aztec/circuits.js';
|
|
3
|
-
import { type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi';
|
|
3
|
+
import { type ContractArtifact, type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi';
|
|
4
4
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import { Fr } from '@aztec/foundation/fields';
|
|
@@ -23,6 +23,8 @@ import { AvmPersistableStateManager } from '../journal/journal.js';
|
|
|
23
23
|
import { NullifierManager } from '../journal/nullifiers.js';
|
|
24
24
|
import { PublicStorage } from '../journal/public_storage.js';
|
|
25
25
|
|
|
26
|
+
export const PUBLIC_DISPATCH_FN_NAME = 'public_dispatch';
|
|
27
|
+
|
|
26
28
|
/**
|
|
27
29
|
* Create a new AVM context with default values.
|
|
28
30
|
*/
|
|
@@ -124,15 +126,51 @@ export function randomMemoryFields(length: number): Field[] {
|
|
|
124
126
|
return [...Array(length)].map(_ => new Field(Fr.random()));
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
export function
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
export function getFunctionSelector(
|
|
130
|
+
functionName: string,
|
|
131
|
+
contractArtifact: ContractArtifact,
|
|
132
|
+
): Promise<FunctionSelector> {
|
|
133
|
+
const fnArtifact = contractArtifact.functions.find(f => f.name === functionName)!;
|
|
134
|
+
assert(!!fnArtifact, `Function ${functionName} not found in ${contractArtifact.name}`);
|
|
135
|
+
const params = fnArtifact.parameters;
|
|
136
|
+
return FunctionSelector.fromNameAndParameters(fnArtifact.name, params);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getContractFunctionArtifact(
|
|
140
|
+
functionName: string,
|
|
141
|
+
contractArtifact: ContractArtifact,
|
|
142
|
+
): FunctionArtifact | undefined {
|
|
143
|
+
const artifact = contractArtifact.functions.find(f => f.name === functionName)!;
|
|
144
|
+
if (!artifact) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return artifact;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function resolveContractAssertionMessage(
|
|
151
|
+
functionName: string,
|
|
152
|
+
revertReason: AvmRevertReason,
|
|
153
|
+
output: Fr[],
|
|
154
|
+
contractArtifact: ContractArtifact,
|
|
155
|
+
): string | undefined {
|
|
156
|
+
traverseCauseChain(revertReason, cause => {
|
|
157
|
+
revertReason = cause as AvmRevertReason;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const functionArtifact = contractArtifact.functions.find(f => f.name === functionName);
|
|
161
|
+
if (!functionArtifact || !revertReason.noirCallStack || !isNoirCallStackUnresolved(revertReason.noirCallStack)) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return resolveAssertionMessageFromRevertData(output, functionArtifact);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function getAvmTestContractFunctionSelector(functionName: string): Promise<FunctionSelector> {
|
|
169
|
+
return getFunctionSelector(functionName, AvmTestContractArtifact);
|
|
132
170
|
}
|
|
133
171
|
|
|
134
172
|
export function getAvmTestContractArtifact(functionName: string): FunctionArtifact {
|
|
135
|
-
const artifact =
|
|
173
|
+
const artifact = getContractFunctionArtifact(functionName, AvmTestContractArtifact);
|
|
136
174
|
assert(
|
|
137
175
|
!!artifact?.bytecode,
|
|
138
176
|
`No bytecode found for function ${functionName}. Try re-running bootstrap.sh on the repository root.`,
|
|
@@ -150,14 +188,5 @@ export function resolveAvmTestContractAssertionMessage(
|
|
|
150
188
|
revertReason: AvmRevertReason,
|
|
151
189
|
output: Fr[],
|
|
152
190
|
): string | undefined {
|
|
153
|
-
|
|
154
|
-
revertReason = cause as AvmRevertReason;
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const functionArtifact = AvmTestContractArtifact.functions.find(f => f.name === functionName);
|
|
158
|
-
if (!functionArtifact || !revertReason.noirCallStack || !isNoirCallStackUnresolved(revertReason.noirCallStack)) {
|
|
159
|
-
return undefined;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return resolveAssertionMessageFromRevertData(output, functionArtifact);
|
|
191
|
+
return resolveContractAssertionMessage(functionName, revertReason, output, AvmTestContractArtifact);
|
|
163
192
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ContractClassPublic,
|
|
3
|
+
type ContractDataSource,
|
|
4
|
+
type ContractInstanceWithAddress,
|
|
5
|
+
type FunctionSelector,
|
|
6
|
+
type PublicFunction,
|
|
7
|
+
computePublicBytecodeCommitment,
|
|
8
|
+
} from '@aztec/circuits.js';
|
|
9
|
+
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
10
|
+
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
11
|
+
import { type Fr } from '@aztec/foundation/fields';
|
|
12
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
13
|
+
|
|
14
|
+
import { PUBLIC_DISPATCH_FN_NAME } from './index.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This class is used during public/avm testing to function as a database of
|
|
18
|
+
* contract contract classes and instances. Tests can populate it with classes
|
|
19
|
+
* and instances and then probe it via the ContractDataSource interface.
|
|
20
|
+
*
|
|
21
|
+
* This class does not include any real merkle trees & merkle operations.
|
|
22
|
+
*/
|
|
23
|
+
export class SimpleContractDataSource implements ContractDataSource {
|
|
24
|
+
public logger = createLogger('simple-contract-data-source');
|
|
25
|
+
|
|
26
|
+
// maps contract class ID to class
|
|
27
|
+
private contractClasses: Map<string, ContractClassPublic> = new Map();
|
|
28
|
+
// maps contract instance address to instance
|
|
29
|
+
private contractInstances: Map<string, ContractInstanceWithAddress> = new Map();
|
|
30
|
+
// maps contract instance address to address
|
|
31
|
+
private contractArtifacts: Map<string, ContractArtifact> = new Map();
|
|
32
|
+
|
|
33
|
+
/////////////////////////////////////////////////////////////
|
|
34
|
+
// Helper functions not in the contract data source interface
|
|
35
|
+
getFirstContractInstance(): ContractInstanceWithAddress {
|
|
36
|
+
return this.contractInstances.values().next().value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
addContractArtifact(classId: Fr, artifact: ContractArtifact): void {
|
|
40
|
+
this.contractArtifacts.set(classId.toString(), artifact);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/////////////////////////////////////////////////////////////
|
|
44
|
+
// ContractDataSource function impelementations
|
|
45
|
+
getPublicFunction(_address: AztecAddress, _selector: FunctionSelector): Promise<PublicFunction> {
|
|
46
|
+
throw new Error('Method not implemented.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getBlockNumber(): Promise<number> {
|
|
50
|
+
throw new Error('Method not implemented.');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
54
|
+
return Promise.resolve(this.contractClasses.get(id.toString()));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
|
|
58
|
+
const contractClass = await this.getContractClass(id);
|
|
59
|
+
return Promise.resolve(computePublicBytecodeCommitment(contractClass!.packedBytecode));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
|
|
63
|
+
return Promise.resolve(this.contractInstances.get(address.toString()));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getContractClassIds(): Promise<Fr[]> {
|
|
67
|
+
throw new Error('Method not implemented.');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
|
|
71
|
+
const contractInstance = await this.getContract(address);
|
|
72
|
+
if (!contractInstance) {
|
|
73
|
+
this.logger.warn(`Contract not found at address: ${address}`);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
this.logger.debug(`Retrieved contract artifact for address: ${address}`);
|
|
77
|
+
this.logger.debug(`Contract class ID: ${contractInstance.contractClassId}`);
|
|
78
|
+
return Promise.resolve(this.contractArtifacts.get(contractInstance!.contractClassId.toString()));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getContractFunctionName(_address: AztecAddress, _selector: FunctionSelector): Promise<string> {
|
|
82
|
+
return Promise.resolve(PUBLIC_DISPATCH_FN_NAME);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
registerContractFunctionSignatures(_address: AztecAddress, _signatures: string[]): Promise<void> {
|
|
86
|
+
return Promise.resolve();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
addContractClass(contractClass: ContractClassPublic): Promise<void> {
|
|
90
|
+
this.contractClasses.set(contractClass.id.toString(), contractClass);
|
|
91
|
+
return Promise.resolve();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
addContractInstance(contractInstance: ContractInstanceWithAddress): Promise<void> {
|
|
95
|
+
this.contractInstances.set(contractInstance.address.toString(), contractInstance);
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -72,7 +72,7 @@ export class AvmPersistableStateManager {
|
|
|
72
72
|
trace: PublicSideEffectTraceInterface,
|
|
73
73
|
doMerkleOperations: boolean = false,
|
|
74
74
|
firstNullifier: Fr,
|
|
75
|
-
) {
|
|
75
|
+
): Promise<AvmPersistableStateManager> {
|
|
76
76
|
const ephemeralForest = await AvmEphemeralForest.create(worldStateDB.getMerkleInterface());
|
|
77
77
|
return new AvmPersistableStateManager(
|
|
78
78
|
worldStateDB,
|
|
@@ -126,11 +126,12 @@ export class AvmPersistableStateManager {
|
|
|
126
126
|
this.trace.merge(forkedState.trace, reverted);
|
|
127
127
|
if (reverted) {
|
|
128
128
|
if (this.doMerkleOperations) {
|
|
129
|
-
this.log.
|
|
129
|
+
this.log.trace(
|
|
130
130
|
`Rolled back nullifier tree to root ${this.merkleTrees.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot()}`,
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
133
|
} else {
|
|
134
|
+
this.log.trace('Merging forked state into parent...');
|
|
134
135
|
this.merkleTrees = forkedState.merkleTrees;
|
|
135
136
|
}
|
|
136
137
|
}
|
|
@@ -144,7 +145,7 @@ export class AvmPersistableStateManager {
|
|
|
144
145
|
*/
|
|
145
146
|
public async writeStorage(contractAddress: AztecAddress, slot: Fr, value: Fr, protocolWrite = false): Promise<void> {
|
|
146
147
|
this.log.debug(`Storage write (address=${contractAddress}, slot=${slot}): value=${value}`);
|
|
147
|
-
const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
|
|
148
|
+
const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot);
|
|
148
149
|
this.log.debug(`leafSlot=${leafSlot}`);
|
|
149
150
|
// Cache storage writes for later reference/reads
|
|
150
151
|
this.publicStorage.write(contractAddress, slot, value);
|
|
@@ -169,7 +170,7 @@ export class AvmPersistableStateManager {
|
|
|
169
170
|
);
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
this.trace.tracePublicStorageWrite(
|
|
173
|
+
await this.trace.tracePublicStorageWrite(
|
|
173
174
|
contractAddress,
|
|
174
175
|
slot,
|
|
175
176
|
value,
|
|
@@ -181,7 +182,7 @@ export class AvmPersistableStateManager {
|
|
|
181
182
|
insertionPath,
|
|
182
183
|
);
|
|
183
184
|
} else {
|
|
184
|
-
this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite);
|
|
185
|
+
await this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite);
|
|
185
186
|
}
|
|
186
187
|
}
|
|
187
188
|
|
|
@@ -195,7 +196,7 @@ export class AvmPersistableStateManager {
|
|
|
195
196
|
public async readStorage(contractAddress: AztecAddress, slot: Fr): Promise<Fr> {
|
|
196
197
|
const { value, cached } = await this.publicStorage.read(contractAddress, slot);
|
|
197
198
|
this.log.debug(`Storage read (address=${contractAddress}, slot=${slot}): value=${value}, cached=${cached}`);
|
|
198
|
-
const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
|
|
199
|
+
const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot);
|
|
199
200
|
this.log.debug(`leafSlot=${leafSlot}`);
|
|
200
201
|
|
|
201
202
|
if (this.doMerkleOperations) {
|
|
@@ -283,34 +284,34 @@ export class AvmPersistableStateManager {
|
|
|
283
284
|
* Write a raw note hash, silo it and make it unique, then trace the write.
|
|
284
285
|
* @param noteHash - the unsiloed note hash to write
|
|
285
286
|
*/
|
|
286
|
-
public writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): void {
|
|
287
|
-
const siloedNoteHash = siloNoteHash(contractAddress, noteHash);
|
|
287
|
+
public async writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): Promise<void> {
|
|
288
|
+
const siloedNoteHash = await siloNoteHash(contractAddress, noteHash);
|
|
288
289
|
|
|
289
|
-
this.writeSiloedNoteHash(siloedNoteHash);
|
|
290
|
+
await this.writeSiloedNoteHash(siloedNoteHash);
|
|
290
291
|
}
|
|
291
292
|
|
|
292
293
|
/**
|
|
293
294
|
* Write a note hash, make it unique, trace the write.
|
|
294
295
|
* @param noteHash - the non unique note hash to write
|
|
295
296
|
*/
|
|
296
|
-
public writeSiloedNoteHash(noteHash: Fr): void {
|
|
297
|
-
const nonce = computeNoteHashNonce(this.firstNullifier, this.trace.getNoteHashCount());
|
|
298
|
-
const uniqueNoteHash = computeUniqueNoteHash(nonce, noteHash);
|
|
297
|
+
public async writeSiloedNoteHash(noteHash: Fr): Promise<void> {
|
|
298
|
+
const nonce = await computeNoteHashNonce(this.firstNullifier, this.trace.getNoteHashCount());
|
|
299
|
+
const uniqueNoteHash = await computeUniqueNoteHash(nonce, noteHash);
|
|
299
300
|
|
|
300
|
-
this.writeUniqueNoteHash(uniqueNoteHash);
|
|
301
|
+
await this.writeUniqueNoteHash(uniqueNoteHash);
|
|
301
302
|
}
|
|
302
303
|
|
|
303
304
|
/**
|
|
304
305
|
* Write a note hash, trace the write.
|
|
305
306
|
* @param noteHash - the siloed unique hash to write
|
|
306
307
|
*/
|
|
307
|
-
public writeUniqueNoteHash(noteHash: Fr): void {
|
|
308
|
+
public async writeUniqueNoteHash(noteHash: Fr): Promise<void> {
|
|
308
309
|
this.log.debug(`noteHashes += @${noteHash}.`);
|
|
309
310
|
|
|
310
311
|
if (this.doMerkleOperations) {
|
|
311
312
|
// Should write a helper for this
|
|
312
313
|
const leafIndex = new Fr(this.merkleTrees.treeMap.get(MerkleTreeId.NOTE_HASH_TREE)!.leafCount);
|
|
313
|
-
const insertionPath = this.merkleTrees.appendNoteHash(noteHash);
|
|
314
|
+
const insertionPath = await this.merkleTrees.appendNoteHash(noteHash);
|
|
314
315
|
this.trace.traceNewNoteHash(noteHash, leafIndex, insertionPath);
|
|
315
316
|
} else {
|
|
316
317
|
this.trace.traceNewNoteHash(noteHash);
|
|
@@ -325,7 +326,7 @@ export class AvmPersistableStateManager {
|
|
|
325
326
|
*/
|
|
326
327
|
public async checkNullifierExists(contractAddress: AztecAddress, nullifier: Fr): Promise<boolean> {
|
|
327
328
|
this.log.debug(`Checking existence of nullifier (address=${contractAddress}, nullifier=${nullifier})`);
|
|
328
|
-
const siloedNullifier = siloNullifier(contractAddress, nullifier);
|
|
329
|
+
const siloedNullifier = await siloNullifier(contractAddress, nullifier);
|
|
329
330
|
const [exists, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership(
|
|
330
331
|
siloedNullifier,
|
|
331
332
|
);
|
|
@@ -407,7 +408,7 @@ export class AvmPersistableStateManager {
|
|
|
407
408
|
*/
|
|
408
409
|
public async writeNullifier(contractAddress: AztecAddress, nullifier: Fr) {
|
|
409
410
|
this.log.debug(`Inserting new nullifier (address=${nullifier}, nullifier=${contractAddress})`);
|
|
410
|
-
const siloedNullifier = siloNullifier(contractAddress, nullifier);
|
|
411
|
+
const siloedNullifier = await siloNullifier(contractAddress, nullifier);
|
|
411
412
|
await this.writeSiloedNullifier(siloedNullifier);
|
|
412
413
|
}
|
|
413
414
|
|
|
@@ -447,13 +448,15 @@ export class AvmPersistableStateManager {
|
|
|
447
448
|
await this.nullifiers.append(siloedNullifier);
|
|
448
449
|
// We append the new nullifier
|
|
449
450
|
this.log.debug(
|
|
450
|
-
`Nullifier tree root before insertion ${this.merkleTrees.treeMap
|
|
451
|
+
`Nullifier tree root before insertion ${await this.merkleTrees.treeMap
|
|
451
452
|
.get(MerkleTreeId.NULLIFIER_TREE)!
|
|
452
453
|
.getRoot()}`,
|
|
453
454
|
);
|
|
454
455
|
const appendResult = await this.merkleTrees.appendNullifier(siloedNullifier);
|
|
455
456
|
this.log.debug(
|
|
456
|
-
`Nullifier tree root after insertion ${this.merkleTrees.treeMap
|
|
457
|
+
`Nullifier tree root after insertion ${await this.merkleTrees.treeMap
|
|
458
|
+
.get(MerkleTreeId.NULLIFIER_TREE)!
|
|
459
|
+
.getRoot()}`,
|
|
457
460
|
);
|
|
458
461
|
const lowLeafPreimage = appendResult.lowWitness.preimage as NullifierLeafPreimage;
|
|
459
462
|
const lowLeafIndex = appendResult.lowWitness.index;
|
|
@@ -524,14 +527,13 @@ export class AvmPersistableStateManager {
|
|
|
524
527
|
}
|
|
525
528
|
|
|
526
529
|
/**
|
|
527
|
-
* Write
|
|
530
|
+
* Write a public log
|
|
528
531
|
* @param contractAddress - address of the contract that emitted the log
|
|
529
|
-
* @param event - log event selector
|
|
530
532
|
* @param log - log contents
|
|
531
533
|
*/
|
|
532
|
-
public
|
|
533
|
-
this.log.debug(`
|
|
534
|
-
this.trace.
|
|
534
|
+
public writePublicLog(contractAddress: AztecAddress, log: Fr[]) {
|
|
535
|
+
this.log.debug(`PublicLog(${contractAddress}) += event with ${log.length} fields.`);
|
|
536
|
+
this.trace.tracePublicLog(contractAddress, log);
|
|
535
537
|
}
|
|
536
538
|
|
|
537
539
|
/**
|
|
@@ -551,7 +553,7 @@ export class AvmPersistableStateManager {
|
|
|
551
553
|
new Array<Fr>(),
|
|
552
554
|
];
|
|
553
555
|
if (!contractAddressIsCanonical(contractAddress)) {
|
|
554
|
-
const contractAddressNullifier = siloNullifier(
|
|
556
|
+
const contractAddressNullifier = await siloNullifier(
|
|
555
557
|
AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
|
|
556
558
|
contractAddress.toField(),
|
|
557
559
|
);
|
|
@@ -616,7 +618,7 @@ export class AvmPersistableStateManager {
|
|
|
616
618
|
new Array<Fr>(),
|
|
617
619
|
];
|
|
618
620
|
if (!contractAddressIsCanonical(contractAddress)) {
|
|
619
|
-
const contractAddressNullifier = siloNullifier(
|
|
621
|
+
const contractAddressNullifier = await siloNullifier(
|
|
620
622
|
AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
|
|
621
623
|
contractAddress.toField(),
|
|
622
624
|
);
|
|
@@ -70,7 +70,7 @@ export class EmitNoteHash extends Instruction {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const noteHash = memory.get(noteHashOffset).toFr();
|
|
73
|
-
context.persistableState.writeNoteHash(context.environment.address, noteHash);
|
|
73
|
+
await context.persistableState.writeNoteHash(context.environment.address, noteHash);
|
|
74
74
|
|
|
75
75
|
memory.assert({ reads: 1, addressing });
|
|
76
76
|
}
|
|
@@ -201,6 +201,7 @@ export class L1ToL2MessageExists extends Instruction {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
export class EmitUnencryptedLog extends Instruction {
|
|
204
|
+
// TODO(#11124): rename unencrypted -> public
|
|
204
205
|
static type: string = 'EMITUNENCRYPTEDLOG';
|
|
205
206
|
static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG;
|
|
206
207
|
// Informs (de)serialization. See Instruction.deserialize.
|
|
@@ -228,7 +229,7 @@ export class EmitUnencryptedLog extends Instruction {
|
|
|
228
229
|
|
|
229
230
|
context.machineState.consumeGas(this.gasCost(logSize));
|
|
230
231
|
const log = memory.getSlice(logOffset, logSize).map(f => f.toFr());
|
|
231
|
-
context.persistableState.
|
|
232
|
+
context.persistableState.writePublicLog(contractAddress, log);
|
|
232
233
|
|
|
233
234
|
memory.assert({ reads: 1 + logSize, addressing });
|
|
234
235
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type AvmContext } from '../avm_context.js';
|
|
2
2
|
import { TypeTag, Uint1, Uint8 } from '../avm_memory_types.js';
|
|
3
|
-
import {
|
|
3
|
+
import { InvalidToRadixInputsError } from '../errors.js';
|
|
4
4
|
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
5
5
|
import { Addressing } from './addressing_mode.js';
|
|
6
6
|
import { Instruction } from './instruction.js';
|
|
7
7
|
|
|
8
8
|
export class ToRadixBE extends Instruction {
|
|
9
|
-
static type: string = '
|
|
9
|
+
static type: string = 'TORADIXBE';
|
|
10
10
|
static readonly opcode: Opcode = Opcode.TORADIXBE;
|
|
11
11
|
|
|
12
12
|
// Informs (de)serialization. See Instruction.deserialize.
|
|
@@ -49,12 +49,21 @@ export class ToRadixBE extends Instruction {
|
|
|
49
49
|
|
|
50
50
|
let value: bigint = memory.get(srcOffset).toBigInt();
|
|
51
51
|
const radix: bigint = memory.get(radixOffset).toBigInt();
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
|
|
53
|
+
if (radix < 2 || radix > 256) {
|
|
54
|
+
throw new InvalidToRadixInputsError(`ToRadixBE instruction's radix should be in range [2,256] (was ${radix}).`);
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
|
|
57
|
+
if (numLimbs < 1 && value != BigInt(0n)) {
|
|
58
|
+
throw new InvalidToRadixInputsError(
|
|
59
|
+
`ToRadixBE instruction's input value is not zero (was ${value}) but numLimbs zero.`,
|
|
60
|
+
);
|
|
57
61
|
}
|
|
62
|
+
|
|
63
|
+
if (outputBits != 0 && radix != BigInt(2n)) {
|
|
64
|
+
throw new InvalidToRadixInputsError(`Radix ${radix} is not equal to 2 and bit mode is activated.`);
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
const radixBN: bigint = BigInt(radix);
|
|
59
68
|
const limbArray = new Array(numLimbs);
|
|
60
69
|
|
|
@@ -34,7 +34,7 @@ export class Poseidon2 extends Instruction {
|
|
|
34
34
|
const inputState = memory.getSlice(inputOffset, Poseidon2.stateSize);
|
|
35
35
|
memory.checkTagsRange(TypeTag.FIELD, inputOffset, Poseidon2.stateSize);
|
|
36
36
|
|
|
37
|
-
const outputState = poseidon2Permutation(inputState);
|
|
37
|
+
const outputState = await poseidon2Permutation(inputState);
|
|
38
38
|
memory.setSlice(
|
|
39
39
|
outputOffset,
|
|
40
40
|
outputState.map(word => new Field(word)),
|
|
@@ -95,20 +95,22 @@ export class MultiScalarMul extends Instruction {
|
|
|
95
95
|
const [firstBaseScalarPair, ...rest]: Array<[Point, Fq]> = grumpkinPoints.map((p, idx) => [p, scalarFqVector[idx]]);
|
|
96
96
|
// Fold the points and scalars into a single point
|
|
97
97
|
// We have to ensure get the first point, since the identity element (point at infinity) isn't quite working in ts
|
|
98
|
-
|
|
98
|
+
let acc = await grumpkin.mul(firstBaseScalarPair[0], firstBaseScalarPair[1]);
|
|
99
|
+
for (const curr of rest) {
|
|
99
100
|
if (curr[1] === Fq.ZERO) {
|
|
100
101
|
// If we multiply by 0, the result will the point at infinity - so we ignore it
|
|
101
|
-
|
|
102
|
+
continue;
|
|
102
103
|
} else if (curr[0].inf) {
|
|
103
104
|
// If we multiply the point at infinity by a scalar, it's still the point at infinity
|
|
104
|
-
|
|
105
|
+
continue;
|
|
105
106
|
} else if (acc.inf) {
|
|
106
107
|
// If we accumulator is the point at infinity, we can just return the current point
|
|
107
|
-
|
|
108
|
+
acc = curr[0];
|
|
108
109
|
} else {
|
|
109
|
-
|
|
110
|
+
acc = await grumpkin.add(acc, await grumpkin.mul(curr[0], curr[1]));
|
|
110
111
|
}
|
|
111
|
-
}
|
|
112
|
+
}
|
|
113
|
+
const outputPoint = acc;
|
|
112
114
|
|
|
113
115
|
// Important to use setSlice() and not set() in the two following statements as
|
|
114
116
|
// this checks that the offsets lie within memory range.
|
package/src/avm/test_utils.ts
CHANGED
|
@@ -11,11 +11,10 @@ import { mock } from 'jest-mock-extended';
|
|
|
11
11
|
import { type WorldStateDB } from '../public/public_db_sources.js';
|
|
12
12
|
import { type PublicSideEffectTraceInterface } from '../public/side_effect_trace_interface.js';
|
|
13
13
|
|
|
14
|
-
export function mockGetBytecode(worldStateDB: WorldStateDB, bytecode: Buffer) {
|
|
14
|
+
export async function mockGetBytecode(worldStateDB: WorldStateDB, bytecode: Buffer) {
|
|
15
|
+
const commitment = await computePublicBytecodeCommitment(bytecode);
|
|
15
16
|
(worldStateDB as jest.Mocked<WorldStateDB>).getBytecode.mockResolvedValue(bytecode);
|
|
16
|
-
(worldStateDB as jest.Mocked<WorldStateDB>).getBytecodeCommitment.mockResolvedValue(
|
|
17
|
-
computePublicBytecodeCommitment(bytecode),
|
|
18
|
-
);
|
|
17
|
+
(worldStateDB as jest.Mocked<WorldStateDB>).getBytecodeCommitment.mockResolvedValue(commitment);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export function mockTraceFork(trace: PublicSideEffectTraceInterface, nestedTrace?: PublicSideEffectTraceInterface) {
|