@aztec/aztec-node 0.0.1-commit.54489865 → 0.0.1-commit.5914bae
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/aztec-node/config.d.ts +7 -4
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +10 -2
- package/dest/aztec-node/server.d.ts +44 -36
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +396 -190
- package/dest/sentinel/sentinel.d.ts +2 -2
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +56 -30
- package/dest/sentinel/store.d.ts +2 -2
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +11 -7
- package/package.json +27 -25
- package/src/aztec-node/config.ts +24 -8
- package/src/aztec-node/server.ts +481 -228
- package/src/sentinel/sentinel.ts +59 -26
- package/src/sentinel/store.ts +12 -12
package/src/aztec-node/server.ts
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
import { Archiver, createArchiver } from '@aztec/archiver';
|
|
2
2
|
import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
|
|
3
3
|
import { type BlobClientInterface, createBlobClientWithFileStores } from '@aztec/blob-client/client';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
type L1_TO_L2_MSG_TREE_HEIGHT,
|
|
7
|
-
type NOTE_HASH_TREE_HEIGHT,
|
|
8
|
-
type NULLIFIER_TREE_HEIGHT,
|
|
9
|
-
type PUBLIC_DATA_TREE_HEIGHT,
|
|
10
|
-
} from '@aztec/constants';
|
|
4
|
+
import { Blob } from '@aztec/blob-lib';
|
|
5
|
+
import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
|
|
11
6
|
import { EpochCache, type EpochCacheInterface } from '@aztec/epoch-cache';
|
|
12
7
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
13
|
-
import { getPublicClient } from '@aztec/ethereum/client';
|
|
8
|
+
import { getPublicClient, makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
14
9
|
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
15
10
|
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
16
11
|
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
17
|
-
import { compactArray, pick } from '@aztec/foundation/collection';
|
|
12
|
+
import { chunkBy, compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
18
13
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
19
14
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
20
15
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
21
16
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
17
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
22
18
|
import { count } from '@aztec/foundation/string';
|
|
23
19
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
24
20
|
import { MembershipWitness, SiblingPath } from '@aztec/foundation/trees';
|
|
25
|
-
import { KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
|
|
21
|
+
import { type KeyStore, KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
|
|
26
22
|
import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
|
|
23
|
+
import { createForwarderL1TxUtilsFromSigners, createL1TxUtilsFromSigners } from '@aztec/node-lib/factories';
|
|
27
24
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
type P2P,
|
|
26
|
+
type P2PClientDeps,
|
|
27
|
+
createP2PClient,
|
|
28
|
+
createTxValidatorForAcceptingTxsOverRPC,
|
|
29
|
+
getDefaultAllowedSetupFunctions,
|
|
30
|
+
} from '@aztec/p2p';
|
|
32
31
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
32
|
+
import { type ProverNode, type ProverNodeDeps, createProverNode } from '@aztec/prover-node';
|
|
33
|
+
import { createKeyStoreForProver } from '@aztec/prover-node/config';
|
|
33
34
|
import { GlobalVariableBuilder, SequencerClient, type SequencerPublisher } from '@aztec/sequencer-client';
|
|
34
35
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
35
36
|
import {
|
|
@@ -41,7 +42,14 @@ import {
|
|
|
41
42
|
} from '@aztec/slasher';
|
|
42
43
|
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
43
44
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
44
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
type BlockData,
|
|
47
|
+
BlockHash,
|
|
48
|
+
type BlockParameter,
|
|
49
|
+
type DataInBlock,
|
|
50
|
+
L2Block,
|
|
51
|
+
type L2BlockSource,
|
|
52
|
+
} from '@aztec/stdlib/block';
|
|
45
53
|
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
46
54
|
import type {
|
|
47
55
|
ContractClassPublic,
|
|
@@ -57,6 +65,7 @@ import {
|
|
|
57
65
|
type AztecNodeAdmin,
|
|
58
66
|
type AztecNodeAdminConfig,
|
|
59
67
|
AztecNodeAdminConfigSchema,
|
|
68
|
+
type AztecNodeDebug,
|
|
60
69
|
type GetContractClassLogsResponse,
|
|
61
70
|
type GetPublicLogsResponse,
|
|
62
71
|
} from '@aztec/stdlib/interfaces/client';
|
|
@@ -69,9 +78,9 @@ import {
|
|
|
69
78
|
type WorldStateSynchronizer,
|
|
70
79
|
tryStop,
|
|
71
80
|
} from '@aztec/stdlib/interfaces/server';
|
|
72
|
-
import type { LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
81
|
+
import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
82
|
+
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
73
83
|
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
74
|
-
import { P2PClientType } from '@aztec/stdlib/p2p';
|
|
75
84
|
import type { Offense, SlashPayloadRound } from '@aztec/stdlib/slashing';
|
|
76
85
|
import type { NullifierLeafPreimage, PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
77
86
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
@@ -101,13 +110,13 @@ import {
|
|
|
101
110
|
FullNodeCheckpointsBuilder,
|
|
102
111
|
NodeKeystoreAdapter,
|
|
103
112
|
ValidatorClient,
|
|
104
|
-
|
|
113
|
+
createProposalHandler,
|
|
105
114
|
createValidatorClient,
|
|
106
|
-
createValidatorForAcceptingTxs,
|
|
107
115
|
} from '@aztec/validator-client';
|
|
116
|
+
import type { SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
108
117
|
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
109
118
|
|
|
110
|
-
import { createPublicClient
|
|
119
|
+
import { createPublicClient } from 'viem';
|
|
111
120
|
|
|
112
121
|
import { createSentinel } from '../sentinel/factory.js';
|
|
113
122
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
@@ -117,7 +126,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
117
126
|
/**
|
|
118
127
|
* The aztec node.
|
|
119
128
|
*/
|
|
120
|
-
export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
129
|
+
export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDebug, Traceable {
|
|
121
130
|
private metrics: NodeMetrics;
|
|
122
131
|
private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
|
|
123
132
|
|
|
@@ -135,6 +144,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
135
144
|
protected readonly l1ToL2MessageSource: L1ToL2MessageSource,
|
|
136
145
|
protected readonly worldStateSynchronizer: WorldStateSynchronizer,
|
|
137
146
|
protected readonly sequencer: SequencerClient | undefined,
|
|
147
|
+
protected readonly proverNode: ProverNode | undefined,
|
|
138
148
|
protected readonly slasherClient: SlasherClientInterface | undefined,
|
|
139
149
|
protected readonly validatorsSentinel: Sentinel | undefined,
|
|
140
150
|
protected readonly epochPruneWatcher: EpochPruneWatcher | undefined,
|
|
@@ -147,12 +157,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
147
157
|
private telemetry: TelemetryClient = getTelemetryClient(),
|
|
148
158
|
private log = createLogger('node'),
|
|
149
159
|
private blobClient?: BlobClientInterface,
|
|
160
|
+
private validatorClient?: ValidatorClient,
|
|
161
|
+
private keyStoreManager?: KeystoreManager,
|
|
162
|
+
private debugLogStore: DebugLogStore = new NullDebugLogStore(),
|
|
150
163
|
) {
|
|
151
164
|
this.metrics = new NodeMetrics(telemetry, 'AztecNodeService');
|
|
152
165
|
this.tracer = telemetry.getTracer('AztecNodeService');
|
|
153
166
|
|
|
154
167
|
this.log.info(`Aztec Node version: ${this.packageVersion}`);
|
|
155
168
|
this.log.info(`Aztec Node started on chain 0x${l1ChainId.toString(16)}`, config.l1Contracts);
|
|
169
|
+
|
|
170
|
+
// A defensive check that protects us against introducing a bug in the complex `createAndSync` function. We must
|
|
171
|
+
// never have debugLogStore enabled when not in test mode because then we would be accumulating debug logs in
|
|
172
|
+
// memory which could be a DoS vector on the sequencer (since no fees are paid for debug logs).
|
|
173
|
+
if (debugLogStore.isEnabled && config.realProofs) {
|
|
174
|
+
throw new Error('debugLogStore should never be enabled when realProofs are set');
|
|
175
|
+
}
|
|
156
176
|
}
|
|
157
177
|
|
|
158
178
|
public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
|
|
@@ -176,11 +196,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
176
196
|
logger?: Logger;
|
|
177
197
|
publisher?: SequencerPublisher;
|
|
178
198
|
dateProvider?: DateProvider;
|
|
179
|
-
p2pClientDeps?: P2PClientDeps
|
|
199
|
+
p2pClientDeps?: P2PClientDeps;
|
|
200
|
+
proverNodeDeps?: Partial<ProverNodeDeps>;
|
|
201
|
+
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
180
202
|
} = {},
|
|
181
203
|
options: {
|
|
182
204
|
prefilledPublicData?: PublicDataTreeLeaf[];
|
|
183
205
|
dontStartSequencer?: boolean;
|
|
206
|
+
dontStartProverNode?: boolean;
|
|
184
207
|
} = {},
|
|
185
208
|
): Promise<AztecNodeService> {
|
|
186
209
|
const config = { ...inputConfig }; // Copy the config so we dont mutate the input object
|
|
@@ -190,16 +213,29 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
190
213
|
const dateProvider = deps.dateProvider ?? new DateProvider();
|
|
191
214
|
const ethereumChain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
192
215
|
|
|
193
|
-
// Build a key store from file if given or from environment otherwise
|
|
216
|
+
// Build a key store from file if given or from environment otherwise.
|
|
217
|
+
// We keep the raw KeyStore available so we can merge with prover keys if enableProverNode is set.
|
|
194
218
|
let keyStoreManager: KeystoreManager | undefined;
|
|
195
219
|
const keyStoreProvided = config.keyStoreDirectory !== undefined && config.keyStoreDirectory.length > 0;
|
|
196
220
|
if (keyStoreProvided) {
|
|
197
221
|
const keyStores = loadKeystores(config.keyStoreDirectory!);
|
|
198
222
|
keyStoreManager = new KeystoreManager(mergeKeystores(keyStores));
|
|
199
223
|
} else {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
const rawKeyStores: KeyStore[] = [];
|
|
225
|
+
const validatorKeyStore = createKeyStoreForValidator(config);
|
|
226
|
+
if (validatorKeyStore) {
|
|
227
|
+
rawKeyStores.push(validatorKeyStore);
|
|
228
|
+
}
|
|
229
|
+
if (config.enableProverNode) {
|
|
230
|
+
const proverKeyStore = createKeyStoreForProver(config);
|
|
231
|
+
if (proverKeyStore) {
|
|
232
|
+
rawKeyStores.push(proverKeyStore);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (rawKeyStores.length > 0) {
|
|
236
|
+
keyStoreManager = new KeystoreManager(
|
|
237
|
+
rawKeyStores.length === 1 ? rawKeyStores[0] : mergeKeystores(rawKeyStores),
|
|
238
|
+
);
|
|
203
239
|
}
|
|
204
240
|
}
|
|
205
241
|
|
|
@@ -210,10 +246,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
210
246
|
if (keyStoreManager === undefined) {
|
|
211
247
|
throw new Error('Failed to create key store, a requirement for running a validator');
|
|
212
248
|
}
|
|
213
|
-
if (!keyStoreProvided) {
|
|
214
|
-
log.warn(
|
|
215
|
-
'KEY STORE CREATED FROM ENVIRONMENT, IT IS RECOMMENDED TO USE A FILE-BASED KEY STORE IN PRODUCTION ENVIRONMENTS',
|
|
216
|
-
);
|
|
249
|
+
if (!keyStoreProvided && process.env.NODE_ENV !== 'test') {
|
|
250
|
+
log.warn("Keystore created from env: it's recommended to use a file-based key store for production");
|
|
217
251
|
}
|
|
218
252
|
ValidatorClient.validateKeyStoreConfiguration(keyStoreManager, log);
|
|
219
253
|
}
|
|
@@ -227,7 +261,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
227
261
|
|
|
228
262
|
const publicClient = createPublicClient({
|
|
229
263
|
chain: ethereumChain.chainInfo,
|
|
230
|
-
transport:
|
|
264
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
|
|
231
265
|
pollingInterval: config.viemPollingIntervalMS,
|
|
232
266
|
});
|
|
233
267
|
|
|
@@ -241,10 +275,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
241
275
|
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
|
|
242
276
|
|
|
243
277
|
const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
|
|
244
|
-
const [l1GenesisTime, slotDuration, rollupVersionFromRollup] = await Promise.all([
|
|
278
|
+
const [l1GenesisTime, slotDuration, rollupVersionFromRollup, rollupManaLimit] = await Promise.all([
|
|
245
279
|
rollupContract.getL1GenesisTime(),
|
|
246
280
|
rollupContract.getSlotDuration(),
|
|
247
281
|
rollupContract.getVersion(),
|
|
282
|
+
rollupContract.getManaLimit().then(Number),
|
|
248
283
|
] as const);
|
|
249
284
|
|
|
250
285
|
config.rollupVersion ??= Number(rollupVersionFromRollup);
|
|
@@ -255,7 +290,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
255
290
|
);
|
|
256
291
|
}
|
|
257
292
|
|
|
258
|
-
const blobClient = await createBlobClientWithFileStores(config,
|
|
293
|
+
const blobClient = await createBlobClientWithFileStores(config, log.createChild('blob-client'));
|
|
259
294
|
|
|
260
295
|
// attempt snapshot sync if possible
|
|
261
296
|
await trySnapshotSync(config, log);
|
|
@@ -279,14 +314,28 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
279
314
|
config.realProofs || config.debugForceTxProofVerification
|
|
280
315
|
? await BBCircuitVerifier.new(config)
|
|
281
316
|
: new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
317
|
+
|
|
318
|
+
let debugLogStore: DebugLogStore;
|
|
282
319
|
if (!config.realProofs) {
|
|
283
320
|
log.warn(`Aztec node is accepting fake proofs`);
|
|
321
|
+
|
|
322
|
+
debugLogStore = new InMemoryDebugLogStore();
|
|
323
|
+
log.info(
|
|
324
|
+
'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
|
|
325
|
+
);
|
|
326
|
+
} else {
|
|
327
|
+
debugLogStore = new NullDebugLogStore();
|
|
284
328
|
}
|
|
329
|
+
|
|
285
330
|
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
286
331
|
|
|
332
|
+
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
333
|
+
if (proverOnly) {
|
|
334
|
+
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
335
|
+
}
|
|
336
|
+
|
|
287
337
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
288
338
|
const p2pClient = await createP2PClient(
|
|
289
|
-
P2PClientType.Full,
|
|
290
339
|
config,
|
|
291
340
|
archiver,
|
|
292
341
|
proofVerifier,
|
|
@@ -298,59 +347,72 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
298
347
|
deps.p2pClientDeps,
|
|
299
348
|
);
|
|
300
349
|
|
|
301
|
-
// We
|
|
302
|
-
|
|
350
|
+
// We'll accumulate sentinel watchers here
|
|
351
|
+
const watchers: Watcher[] = [];
|
|
303
352
|
|
|
304
|
-
// Create FullNodeCheckpointsBuilder for
|
|
353
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
354
|
+
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
305
355
|
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
|
|
306
|
-
{
|
|
356
|
+
{
|
|
357
|
+
...config,
|
|
358
|
+
l1GenesisTime,
|
|
359
|
+
slotDuration: Number(slotDuration),
|
|
360
|
+
rollupManaLimit,
|
|
361
|
+
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
362
|
+
},
|
|
307
363
|
worldStateSynchronizer,
|
|
308
364
|
archiver,
|
|
309
365
|
dateProvider,
|
|
310
366
|
telemetry,
|
|
311
367
|
);
|
|
312
368
|
|
|
313
|
-
|
|
314
|
-
const watchers: Watcher[] = [];
|
|
369
|
+
let validatorClient: ValidatorClient | undefined;
|
|
315
370
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
371
|
+
if (!proverOnly) {
|
|
372
|
+
// Create validator client if required
|
|
373
|
+
validatorClient = await createValidatorClient(config, {
|
|
374
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
375
|
+
worldState: worldStateSynchronizer,
|
|
376
|
+
p2pClient,
|
|
377
|
+
telemetry,
|
|
378
|
+
dateProvider,
|
|
379
|
+
epochCache,
|
|
380
|
+
blockSource: archiver,
|
|
381
|
+
l1ToL2MessageSource: archiver,
|
|
382
|
+
keyStoreManager,
|
|
383
|
+
blobClient,
|
|
384
|
+
slashingProtectionDb: deps.slashingProtectionDb,
|
|
385
|
+
});
|
|
329
386
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
387
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
388
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
389
|
+
// like attestations or auths will fail.
|
|
390
|
+
if (validatorClient) {
|
|
391
|
+
watchers.push(validatorClient);
|
|
392
|
+
if (!options.dontStartSequencer) {
|
|
393
|
+
await validatorClient.registerHandlers();
|
|
394
|
+
}
|
|
337
395
|
}
|
|
338
396
|
}
|
|
339
397
|
|
|
340
|
-
// If there's no validator client
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
398
|
+
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
399
|
+
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
400
|
+
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
401
|
+
// Checkpoint proposals are handled if the blob client can upload blobs.
|
|
402
|
+
if (!validatorClient) {
|
|
403
|
+
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
404
|
+
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
405
|
+
createProposalHandler(config, {
|
|
345
406
|
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
346
407
|
worldState: worldStateSynchronizer,
|
|
347
408
|
epochCache,
|
|
348
409
|
blockSource: archiver,
|
|
349
410
|
l1ToL2MessageSource: archiver,
|
|
350
411
|
p2pClient,
|
|
412
|
+
blobClient,
|
|
351
413
|
dateProvider,
|
|
352
414
|
telemetry,
|
|
353
|
-
}).
|
|
415
|
+
}).register(p2pClient, reexecute);
|
|
354
416
|
}
|
|
355
417
|
|
|
356
418
|
// Start world state and wait for it to sync to the archiver.
|
|
@@ -359,29 +421,33 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
359
421
|
// Start p2p. Note that it depends on world state to be running.
|
|
360
422
|
await p2pClient.start();
|
|
361
423
|
|
|
362
|
-
|
|
363
|
-
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
364
|
-
watchers.push(validatorsSentinel);
|
|
365
|
-
}
|
|
366
|
-
|
|
424
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
367
425
|
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
368
|
-
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
369
|
-
epochPruneWatcher = new EpochPruneWatcher(
|
|
370
|
-
archiver,
|
|
371
|
-
archiver,
|
|
372
|
-
epochCache,
|
|
373
|
-
p2pClient.getTxProvider(),
|
|
374
|
-
validatorCheckpointsBuilder,
|
|
375
|
-
config,
|
|
376
|
-
);
|
|
377
|
-
watchers.push(epochPruneWatcher);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
381
426
|
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
427
|
+
|
|
428
|
+
if (!proverOnly) {
|
|
429
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
430
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
431
|
+
watchers.push(validatorsSentinel);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
435
|
+
epochPruneWatcher = new EpochPruneWatcher(
|
|
436
|
+
archiver,
|
|
437
|
+
archiver,
|
|
438
|
+
epochCache,
|
|
439
|
+
p2pClient.getTxProvider(),
|
|
440
|
+
validatorCheckpointsBuilder,
|
|
441
|
+
config,
|
|
442
|
+
);
|
|
443
|
+
watchers.push(epochPruneWatcher);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
447
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
448
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
449
|
+
watchers.push(attestationsBlockWatcher);
|
|
450
|
+
}
|
|
385
451
|
}
|
|
386
452
|
|
|
387
453
|
// Start p2p-related services once the archiver has completed sync
|
|
@@ -396,6 +462,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
396
462
|
})
|
|
397
463
|
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
398
464
|
|
|
465
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, {
|
|
466
|
+
l1Contracts: config.l1Contracts,
|
|
467
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
468
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
469
|
+
l1GenesisTime,
|
|
470
|
+
slotDuration: Number(slotDuration),
|
|
471
|
+
});
|
|
472
|
+
|
|
399
473
|
// Validator enabled, create/start relevant service
|
|
400
474
|
let sequencer: SequencerClient | undefined;
|
|
401
475
|
let slasherClient: SlasherClientInterface | undefined;
|
|
@@ -418,28 +492,29 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
418
492
|
);
|
|
419
493
|
await slasherClient.start();
|
|
420
494
|
|
|
421
|
-
const l1TxUtils = config.
|
|
422
|
-
? await
|
|
495
|
+
const l1TxUtils = config.sequencerPublisherForwarderAddress
|
|
496
|
+
? await createForwarderL1TxUtilsFromSigners(
|
|
423
497
|
publicClient,
|
|
424
498
|
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
425
|
-
config.
|
|
499
|
+
config.sequencerPublisherForwarderAddress,
|
|
426
500
|
{ ...config, scope: 'sequencer' },
|
|
427
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider },
|
|
501
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
428
502
|
)
|
|
429
|
-
: await
|
|
503
|
+
: await createL1TxUtilsFromSigners(
|
|
430
504
|
publicClient,
|
|
431
505
|
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
432
506
|
{ ...config, scope: 'sequencer' },
|
|
433
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider },
|
|
507
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
434
508
|
);
|
|
435
509
|
|
|
436
510
|
// Create and start the sequencer client
|
|
437
511
|
const checkpointsBuilder = new CheckpointsBuilder(
|
|
438
|
-
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
|
|
512
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
439
513
|
worldStateSynchronizer,
|
|
440
514
|
archiver,
|
|
441
515
|
dateProvider,
|
|
442
516
|
telemetry,
|
|
517
|
+
debugLogStore,
|
|
443
518
|
);
|
|
444
519
|
|
|
445
520
|
sequencer = await SequencerClient.new(config, {
|
|
@@ -457,6 +532,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
457
532
|
dateProvider,
|
|
458
533
|
blobClient,
|
|
459
534
|
nodeKeyStore: keyStoreManager!,
|
|
535
|
+
globalVariableBuilder,
|
|
460
536
|
});
|
|
461
537
|
}
|
|
462
538
|
|
|
@@ -467,14 +543,30 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
467
543
|
log.warn(`Sequencer created but not started`);
|
|
468
544
|
}
|
|
469
545
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
546
|
+
// Create prover node subsystem if enabled
|
|
547
|
+
let proverNode: ProverNode | undefined;
|
|
548
|
+
if (config.enableProverNode) {
|
|
549
|
+
proverNode = await createProverNode(config, {
|
|
550
|
+
...deps.proverNodeDeps,
|
|
551
|
+
telemetry,
|
|
552
|
+
dateProvider,
|
|
553
|
+
archiver,
|
|
554
|
+
worldStateSynchronizer,
|
|
555
|
+
p2pClient,
|
|
556
|
+
epochCache,
|
|
557
|
+
blobClient,
|
|
558
|
+
keyStoreManager,
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
if (!options.dontStartProverNode) {
|
|
562
|
+
await proverNode.start();
|
|
563
|
+
log.info(`Prover node subsystem started`);
|
|
564
|
+
} else {
|
|
565
|
+
log.info(`Prover node subsystem created but not started`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
476
568
|
|
|
477
|
-
|
|
569
|
+
const node = new AztecNodeService(
|
|
478
570
|
config,
|
|
479
571
|
p2pClient,
|
|
480
572
|
archiver,
|
|
@@ -483,6 +575,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
483
575
|
archiver,
|
|
484
576
|
worldStateSynchronizer,
|
|
485
577
|
sequencer,
|
|
578
|
+
proverNode,
|
|
486
579
|
slasherClient,
|
|
487
580
|
validatorsSentinel,
|
|
488
581
|
epochPruneWatcher,
|
|
@@ -495,7 +588,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
495
588
|
telemetry,
|
|
496
589
|
log,
|
|
497
590
|
blobClient,
|
|
591
|
+
validatorClient,
|
|
592
|
+
keyStoreManager,
|
|
593
|
+
debugLogStore,
|
|
498
594
|
);
|
|
595
|
+
|
|
596
|
+
return node;
|
|
499
597
|
}
|
|
500
598
|
|
|
501
599
|
/**
|
|
@@ -506,6 +604,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
506
604
|
return this.sequencer;
|
|
507
605
|
}
|
|
508
606
|
|
|
607
|
+
/** Returns the prover node subsystem, if enabled. */
|
|
608
|
+
public getProverNode(): ProverNode | undefined {
|
|
609
|
+
return this.proverNode;
|
|
610
|
+
}
|
|
611
|
+
|
|
509
612
|
public getBlockSource(): L2BlockSource {
|
|
510
613
|
return this.blockSource;
|
|
511
614
|
}
|
|
@@ -531,7 +634,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
531
634
|
}
|
|
532
635
|
|
|
533
636
|
public async getAllowedPublicSetup(): Promise<AllowedElement[]> {
|
|
534
|
-
return this.config.
|
|
637
|
+
return [...(await getDefaultAllowedSetupFunctions()), ...(this.config.txPublicSetupAllowListExtend ?? [])];
|
|
535
638
|
}
|
|
536
639
|
|
|
537
640
|
/**
|
|
@@ -559,6 +662,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
559
662
|
enr,
|
|
560
663
|
l1ContractAddresses: contractAddresses,
|
|
561
664
|
protocolContractAddresses: protocolContractAddresses,
|
|
665
|
+
realProofs: !!this.config.realProofs,
|
|
562
666
|
};
|
|
563
667
|
|
|
564
668
|
return nodeInfo;
|
|
@@ -625,6 +729,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
625
729
|
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
626
730
|
}
|
|
627
731
|
|
|
732
|
+
public getCheckpointsDataForEpoch(epochNumber: EpochNumber) {
|
|
733
|
+
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
734
|
+
}
|
|
735
|
+
|
|
628
736
|
/**
|
|
629
737
|
* Method to fetch the current min L2 fees.
|
|
630
738
|
* @returns The current min L2 fees.
|
|
@@ -657,6 +765,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
657
765
|
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
658
766
|
}
|
|
659
767
|
|
|
768
|
+
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
769
|
+
return this.blockSource.getCheckpointNumber();
|
|
770
|
+
}
|
|
771
|
+
|
|
660
772
|
/**
|
|
661
773
|
* Method to fetch the version of the package.
|
|
662
774
|
* @returns The node package version
|
|
@@ -767,8 +879,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
767
879
|
}
|
|
768
880
|
|
|
769
881
|
await this.p2pClient!.sendTx(tx);
|
|
770
|
-
|
|
771
|
-
this.
|
|
882
|
+
const duration = timer.ms();
|
|
883
|
+
this.metrics.receivedTx(duration, true);
|
|
884
|
+
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
772
885
|
}
|
|
773
886
|
|
|
774
887
|
public async getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
|
|
@@ -780,18 +893,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
780
893
|
// Then get the actual tx from the archiver, which tracks every tx in a mined block.
|
|
781
894
|
const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
|
|
782
895
|
|
|
896
|
+
let receipt: TxReceipt;
|
|
783
897
|
if (settledTxReceipt) {
|
|
784
|
-
|
|
785
|
-
return settledTxReceipt;
|
|
898
|
+
receipt = settledTxReceipt;
|
|
786
899
|
} else if (isKnownToPool) {
|
|
787
900
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
788
901
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
789
902
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
790
|
-
|
|
903
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
791
904
|
} else {
|
|
792
905
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
793
|
-
|
|
906
|
+
receipt = new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
|
|
794
907
|
}
|
|
908
|
+
|
|
909
|
+
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
910
|
+
|
|
911
|
+
return receipt;
|
|
795
912
|
}
|
|
796
913
|
|
|
797
914
|
public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
@@ -808,6 +925,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
808
925
|
await tryStop(this.slasherClient);
|
|
809
926
|
await tryStop(this.proofVerifier);
|
|
810
927
|
await tryStop(this.sequencer);
|
|
928
|
+
await tryStop(this.proverNode);
|
|
811
929
|
await tryStop(this.p2pClient);
|
|
812
930
|
await tryStop(this.worldStateSynchronizer);
|
|
813
931
|
await tryStop(this.blockSource);
|
|
@@ -857,100 +975,104 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
857
975
|
}
|
|
858
976
|
|
|
859
977
|
public async findLeavesIndexes(
|
|
860
|
-
|
|
978
|
+
referenceBlock: BlockParameter,
|
|
861
979
|
treeId: MerkleTreeId,
|
|
862
980
|
leafValues: Fr[],
|
|
863
981
|
): Promise<(DataInBlock<bigint> | undefined)[]> {
|
|
864
|
-
const committedDb = await this
|
|
982
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
865
983
|
const maybeIndices = await committedDb.findLeafIndices(
|
|
866
984
|
treeId,
|
|
867
985
|
leafValues.map(x => x.toBuffer()),
|
|
868
986
|
);
|
|
869
|
-
//
|
|
870
|
-
const
|
|
987
|
+
// Filter out undefined values to query block numbers only for found leaves
|
|
988
|
+
const definedIndices = maybeIndices.filter(x => x !== undefined);
|
|
871
989
|
|
|
872
|
-
// Now we find the block numbers for the indices
|
|
873
|
-
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId,
|
|
990
|
+
// Now we find the block numbers for the defined indices
|
|
991
|
+
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, definedIndices);
|
|
874
992
|
|
|
875
|
-
//
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
993
|
+
// Build a map from leaf index to block number
|
|
994
|
+
const indexToBlockNumber = new Map<bigint, BlockNumber>();
|
|
995
|
+
for (let i = 0; i < definedIndices.length; i++) {
|
|
996
|
+
const blockNumber = blockNumbers[i];
|
|
997
|
+
if (blockNumber === undefined) {
|
|
998
|
+
throw new Error(
|
|
999
|
+
`Block number is undefined for leaf index ${definedIndices[i]} in tree ${MerkleTreeId[treeId]}`,
|
|
1000
|
+
);
|
|
879
1001
|
}
|
|
1002
|
+
indexToBlockNumber.set(definedIndices[i], blockNumber);
|
|
880
1003
|
}
|
|
881
1004
|
|
|
882
1005
|
// Get unique block numbers in order to optimize num calls to getLeafValue function.
|
|
883
|
-
const uniqueBlockNumbers = [...new Set(
|
|
1006
|
+
const uniqueBlockNumbers = [...new Set(indexToBlockNumber.values())];
|
|
884
1007
|
|
|
885
|
-
// Now we obtain the block hashes from the archive tree
|
|
886
|
-
// (note that block number corresponds to the leaf index in the archive tree).
|
|
1008
|
+
// Now we obtain the block hashes from the archive tree (block number = leaf index in archive tree).
|
|
887
1009
|
const blockHashes = await Promise.all(
|
|
888
1010
|
uniqueBlockNumbers.map(blockNumber => {
|
|
889
1011
|
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
890
1012
|
}),
|
|
891
1013
|
);
|
|
892
1014
|
|
|
893
|
-
//
|
|
1015
|
+
// Build a map from block number to block hash
|
|
1016
|
+
const blockNumberToHash = new Map<BlockNumber, Fr>();
|
|
894
1017
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
895
|
-
|
|
1018
|
+
const blockHash = blockHashes[i];
|
|
1019
|
+
if (blockHash === undefined) {
|
|
896
1020
|
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
897
1021
|
}
|
|
1022
|
+
blockNumberToHash.set(uniqueBlockNumbers[i], blockHash);
|
|
898
1023
|
}
|
|
899
1024
|
|
|
900
1025
|
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
901
|
-
return maybeIndices.map(
|
|
1026
|
+
return maybeIndices.map(index => {
|
|
902
1027
|
if (index === undefined) {
|
|
903
1028
|
return undefined;
|
|
904
1029
|
}
|
|
905
|
-
const blockNumber =
|
|
1030
|
+
const blockNumber = indexToBlockNumber.get(index);
|
|
906
1031
|
if (blockNumber === undefined) {
|
|
907
|
-
|
|
1032
|
+
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
908
1033
|
}
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
return undefined;
|
|
1034
|
+
const blockHash = blockNumberToHash.get(blockNumber);
|
|
1035
|
+
if (blockHash === undefined) {
|
|
1036
|
+
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
913
1037
|
}
|
|
914
1038
|
return {
|
|
915
|
-
l2BlockNumber:
|
|
1039
|
+
l2BlockNumber: blockNumber,
|
|
916
1040
|
l2BlockHash: new BlockHash(blockHash),
|
|
917
1041
|
data: index,
|
|
918
1042
|
};
|
|
919
1043
|
});
|
|
920
1044
|
}
|
|
921
1045
|
|
|
922
|
-
public async
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
): Promise<SiblingPath<typeof NULLIFIER_TREE_HEIGHT>> {
|
|
926
|
-
const committedDb = await this.#getWorldState(block);
|
|
927
|
-
return committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
public async getNoteHashSiblingPath(
|
|
931
|
-
block: BlockParameter,
|
|
932
|
-
leafIndex: bigint,
|
|
933
|
-
): Promise<SiblingPath<typeof NOTE_HASH_TREE_HEIGHT>> {
|
|
934
|
-
const committedDb = await this.#getWorldState(block);
|
|
935
|
-
return committedDb.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex);
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
public async getArchiveMembershipWitness(
|
|
939
|
-
block: BlockParameter,
|
|
940
|
-
archive: Fr,
|
|
1046
|
+
public async getBlockHashMembershipWitness(
|
|
1047
|
+
referenceBlock: BlockParameter,
|
|
1048
|
+
blockHash: BlockHash,
|
|
941
1049
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
942
|
-
|
|
943
|
-
|
|
1050
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1051
|
+
if (referenceBlock === BlockNumber.ZERO) {
|
|
1052
|
+
return undefined;
|
|
1053
|
+
}
|
|
1054
|
+
if (BlockHash.isBlockHash(referenceBlock)) {
|
|
1055
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1056
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
1057
|
+
return undefined;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
1061
|
+
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1062
|
+
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1063
|
+
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1064
|
+
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
1065
|
+
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
|
|
944
1066
|
return pathAndIndex === undefined
|
|
945
1067
|
? undefined
|
|
946
1068
|
: MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
947
1069
|
}
|
|
948
1070
|
|
|
949
1071
|
public async getNoteHashMembershipWitness(
|
|
950
|
-
|
|
1072
|
+
referenceBlock: BlockParameter,
|
|
951
1073
|
noteHash: Fr,
|
|
952
1074
|
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
953
|
-
const committedDb = await this
|
|
1075
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
954
1076
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
|
|
955
1077
|
MerkleTreeId.NOTE_HASH_TREE,
|
|
956
1078
|
[noteHash],
|
|
@@ -961,10 +1083,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
961
1083
|
}
|
|
962
1084
|
|
|
963
1085
|
public async getL1ToL2MessageMembershipWitness(
|
|
964
|
-
|
|
1086
|
+
referenceBlock: BlockParameter,
|
|
965
1087
|
l1ToL2Message: Fr,
|
|
966
1088
|
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
|
|
967
|
-
const db = await this
|
|
1089
|
+
const db = await this.getWorldState(referenceBlock);
|
|
968
1090
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
|
|
969
1091
|
if (!witness) {
|
|
970
1092
|
return undefined;
|
|
@@ -974,11 +1096,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
974
1096
|
return [witness.index, witness.path];
|
|
975
1097
|
}
|
|
976
1098
|
|
|
977
|
-
public async
|
|
1099
|
+
public async getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise<CheckpointNumber | undefined> {
|
|
978
1100
|
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
979
|
-
return messageIndex
|
|
980
|
-
? BlockNumber.fromCheckpointNumber(InboxLeaf.checkpointNumberFromIndex(messageIndex))
|
|
981
|
-
: undefined;
|
|
1101
|
+
return messageIndex ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined;
|
|
982
1102
|
}
|
|
983
1103
|
|
|
984
1104
|
/**
|
|
@@ -999,45 +1119,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
999
1119
|
public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
|
|
1000
1120
|
// Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
|
|
1001
1121
|
const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
|
|
1002
|
-
const blocksInCheckpoints
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
for (const checkpointedBlock of checkpointedBlocks) {
|
|
1006
|
-
const block = checkpointedBlock.block;
|
|
1007
|
-
const slotNumber = block.header.globalVariables.slotNumber;
|
|
1008
|
-
if (slotNumber !== previousSlotNumber) {
|
|
1009
|
-
checkpointIndex++;
|
|
1010
|
-
blocksInCheckpoints.push([]);
|
|
1011
|
-
previousSlotNumber = slotNumber;
|
|
1012
|
-
}
|
|
1013
|
-
blocksInCheckpoints[checkpointIndex].push(block);
|
|
1014
|
-
}
|
|
1122
|
+
const blocksInCheckpoints = chunkBy(checkpointedBlocks, cb => cb.block.header.globalVariables.slotNumber).map(
|
|
1123
|
+
group => group.map(cb => cb.block),
|
|
1124
|
+
);
|
|
1015
1125
|
return blocksInCheckpoints.map(blocks =>
|
|
1016
1126
|
blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
|
|
1017
1127
|
);
|
|
1018
1128
|
}
|
|
1019
1129
|
|
|
1020
|
-
public async getArchiveSiblingPath(
|
|
1021
|
-
block: BlockParameter,
|
|
1022
|
-
leafIndex: bigint,
|
|
1023
|
-
): Promise<SiblingPath<typeof ARCHIVE_HEIGHT>> {
|
|
1024
|
-
const committedDb = await this.#getWorldState(block);
|
|
1025
|
-
return committedDb.getSiblingPath(MerkleTreeId.ARCHIVE, leafIndex);
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
public async getPublicDataSiblingPath(
|
|
1029
|
-
block: BlockParameter,
|
|
1030
|
-
leafIndex: bigint,
|
|
1031
|
-
): Promise<SiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>> {
|
|
1032
|
-
const committedDb = await this.#getWorldState(block);
|
|
1033
|
-
return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
1130
|
public async getNullifierMembershipWitness(
|
|
1037
|
-
|
|
1131
|
+
referenceBlock: BlockParameter,
|
|
1038
1132
|
nullifier: Fr,
|
|
1039
1133
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1040
|
-
const db = await this
|
|
1134
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1041
1135
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
|
|
1042
1136
|
if (!witness) {
|
|
1043
1137
|
return undefined;
|
|
@@ -1052,32 +1146,20 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1052
1146
|
return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path);
|
|
1053
1147
|
}
|
|
1054
1148
|
|
|
1055
|
-
/**
|
|
1056
|
-
* Returns a low nullifier membership witness for a given nullifier at a given block.
|
|
1057
|
-
* @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
|
|
1058
|
-
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1059
|
-
* @returns The low nullifier membership witness (if found).
|
|
1060
|
-
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
1061
|
-
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
|
|
1062
|
-
* we are trying to prove non-inclusion for.
|
|
1063
|
-
*
|
|
1064
|
-
* Note: This function returns the membership witness of the nullifier itself and not the low nullifier when
|
|
1065
|
-
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1066
|
-
* index of the nullifier itself when it already exists in the tree.
|
|
1067
|
-
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1068
|
-
*/
|
|
1069
1149
|
public async getLowNullifierMembershipWitness(
|
|
1070
|
-
|
|
1150
|
+
referenceBlock: BlockParameter,
|
|
1071
1151
|
nullifier: Fr,
|
|
1072
1152
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1073
|
-
const committedDb = await this
|
|
1153
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1074
1154
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1075
1155
|
if (!findResult) {
|
|
1076
1156
|
return undefined;
|
|
1077
1157
|
}
|
|
1078
1158
|
const { index, alreadyPresent } = findResult;
|
|
1079
1159
|
if (alreadyPresent) {
|
|
1080
|
-
|
|
1160
|
+
throw new Error(
|
|
1161
|
+
`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`,
|
|
1162
|
+
);
|
|
1081
1163
|
}
|
|
1082
1164
|
const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
|
|
1083
1165
|
|
|
@@ -1085,8 +1167,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1085
1167
|
return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
|
|
1086
1168
|
}
|
|
1087
1169
|
|
|
1088
|
-
async getPublicDataWitness(
|
|
1089
|
-
const committedDb = await this
|
|
1170
|
+
async getPublicDataWitness(referenceBlock: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
1171
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1090
1172
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1091
1173
|
if (!lowLeafResult) {
|
|
1092
1174
|
return undefined;
|
|
@@ -1100,8 +1182,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1100
1182
|
}
|
|
1101
1183
|
}
|
|
1102
1184
|
|
|
1103
|
-
public async getPublicStorageAt(
|
|
1104
|
-
const committedDb = await this
|
|
1185
|
+
public async getPublicStorageAt(referenceBlock: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
1186
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1105
1187
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1106
1188
|
|
|
1107
1189
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
@@ -1142,6 +1224,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1142
1224
|
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1143
1225
|
}
|
|
1144
1226
|
|
|
1227
|
+
public getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
|
|
1228
|
+
return this.blockSource.getBlockData(number);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
public getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
1232
|
+
return this.blockSource.getBlockDataByArchive(archive);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1145
1235
|
/**
|
|
1146
1236
|
* Simulates the public part of a transaction with the current state.
|
|
1147
1237
|
* @param tx - The transaction to simulate.
|
|
@@ -1165,7 +1255,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1165
1255
|
}
|
|
1166
1256
|
|
|
1167
1257
|
const txHash = tx.getTxHash();
|
|
1168
|
-
const
|
|
1258
|
+
const latestBlockNumber = await this.blockSource.getBlockNumber();
|
|
1259
|
+
const blockNumber = BlockNumber.add(latestBlockNumber, 1);
|
|
1169
1260
|
|
|
1170
1261
|
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
1171
1262
|
const coinbase = EthAddress.ZERO;
|
|
@@ -1189,6 +1280,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1189
1280
|
blockNumber,
|
|
1190
1281
|
});
|
|
1191
1282
|
|
|
1283
|
+
// Ensure world-state has caught up with the latest block we loaded from the archiver
|
|
1284
|
+
await this.worldStateSynchronizer.syncImmediate(latestBlockNumber);
|
|
1192
1285
|
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
1193
1286
|
try {
|
|
1194
1287
|
const config = PublicSimulatorConfig.from({
|
|
@@ -1204,7 +1297,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1204
1297
|
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1205
1298
|
|
|
1206
1299
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1207
|
-
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([tx]);
|
|
1300
|
+
const [processedTxs, failedTxs, _usedTxs, returns, debugLogs] = await processor.process([tx]);
|
|
1208
1301
|
// REFACTOR: Consider returning the error rather than throwing
|
|
1209
1302
|
if (failedTxs.length) {
|
|
1210
1303
|
this.log.warn(`Simulated tx ${txHash} fails: ${failedTxs[0].error}`, { txHash });
|
|
@@ -1218,6 +1311,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1218
1311
|
processedTx.txEffect,
|
|
1219
1312
|
returns,
|
|
1220
1313
|
processedTx.gasUsed,
|
|
1314
|
+
debugLogs,
|
|
1221
1315
|
);
|
|
1222
1316
|
} finally {
|
|
1223
1317
|
await merkleTreeFork.close();
|
|
@@ -1231,10 +1325,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1231
1325
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1232
1326
|
const verifier = isSimulation ? undefined : this.proofVerifier;
|
|
1233
1327
|
|
|
1234
|
-
// We accept transactions if they are not expired by the next slot (checked based on the
|
|
1328
|
+
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1235
1329
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1236
1330
|
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
1237
|
-
const
|
|
1331
|
+
const l1Constants = await this.blockSource.getL1Constants();
|
|
1332
|
+
const validator = createTxValidatorForAcceptingTxsOverRPC(
|
|
1238
1333
|
db,
|
|
1239
1334
|
this.contractDataSource,
|
|
1240
1335
|
verifier,
|
|
@@ -1243,10 +1338,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1243
1338
|
blockNumber,
|
|
1244
1339
|
l1ChainId: this.l1ChainId,
|
|
1245
1340
|
rollupVersion: this.version,
|
|
1246
|
-
setupAllowList:
|
|
1341
|
+
setupAllowList: [
|
|
1342
|
+
...(await getDefaultAllowedSetupFunctions()),
|
|
1343
|
+
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1344
|
+
],
|
|
1247
1345
|
gasFees: await this.getCurrentMinFees(),
|
|
1248
1346
|
skipFeeEnforcement,
|
|
1249
1347
|
txsPermitted: !this.config.disableTransactions,
|
|
1348
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
1349
|
+
maxBlockL2Gas: this.config.validateMaxL2BlockGas,
|
|
1350
|
+
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
1250
1351
|
},
|
|
1251
1352
|
this.log.getBindings(),
|
|
1252
1353
|
);
|
|
@@ -1413,6 +1514,128 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1413
1514
|
}
|
|
1414
1515
|
}
|
|
1415
1516
|
|
|
1517
|
+
public async reloadKeystore(): Promise<void> {
|
|
1518
|
+
if (!this.config.keyStoreDirectory?.length) {
|
|
1519
|
+
throw new BadRequestError(
|
|
1520
|
+
'Cannot reload keystore: node is not using a file-based keystore. ' +
|
|
1521
|
+
'Set KEY_STORE_DIRECTORY to use file-based keystores.',
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
if (!this.validatorClient) {
|
|
1525
|
+
throw new BadRequestError('Cannot reload keystore: validator is not enabled.');
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
this.log.info('Reloading keystore from disk');
|
|
1529
|
+
|
|
1530
|
+
// Re-read and validate keystore files
|
|
1531
|
+
const keyStores = loadKeystores(this.config.keyStoreDirectory);
|
|
1532
|
+
const newManager = new KeystoreManager(mergeKeystores(keyStores));
|
|
1533
|
+
await newManager.validateSigners();
|
|
1534
|
+
ValidatorClient.validateKeyStoreConfiguration(newManager, this.log);
|
|
1535
|
+
|
|
1536
|
+
// Validate that every validator's publisher keys overlap with the L1 signers
|
|
1537
|
+
// that were initialized at startup. Publishers cannot be hot-reloaded, so a
|
|
1538
|
+
// validator with a publisher key that doesn't match any existing L1 signer
|
|
1539
|
+
// would silently fail on every proposer slot.
|
|
1540
|
+
if (this.keyStoreManager && this.sequencer) {
|
|
1541
|
+
const oldAdapter = NodeKeystoreAdapter.fromKeyStoreManager(this.keyStoreManager);
|
|
1542
|
+
const availablePublishers = new Set(
|
|
1543
|
+
oldAdapter
|
|
1544
|
+
.getAttesterAddresses()
|
|
1545
|
+
.flatMap(a => oldAdapter.getPublisherAddresses(a).map(p => p.toString().toLowerCase())),
|
|
1546
|
+
);
|
|
1547
|
+
|
|
1548
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
1549
|
+
for (const attester of newAdapter.getAttesterAddresses()) {
|
|
1550
|
+
const pubs = newAdapter.getPublisherAddresses(attester);
|
|
1551
|
+
if (pubs.length > 0 && !pubs.some(p => availablePublishers.has(p.toString().toLowerCase()))) {
|
|
1552
|
+
throw new BadRequestError(
|
|
1553
|
+
`Cannot reload keystore: validator ${attester} has publisher keys ` +
|
|
1554
|
+
`[${pubs.map(p => p.toString()).join(', ')}] but none match the L1 signers initialized at startup ` +
|
|
1555
|
+
`[${[...availablePublishers].join(', ')}]. Publishers cannot be hot-reloaded — ` +
|
|
1556
|
+
`use an existing publisher key or restart the node.`,
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// Build adapters for old and new keystores to compute diff
|
|
1563
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
1564
|
+
const newAddresses = newAdapter.getAttesterAddresses();
|
|
1565
|
+
const oldAddresses = this.keyStoreManager
|
|
1566
|
+
? NodeKeystoreAdapter.fromKeyStoreManager(this.keyStoreManager).getAttesterAddresses()
|
|
1567
|
+
: [];
|
|
1568
|
+
|
|
1569
|
+
const oldSet = new Set(oldAddresses.map(a => a.toString()));
|
|
1570
|
+
const newSet = new Set(newAddresses.map(a => a.toString()));
|
|
1571
|
+
const added = newAddresses.filter(a => !oldSet.has(a.toString()));
|
|
1572
|
+
const removed = oldAddresses.filter(a => !newSet.has(a.toString()));
|
|
1573
|
+
|
|
1574
|
+
if (added.length > 0) {
|
|
1575
|
+
this.log.info(`Keystore reload: adding attester keys: ${added.map(a => a.toString()).join(', ')}`);
|
|
1576
|
+
}
|
|
1577
|
+
if (removed.length > 0) {
|
|
1578
|
+
this.log.info(`Keystore reload: removing attester keys: ${removed.map(a => a.toString()).join(', ')}`);
|
|
1579
|
+
}
|
|
1580
|
+
if (added.length === 0 && removed.length === 0) {
|
|
1581
|
+
this.log.info('Keystore reload: attester keys unchanged');
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// Update the validator client (coinbase, feeRecipient, attester keys)
|
|
1585
|
+
this.validatorClient.reloadKeystore(newManager);
|
|
1586
|
+
|
|
1587
|
+
// Update the publisher factory's keystore so newly-added validators
|
|
1588
|
+
// can be matched to existing publisher keys when proposing blocks.
|
|
1589
|
+
if (this.sequencer) {
|
|
1590
|
+
this.sequencer.updatePublisherNodeKeyStore(newAdapter);
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// Update slasher's "don't-slash-self" list with new validator addresses
|
|
1594
|
+
if (this.slasherClient && !this.config.slashSelfAllowed) {
|
|
1595
|
+
const slashValidatorsNever = unique(
|
|
1596
|
+
[...(this.config.slashValidatorsNever ?? []), ...newAddresses].map(a => a.toString()),
|
|
1597
|
+
).map(EthAddress.fromString);
|
|
1598
|
+
this.slasherClient.updateConfig({ slashValidatorsNever });
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
this.keyStoreManager = newManager;
|
|
1602
|
+
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
public async mineBlock(): Promise<void> {
|
|
1606
|
+
if (!this.sequencer) {
|
|
1607
|
+
throw new BadRequestError('Cannot mine block: no sequencer is running');
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
const currentBlockNumber = await this.getBlockNumber();
|
|
1611
|
+
|
|
1612
|
+
// Use slot duration + 50% buffer as the timeout so this works on running networks too
|
|
1613
|
+
const { slotDuration } = await this.blockSource.getL1Constants();
|
|
1614
|
+
const timeoutSeconds = Math.ceil(slotDuration * 1.5);
|
|
1615
|
+
|
|
1616
|
+
// Temporarily set minTxsPerBlock to 0 so the sequencer produces a block even with no txs
|
|
1617
|
+
const originalMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock;
|
|
1618
|
+
this.sequencer.updateConfig({ minTxsPerBlock: 0 });
|
|
1619
|
+
|
|
1620
|
+
try {
|
|
1621
|
+
// Trigger the sequencer to produce a block immediately
|
|
1622
|
+
void this.sequencer.trigger();
|
|
1623
|
+
|
|
1624
|
+
// Wait for the new L2 block to appear
|
|
1625
|
+
await retryUntil(
|
|
1626
|
+
async () => {
|
|
1627
|
+
const newBlockNumber = await this.getBlockNumber();
|
|
1628
|
+
return newBlockNumber > currentBlockNumber ? true : undefined;
|
|
1629
|
+
},
|
|
1630
|
+
'mineBlock',
|
|
1631
|
+
timeoutSeconds,
|
|
1632
|
+
0.1,
|
|
1633
|
+
);
|
|
1634
|
+
} finally {
|
|
1635
|
+
this.sequencer.updateConfig({ minTxsPerBlock: originalMinTxsPerBlock });
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1416
1639
|
#getInitialHeaderHash(): Promise<BlockHash> {
|
|
1417
1640
|
if (!this.initialHeaderHashPromise) {
|
|
1418
1641
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1425,7 +1648,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1425
1648
|
* @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
|
|
1426
1649
|
* @returns An instance of a committed MerkleTreeOperations
|
|
1427
1650
|
*/
|
|
1428
|
-
async
|
|
1651
|
+
protected async getWorldState(block: BlockParameter) {
|
|
1429
1652
|
let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
|
|
1430
1653
|
try {
|
|
1431
1654
|
// Attempt to sync the world state if necessary
|
|
@@ -1439,6 +1662,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1439
1662
|
return this.worldStateSynchronizer.getCommitted();
|
|
1440
1663
|
}
|
|
1441
1664
|
|
|
1665
|
+
// Get the block number, either directly from the parameter or by quering the archiver with the block hash
|
|
1666
|
+
let blockNumber: BlockNumber;
|
|
1442
1667
|
if (BlockHash.isBlockHash(block)) {
|
|
1443
1668
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1444
1669
|
if (block.equals(initialBlockHash)) {
|
|
@@ -1452,22 +1677,50 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1452
1677
|
`Block hash ${block.toString()} not found when querying world state. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1453
1678
|
);
|
|
1454
1679
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1680
|
+
|
|
1681
|
+
blockNumber = header.getBlockNumber();
|
|
1682
|
+
} else {
|
|
1683
|
+
blockNumber = block as BlockNumber;
|
|
1458
1684
|
}
|
|
1459
1685
|
|
|
1460
|
-
//
|
|
1461
|
-
{
|
|
1462
|
-
|
|
1686
|
+
// Check it's within world state sync range
|
|
1687
|
+
if (blockNumber > blockSyncedTo) {
|
|
1688
|
+
throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
|
|
1689
|
+
}
|
|
1690
|
+
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1463
1691
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1692
|
+
const snapshot = this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1693
|
+
|
|
1694
|
+
// Double-check world-state synced to the same block hash as was requested
|
|
1695
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1696
|
+
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1697
|
+
if (!blockHash || !new BlockHash(blockHash).equals(block)) {
|
|
1698
|
+
throw new Error(
|
|
1699
|
+
`Block hash ${block.toString()} not found in world state at block number ${blockNumber}. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1700
|
+
);
|
|
1466
1701
|
}
|
|
1702
|
+
}
|
|
1467
1703
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1704
|
+
return snapshot;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/** Resolves a block parameter to a block number. */
|
|
1708
|
+
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1709
|
+
if (block === 'latest') {
|
|
1710
|
+
return BlockNumber(await this.blockSource.getBlockNumber());
|
|
1711
|
+
}
|
|
1712
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1713
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1714
|
+
if (block.equals(initialBlockHash)) {
|
|
1715
|
+
return BlockNumber.ZERO;
|
|
1716
|
+
}
|
|
1717
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1718
|
+
if (!header) {
|
|
1719
|
+
throw new Error(`Block hash ${block.toString()} not found.`);
|
|
1720
|
+
}
|
|
1721
|
+
return header.getBlockNumber();
|
|
1470
1722
|
}
|
|
1723
|
+
return block as BlockNumber;
|
|
1471
1724
|
}
|
|
1472
1725
|
|
|
1473
1726
|
/**
|