@aztec/simulator 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/acvm/oracle/oracle.d.ts +2 -2
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +5 -5
- package/dest/acvm/oracle/typed_oracle.d.ts +2 -2
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +3 -3
- package/dest/avm/avm_execution_environment.d.ts +3 -2
- package/dest/avm/avm_execution_environment.d.ts.map +1 -1
- package/dest/avm/avm_execution_environment.js +6 -5
- package/dest/avm/avm_memory_types.d.ts +119 -38
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_memory_types.js +91 -109
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +2 -3
- package/dest/avm/errors.d.ts +3 -1
- package/dest/avm/errors.d.ts.map +1 -1
- package/dest/avm/errors.js +9 -3
- package/dest/avm/fixtures/index.d.ts +4 -0
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +10 -2
- package/dest/avm/journal/host_storage.d.ts +1 -1
- package/dest/avm/journal/host_storage.d.ts.map +1 -1
- package/dest/avm/opcodes/addressing_mode.d.ts +24 -0
- package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -0
- package/dest/avm/opcodes/addressing_mode.js +62 -0
- package/dest/avm/opcodes/comparators.d.ts.map +1 -1
- package/dest/avm/opcodes/comparators.js +8 -5
- package/dest/avm/opcodes/instruction.d.ts +4 -4
- 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.map +1 -1
- package/dest/avm/opcodes/memory.js +5 -3
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/bytecode_serialization.js +24 -22
- package/dest/avm/serialization/instruction_serialization.d.ts +17 -15
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +18 -16
- package/dest/avm/temporary_executor_migration.d.ts +25 -0
- package/dest/avm/temporary_executor_migration.d.ts.map +1 -0
- package/dest/avm/temporary_executor_migration.js +71 -0
- package/dest/client/client_execution_context.d.ts +4 -2
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +7 -4
- package/dest/client/execution_result.d.ts +2 -0
- package/dest/client/execution_result.d.ts.map +1 -1
- package/dest/client/execution_result.js +1 -1
- package/dest/client/simulator.d.ts +10 -5
- package/dest/client/simulator.d.ts.map +1 -1
- package/dest/client/simulator.js +19 -11
- package/dest/client/unconstrained_execution.js +2 -2
- package/dest/public/execution.js +2 -2
- package/dest/public/executor.d.ts +7 -0
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +26 -1
- package/dest/public/public_execution_context.js +2 -2
- package/dest/public/state_actions.d.ts +1 -1
- package/dest/public/state_actions.d.ts.map +1 -1
- package/dest/public/state_actions.js +5 -6
- package/dest/utils.d.ts +5 -20
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +4 -20
- package/package.json +7 -5
- package/src/acvm/acvm.ts +156 -0
- package/src/acvm/acvm_types.ts +11 -0
- package/src/acvm/deserialize.ts +36 -0
- package/src/acvm/index.ts +5 -0
- package/src/acvm/oracle/debug.ts +109 -0
- package/src/acvm/oracle/index.ts +17 -0
- package/src/acvm/oracle/oracle.ts +332 -0
- package/src/acvm/oracle/typed_oracle.ts +217 -0
- package/src/acvm/serialize.ts +75 -0
- package/src/avm/avm_context.ts +63 -0
- package/src/avm/avm_execution_environment.ts +98 -0
- package/src/avm/avm_machine_state.ts +93 -0
- package/src/avm/avm_memory_types.ts +309 -0
- package/src/avm/avm_message_call_result.ts +29 -0
- package/src/avm/avm_simulator.ts +89 -0
- package/src/avm/errors.ts +57 -0
- package/src/avm/fixtures/index.ts +90 -0
- package/src/avm/journal/host_storage.ts +20 -0
- package/src/avm/journal/index.ts +2 -0
- package/src/avm/journal/journal.ts +266 -0
- package/src/avm/opcodes/.eslintrc.cjs +8 -0
- package/src/avm/opcodes/accrued_substate.ts +92 -0
- package/src/avm/opcodes/addressing_mode.ts +66 -0
- package/src/avm/opcodes/arithmetic.ts +79 -0
- package/src/avm/opcodes/bitwise.ts +129 -0
- package/src/avm/opcodes/comparators.ts +72 -0
- package/src/avm/opcodes/control_flow.ts +129 -0
- package/src/avm/opcodes/environment_getters.ts +199 -0
- package/src/avm/opcodes/external_calls.ts +122 -0
- package/src/avm/opcodes/index.ts +10 -0
- package/src/avm/opcodes/instruction.ts +64 -0
- package/src/avm/opcodes/instruction_impl.ts +52 -0
- package/src/avm/opcodes/memory.ts +193 -0
- package/src/avm/opcodes/storage.ts +76 -0
- package/src/avm/serialization/buffer_cursor.ts +109 -0
- package/src/avm/serialization/bytecode_serialization.ts +172 -0
- package/src/avm/serialization/instruction_serialization.ts +167 -0
- package/src/avm/temporary_executor_migration.ts +108 -0
- package/src/client/client_execution_context.ts +472 -0
- package/src/client/db_oracle.ts +184 -0
- package/src/client/execution_note_cache.ts +90 -0
- package/src/client/execution_result.ts +89 -0
- package/src/client/index.ts +3 -0
- package/src/client/pick_notes.ts +125 -0
- package/src/client/private_execution.ts +78 -0
- package/src/client/simulator.ts +316 -0
- package/src/client/unconstrained_execution.ts +49 -0
- package/src/client/view_data_oracle.ts +243 -0
- package/src/common/errors.ts +61 -0
- package/src/common/index.ts +3 -0
- package/src/common/packed_args_cache.ts +55 -0
- package/src/common/side_effect_counter.ts +12 -0
- package/src/index.ts +3 -0
- package/src/public/db.ts +85 -0
- package/src/public/execution.ts +137 -0
- package/src/public/executor.ts +158 -0
- package/src/public/index.ts +9 -0
- package/src/public/public_execution_context.ts +217 -0
- package/src/public/state_actions.ts +100 -0
- package/src/test/utils.ts +38 -0
- package/src/utils.ts +18 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { GlobalVariables } from '@aztec/circuits.js';
|
|
2
|
+
import { FunctionSelector } from '@aztec/foundation/abi';
|
|
3
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
4
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
6
|
+
|
|
7
|
+
import { mock } from 'jest-mock-extended';
|
|
8
|
+
import merge from 'lodash.merge';
|
|
9
|
+
|
|
10
|
+
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js';
|
|
11
|
+
import { AvmContext } from '../avm_context.js';
|
|
12
|
+
import { AvmExecutionEnvironment } from '../avm_execution_environment.js';
|
|
13
|
+
import { AvmMachineState } from '../avm_machine_state.js';
|
|
14
|
+
import { HostStorage } from '../journal/host_storage.js';
|
|
15
|
+
import { AvmWorldStateJournal } from '../journal/journal.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create a new AVM context with default values.
|
|
19
|
+
*/
|
|
20
|
+
export function initContext(overrides?: {
|
|
21
|
+
worldState?: AvmWorldStateJournal;
|
|
22
|
+
env?: AvmExecutionEnvironment;
|
|
23
|
+
machineState?: AvmMachineState;
|
|
24
|
+
}): AvmContext {
|
|
25
|
+
return new AvmContext(
|
|
26
|
+
overrides?.worldState || initMockWorldStateJournal(),
|
|
27
|
+
overrides?.env || initExecutionEnvironment(),
|
|
28
|
+
overrides?.machineState || initMachineState(),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Creates an empty world state with mocked storage. */
|
|
33
|
+
export function initMockWorldStateJournal(): AvmWorldStateJournal {
|
|
34
|
+
const hostStorage = new HostStorage(mock<PublicStateDB>(), mock<PublicContractsDB>(), mock<CommitmentsDB>());
|
|
35
|
+
return new AvmWorldStateJournal(hostStorage);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object
|
|
40
|
+
*/
|
|
41
|
+
export function initExecutionEnvironment(overrides?: Partial<AvmExecutionEnvironment>): AvmExecutionEnvironment {
|
|
42
|
+
return new AvmExecutionEnvironment(
|
|
43
|
+
overrides?.address ?? AztecAddress.zero(),
|
|
44
|
+
overrides?.storageAddress ?? AztecAddress.zero(),
|
|
45
|
+
overrides?.origin ?? AztecAddress.zero(),
|
|
46
|
+
overrides?.sender ?? AztecAddress.zero(),
|
|
47
|
+
overrides?.portal ?? EthAddress.ZERO,
|
|
48
|
+
overrides?.feePerL1Gas ?? Fr.zero(),
|
|
49
|
+
overrides?.feePerL2Gas ?? Fr.zero(),
|
|
50
|
+
overrides?.feePerDaGas ?? Fr.zero(),
|
|
51
|
+
overrides?.contractCallDepth ?? Fr.zero(),
|
|
52
|
+
overrides?.globals ?? GlobalVariables.empty(),
|
|
53
|
+
overrides?.isStaticCall ?? false,
|
|
54
|
+
overrides?.isDelegateCall ?? false,
|
|
55
|
+
overrides?.calldata ?? [],
|
|
56
|
+
overrides?.temporaryFunctionSelector ?? FunctionSelector.empty(),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object
|
|
62
|
+
*/
|
|
63
|
+
export function initGlobalVariables(overrides?: Partial<GlobalVariables>): GlobalVariables {
|
|
64
|
+
return new GlobalVariables(
|
|
65
|
+
overrides?.chainId ?? Fr.zero(),
|
|
66
|
+
overrides?.version ?? Fr.zero(),
|
|
67
|
+
overrides?.blockNumber ?? Fr.zero(),
|
|
68
|
+
overrides?.timestamp ?? Fr.zero(),
|
|
69
|
+
overrides?.coinbase ?? EthAddress.ZERO,
|
|
70
|
+
overrides?.feeRecipient ?? AztecAddress.zero(),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create an empty instance of the Machine State where all values are zero, unless overridden in the overrides object
|
|
76
|
+
*/
|
|
77
|
+
export function initMachineState(overrides?: Partial<AvmMachineState>): AvmMachineState {
|
|
78
|
+
return AvmMachineState.fromState({
|
|
79
|
+
l1GasLeft: overrides?.l1GasLeft ?? 0,
|
|
80
|
+
l2GasLeft: overrides?.l2GasLeft ?? 0,
|
|
81
|
+
daGasLeft: overrides?.daGasLeft ?? 0,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create a new object with all the same properties as the original, except for the ones in the overrides object.
|
|
87
|
+
*/
|
|
88
|
+
export function allSameExcept(original: any, overrides: any): any {
|
|
89
|
+
return merge({}, original, overrides);
|
|
90
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../public/db.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Host storage
|
|
5
|
+
*
|
|
6
|
+
* A wrapper around the node dbs
|
|
7
|
+
*/
|
|
8
|
+
export class HostStorage {
|
|
9
|
+
public readonly publicStateDb: PublicStateDB;
|
|
10
|
+
|
|
11
|
+
public readonly contractsDb: PublicContractsDB;
|
|
12
|
+
|
|
13
|
+
public readonly commitmentsDb: CommitmentsDB;
|
|
14
|
+
|
|
15
|
+
constructor(publicStateDb: PublicStateDB, contractsDb: PublicContractsDB, commitmentsDb: CommitmentsDB) {
|
|
16
|
+
this.publicStateDb = publicStateDb;
|
|
17
|
+
this.contractsDb = contractsDb;
|
|
18
|
+
this.commitmentsDb = commitmentsDb;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
2
|
+
|
|
3
|
+
import { HostStorage } from './host_storage.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Data held within the journal
|
|
7
|
+
*/
|
|
8
|
+
export type JournalData = {
|
|
9
|
+
newNoteHashes: Fr[];
|
|
10
|
+
newNullifiers: Fr[];
|
|
11
|
+
newL1Messages: Fr[][];
|
|
12
|
+
newLogs: Fr[][];
|
|
13
|
+
|
|
14
|
+
/** contract address -\> key -\> value */
|
|
15
|
+
currentStorageValue: Map<bigint, Map<bigint, Fr>>;
|
|
16
|
+
|
|
17
|
+
/** contract address -\> key -\> value[] (stored in order of access) */
|
|
18
|
+
storageWrites: Map<bigint, Map<bigint, Fr[]>>;
|
|
19
|
+
/** contract address -\> key -\> value[] (stored in order of access) */
|
|
20
|
+
storageReads: Map<bigint, Map<bigint, Fr[]>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A cache of the current state of the AVM
|
|
25
|
+
* The interpreter should make any state queries through this object
|
|
26
|
+
*
|
|
27
|
+
* When a nested context succeeds, it's journal is merge into the parent
|
|
28
|
+
* When a call fails, it's journal is discarded and the parent is used from this point forward
|
|
29
|
+
* When a call succeeds's we can merge a child into its parent
|
|
30
|
+
*/
|
|
31
|
+
export class AvmWorldStateJournal {
|
|
32
|
+
/** Reference to node storage */
|
|
33
|
+
public readonly hostStorage: HostStorage;
|
|
34
|
+
|
|
35
|
+
// Reading state - must be tracked for vm execution
|
|
36
|
+
// contract address -> key -> value[] (array stored in order of reads)
|
|
37
|
+
private storageReads: Map<bigint, Map<bigint, Fr[]>> = new Map();
|
|
38
|
+
private storageWrites: Map<bigint, Map<bigint, Fr[]>> = new Map();
|
|
39
|
+
|
|
40
|
+
// New written state
|
|
41
|
+
private newNoteHashes: Fr[] = [];
|
|
42
|
+
private newNullifiers: Fr[] = [];
|
|
43
|
+
|
|
44
|
+
// New Substate
|
|
45
|
+
private newL1Messages: Fr[][] = [];
|
|
46
|
+
private newLogs: Fr[][] = [];
|
|
47
|
+
|
|
48
|
+
// contract address -> key -> value
|
|
49
|
+
private currentStorageValue: Map<bigint, Map<bigint, Fr>> = new Map();
|
|
50
|
+
|
|
51
|
+
private parentJournal: AvmWorldStateJournal | undefined;
|
|
52
|
+
|
|
53
|
+
constructor(hostStorage: HostStorage, parentJournal?: AvmWorldStateJournal) {
|
|
54
|
+
this.hostStorage = hostStorage;
|
|
55
|
+
this.parentJournal = parentJournal;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create a new world state journal forked from this one
|
|
60
|
+
*/
|
|
61
|
+
public fork() {
|
|
62
|
+
return new AvmWorldStateJournal(this.hostStorage, this);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Write storage into journal
|
|
67
|
+
*
|
|
68
|
+
* @param contractAddress -
|
|
69
|
+
* @param key -
|
|
70
|
+
* @param value -
|
|
71
|
+
*/
|
|
72
|
+
public writeStorage(contractAddress: Fr, key: Fr, value: Fr) {
|
|
73
|
+
let contractMap = this.currentStorageValue.get(contractAddress.toBigInt());
|
|
74
|
+
if (!contractMap) {
|
|
75
|
+
contractMap = new Map();
|
|
76
|
+
this.currentStorageValue.set(contractAddress.toBigInt(), contractMap);
|
|
77
|
+
}
|
|
78
|
+
contractMap.set(key.toBigInt(), value);
|
|
79
|
+
|
|
80
|
+
// We want to keep track of all performed writes in the journal
|
|
81
|
+
this.journalWrite(contractAddress, key, value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Read storage from journal
|
|
86
|
+
* Read from host storage on cache miss
|
|
87
|
+
*
|
|
88
|
+
* @param contractAddress -
|
|
89
|
+
* @param key -
|
|
90
|
+
* @returns current value
|
|
91
|
+
*/
|
|
92
|
+
public async readStorage(contractAddress: Fr, key: Fr): Promise<Fr> {
|
|
93
|
+
// - We first try this journal's storage cache ( if written to before in this call frame )
|
|
94
|
+
// - Then we try the parent journal's storage cache ( if it exists ) ( written to earlier in this block )
|
|
95
|
+
// - Finally we try the host storage ( a trip to the database )
|
|
96
|
+
|
|
97
|
+
// Do not early return as we want to keep track of reads in this.storageReads
|
|
98
|
+
let value = this.currentStorageValue.get(contractAddress.toBigInt())?.get(key.toBigInt());
|
|
99
|
+
if (!value && this.parentJournal) {
|
|
100
|
+
value = await this.parentJournal?.readStorage(contractAddress, key);
|
|
101
|
+
}
|
|
102
|
+
if (!value) {
|
|
103
|
+
value = await this.hostStorage.publicStateDb.storageRead(contractAddress, key);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.journalRead(contractAddress, key, value);
|
|
107
|
+
return Promise.resolve(value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* We want to keep track of all performed reads in the journal
|
|
112
|
+
* This information is hinted to the avm circuit
|
|
113
|
+
|
|
114
|
+
* @param contractAddress -
|
|
115
|
+
* @param key -
|
|
116
|
+
* @param value -
|
|
117
|
+
*/
|
|
118
|
+
journalUpdate(map: Map<bigint, Map<bigint, Fr[]>>, contractAddress: Fr, key: Fr, value: Fr): void {
|
|
119
|
+
let contractMap = map.get(contractAddress.toBigInt());
|
|
120
|
+
if (!contractMap) {
|
|
121
|
+
contractMap = new Map<bigint, Array<Fr>>();
|
|
122
|
+
map.set(contractAddress.toBigInt(), contractMap);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let accessArray = contractMap.get(key.toBigInt());
|
|
126
|
+
if (!accessArray) {
|
|
127
|
+
accessArray = new Array<Fr>();
|
|
128
|
+
contractMap.set(key.toBigInt(), accessArray);
|
|
129
|
+
}
|
|
130
|
+
accessArray.push(value);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Create an instance of journalUpdate that appends to the read array
|
|
134
|
+
private journalRead = this.journalUpdate.bind(this, this.storageReads);
|
|
135
|
+
// Create an instance of journalUpdate that appends to the writes array
|
|
136
|
+
private journalWrite = this.journalUpdate.bind(this, this.storageWrites);
|
|
137
|
+
|
|
138
|
+
public writeNoteHash(noteHash: Fr) {
|
|
139
|
+
this.newNoteHashes.push(noteHash);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public writeL1Message(message: Fr[]) {
|
|
143
|
+
this.newL1Messages.push(message);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public writeNullifier(nullifier: Fr) {
|
|
147
|
+
this.newNullifiers.push(nullifier);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public writeLog(log: Fr[]) {
|
|
151
|
+
this.newLogs.push(log);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Accept nested world state, merging in its journal, and accepting its state modifications
|
|
156
|
+
* - Utxo objects are concatenated
|
|
157
|
+
* - Public state changes are merged, with the value in the incoming journal taking precedent
|
|
158
|
+
* - Public state journals (r/w logs), with the accessing being appended in chronological order
|
|
159
|
+
*/
|
|
160
|
+
public acceptNestedWorldState(nestedJournal: AvmWorldStateJournal) {
|
|
161
|
+
// Merge UTXOs
|
|
162
|
+
this.newNoteHashes = this.newNoteHashes.concat(nestedJournal.newNoteHashes);
|
|
163
|
+
this.newL1Messages = this.newL1Messages.concat(nestedJournal.newL1Messages);
|
|
164
|
+
this.newNullifiers = this.newNullifiers.concat(nestedJournal.newNullifiers);
|
|
165
|
+
this.newLogs = this.newLogs.concat(nestedJournal.newLogs);
|
|
166
|
+
|
|
167
|
+
// Merge Public State
|
|
168
|
+
mergeCurrentValueMaps(this.currentStorageValue, nestedJournal.currentStorageValue);
|
|
169
|
+
|
|
170
|
+
// Merge storage read and write journals
|
|
171
|
+
mergeContractJournalMaps(this.storageReads, nestedJournal.storageReads);
|
|
172
|
+
mergeContractJournalMaps(this.storageWrites, nestedJournal.storageWrites);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Reject nested world state, merging in its journal, but not accepting its state modifications
|
|
177
|
+
* - Utxo objects are concatenated
|
|
178
|
+
* - Public state changes are dropped
|
|
179
|
+
* - Public state journals (r/w logs) are maintained, with the accessing being appended in chronological order
|
|
180
|
+
*/
|
|
181
|
+
public rejectNestedWorldState(nestedJournal: AvmWorldStateJournal) {
|
|
182
|
+
// Merge storage read and write journals
|
|
183
|
+
mergeContractJournalMaps(this.storageReads, nestedJournal.storageReads);
|
|
184
|
+
mergeContractJournalMaps(this.storageWrites, nestedJournal.storageWrites);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Access the current state of the journal
|
|
189
|
+
*
|
|
190
|
+
* @returns a JournalData object
|
|
191
|
+
*/
|
|
192
|
+
public flush(): JournalData {
|
|
193
|
+
return {
|
|
194
|
+
newNoteHashes: this.newNoteHashes,
|
|
195
|
+
newNullifiers: this.newNullifiers,
|
|
196
|
+
newL1Messages: this.newL1Messages,
|
|
197
|
+
newLogs: this.newLogs,
|
|
198
|
+
currentStorageValue: this.currentStorageValue,
|
|
199
|
+
storageReads: this.storageReads,
|
|
200
|
+
storageWrites: this.storageWrites,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Merges two contract current value together
|
|
207
|
+
* Where childMap keys will take precedent over the hostMap
|
|
208
|
+
* The assumption being that the child map is created at a later time
|
|
209
|
+
* And thus contains more up to date information
|
|
210
|
+
*
|
|
211
|
+
* @param hostMap - The map to be merged into
|
|
212
|
+
* @param childMap - The map to be merged from
|
|
213
|
+
*/
|
|
214
|
+
function mergeCurrentValueMaps(hostMap: Map<bigint, Map<bigint, Fr>>, childMap: Map<bigint, Map<bigint, Fr>>) {
|
|
215
|
+
for (const [key, value] of childMap) {
|
|
216
|
+
const map1Value = hostMap.get(key);
|
|
217
|
+
if (!map1Value) {
|
|
218
|
+
hostMap.set(key, value);
|
|
219
|
+
} else {
|
|
220
|
+
mergeStorageCurrentValueMaps(map1Value, value);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @param hostMap - The map to be merge into
|
|
227
|
+
* @param childMap - The map to be merged from
|
|
228
|
+
*/
|
|
229
|
+
function mergeStorageCurrentValueMaps(hostMap: Map<bigint, Fr>, childMap: Map<bigint, Fr>) {
|
|
230
|
+
for (const [key, value] of childMap) {
|
|
231
|
+
hostMap.set(key, value);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Merges two contract journalling maps together
|
|
237
|
+
* For read maps, we just append the childMap arrays into the host map arrays, as the order is important
|
|
238
|
+
*
|
|
239
|
+
* @param hostMap - The map to be merged into
|
|
240
|
+
* @param childMap - The map to be merged from
|
|
241
|
+
*/
|
|
242
|
+
function mergeContractJournalMaps(hostMap: Map<bigint, Map<bigint, Fr[]>>, childMap: Map<bigint, Map<bigint, Fr[]>>) {
|
|
243
|
+
for (const [key, value] of childMap) {
|
|
244
|
+
const map1Value = hostMap.get(key);
|
|
245
|
+
if (!map1Value) {
|
|
246
|
+
hostMap.set(key, value);
|
|
247
|
+
} else {
|
|
248
|
+
mergeStorageJournalMaps(map1Value, value);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @param hostMap - The map to be merge into
|
|
255
|
+
* @param childMap - The map to be merged from
|
|
256
|
+
*/
|
|
257
|
+
function mergeStorageJournalMaps(hostMap: Map<bigint, Fr[]>, childMap: Map<bigint, Fr[]>) {
|
|
258
|
+
for (const [key, value] of childMap) {
|
|
259
|
+
const readArr = hostMap.get(key);
|
|
260
|
+
if (!readArr) {
|
|
261
|
+
hostMap.set(key, value);
|
|
262
|
+
} else {
|
|
263
|
+
hostMap.set(key, readArr?.concat(...value));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { AvmContext } from '../avm_context.js';
|
|
2
|
+
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
3
|
+
import { Instruction } from './instruction.js';
|
|
4
|
+
import { StaticCallStorageAlterError } from './storage.js';
|
|
5
|
+
|
|
6
|
+
export class EmitNoteHash extends Instruction {
|
|
7
|
+
static type: string = 'EMITNOTEHASH';
|
|
8
|
+
static readonly opcode: Opcode = Opcode.EMITNOTEHASH;
|
|
9
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
10
|
+
static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32];
|
|
11
|
+
|
|
12
|
+
constructor(private indirect: number, private noteHashOffset: number) {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async execute(context: AvmContext): Promise<void> {
|
|
17
|
+
if (context.environment.isStaticCall) {
|
|
18
|
+
throw new StaticCallStorageAlterError();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const noteHash = context.machineState.memory.get(this.noteHashOffset).toFr();
|
|
22
|
+
context.worldState.writeNoteHash(noteHash);
|
|
23
|
+
|
|
24
|
+
context.machineState.incrementPc();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class EmitNullifier extends Instruction {
|
|
29
|
+
static type: string = 'EMITNULLIFIER';
|
|
30
|
+
static readonly opcode: Opcode = Opcode.EMITNULLIFIER;
|
|
31
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
32
|
+
static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32];
|
|
33
|
+
|
|
34
|
+
constructor(private indirect: number, private nullifierOffset: number) {
|
|
35
|
+
super();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async execute(context: AvmContext): Promise<void> {
|
|
39
|
+
if (context.environment.isStaticCall) {
|
|
40
|
+
throw new StaticCallStorageAlterError();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const nullifier = context.machineState.memory.get(this.nullifierOffset).toFr();
|
|
44
|
+
context.worldState.writeNullifier(nullifier);
|
|
45
|
+
|
|
46
|
+
context.machineState.incrementPc();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class EmitUnencryptedLog extends Instruction {
|
|
51
|
+
static type: string = 'EMITUNENCRYPTEDLOG';
|
|
52
|
+
static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG;
|
|
53
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
54
|
+
static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32];
|
|
55
|
+
|
|
56
|
+
constructor(private indirect: number, private logOffset: number, private logSize: number) {
|
|
57
|
+
super();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async execute(context: AvmContext): Promise<void> {
|
|
61
|
+
if (context.environment.isStaticCall) {
|
|
62
|
+
throw new StaticCallStorageAlterError();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const log = context.machineState.memory.getSlice(this.logOffset, this.logSize).map(f => f.toFr());
|
|
66
|
+
context.worldState.writeLog(log);
|
|
67
|
+
|
|
68
|
+
context.machineState.incrementPc();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class SendL2ToL1Message extends Instruction {
|
|
73
|
+
static type: string = 'SENDL2TOL1MSG';
|
|
74
|
+
static readonly opcode: Opcode = Opcode.SENDL2TOL1MSG;
|
|
75
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
76
|
+
static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32];
|
|
77
|
+
|
|
78
|
+
constructor(private indirect: number, private msgOffset: number, private msgSize: number) {
|
|
79
|
+
super();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async execute(context: AvmContext): Promise<void> {
|
|
83
|
+
if (context.environment.isStaticCall) {
|
|
84
|
+
throw new StaticCallStorageAlterError();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const msg = context.machineState.memory.getSlice(this.msgOffset, this.msgSize).map(f => f.toFr());
|
|
88
|
+
context.worldState.writeL1Message(msg);
|
|
89
|
+
|
|
90
|
+
context.machineState.incrementPc();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { strict as assert } from 'assert';
|
|
2
|
+
|
|
3
|
+
import { TaggedMemory, TypeTag } from '../avm_memory_types.js';
|
|
4
|
+
|
|
5
|
+
export enum AddressingMode {
|
|
6
|
+
DIRECT,
|
|
7
|
+
INDIRECT,
|
|
8
|
+
INDIRECT_PLUS_CONSTANT, // Not implemented yet.
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** A class to represent the addressing mode of an instruction. */
|
|
12
|
+
export class Addressing {
|
|
13
|
+
public constructor(
|
|
14
|
+
/** The addressing mode for each operand. The length of this array is the number of operands of the instruction. */
|
|
15
|
+
private readonly modePerOperand: AddressingMode[],
|
|
16
|
+
) {
|
|
17
|
+
assert(modePerOperand.length <= 8, 'At most 8 operands are supported');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public static fromWire(wireModes: number): Addressing {
|
|
21
|
+
// The modes are stored in the wire format as a byte, with each bit representing the mode for an operand.
|
|
22
|
+
// The least significant bit represents the zeroth operand, and the most significant bit represents the last operand.
|
|
23
|
+
const modes = new Array<AddressingMode>(8);
|
|
24
|
+
for (let i = 0; i < 8; i++) {
|
|
25
|
+
modes[i] = (wireModes & (1 << i)) === 0 ? AddressingMode.DIRECT : AddressingMode.INDIRECT;
|
|
26
|
+
}
|
|
27
|
+
return new Addressing(modes);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public toWire(): number {
|
|
31
|
+
// The modes are stored in the wire format as a byte, with each bit representing the mode for an operand.
|
|
32
|
+
// The least significant bit represents the zeroth operand, and the least significant bit represents the last operand.
|
|
33
|
+
let wire: number = 0;
|
|
34
|
+
for (let i = 0; i < 8; i++) {
|
|
35
|
+
if (this.modePerOperand[i] === AddressingMode.INDIRECT) {
|
|
36
|
+
wire |= 1 << i;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return wire;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolves the offsets using the addressing mode.
|
|
44
|
+
* @param offsets The offsets to resolve.
|
|
45
|
+
* @param mem The memory to use for resolution.
|
|
46
|
+
* @returns The resolved offsets. The length of the returned array is the same as the length of the input array.
|
|
47
|
+
*/
|
|
48
|
+
public resolve(offsets: number[], mem: TaggedMemory): number[] {
|
|
49
|
+
assert(offsets.length <= this.modePerOperand.length);
|
|
50
|
+
const resolved = new Array(offsets.length);
|
|
51
|
+
for (const [i, offset] of offsets.entries()) {
|
|
52
|
+
switch (this.modePerOperand[i]) {
|
|
53
|
+
case AddressingMode.INDIRECT:
|
|
54
|
+
mem.checkTag(TypeTag.UINT32, offset);
|
|
55
|
+
resolved[i] = Number(mem.get(offset).toBigInt());
|
|
56
|
+
break;
|
|
57
|
+
case AddressingMode.DIRECT:
|
|
58
|
+
resolved[i] = offset;
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
throw new Error(`Unimplemented addressing mode: ${AddressingMode[this.modePerOperand[i]]}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return resolved;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { AvmContext } from '../avm_context.js';
|
|
2
|
+
import { Opcode } from '../serialization/instruction_serialization.js';
|
|
3
|
+
import { ThreeOperandInstruction } from './instruction_impl.js';
|
|
4
|
+
|
|
5
|
+
export class Add extends ThreeOperandInstruction {
|
|
6
|
+
static readonly type: string = 'ADD';
|
|
7
|
+
static readonly opcode = Opcode.ADD;
|
|
8
|
+
|
|
9
|
+
constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
|
|
10
|
+
super(indirect, inTag, aOffset, bOffset, dstOffset);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async execute(context: AvmContext): Promise<void> {
|
|
14
|
+
const a = context.machineState.memory.get(this.aOffset);
|
|
15
|
+
const b = context.machineState.memory.get(this.bOffset);
|
|
16
|
+
|
|
17
|
+
const dest = a.add(b);
|
|
18
|
+
context.machineState.memory.set(this.dstOffset, dest);
|
|
19
|
+
|
|
20
|
+
context.machineState.incrementPc();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class Sub extends ThreeOperandInstruction {
|
|
25
|
+
static readonly type: string = 'SUB';
|
|
26
|
+
static readonly opcode = Opcode.SUB;
|
|
27
|
+
|
|
28
|
+
constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
|
|
29
|
+
super(indirect, inTag, aOffset, bOffset, dstOffset);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async execute(context: AvmContext): Promise<void> {
|
|
33
|
+
const a = context.machineState.memory.get(this.aOffset);
|
|
34
|
+
const b = context.machineState.memory.get(this.bOffset);
|
|
35
|
+
|
|
36
|
+
const dest = a.sub(b);
|
|
37
|
+
context.machineState.memory.set(this.dstOffset, dest);
|
|
38
|
+
|
|
39
|
+
context.machineState.incrementPc();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class Mul extends ThreeOperandInstruction {
|
|
44
|
+
static type: string = 'MUL';
|
|
45
|
+
static readonly opcode = Opcode.MUL;
|
|
46
|
+
|
|
47
|
+
constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
|
|
48
|
+
super(indirect, inTag, aOffset, bOffset, dstOffset);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async execute(context: AvmContext): Promise<void> {
|
|
52
|
+
const a = context.machineState.memory.get(this.aOffset);
|
|
53
|
+
const b = context.machineState.memory.get(this.bOffset);
|
|
54
|
+
|
|
55
|
+
const dest = a.mul(b);
|
|
56
|
+
context.machineState.memory.set(this.dstOffset, dest);
|
|
57
|
+
|
|
58
|
+
context.machineState.incrementPc();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class Div extends ThreeOperandInstruction {
|
|
63
|
+
static type: string = 'DIV';
|
|
64
|
+
static readonly opcode = Opcode.DIV;
|
|
65
|
+
|
|
66
|
+
constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
|
|
67
|
+
super(indirect, inTag, aOffset, bOffset, dstOffset);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async execute(context: AvmContext): Promise<void> {
|
|
71
|
+
const a = context.machineState.memory.get(this.aOffset);
|
|
72
|
+
const b = context.machineState.memory.get(this.bOffset);
|
|
73
|
+
|
|
74
|
+
const dest = a.div(b);
|
|
75
|
+
context.machineState.memory.set(this.dstOffset, dest);
|
|
76
|
+
|
|
77
|
+
context.machineState.incrementPc();
|
|
78
|
+
}
|
|
79
|
+
}
|