@aztec/simulator 0.0.1-commit.8afd444 → 0.0.1-commit.9117c5f5a
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 +4 -4
- package/dest/public/avm/avm_gas.js +3 -3
- package/dest/public/avm/fixtures/account_proof_fetcher.d.ts +2 -0
- package/dest/public/avm/fixtures/account_proof_fetcher.d.ts.map +1 -0
- package/dest/public/avm/fixtures/account_proof_fetcher.js +152 -0
- package/dest/public/avm/opcodes/accrued_substate.d.ts +2 -2
- package/dest/public/avm/opcodes/accrued_substate.d.ts.map +1 -1
- package/dest/public/avm/opcodes/accrued_substate.js +3 -4
- package/dest/public/avm/serialization/bytecode_serialization.js +3 -3
- package/dest/public/avm/serialization/instruction_serialization.d.ts +2 -2
- package/dest/public/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/public/avm/serialization/instruction_serialization.js +1 -1
- package/dest/public/fixtures/amm_test.js +2 -2
- package/dest/public/fixtures/opcode_spammer.d.ts +1 -1
- package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -1
- package/dest/public/fixtures/opcode_spammer.js +6 -6
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts +1 -1
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -1
- package/dest/public/fixtures/public_tx_simulation_tester.js +29 -2
- package/dest/public/fixtures/utils.js +4 -4
- package/dest/public/hinting_db_sources.d.ts +2 -2
- package/dest/public/hinting_db_sources.d.ts.map +1 -1
- package/dest/public/hinting_db_sources.js +1 -1
- package/dest/public/public_processor/guarded_merkle_tree.d.ts +2 -2
- package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -1
- package/dest/public/public_processor/guarded_merkle_tree.js +1 -1
- package/dest/public/public_processor/public_processor.d.ts +5 -3
- package/dest/public/public_processor/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor/public_processor.js +17 -7
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator.js +2 -3
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.js +2 -2
- package/dest/public/public_tx_simulator/factories.d.ts +2 -2
- package/dest/public/public_tx_simulator/factories.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/factories.js +2 -2
- package/dest/public/public_tx_simulator/public_tx_simulator.js +2 -2
- package/dest/public/test_executor_metrics.d.ts +6 -1
- package/dest/public/test_executor_metrics.d.ts.map +1 -1
- package/dest/public/test_executor_metrics.js +22 -0
- package/package.json +16 -16
- package/src/public/avm/avm_gas.ts +2 -2
- package/src/public/avm/fixtures/account_proof.json +553 -0
- package/src/public/avm/fixtures/account_proof_fetcher.ts +166 -0
- package/src/public/avm/opcodes/accrued_substate.ts +3 -4
- package/src/public/avm/serialization/bytecode_serialization.ts +2 -2
- package/src/public/avm/serialization/instruction_serialization.ts +1 -1
- package/src/public/fixtures/amm_test.ts +2 -2
- package/src/public/fixtures/opcode_spammer.ts +6 -10
- package/src/public/fixtures/public_tx_simulation_tester.ts +34 -3
- package/src/public/fixtures/utils.ts +4 -4
- package/src/public/fuzzing/avm_fuzzer_simulator.ts +1 -1
- package/src/public/hinting_db_sources.ts +1 -1
- package/src/public/public_processor/guarded_merkle_tree.ts +1 -1
- package/src/public/public_processor/public_processor.ts +24 -15
- package/src/public/public_tx_simulator/cpp_public_tx_simulator.ts +2 -3
- package/src/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.ts +2 -2
- package/src/public/public_tx_simulator/factories.ts +2 -1
- package/src/public/public_tx_simulator/public_tx_simulator.ts +3 -3
- package/src/public/test_executor_metrics.ts +24 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches an account proof from the Ethereum mainnet and saves it as account_proof.json.
|
|
3
|
+
* This script is not using any Aztec library code, so it's easily portable.
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { createPublicClient, fromRlp, hexToBytes, http } from 'viem';
|
|
9
|
+
import { mainnet } from 'viem/chains';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
const RPC_URL = process.env.RPC_URL;
|
|
14
|
+
const ADDRESS = (process.env.ADDRESS || '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045') as `0x${string}`;
|
|
15
|
+
const BLOCK_TAG = process.env.BLOCK_NUMBER ? BigInt(process.env.BLOCK_NUMBER) : 'latest';
|
|
16
|
+
const MAX_ACCOUNT_PATH = 15;
|
|
17
|
+
|
|
18
|
+
function padTo(arr: number[], len: number) {
|
|
19
|
+
return [...arr, ...Array(len - arr.length).fill(0)].slice(0, len);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toBytes(hex: `0x${string}`) {
|
|
23
|
+
return Array.from(hexToBytes(hex));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function bytesToU64s(bytes: number[]) {
|
|
27
|
+
const paddedBytes = padTo(bytes, 32);
|
|
28
|
+
return Array.from({ length: 4 }, (_, i) => {
|
|
29
|
+
let val = 0n;
|
|
30
|
+
for (let j = 0; j < 8; j++) {
|
|
31
|
+
val += BigInt(paddedBytes[i * 8 + j]) << BigInt(j * 8);
|
|
32
|
+
}
|
|
33
|
+
return val.toString();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function toBytesAndLen(val: bigint | number) {
|
|
38
|
+
if (val === 0n || val === 0) {
|
|
39
|
+
return { bytes: [0], length: 0 };
|
|
40
|
+
}
|
|
41
|
+
let hex = val.toString(16);
|
|
42
|
+
if (hex.length % 2) {
|
|
43
|
+
hex = '0' + hex;
|
|
44
|
+
}
|
|
45
|
+
const bytes = toBytes(`0x${hex}`);
|
|
46
|
+
return { bytes, length: bytes.length };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseNode(rlp: `0x${string}`) {
|
|
50
|
+
// Should be safe when working with branches and extensions without embedded children.
|
|
51
|
+
const decoded = fromRlp(rlp) as `0x${string}`[];
|
|
52
|
+
const node = {
|
|
53
|
+
rows: Array(16)
|
|
54
|
+
.fill(0)
|
|
55
|
+
.map(() => Array(32).fill(0)),
|
|
56
|
+
row_exist: Array(16).fill(false),
|
|
57
|
+
node_type: 0,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (decoded.length === 17) {
|
|
61
|
+
for (let i = 0; i < 16; i++) {
|
|
62
|
+
if (decoded[i] !== '0x') {
|
|
63
|
+
node.row_exist[i] = true;
|
|
64
|
+
node.rows[i] = padTo(toBytes(decoded[i]), 32);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else if (decoded.length === 2) {
|
|
68
|
+
const keyBytes = toBytes(decoded[0]);
|
|
69
|
+
const prefix = keyBytes[0];
|
|
70
|
+
if (prefix >> 4 >= 2) {
|
|
71
|
+
throw new Error('Unsupported: leaf node in proof path');
|
|
72
|
+
}
|
|
73
|
+
node.node_type = 1;
|
|
74
|
+
// Extension header format expected by the noir code: check out storage_proof types.nr.
|
|
75
|
+
node.rows[0][0] = prefix >> 4;
|
|
76
|
+
node.rows[0][8] = prefix & 0x0f;
|
|
77
|
+
node.rows[0][16] = keyBytes.length - 1;
|
|
78
|
+
|
|
79
|
+
for (let i = 1; i < keyBytes.length && i < 32; i++) {
|
|
80
|
+
node.rows[1][i - 1] = keyBytes[i];
|
|
81
|
+
}
|
|
82
|
+
node.rows[2] = padTo(toBytes(decoded[1]), 32);
|
|
83
|
+
node.row_exist[0] = node.row_exist[1] = node.row_exist[2] = true;
|
|
84
|
+
}
|
|
85
|
+
return node;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function parseProof(proof: `0x${string}`[], maxLen: number) {
|
|
89
|
+
const nodes = proof.slice(0, -1).slice(0, maxLen).map(parseNode);
|
|
90
|
+
while (nodes.length < maxLen) {
|
|
91
|
+
nodes.push({
|
|
92
|
+
rows: Array(16)
|
|
93
|
+
.fill(0)
|
|
94
|
+
.map(() => Array(32).fill(0)),
|
|
95
|
+
row_exist: Array(16).fill(false),
|
|
96
|
+
node_type: 0,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return nodes;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function nodeToLibFormat(node: { rows: number[][]; row_exist: boolean[]; node_type: number }) {
|
|
103
|
+
return {
|
|
104
|
+
rows: node.rows.map(bytesToU64s),
|
|
105
|
+
row_exist: node.row_exist,
|
|
106
|
+
node_type: String(node.node_type),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function main() {
|
|
111
|
+
if (!RPC_URL) {
|
|
112
|
+
throw new Error('RPC_URL is not set');
|
|
113
|
+
}
|
|
114
|
+
console.log(`Fetching account proof for ${ADDRESS}`);
|
|
115
|
+
|
|
116
|
+
const client = createPublicClient({
|
|
117
|
+
chain: mainnet,
|
|
118
|
+
transport: http(RPC_URL),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const [blockNumber, proof, block] = await Promise.all([
|
|
122
|
+
client.getBlockNumber(),
|
|
123
|
+
client.getProof({
|
|
124
|
+
address: ADDRESS,
|
|
125
|
+
storageKeys: [],
|
|
126
|
+
blockNumber: BLOCK_TAG === 'latest' ? undefined : BLOCK_TAG,
|
|
127
|
+
}),
|
|
128
|
+
client.getBlock({
|
|
129
|
+
blockNumber: BLOCK_TAG === 'latest' ? undefined : BLOCK_TAG,
|
|
130
|
+
}),
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
console.log(`Block: ${blockNumber}, Account nodes: ${proof.accountProof.length}`);
|
|
134
|
+
|
|
135
|
+
// The -1 is because the last node in the proof is the leaf, which is excluded from path verification.
|
|
136
|
+
const accountPathLen = proof.accountProof.length - 1;
|
|
137
|
+
if (accountPathLen > MAX_ACCOUNT_PATH) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Account proof path length ${accountPathLen} exceeds MAX_ACCOUNT_PATH ${MAX_ACCOUNT_PATH}. Increase the limit.`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const nonce = toBytesAndLen(proof.nonce);
|
|
144
|
+
const balance = toBytesAndLen(proof.balance);
|
|
145
|
+
|
|
146
|
+
const data = {
|
|
147
|
+
block_number: String(blockNumber),
|
|
148
|
+
node_length: String(accountPathLen),
|
|
149
|
+
root: bytesToU64s(toBytes(block.stateRoot)),
|
|
150
|
+
nodes: parseProof(proof.accountProof, MAX_ACCOUNT_PATH).map(nodeToLibFormat),
|
|
151
|
+
account: {
|
|
152
|
+
address: toBytes(ADDRESS).map(String),
|
|
153
|
+
balance: padTo(balance.bytes, 32).map(String),
|
|
154
|
+
balance_length: String(balance.length),
|
|
155
|
+
code_hash: bytesToU64s(toBytes(proof.codeHash)),
|
|
156
|
+
nonce: padTo(nonce.bytes, 8).map(String),
|
|
157
|
+
nonce_length: String(nonce.length),
|
|
158
|
+
storage_hash: bytesToU64s(toBytes(proof.storageHash)),
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
fs.writeFileSync(join(__dirname, 'account_proof.json'), JSON.stringify(data, null, 2));
|
|
163
|
+
console.log('account_proof.json generated');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch(console.error);
|
|
@@ -204,10 +204,9 @@ export class L1ToL2MessageExists extends Instruction {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
export class
|
|
208
|
-
|
|
209
|
-
static
|
|
210
|
-
static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG;
|
|
207
|
+
export class EmitPublicLog extends Instruction {
|
|
208
|
+
static type: string = 'EMITPUBLICLOG';
|
|
209
|
+
static readonly opcode: Opcode = Opcode.EMITPUBLICLOG;
|
|
211
210
|
// Informs (de)serialization. See Instruction.deserialize.
|
|
212
211
|
static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT16, OperandType.UINT16];
|
|
213
212
|
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
EcAdd,
|
|
20
20
|
EmitNoteHash,
|
|
21
21
|
EmitNullifier,
|
|
22
|
-
|
|
22
|
+
EmitPublicLog,
|
|
23
23
|
Eq,
|
|
24
24
|
FieldDiv,
|
|
25
25
|
GetContractInstance,
|
|
@@ -129,7 +129,7 @@ export const INSTRUCTION_SET = new Map<Opcode, InstructionDeserializer>([
|
|
|
129
129
|
[L1ToL2MessageExists.opcode, Instruction.fromBuffer.bind(L1ToL2MessageExists)], // Messages
|
|
130
130
|
|
|
131
131
|
// Accrued Substate
|
|
132
|
-
[
|
|
132
|
+
[EmitPublicLog.opcode, Instruction.fromBuffer.bind(EmitPublicLog)],
|
|
133
133
|
[SendL2ToL1Message.opcode, Instruction.fromBuffer.bind(SendL2ToL1Message)],
|
|
134
134
|
[GetContractInstance.opcode, Instruction.fromBuffer.bind(GetContractInstance)],
|
|
135
135
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DomainSeparator } from '@aztec/constants';
|
|
2
2
|
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import type { Logger } from '@aztec/foundation/log';
|
|
@@ -326,6 +326,6 @@ async function removeLiquidity(
|
|
|
326
326
|
async function computePartialNoteValidityCommitment(partialNote: { commitment: Fr }, completer: AztecAddress) {
|
|
327
327
|
return await poseidon2HashWithSeparator(
|
|
328
328
|
[partialNote.commitment, completer],
|
|
329
|
-
|
|
329
|
+
DomainSeparator.PARTIAL_NOTE_VALIDITY_COMMITMENT,
|
|
330
330
|
);
|
|
331
331
|
}
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
* - `EMITNOTEHASH`: max 64 per TX
|
|
144
144
|
* - `EMITNULLIFIER`: max 63 per TX (one reserved for TX nullifier)
|
|
145
145
|
* - `SENDL2TOL1MSG`: max 8 per TX
|
|
146
|
-
* - `
|
|
146
|
+
* - `EMITPUBLICLOG`: limited by total log payload size
|
|
147
147
|
*
|
|
148
148
|
* By having the inner contract REVERT after emitting side effects, those effects are discarded, allowing the outer contract to call it again. This enables thousands of opcode executions per TX instead of just the limit.
|
|
149
149
|
*
|
|
@@ -182,7 +182,7 @@ import {
|
|
|
182
182
|
EcAdd,
|
|
183
183
|
EmitNoteHash,
|
|
184
184
|
EmitNullifier,
|
|
185
|
-
|
|
185
|
+
EmitPublicLog,
|
|
186
186
|
Eq,
|
|
187
187
|
FieldDiv,
|
|
188
188
|
GetContractInstance,
|
|
@@ -1242,17 +1242,15 @@ export const SPAM_CONFIGS: Partial<Record<Opcode, SpamConfig[]>> = {
|
|
|
1242
1242
|
},
|
|
1243
1243
|
],
|
|
1244
1244
|
|
|
1245
|
-
//
|
|
1246
|
-
[Opcode.
|
|
1245
|
+
// EMITPUBLICLOG - two configs: minimal (many small logs) and max-size (one large log)
|
|
1246
|
+
[Opcode.EMITPUBLICLOG]: [
|
|
1247
1247
|
{
|
|
1248
1248
|
label: 'Many empty logs, revert, repeat',
|
|
1249
1249
|
setup: [
|
|
1250
1250
|
{ offset: 0, value: new Uint32(0n) }, // logSize = 0 fields (minimal)
|
|
1251
1251
|
{ offset: 1, value: new Uint32(0n) }, // revertSize
|
|
1252
1252
|
],
|
|
1253
|
-
targetInstructions: () => [
|
|
1254
|
-
new EmitUnencryptedLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 1),
|
|
1255
|
-
], // logOffset doesn't matter when size is 0
|
|
1253
|
+
targetInstructions: () => [new EmitPublicLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 1)], // logOffset doesn't matter when size is 0
|
|
1256
1254
|
cleanupInstructions: () => [
|
|
1257
1255
|
new Revert(/*addressing_mode=*/ 0, /*retSizeOffset=*/ 1, /*returnOffset=*/ 0).as(
|
|
1258
1256
|
Opcode.REVERT_8,
|
|
@@ -1276,9 +1274,7 @@ export const SPAM_CONFIGS: Partial<Record<Opcode, SpamConfig[]>> = {
|
|
|
1276
1274
|
// value: new Field(0n),
|
|
1277
1275
|
//})),
|
|
1278
1276
|
],
|
|
1279
|
-
targetInstructions: () => [
|
|
1280
|
-
new EmitUnencryptedLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 2),
|
|
1281
|
-
], // uses logOffset 2 (uninitialized Field(0))
|
|
1277
|
+
targetInstructions: () => [new EmitPublicLog(/*addressing_mode=*/ 0, /*logSizeOffset=*/ 0, /*logOffset=*/ 2)], // uses logOffset 2 (uninitialized Field(0))
|
|
1282
1278
|
cleanupInstructions: () => [
|
|
1283
1279
|
new Revert(/*addressing_mode=*/ 0, /*retSizeOffset=*/ 1, /*returnOffset=*/ 0).as(
|
|
1284
1280
|
Opcode.REVERT_8,
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_TEARDOWN_DA_GAS_LIMIT,
|
|
3
|
+
DEFAULT_TEARDOWN_L2_GAS_LIMIT,
|
|
4
|
+
PUBLIC_TX_L2_GAS_OVERHEAD,
|
|
5
|
+
TX_DA_GAS_OVERHEAD,
|
|
6
|
+
} from '@aztec/constants';
|
|
2
7
|
import { asyncMap } from '@aztec/foundation/async-map';
|
|
3
8
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
4
9
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
@@ -131,8 +136,11 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
|
|
|
131
136
|
teardownCallRequest,
|
|
132
137
|
feePayer,
|
|
133
138
|
/*gasUsedByPrivate*/ teardownCall
|
|
134
|
-
? new Gas(
|
|
135
|
-
|
|
139
|
+
? new Gas(
|
|
140
|
+
DEFAULT_TEARDOWN_DA_GAS_LIMIT + TX_DA_GAS_OVERHEAD,
|
|
141
|
+
DEFAULT_TEARDOWN_L2_GAS_LIMIT + PUBLIC_TX_L2_GAS_OVERHEAD,
|
|
142
|
+
)
|
|
143
|
+
: new Gas(TX_DA_GAS_OVERHEAD, PUBLIC_TX_L2_GAS_OVERHEAD),
|
|
136
144
|
defaultGlobals(),
|
|
137
145
|
);
|
|
138
146
|
}
|
|
@@ -161,6 +169,8 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
|
|
|
161
169
|
}
|
|
162
170
|
const avmResult = await this.simulator.simulate(tx, fullTxLabel);
|
|
163
171
|
|
|
172
|
+
await this.#recordBytecodeSizes(fullTxLabel, [...setupCalls, ...appCalls, ...(teardownCall ? [teardownCall] : [])]);
|
|
173
|
+
|
|
164
174
|
// Something like this is often useful for debugging:
|
|
165
175
|
//if (avmResult.revertReason) {
|
|
166
176
|
// // resolve / enrich revert reason
|
|
@@ -277,6 +287,27 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
|
|
|
277
287
|
|
|
278
288
|
return new PublicCallRequestWithCalldata(request, calldata);
|
|
279
289
|
}
|
|
290
|
+
|
|
291
|
+
// WARNING: Deduplicates by artifact name, so two different artifacts with the same name
|
|
292
|
+
// in a single tx would only record the first one's bytecode size.
|
|
293
|
+
async #recordBytecodeSizes(txLabel: string, calls: TestEnqueuedCall[]) {
|
|
294
|
+
const seenArtifactNames = new Set<string>();
|
|
295
|
+
for (const call of calls) {
|
|
296
|
+
const artifact = await this.contractDataSource.getContractArtifact(call.address);
|
|
297
|
+
if (!artifact || seenArtifactNames.has(artifact.name)) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
seenArtifactNames.add(artifact.name);
|
|
301
|
+
const instance = await this.contractDataSource.getContract(call.address);
|
|
302
|
+
if (!instance) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const contractClass = await this.contractDataSource.getContractClass(instance.currentContractClassId);
|
|
306
|
+
if (contractClass) {
|
|
307
|
+
this.metrics.recordBytecodeSize(txLabel, artifact.name, contractClass.packedBytecode.length);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
280
311
|
}
|
|
281
312
|
|
|
282
313
|
export function defaultGlobals() {
|
|
@@ -134,13 +134,13 @@ export async function createTxForPublicCalls(
|
|
|
134
134
|
const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
|
|
135
135
|
const header = BlockHeader.empty({ globalVariables: globals });
|
|
136
136
|
const constantData = new TxConstantData(header, txContext, Fr.zero(), Fr.zero());
|
|
137
|
-
const
|
|
137
|
+
const expirationTimestamp = 0n; // Not used in the simulator.
|
|
138
138
|
|
|
139
139
|
const txData = new PrivateKernelTailCircuitPublicInputs(
|
|
140
140
|
constantData,
|
|
141
141
|
/*gasUsed=*/ gasUsedByPrivate,
|
|
142
142
|
feePayer,
|
|
143
|
-
|
|
143
|
+
expirationTimestamp,
|
|
144
144
|
forPublic,
|
|
145
145
|
);
|
|
146
146
|
|
|
@@ -171,13 +171,13 @@ export async function createTxForPrivateOnly(
|
|
|
171
171
|
const gasSettings = new GasSettings(gasLimits, Gas.empty(), maxFeesPerGas, GasFees.empty());
|
|
172
172
|
const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
|
|
173
173
|
const constantData = new TxConstantData(BlockHeader.empty(), txContext, Fr.zero(), Fr.zero());
|
|
174
|
-
const
|
|
174
|
+
const expirationTimestamp = 0n; // Not used in the simulator.
|
|
175
175
|
|
|
176
176
|
const txData = new PrivateKernelTailCircuitPublicInputs(
|
|
177
177
|
constantData,
|
|
178
178
|
/*gasUsed=*/ gasUsedByPrivate,
|
|
179
179
|
feePayer,
|
|
180
|
-
|
|
180
|
+
expirationTimestamp,
|
|
181
181
|
/*forPublic=*/ undefined,
|
|
182
182
|
forRollup,
|
|
183
183
|
);
|
|
@@ -146,7 +146,7 @@ async function createTxFromHint(cppTx: AvmTxHint): Promise<Tx> {
|
|
|
146
146
|
constants,
|
|
147
147
|
cppTx.gasUsedByPrivate,
|
|
148
148
|
cppTx.feePayer,
|
|
149
|
-
0n, //
|
|
149
|
+
0n, // expirationTimestamp
|
|
150
150
|
forPublic,
|
|
151
151
|
undefined, // forRollup - not needed for public simulation
|
|
152
152
|
);
|
|
@@ -572,7 +572,7 @@ export class HintingMerkleWriteOperations implements MerkleTreeWriteOperations {
|
|
|
572
572
|
return await this.db.close();
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
-
async [Symbol.
|
|
575
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
576
576
|
await this.close();
|
|
577
577
|
}
|
|
578
578
|
|
|
@@ -82,7 +82,7 @@ export class GuardedMerkleTreeOperations implements MerkleTreeWriteOperations {
|
|
|
82
82
|
return this.guardAndPush(() => this.target.close());
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
async [Symbol.
|
|
85
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
86
86
|
await this.close();
|
|
87
87
|
}
|
|
88
88
|
getTreeInfo(treeId: MerkleTreeId): Promise<TreeInfo> {
|
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
PublicProcessorValidator,
|
|
26
26
|
SequencerConfig,
|
|
27
27
|
} from '@aztec/stdlib/interfaces/server';
|
|
28
|
+
import { type DebugLog, type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
28
29
|
import { ProvingRequestType } from '@aztec/stdlib/proofs';
|
|
29
30
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
30
31
|
import {
|
|
@@ -130,7 +131,6 @@ class PublicProcessorTimeoutError extends Error {
|
|
|
130
131
|
*/
|
|
131
132
|
export class PublicProcessor implements Traceable {
|
|
132
133
|
private metrics: PublicProcessorMetrics;
|
|
133
|
-
|
|
134
134
|
constructor(
|
|
135
135
|
protected globalVariables: GlobalVariables,
|
|
136
136
|
private guardedMerkleTree: GuardedMerkleTreeOperations,
|
|
@@ -140,6 +140,7 @@ export class PublicProcessor implements Traceable {
|
|
|
140
140
|
telemetryClient: TelemetryClient = getTelemetryClient(),
|
|
141
141
|
private log: Logger,
|
|
142
142
|
private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs' | 'fakeThrowAfterProcessingTxCount'> = {},
|
|
143
|
+
private debugLogStore: DebugLogStore = new NullDebugLogStore(),
|
|
143
144
|
) {
|
|
144
145
|
this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
|
|
145
146
|
}
|
|
@@ -159,12 +160,13 @@ export class PublicProcessor implements Traceable {
|
|
|
159
160
|
txs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
160
161
|
limits: PublicProcessorLimits = {},
|
|
161
162
|
validator: PublicProcessorValidator = {},
|
|
162
|
-
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], number]> {
|
|
163
|
+
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], number, DebugLog[]]> {
|
|
163
164
|
const { maxTransactions, maxBlockSize, deadline, maxBlockGas, maxBlobFields } = limits;
|
|
164
165
|
const { preprocessValidator, nullifierCache } = validator;
|
|
165
166
|
const result: ProcessedTx[] = [];
|
|
166
167
|
const usedTxs: Tx[] = [];
|
|
167
168
|
const failed: FailedTx[] = [];
|
|
169
|
+
const debugLogs: DebugLog[] = [];
|
|
168
170
|
const timer = new Timer();
|
|
169
171
|
|
|
170
172
|
let totalSizeInBytes = 0;
|
|
@@ -241,7 +243,7 @@ export class PublicProcessor implements Traceable {
|
|
|
241
243
|
this.contractsDB.createCheckpoint();
|
|
242
244
|
|
|
243
245
|
try {
|
|
244
|
-
const [processedTx, returnValues] = await this.processTx(tx, deadline);
|
|
246
|
+
const [processedTx, returnValues, txDebugLogs] = await this.processTx(tx, deadline);
|
|
245
247
|
|
|
246
248
|
// Inject a fake processing failure after N txs if requested
|
|
247
249
|
const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
|
|
@@ -290,6 +292,9 @@ export class PublicProcessor implements Traceable {
|
|
|
290
292
|
result.push(processedTx);
|
|
291
293
|
usedTxs.push(tx);
|
|
292
294
|
returns = returns.concat(returnValues);
|
|
295
|
+
debugLogs.push(...txDebugLogs);
|
|
296
|
+
|
|
297
|
+
this.debugLogStore.storeLogs(processedTx.hash.toString(), txDebugLogs);
|
|
293
298
|
|
|
294
299
|
totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
|
|
295
300
|
totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
|
|
@@ -363,7 +368,7 @@ export class PublicProcessor implements Traceable {
|
|
|
363
368
|
totalSizeInBytes,
|
|
364
369
|
});
|
|
365
370
|
|
|
366
|
-
return [result, failed, usedTxs, returns, totalBlobFields];
|
|
371
|
+
return [result, failed, usedTxs, returns, totalBlobFields, debugLogs];
|
|
367
372
|
}
|
|
368
373
|
|
|
369
374
|
private async checkWorldStateUnchanged(
|
|
@@ -383,8 +388,13 @@ export class PublicProcessor implements Traceable {
|
|
|
383
388
|
}
|
|
384
389
|
|
|
385
390
|
@trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString() }))
|
|
386
|
-
private async processTx(
|
|
387
|
-
|
|
391
|
+
private async processTx(
|
|
392
|
+
tx: Tx,
|
|
393
|
+
deadline: Date | undefined,
|
|
394
|
+
): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
|
|
395
|
+
const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
|
|
396
|
+
this.processTxWithinDeadline(tx, deadline),
|
|
397
|
+
);
|
|
388
398
|
|
|
389
399
|
this.log.verbose(
|
|
390
400
|
!tx.hasPublicCalls()
|
|
@@ -407,7 +417,7 @@ export class PublicProcessor implements Traceable {
|
|
|
407
417
|
},
|
|
408
418
|
);
|
|
409
419
|
|
|
410
|
-
return [processedTx, returnValues ?? []];
|
|
420
|
+
return [processedTx, returnValues ?? [], debugLogs];
|
|
411
421
|
}
|
|
412
422
|
|
|
413
423
|
private async doTreeInsertionsForPrivateOnlyTx(processedTx: ProcessedTx): Promise<void> {
|
|
@@ -441,10 +451,9 @@ export class PublicProcessor implements Traceable {
|
|
|
441
451
|
private async processTxWithinDeadline(
|
|
442
452
|
tx: Tx,
|
|
443
453
|
deadline: Date | undefined,
|
|
444
|
-
): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined]> {
|
|
445
|
-
const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined]> =
|
|
446
|
-
? () => this.processTxWithPublicCalls(tx)
|
|
447
|
-
: () => this.processPrivateOnlyTx(tx);
|
|
454
|
+
): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
|
|
455
|
+
const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
|
|
456
|
+
tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
|
|
448
457
|
|
|
449
458
|
// Fake a delay per tx if instructed (used for tests)
|
|
450
459
|
const fakeDelayPerTxMs = this.opts.fakeProcessingDelayPerTxMs;
|
|
@@ -512,7 +521,7 @@ export class PublicProcessor implements Traceable {
|
|
|
512
521
|
@trackSpan('PublicProcessor.processPrivateOnlyTx', (tx: Tx) => ({
|
|
513
522
|
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
514
523
|
}))
|
|
515
|
-
private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined]> {
|
|
524
|
+
private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined, DebugLog[]]> {
|
|
516
525
|
const gasFees = this.globalVariables.gasFees;
|
|
517
526
|
const transactionFee = computeTransactionFee(gasFees, tx.data.constants.txContext.gasSettings, tx.data.gasUsed);
|
|
518
527
|
|
|
@@ -537,13 +546,13 @@ export class PublicProcessor implements Traceable {
|
|
|
537
546
|
|
|
538
547
|
await this.contractsDB.addNewContracts(tx);
|
|
539
548
|
|
|
540
|
-
return [processedTx, undefined];
|
|
549
|
+
return [processedTx, undefined, []];
|
|
541
550
|
}
|
|
542
551
|
|
|
543
552
|
@trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({
|
|
544
553
|
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
545
554
|
}))
|
|
546
|
-
private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
|
|
555
|
+
private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
|
|
547
556
|
const timer = new Timer();
|
|
548
557
|
|
|
549
558
|
const result = await this.publicTxSimulator.simulate(tx);
|
|
@@ -581,7 +590,7 @@ export class PublicProcessor implements Traceable {
|
|
|
581
590
|
revertReason,
|
|
582
591
|
);
|
|
583
592
|
|
|
584
|
-
return [processedTx, appLogicReturnValues];
|
|
593
|
+
return [processedTx, appLogicReturnValues, result.logs ?? []];
|
|
585
594
|
}
|
|
586
595
|
|
|
587
596
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Logger, type LoggerBindings, createLogger
|
|
1
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { sleep } from '@aztec/foundation/sleep';
|
|
3
3
|
import { type CancellationToken, avmSimulate, cancelSimulation, createCancellationToken } from '@aztec/native';
|
|
4
4
|
import { ProtocolContractsList } from '@aztec/protocol-contracts';
|
|
@@ -100,8 +100,7 @@ export class CppPublicTxSimulator extends PublicTxSimulator implements PublicTxS
|
|
|
100
100
|
inputBuffer,
|
|
101
101
|
contractProvider,
|
|
102
102
|
wsCppHandle,
|
|
103
|
-
|
|
104
|
-
// TODO: re-enable logging
|
|
103
|
+
this.log.level,
|
|
105
104
|
undefined,
|
|
106
105
|
this.cancellationToken,
|
|
107
106
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Logger, type LoggerBindings, createLogger
|
|
1
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { avmSimulateWithHintedDbs } from '@aztec/native';
|
|
3
3
|
import {
|
|
4
4
|
AvmCircuitInputs,
|
|
@@ -75,7 +75,7 @@ export class CppPublicTxSimulatorHintedDbs extends PublicTxSimulator implements
|
|
|
75
75
|
|
|
76
76
|
let resultBuffer: Buffer;
|
|
77
77
|
try {
|
|
78
|
-
resultBuffer = await avmSimulateWithHintedDbs(inputBuffer,
|
|
78
|
+
resultBuffer = await avmSimulateWithHintedDbs(inputBuffer, this.log.level);
|
|
79
79
|
} catch (error: any) {
|
|
80
80
|
throw new SimulationError(`C++ hinted simulation failed: ${error.message}`, []);
|
|
81
81
|
}
|
|
@@ -19,10 +19,11 @@ export function createPublicTxSimulatorForBlockBuilding(
|
|
|
19
19
|
globalVariables: GlobalVariables,
|
|
20
20
|
telemetryClient: TelemetryClient,
|
|
21
21
|
bindings?: LoggerBindings,
|
|
22
|
+
collectDebugLogs = false,
|
|
22
23
|
) {
|
|
23
24
|
const config = PublicSimulatorConfig.from({
|
|
24
25
|
skipFeeEnforcement: false,
|
|
25
|
-
collectDebugLogs
|
|
26
|
+
collectDebugLogs,
|
|
26
27
|
collectHints: false,
|
|
27
28
|
collectPublicInputs: false,
|
|
28
29
|
collectStatistics: false,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MAX_PROCESSABLE_L2_GAS } from '@aztec/constants';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { ProtocolContractAddress, ProtocolContractsList } from '@aztec/protocol-contracts';
|
|
@@ -199,8 +199,8 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
|
|
|
199
199
|
|
|
200
200
|
// Such transactions should be filtered by GasTxValidator.
|
|
201
201
|
assert(
|
|
202
|
-
context.getActualGasUsed().l2Gas <=
|
|
203
|
-
`Transaction consumes ${context.getActualGasUsed().l2Gas} L2 gas, which exceeds the
|
|
202
|
+
context.getActualGasUsed().l2Gas <= MAX_PROCESSABLE_L2_GAS,
|
|
203
|
+
`Transaction consumes ${context.getActualGasUsed().l2Gas} L2 gas, which exceeds the maximum processable gas of ${MAX_PROCESSABLE_L2_GAS}`,
|
|
204
204
|
);
|
|
205
205
|
await this.payFee(context);
|
|
206
206
|
|
|
@@ -21,6 +21,7 @@ export interface PublicTxMetrics {
|
|
|
21
21
|
totalDurationMs: number;
|
|
22
22
|
manaUsed: number | undefined;
|
|
23
23
|
totalInstructionsExecuted: number;
|
|
24
|
+
bytecodeSizes: { contractName: string; sizeBytes: number }[];
|
|
24
25
|
nonRevertiblePrivateInsertionsUs: number | undefined;
|
|
25
26
|
revertiblePrivateInsertionsUs: number | undefined;
|
|
26
27
|
enqueuedCalls: PublicEnqueuedCallMetrics[];
|
|
@@ -62,6 +63,7 @@ function createEmptyTxMetrics(): PublicTxMetrics {
|
|
|
62
63
|
totalDurationMs: 0,
|
|
63
64
|
manaUsed: 0,
|
|
64
65
|
totalInstructionsExecuted: 0,
|
|
66
|
+
bytecodeSizes: [],
|
|
65
67
|
nonRevertiblePrivateInsertionsUs: undefined,
|
|
66
68
|
revertiblePrivateInsertionsUs: undefined,
|
|
67
69
|
enqueuedCalls: [],
|
|
@@ -172,6 +174,12 @@ export class TestExecutorMetrics implements ExecutorMetricsInterface {
|
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
177
|
+
recordBytecodeSize(txLabel: string, contractName: string, sizeBytes: number) {
|
|
178
|
+
const txMetrics = this.txMetrics.get(txLabel);
|
|
179
|
+
assert(txMetrics, `Cannot record bytecode size for unknown tx label: ${txLabel}`);
|
|
180
|
+
txMetrics.bytecodeSizes.push({ contractName, sizeBytes });
|
|
181
|
+
}
|
|
182
|
+
|
|
175
183
|
recordProverMetrics(txLabel: string, metrics: Partial<PublicTxMetrics>) {
|
|
176
184
|
if (!this.txMetrics.has(txLabel)) {
|
|
177
185
|
this.txMetrics.set(txLabel, createEmptyTxMetrics());
|
|
@@ -216,6 +224,15 @@ export class TestExecutorMetrics implements ExecutorMetricsInterface {
|
|
|
216
224
|
) {
|
|
217
225
|
pretty += `${INDENT0}Total instructions executed: ${fmtNum(txMetrics.totalInstructionsExecuted)}\n`;
|
|
218
226
|
}
|
|
227
|
+
if (
|
|
228
|
+
(filter === PublicTxMetricsFilter.TOTALS || filter === PublicTxMetricsFilter.ALL) &&
|
|
229
|
+
txMetrics.bytecodeSizes.length > 0
|
|
230
|
+
) {
|
|
231
|
+
pretty += `${INDENT0}Bytecode sizes:\n`;
|
|
232
|
+
for (const { contractName, sizeBytes } of txMetrics.bytecodeSizes) {
|
|
233
|
+
pretty += `${INDENT1}${contractName}: ${fmtNum(sizeBytes, 'bytes')}\n`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
219
236
|
if (filter === PublicTxMetricsFilter.DURATIONS || filter === PublicTxMetricsFilter.ALL) {
|
|
220
237
|
pretty += `${INDENT0}Private insertions:\n`;
|
|
221
238
|
pretty += `${INDENT1}Non-revertible: ${fmtNum(txMetrics.nonRevertiblePrivateInsertionsUs! / 1_000, 'ms')}\n`;
|
|
@@ -387,6 +404,13 @@ export class TestExecutorMetrics implements ExecutorMetricsInterface {
|
|
|
387
404
|
});
|
|
388
405
|
}
|
|
389
406
|
}
|
|
407
|
+
for (const { contractName, sizeBytes } of txMetrics.bytecodeSizes) {
|
|
408
|
+
data.push({
|
|
409
|
+
name: `${txLabel}/bytecodeSizeBytes/${contractName}`,
|
|
410
|
+
value: sizeBytes,
|
|
411
|
+
unit: 'bytes',
|
|
412
|
+
});
|
|
413
|
+
}
|
|
390
414
|
}
|
|
391
415
|
return JSON.stringify(data, null, indent);
|
|
392
416
|
}
|