@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,64 @@
|
|
|
1
|
+
import { strict as assert } from 'assert';
|
|
2
|
+
|
|
3
|
+
import type { AvmContext } from '../avm_context.js';
|
|
4
|
+
import { BufferCursor } from '../serialization/buffer_cursor.js';
|
|
5
|
+
import { OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js';
|
|
6
|
+
|
|
7
|
+
type InstructionConstructor = {
|
|
8
|
+
new (...args: any[]): Instruction;
|
|
9
|
+
wireFormat?: OperandType[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parent class for all AVM instructions.
|
|
14
|
+
* It's most important aspects are execute and (de)serialize.
|
|
15
|
+
*/
|
|
16
|
+
export abstract class Instruction {
|
|
17
|
+
/**
|
|
18
|
+
* Execute the instruction.
|
|
19
|
+
* Instruction sub-classes must implement this.
|
|
20
|
+
* As an AvmContext executes its contract code, it calls this function for
|
|
21
|
+
* each instruction until the machine state signals "halted".
|
|
22
|
+
* @param context - The AvmContext in which the instruction executes.
|
|
23
|
+
*/
|
|
24
|
+
public abstract execute(context: AvmContext): Promise<void>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generate a string representation of the instruction including
|
|
28
|
+
* the instruction sub-class name all of its flags and operands.
|
|
29
|
+
* @returns Thee string representation.
|
|
30
|
+
*/
|
|
31
|
+
public toString(): string {
|
|
32
|
+
let instructionStr = this.constructor.name + ': ';
|
|
33
|
+
// assumes that all properties are flags or operands
|
|
34
|
+
for (const prop of Object.getOwnPropertyNames(this) as (keyof Instruction)[]) {
|
|
35
|
+
instructionStr += `${prop}:${this[prop].toString()}, `;
|
|
36
|
+
}
|
|
37
|
+
return instructionStr;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Serialize the instruction to a Buffer according to its wire format specified in its subclass.
|
|
42
|
+
* If you want to use this, your subclass should specify a {@code static wireFormat: OperandType[]}.
|
|
43
|
+
* @param this - The instruction to serialize.
|
|
44
|
+
* @returns The serialized instruction.
|
|
45
|
+
*/
|
|
46
|
+
public serialize(this: any): Buffer {
|
|
47
|
+
assert(!!this.constructor.wireFormat, 'wireFormat must be defined on the class');
|
|
48
|
+
return serialize(this.constructor.wireFormat, this);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Deserializes a subclass of Instruction from a Buffer.
|
|
53
|
+
* If you want to use this, your subclass should specify a {@code static wireFormat: OperandType[]}.
|
|
54
|
+
* @param this Class object to deserialize to.
|
|
55
|
+
* @param buf Buffer to read from.
|
|
56
|
+
* @returns Constructed instance of Class.
|
|
57
|
+
*/
|
|
58
|
+
public static deserialize(this: InstructionConstructor, buf: BufferCursor | Buffer): Instruction {
|
|
59
|
+
assert(!!this.wireFormat, 'wireFormat must be defined on the instruction class');
|
|
60
|
+
const res = deserialize(buf, this.wireFormat);
|
|
61
|
+
const args = res.slice(1); // Remove opcode.
|
|
62
|
+
return new this(...args);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { OperandType } from '../serialization/instruction_serialization.js';
|
|
2
|
+
import { Instruction } from './instruction.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Covers (de)serialization for an instruction with:
|
|
6
|
+
* indirect, inTag, and two UINT32s.
|
|
7
|
+
*/
|
|
8
|
+
export abstract class TwoOperandInstruction extends Instruction {
|
|
9
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
10
|
+
static readonly wireFormat: OperandType[] = [
|
|
11
|
+
OperandType.UINT8,
|
|
12
|
+
OperandType.UINT8,
|
|
13
|
+
OperandType.UINT8,
|
|
14
|
+
OperandType.UINT32,
|
|
15
|
+
OperandType.UINT32,
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
protected indirect: number,
|
|
20
|
+
protected inTag: number,
|
|
21
|
+
protected aOffset: number,
|
|
22
|
+
protected dstOffset: number,
|
|
23
|
+
) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Covers (de)serialization for an instruction with:
|
|
30
|
+
* indirect, inTag, and three UINT32s.
|
|
31
|
+
*/
|
|
32
|
+
export abstract class ThreeOperandInstruction extends Instruction {
|
|
33
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
34
|
+
static readonly wireFormat: OperandType[] = [
|
|
35
|
+
OperandType.UINT8,
|
|
36
|
+
OperandType.UINT8,
|
|
37
|
+
OperandType.UINT8,
|
|
38
|
+
OperandType.UINT32,
|
|
39
|
+
OperandType.UINT32,
|
|
40
|
+
OperandType.UINT32,
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
protected indirect: number,
|
|
45
|
+
protected inTag: number,
|
|
46
|
+
protected aOffset: number,
|
|
47
|
+
protected bOffset: number,
|
|
48
|
+
protected dstOffset: number,
|
|
49
|
+
) {
|
|
50
|
+
super();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { AvmContext } from '../avm_context.js';
|
|
2
|
+
import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js';
|
|
3
|
+
import { InstructionExecutionError } from '../errors.js';
|
|
4
|
+
import { BufferCursor } from '../serialization/buffer_cursor.js';
|
|
5
|
+
import { Opcode, OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js';
|
|
6
|
+
import { Addressing } from './addressing_mode.js';
|
|
7
|
+
import { Instruction } from './instruction.js';
|
|
8
|
+
import { TwoOperandInstruction } from './instruction_impl.js';
|
|
9
|
+
|
|
10
|
+
const TAG_TO_OPERAND_TYPE = new Map<TypeTag, OperandType>([
|
|
11
|
+
[TypeTag.UINT8, OperandType.UINT8],
|
|
12
|
+
[TypeTag.UINT16, OperandType.UINT16],
|
|
13
|
+
[TypeTag.UINT32, OperandType.UINT32],
|
|
14
|
+
[TypeTag.UINT64, OperandType.UINT64],
|
|
15
|
+
[TypeTag.UINT128, OperandType.UINT128],
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function getOperandTypeFromInTag(inTag: number | bigint): OperandType {
|
|
19
|
+
inTag = inTag as number;
|
|
20
|
+
const tagOperandType = TAG_TO_OPERAND_TYPE.get(inTag);
|
|
21
|
+
if (tagOperandType === undefined) {
|
|
22
|
+
throw new Error(`Invalid tag ${inTag} for SET.`);
|
|
23
|
+
}
|
|
24
|
+
return tagOperandType;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class Set extends Instruction {
|
|
28
|
+
static readonly type: string = 'SET';
|
|
29
|
+
static readonly opcode: Opcode = Opcode.SET;
|
|
30
|
+
|
|
31
|
+
private static readonly wireFormatBeforeConst: OperandType[] = [
|
|
32
|
+
OperandType.UINT8,
|
|
33
|
+
OperandType.UINT8,
|
|
34
|
+
OperandType.UINT8,
|
|
35
|
+
];
|
|
36
|
+
private static readonly wireFormatAfterConst: OperandType[] = [OperandType.UINT32];
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
private indirect: number,
|
|
40
|
+
private inTag: number,
|
|
41
|
+
private value: bigint | number,
|
|
42
|
+
private dstOffset: number,
|
|
43
|
+
) {
|
|
44
|
+
super();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** We need to use a custom serialize function because of the variable length of the value. */
|
|
48
|
+
public serialize(): Buffer {
|
|
49
|
+
const format: OperandType[] = [
|
|
50
|
+
...Set.wireFormatBeforeConst,
|
|
51
|
+
getOperandTypeFromInTag(this.inTag),
|
|
52
|
+
...Set.wireFormatAfterConst,
|
|
53
|
+
];
|
|
54
|
+
return serialize(format, this);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** We need to use a custom deserialize function because of the variable length of the value. */
|
|
58
|
+
public static deserialize(this: typeof Set, buf: BufferCursor | Buffer): Set {
|
|
59
|
+
if (buf instanceof Buffer) {
|
|
60
|
+
buf = new BufferCursor(buf);
|
|
61
|
+
}
|
|
62
|
+
const beforeConst = deserialize(buf, Set.wireFormatBeforeConst);
|
|
63
|
+
const tag = beforeConst[beforeConst.length - 1];
|
|
64
|
+
const val = deserialize(buf, [getOperandTypeFromInTag(tag)]);
|
|
65
|
+
const afterConst = deserialize(buf, Set.wireFormatAfterConst);
|
|
66
|
+
const res = [...beforeConst, ...val, ...afterConst];
|
|
67
|
+
const args = res.slice(1) as ConstructorParameters<typeof Set>; // Remove opcode.
|
|
68
|
+
return new this(...args);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async execute(context: AvmContext): Promise<void> {
|
|
72
|
+
// Per the YP, the tag cannot be a field.
|
|
73
|
+
if ([TypeTag.FIELD, TypeTag.UNINITIALIZED, TypeTag.INVALID].includes(this.inTag)) {
|
|
74
|
+
throw new InstructionExecutionError(`Invalid tag ${TypeTag[this.inTag]} for SET.`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const res = TaggedMemory.integralFromTag(this.value, this.inTag);
|
|
78
|
+
context.machineState.memory.set(this.dstOffset, res);
|
|
79
|
+
|
|
80
|
+
context.machineState.incrementPc();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class CMov extends Instruction {
|
|
85
|
+
static readonly type: string = 'CMOV';
|
|
86
|
+
static readonly opcode: Opcode = Opcode.CMOV;
|
|
87
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
88
|
+
static readonly wireFormat: OperandType[] = [
|
|
89
|
+
OperandType.UINT8,
|
|
90
|
+
OperandType.UINT8,
|
|
91
|
+
OperandType.UINT32,
|
|
92
|
+
OperandType.UINT32,
|
|
93
|
+
OperandType.UINT32,
|
|
94
|
+
OperandType.UINT32,
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
constructor(
|
|
98
|
+
private indirect: number,
|
|
99
|
+
private aOffset: number,
|
|
100
|
+
private bOffset: number,
|
|
101
|
+
private condOffset: number,
|
|
102
|
+
private dstOffset: number,
|
|
103
|
+
) {
|
|
104
|
+
super();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async execute(context: AvmContext): Promise<void> {
|
|
108
|
+
const a = context.machineState.memory.get(this.aOffset);
|
|
109
|
+
const b = context.machineState.memory.get(this.bOffset);
|
|
110
|
+
const cond = context.machineState.memory.get(this.condOffset);
|
|
111
|
+
|
|
112
|
+
// TODO: reconsider toBigInt() here
|
|
113
|
+
context.machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b);
|
|
114
|
+
|
|
115
|
+
context.machineState.incrementPc();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class Cast extends TwoOperandInstruction {
|
|
120
|
+
static readonly type: string = 'CAST';
|
|
121
|
+
static readonly opcode = Opcode.CAST;
|
|
122
|
+
|
|
123
|
+
constructor(indirect: number, dstTag: number, aOffset: number, dstOffset: number) {
|
|
124
|
+
super(indirect, dstTag, aOffset, dstOffset);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async execute(context: AvmContext): Promise<void> {
|
|
128
|
+
const a = context.machineState.memory.get(this.aOffset);
|
|
129
|
+
|
|
130
|
+
// TODO: consider not using toBigInt()
|
|
131
|
+
const casted =
|
|
132
|
+
this.inTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.inTag);
|
|
133
|
+
|
|
134
|
+
context.machineState.memory.set(this.dstOffset, casted);
|
|
135
|
+
|
|
136
|
+
context.machineState.incrementPc();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export class Mov extends Instruction {
|
|
141
|
+
static readonly type: string = 'MOV';
|
|
142
|
+
static readonly opcode: Opcode = Opcode.MOV;
|
|
143
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
144
|
+
static readonly wireFormat: OperandType[] = [
|
|
145
|
+
OperandType.UINT8,
|
|
146
|
+
OperandType.UINT8,
|
|
147
|
+
OperandType.UINT32,
|
|
148
|
+
OperandType.UINT32,
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
constructor(private indirect: number, private srcOffset: number, private dstOffset: number) {
|
|
152
|
+
super();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async execute(context: AvmContext): Promise<void> {
|
|
156
|
+
const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve(
|
|
157
|
+
[this.srcOffset, this.dstOffset],
|
|
158
|
+
context.machineState.memory,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const a = context.machineState.memory.get(srcOffset);
|
|
162
|
+
|
|
163
|
+
context.machineState.memory.set(dstOffset, a);
|
|
164
|
+
|
|
165
|
+
context.machineState.incrementPc();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export class CalldataCopy extends Instruction {
|
|
170
|
+
static readonly type: string = 'CALLDATACOPY';
|
|
171
|
+
static readonly opcode: Opcode = Opcode.CALLDATACOPY;
|
|
172
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
173
|
+
static readonly wireFormat: OperandType[] = [
|
|
174
|
+
OperandType.UINT8,
|
|
175
|
+
OperandType.UINT8,
|
|
176
|
+
OperandType.UINT32,
|
|
177
|
+
OperandType.UINT32,
|
|
178
|
+
OperandType.UINT32,
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
constructor(private indirect: number, private cdOffset: number, private copySize: number, private dstOffset: number) {
|
|
182
|
+
super();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async execute(context: AvmContext): Promise<void> {
|
|
186
|
+
const transformedData = context.environment.calldata
|
|
187
|
+
.slice(this.cdOffset, this.cdOffset + this.copySize)
|
|
188
|
+
.map(f => new Field(f));
|
|
189
|
+
context.machineState.memory.setSlice(this.dstOffset, transformedData);
|
|
190
|
+
|
|
191
|
+
context.machineState.incrementPc();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
2
|
+
|
|
3
|
+
import type { AvmContext } from '../avm_context.js';
|
|
4
|
+
import { Field } from '../avm_memory_types.js';
|
|
5
|
+
import { InstructionExecutionError } from '../errors.js';
|
|
6
|
+
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
7
|
+
import { Instruction } from './instruction.js';
|
|
8
|
+
|
|
9
|
+
abstract class BaseStorageInstruction extends Instruction {
|
|
10
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
11
|
+
public static readonly wireFormat: OperandType[] = [
|
|
12
|
+
OperandType.UINT8,
|
|
13
|
+
OperandType.UINT8,
|
|
14
|
+
OperandType.UINT32,
|
|
15
|
+
OperandType.UINT32,
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
constructor(protected indirect: number, protected aOffset: number, protected bOffset: number) {
|
|
19
|
+
super();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class SStore extends BaseStorageInstruction {
|
|
24
|
+
static readonly type: string = 'SSTORE';
|
|
25
|
+
static readonly opcode = Opcode.SSTORE;
|
|
26
|
+
|
|
27
|
+
constructor(indirect: number, srcOffset: number, slotOffset: number) {
|
|
28
|
+
super(indirect, srcOffset, slotOffset);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async execute(context: AvmContext): Promise<void> {
|
|
32
|
+
if (context.environment.isStaticCall) {
|
|
33
|
+
throw new StaticCallStorageAlterError();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const slot = context.machineState.memory.get(this.aOffset);
|
|
37
|
+
const data = context.machineState.memory.get(this.bOffset);
|
|
38
|
+
|
|
39
|
+
context.worldState.writeStorage(
|
|
40
|
+
context.environment.storageAddress,
|
|
41
|
+
new Fr(slot.toBigInt()),
|
|
42
|
+
new Fr(data.toBigInt()),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
context.machineState.incrementPc();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class SLoad extends BaseStorageInstruction {
|
|
50
|
+
static readonly type: string = 'SLOAD';
|
|
51
|
+
static readonly opcode = Opcode.SLOAD;
|
|
52
|
+
|
|
53
|
+
constructor(indirect: number, slotOffset: number, dstOffset: number) {
|
|
54
|
+
super(indirect, slotOffset, dstOffset);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async execute(context: AvmContext): Promise<void> {
|
|
58
|
+
const slot = context.machineState.memory.get(this.aOffset);
|
|
59
|
+
|
|
60
|
+
const data: Fr = await context.worldState.readStorage(context.environment.storageAddress, new Fr(slot.toBigInt()));
|
|
61
|
+
|
|
62
|
+
context.machineState.memory.set(this.bOffset, new Field(data));
|
|
63
|
+
|
|
64
|
+
context.machineState.incrementPc();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Error is thrown when a static call attempts to alter storage
|
|
70
|
+
*/
|
|
71
|
+
export class StaticCallStorageAlterError extends InstructionExecutionError {
|
|
72
|
+
constructor() {
|
|
73
|
+
super('Static calls cannot alter storage');
|
|
74
|
+
this.name = 'StaticCallStorageAlterError';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { strict as assert } from 'assert';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* A Buffer-like class that automatically advances the position.
|
|
5
|
+
*/
|
|
6
|
+
export class BufferCursor {
|
|
7
|
+
constructor(private _buffer: Buffer, private _position: number = 0) {}
|
|
8
|
+
|
|
9
|
+
public position(): number {
|
|
10
|
+
return this._position;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public eof(): boolean {
|
|
14
|
+
return this._position === this._buffer.length;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public bufferAtPosition(): Buffer {
|
|
18
|
+
return this._buffer.subarray(this._position);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public advance(n: number): void {
|
|
22
|
+
this._position += n;
|
|
23
|
+
assert(n < this._buffer.length);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public readUint8(): number {
|
|
27
|
+
const ret = this._buffer.readUint8(this._position);
|
|
28
|
+
this._position += 1;
|
|
29
|
+
return ret;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public readUint16LE(): number {
|
|
33
|
+
const ret = this._buffer.readUint16LE(this._position);
|
|
34
|
+
this._position += 2;
|
|
35
|
+
return ret;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public readUint16BE(): number {
|
|
39
|
+
const ret = this._buffer.readUint16BE(this._position);
|
|
40
|
+
this._position += 2;
|
|
41
|
+
return ret;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public readUint32LE(): number {
|
|
45
|
+
const ret = this._buffer.readUint32LE(this._position);
|
|
46
|
+
this._position += 4;
|
|
47
|
+
return ret;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public readUint32BE(): number {
|
|
51
|
+
const ret = this._buffer.readUint32BE(this._position);
|
|
52
|
+
this._position += 4;
|
|
53
|
+
return ret;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public readBigInt64LE(): bigint {
|
|
57
|
+
const ret = this._buffer.readBigInt64LE(this._position);
|
|
58
|
+
this._position += 8;
|
|
59
|
+
return ret;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public readBigInt64BE(): bigint {
|
|
63
|
+
const ret = this._buffer.readBigInt64BE(this._position);
|
|
64
|
+
this._position += 8;
|
|
65
|
+
return ret;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public writeUint8(v: number) {
|
|
69
|
+
const ret = this._buffer.writeUint8(v, this._position);
|
|
70
|
+
this._position += 1;
|
|
71
|
+
return ret;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public writeUint16LE(v: number) {
|
|
75
|
+
const ret = this._buffer.writeUint16LE(v, this._position);
|
|
76
|
+
this._position += 2;
|
|
77
|
+
return ret;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public writeUint16BE(v: number) {
|
|
81
|
+
const ret = this._buffer.writeUint16BE(v, this._position);
|
|
82
|
+
this._position += 2;
|
|
83
|
+
return ret;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public writeUint32LE(v: number) {
|
|
87
|
+
const ret = this._buffer.writeUint32LE(v, this._position);
|
|
88
|
+
this._position += 4;
|
|
89
|
+
return ret;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public writeUint32BE(v: number) {
|
|
93
|
+
const ret = this._buffer.writeUint32BE(v, this._position);
|
|
94
|
+
this._position += 4;
|
|
95
|
+
return ret;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public writeBigInt64LE(v: bigint) {
|
|
99
|
+
const ret = this._buffer.writeBigInt64LE(v, this._position);
|
|
100
|
+
this._position += 8;
|
|
101
|
+
return ret;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public writeBigInt64BE(v: bigint) {
|
|
105
|
+
const ret = this._buffer.writeBigInt64BE(v, this._position);
|
|
106
|
+
this._position += 8;
|
|
107
|
+
return ret;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Add,
|
|
3
|
+
Address,
|
|
4
|
+
And,
|
|
5
|
+
BlockNumber,
|
|
6
|
+
CMov,
|
|
7
|
+
Call,
|
|
8
|
+
CalldataCopy,
|
|
9
|
+
Cast,
|
|
10
|
+
ChainId,
|
|
11
|
+
Div,
|
|
12
|
+
EmitNoteHash,
|
|
13
|
+
EmitNullifier,
|
|
14
|
+
EmitUnencryptedLog,
|
|
15
|
+
Eq,
|
|
16
|
+
FeePerDAGas,
|
|
17
|
+
FeePerL1Gas,
|
|
18
|
+
FeePerL2Gas,
|
|
19
|
+
InternalCall,
|
|
20
|
+
InternalReturn,
|
|
21
|
+
Jump,
|
|
22
|
+
JumpI,
|
|
23
|
+
Lt,
|
|
24
|
+
Lte,
|
|
25
|
+
Mov,
|
|
26
|
+
Mul,
|
|
27
|
+
Not,
|
|
28
|
+
Or,
|
|
29
|
+
Origin,
|
|
30
|
+
Portal,
|
|
31
|
+
Return,
|
|
32
|
+
Revert,
|
|
33
|
+
SLoad,
|
|
34
|
+
SStore,
|
|
35
|
+
SendL2ToL1Message,
|
|
36
|
+
Sender,
|
|
37
|
+
Set,
|
|
38
|
+
Shl,
|
|
39
|
+
Shr,
|
|
40
|
+
StaticCall,
|
|
41
|
+
StorageAddress,
|
|
42
|
+
Sub,
|
|
43
|
+
Timestamp,
|
|
44
|
+
Version,
|
|
45
|
+
Xor,
|
|
46
|
+
} from '../opcodes/index.js';
|
|
47
|
+
import type { Instruction } from '../opcodes/index.js';
|
|
48
|
+
import { BufferCursor } from './buffer_cursor.js';
|
|
49
|
+
import { Opcode } from './instruction_serialization.js';
|
|
50
|
+
|
|
51
|
+
interface DeserializableInstruction {
|
|
52
|
+
deserialize(buf: BufferCursor | Buffer): Instruction;
|
|
53
|
+
opcode: Opcode;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type InstructionSet = Map<Opcode, DeserializableInstruction>;
|
|
57
|
+
// TODO(4359): This is a function so that Call and StaticCall can be lazily resolved.
|
|
58
|
+
// This is a temporary solution until we solve the dependency cycle.
|
|
59
|
+
const INSTRUCTION_SET = () =>
|
|
60
|
+
new Map<Opcode, DeserializableInstruction>([
|
|
61
|
+
[Add.opcode, Add],
|
|
62
|
+
[Sub.opcode, Sub],
|
|
63
|
+
[Mul.opcode, Mul],
|
|
64
|
+
[Div.opcode, Div],
|
|
65
|
+
[Eq.opcode, Eq],
|
|
66
|
+
[Lt.opcode, Lt],
|
|
67
|
+
[Lte.opcode, Lte],
|
|
68
|
+
[And.opcode, And],
|
|
69
|
+
[Or.opcode, Or],
|
|
70
|
+
[Xor.opcode, Xor],
|
|
71
|
+
[Not.opcode, Not],
|
|
72
|
+
[Shl.opcode, Shl],
|
|
73
|
+
[Shr.opcode, Shr],
|
|
74
|
+
[Cast.opcode, Cast],
|
|
75
|
+
[Address.opcode, Address],
|
|
76
|
+
[StorageAddress.opcode, StorageAddress],
|
|
77
|
+
[Origin.opcode, Origin],
|
|
78
|
+
[Sender.opcode, Sender],
|
|
79
|
+
[Portal.opcode, Portal],
|
|
80
|
+
[FeePerL1Gas.opcode, FeePerL1Gas],
|
|
81
|
+
[FeePerL2Gas.opcode, FeePerL2Gas],
|
|
82
|
+
[FeePerDAGas.opcode, FeePerDAGas],
|
|
83
|
+
//[Contractcalldepth.opcode, Contractcalldepth],
|
|
84
|
+
// Execution Environment - Globals
|
|
85
|
+
[ChainId.opcode, ChainId],
|
|
86
|
+
[Version.opcode, Version],
|
|
87
|
+
[BlockNumber.opcode, BlockNumber],
|
|
88
|
+
[Timestamp.opcode, Timestamp],
|
|
89
|
+
//[Coinbase.opcode, Coinbase],
|
|
90
|
+
//[Blockl1gaslimit.opcode, Blockl1gaslimit],
|
|
91
|
+
//[Blockl2gaslimit.opcode, Blockl2gaslimit],
|
|
92
|
+
//[Blockdagaslimit.opcode, Blockdagaslimit],
|
|
93
|
+
// Execution Environment - Calldata
|
|
94
|
+
[CalldataCopy.opcode, CalldataCopy],
|
|
95
|
+
|
|
96
|
+
// Machine State
|
|
97
|
+
// Machine State - Gas
|
|
98
|
+
//[L1gasleft.opcode, L1gasleft],
|
|
99
|
+
//[L2gasleft.opcode, L2gasleft],
|
|
100
|
+
//[Dagasleft.opcode, Dagasleft],
|
|
101
|
+
// Machine State - Internal Control Flow
|
|
102
|
+
[Jump.opcode, Jump],
|
|
103
|
+
[JumpI.opcode, JumpI],
|
|
104
|
+
[InternalCall.opcode, InternalCall],
|
|
105
|
+
[InternalReturn.opcode, InternalReturn],
|
|
106
|
+
[Set.opcode, Set],
|
|
107
|
+
[Mov.opcode, Mov],
|
|
108
|
+
[CMov.opcode, CMov],
|
|
109
|
+
|
|
110
|
+
// World State
|
|
111
|
+
[SLoad.opcode, SLoad], // Public Storage
|
|
112
|
+
[SStore.opcode, SStore], // Public Storage
|
|
113
|
+
//[NoteHashExists.opcode, NoteHashExists], // Notes & Nullifiers
|
|
114
|
+
[EmitNoteHash.opcode, EmitNoteHash], // Notes & Nullifiers
|
|
115
|
+
//[NullifierExists.opcode, NullifierExists], // Notes & Nullifiers
|
|
116
|
+
[EmitNullifier.opcode, EmitNullifier], // Notes & Nullifiers
|
|
117
|
+
//[Readl1tol2msg.opcode, Readl1tol2msg], // Messages
|
|
118
|
+
//[HeaderMember.opcode, HeaderMember], // Header
|
|
119
|
+
|
|
120
|
+
// Accrued Substate
|
|
121
|
+
[EmitUnencryptedLog.opcode, EmitUnencryptedLog],
|
|
122
|
+
[SendL2ToL1Message.opcode, SendL2ToL1Message],
|
|
123
|
+
|
|
124
|
+
// Control Flow - Contract Calls
|
|
125
|
+
[Call.opcode, Call],
|
|
126
|
+
[StaticCall.opcode, StaticCall],
|
|
127
|
+
[Return.opcode, Return],
|
|
128
|
+
[Revert.opcode, Revert],
|
|
129
|
+
|
|
130
|
+
// Gadgets
|
|
131
|
+
//[Keccak.opcode, Keccak],
|
|
132
|
+
//[Poseidon.opcode, Poseidon],
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
interface Serializable {
|
|
136
|
+
serialize(): Buffer;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Serializes an array of instructions to bytecode.
|
|
141
|
+
*/
|
|
142
|
+
export function encodeToBytecode(instructions: Serializable[]): Buffer {
|
|
143
|
+
return Buffer.concat(instructions.map(i => i.serialize()));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Convert a buffer of bytecode into an array of instructions.
|
|
148
|
+
* @param bytecode Buffer of bytecode.
|
|
149
|
+
* @param instructionSet Optional {@code InstructionSet} to be used for deserialization.
|
|
150
|
+
* @returns Bytecode decoded into an ordered array of Instructions
|
|
151
|
+
*/
|
|
152
|
+
export function decodeFromBytecode(
|
|
153
|
+
bytecode: Buffer,
|
|
154
|
+
instructionSet: InstructionSet = INSTRUCTION_SET(),
|
|
155
|
+
): Instruction[] {
|
|
156
|
+
const instructions: Instruction[] = [];
|
|
157
|
+
const cursor = new BufferCursor(bytecode);
|
|
158
|
+
|
|
159
|
+
while (!cursor.eof()) {
|
|
160
|
+
const opcode: Opcode = cursor.bufferAtPosition().readUint8(); // peek.
|
|
161
|
+
const instructionDeserializerOrUndef = instructionSet.get(opcode);
|
|
162
|
+
if (instructionDeserializerOrUndef === undefined) {
|
|
163
|
+
throw new Error(`Opcode ${Opcode[opcode]} (0x${opcode.toString(16)}) not implemented`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const instructionDeserializer: DeserializableInstruction = instructionDeserializerOrUndef;
|
|
167
|
+
const i: Instruction = instructionDeserializer.deserialize(cursor);
|
|
168
|
+
instructions.push(i);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return instructions;
|
|
172
|
+
}
|