@aztec/aztec-node 0.0.0-test.1 → 0.0.1-commit.03f7ef2
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 +18 -10
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +81 -14
- package/dest/aztec-node/node_metrics.d.ts +5 -1
- package/dest/aztec-node/node_metrics.d.ts.map +1 -1
- package/dest/aztec-node/node_metrics.js +21 -0
- package/dest/aztec-node/server.d.ts +110 -82
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +581 -236
- package/dest/bin/index.d.ts +1 -1
- package/dest/bin/index.js +4 -2
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/sentinel/config.d.ts +8 -0
- package/dest/sentinel/config.d.ts.map +1 -0
- package/dest/sentinel/config.js +29 -0
- package/dest/sentinel/factory.d.ts +9 -0
- package/dest/sentinel/factory.d.ts.map +1 -0
- package/dest/sentinel/factory.js +17 -0
- package/dest/sentinel/index.d.ts +3 -0
- package/dest/sentinel/index.d.ts.map +1 -0
- package/dest/sentinel/index.js +1 -0
- package/dest/sentinel/sentinel.d.ts +92 -0
- package/dest/sentinel/sentinel.d.ts.map +1 -0
- package/dest/sentinel/sentinel.js +398 -0
- package/dest/sentinel/store.d.ts +35 -0
- package/dest/sentinel/store.d.ts.map +1 -0
- package/dest/sentinel/store.js +170 -0
- package/dest/test/index.d.ts +31 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +1 -0
- package/package.json +45 -34
- package/src/aztec-node/config.ts +132 -25
- package/src/aztec-node/node_metrics.ts +28 -0
- package/src/aztec-node/server.ts +776 -319
- package/src/bin/index.ts +4 -2
- package/src/index.ts +0 -1
- package/src/sentinel/config.ts +37 -0
- package/src/sentinel/factory.ts +36 -0
- package/src/sentinel/index.ts +8 -0
- package/src/sentinel/sentinel.ts +501 -0
- package/src/sentinel/store.ts +185 -0
- package/src/test/index.ts +32 -0
- package/dest/aztec-node/http_rpc_server.d.ts +0 -8
- package/dest/aztec-node/http_rpc_server.d.ts.map +0 -1
- package/dest/aztec-node/http_rpc_server.js +0 -9
- package/src/aztec-node/http_rpc_server.ts +0 -11
package/src/aztec-node/server.ts
CHANGED
|
@@ -1,39 +1,67 @@
|
|
|
1
|
-
import { createArchiver } from '@aztec/archiver';
|
|
2
|
-
import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
|
|
3
|
-
import { type
|
|
1
|
+
import { Archiver, createArchiver } from '@aztec/archiver';
|
|
2
|
+
import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
|
|
3
|
+
import { type BlobClientInterface, createBlobClient } from '@aztec/blob-client/client';
|
|
4
4
|
import {
|
|
5
|
-
type
|
|
5
|
+
type BlobFileStoreMetadata,
|
|
6
|
+
createReadOnlyFileStoreBlobClients,
|
|
7
|
+
createWritableFileStoreBlobClient,
|
|
8
|
+
} from '@aztec/blob-client/filestore';
|
|
9
|
+
import {
|
|
10
|
+
ARCHIVE_HEIGHT,
|
|
6
11
|
INITIAL_L2_BLOCK_NUM,
|
|
7
12
|
type L1_TO_L2_MSG_TREE_HEIGHT,
|
|
8
13
|
type NOTE_HASH_TREE_HEIGHT,
|
|
9
14
|
type NULLIFIER_TREE_HEIGHT,
|
|
10
15
|
type PUBLIC_DATA_TREE_HEIGHT,
|
|
11
|
-
REGISTERER_CONTRACT_ADDRESS,
|
|
12
16
|
} from '@aztec/constants';
|
|
13
|
-
import { EpochCache } from '@aztec/epoch-cache';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
17
|
+
import { EpochCache, type EpochCacheInterface } from '@aztec/epoch-cache';
|
|
18
|
+
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
19
|
+
import { getPublicClient } from '@aztec/ethereum/client';
|
|
20
|
+
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
21
|
+
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
22
|
+
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
23
|
+
import { compactArray, pick } from '@aztec/foundation/collection';
|
|
24
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
16
25
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
17
|
-
import {
|
|
26
|
+
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
18
27
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
28
|
+
import { count } from '@aztec/foundation/string';
|
|
19
29
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
20
|
-
import { SiblingPath } from '@aztec/foundation/trees';
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
|
|
30
|
+
import { MembershipWitness, SiblingPath } from '@aztec/foundation/trees';
|
|
31
|
+
import { KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
|
|
32
|
+
import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
|
|
33
|
+
import {
|
|
34
|
+
createForwarderL1TxUtilsFromEthSigner,
|
|
35
|
+
createL1TxUtilsWithBlobsFromEthSigner,
|
|
36
|
+
} from '@aztec/node-lib/factories';
|
|
37
|
+
import { type P2P, type P2PClientDeps, createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
|
|
25
38
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
26
39
|
import {
|
|
40
|
+
BlockBuilder,
|
|
27
41
|
GlobalVariableBuilder,
|
|
28
42
|
SequencerClient,
|
|
29
43
|
type SequencerPublisher,
|
|
30
|
-
createSlasherClient,
|
|
31
44
|
createValidatorForAcceptingTxs,
|
|
32
|
-
getDefaultAllowedSetupFunctions,
|
|
33
45
|
} from '@aztec/sequencer-client';
|
|
46
|
+
import { CheckpointsBuilder } from '@aztec/sequencer-client';
|
|
34
47
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
48
|
+
import {
|
|
49
|
+
AttestationsBlockWatcher,
|
|
50
|
+
EpochPruneWatcher,
|
|
51
|
+
type SlasherClientInterface,
|
|
52
|
+
type Watcher,
|
|
53
|
+
createSlasher,
|
|
54
|
+
} from '@aztec/slasher';
|
|
55
|
+
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
35
56
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
36
|
-
import
|
|
57
|
+
import {
|
|
58
|
+
type BlockParameter,
|
|
59
|
+
type DataInBlock,
|
|
60
|
+
type L2Block,
|
|
61
|
+
L2BlockHash,
|
|
62
|
+
type L2BlockSource,
|
|
63
|
+
type PublishedL2Block,
|
|
64
|
+
} from '@aztec/stdlib/block';
|
|
37
65
|
import type {
|
|
38
66
|
ContractClassPublic,
|
|
39
67
|
ContractDataSource,
|
|
@@ -41,34 +69,44 @@ import type {
|
|
|
41
69
|
NodeInfo,
|
|
42
70
|
ProtocolContractAddresses,
|
|
43
71
|
} from '@aztec/stdlib/contract';
|
|
44
|
-
import
|
|
45
|
-
import { computePublicDataTreeLeafSlot
|
|
46
|
-
import type { AztecNode, GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
72
|
+
import { GasFees } from '@aztec/stdlib/gas';
|
|
73
|
+
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
47
74
|
import {
|
|
75
|
+
type AztecNode,
|
|
76
|
+
type AztecNodeAdmin,
|
|
77
|
+
type AztecNodeAdminConfig,
|
|
78
|
+
AztecNodeAdminConfigSchema,
|
|
79
|
+
type GetContractClassLogsResponse,
|
|
80
|
+
type GetPublicLogsResponse,
|
|
81
|
+
} from '@aztec/stdlib/interfaces/client';
|
|
82
|
+
import {
|
|
83
|
+
type AllowedElement,
|
|
48
84
|
type ClientProtocolCircuitVerifier,
|
|
49
85
|
type L2LogsSource,
|
|
50
|
-
type ProverConfig,
|
|
51
|
-
type SequencerConfig,
|
|
52
86
|
type Service,
|
|
53
87
|
type WorldStateSyncStatus,
|
|
54
88
|
type WorldStateSynchronizer,
|
|
55
89
|
tryStop,
|
|
56
90
|
} from '@aztec/stdlib/interfaces/server';
|
|
57
|
-
import type { LogFilter,
|
|
58
|
-
import type
|
|
91
|
+
import type { LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
92
|
+
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
59
93
|
import { P2PClientType } from '@aztec/stdlib/p2p';
|
|
60
|
-
import {
|
|
94
|
+
import type { Offense, SlashPayloadRound } from '@aztec/stdlib/slashing';
|
|
61
95
|
import type { NullifierLeafPreimage, PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
96
|
+
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
62
97
|
import {
|
|
63
98
|
type BlockHeader,
|
|
99
|
+
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
100
|
+
type IndexedTxEffect,
|
|
64
101
|
PublicSimulationOutput,
|
|
65
102
|
Tx,
|
|
66
|
-
TxEffect,
|
|
67
103
|
type TxHash,
|
|
68
104
|
TxReceipt,
|
|
69
105
|
TxStatus,
|
|
70
106
|
type TxValidationResult,
|
|
71
107
|
} from '@aztec/stdlib/tx';
|
|
108
|
+
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
109
|
+
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
72
110
|
import {
|
|
73
111
|
Attributes,
|
|
74
112
|
type TelemetryClient,
|
|
@@ -77,19 +115,30 @@ import {
|
|
|
77
115
|
getTelemetryClient,
|
|
78
116
|
trackSpan,
|
|
79
117
|
} from '@aztec/telemetry-client';
|
|
80
|
-
import {
|
|
118
|
+
import {
|
|
119
|
+
NodeKeystoreAdapter,
|
|
120
|
+
ValidatorClient,
|
|
121
|
+
createBlockProposalHandler,
|
|
122
|
+
createValidatorClient,
|
|
123
|
+
} from '@aztec/validator-client';
|
|
81
124
|
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
82
125
|
|
|
83
|
-
import {
|
|
126
|
+
import { createPublicClient, fallback, http } from 'viem';
|
|
127
|
+
|
|
128
|
+
import { createSentinel } from '../sentinel/factory.js';
|
|
129
|
+
import { Sentinel } from '../sentinel/sentinel.js';
|
|
130
|
+
import { type AztecNodeConfig, createKeyStoreForValidator } from './config.js';
|
|
84
131
|
import { NodeMetrics } from './node_metrics.js';
|
|
85
132
|
|
|
86
133
|
/**
|
|
87
134
|
* The aztec node.
|
|
88
135
|
*/
|
|
89
|
-
export class AztecNodeService implements AztecNode, Traceable {
|
|
90
|
-
private packageVersion: string;
|
|
136
|
+
export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
91
137
|
private metrics: NodeMetrics;
|
|
92
138
|
|
|
139
|
+
// Prevent two snapshot operations to happen simultaneously
|
|
140
|
+
private isUploadingSnapshot = false;
|
|
141
|
+
|
|
93
142
|
public readonly tracer: Tracer;
|
|
94
143
|
|
|
95
144
|
constructor(
|
|
@@ -99,17 +148,20 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
99
148
|
protected readonly logsSource: L2LogsSource,
|
|
100
149
|
protected readonly contractDataSource: ContractDataSource,
|
|
101
150
|
protected readonly l1ToL2MessageSource: L1ToL2MessageSource,
|
|
102
|
-
protected readonly nullifierSource: NullifierWithBlockSource,
|
|
103
151
|
protected readonly worldStateSynchronizer: WorldStateSynchronizer,
|
|
104
152
|
protected readonly sequencer: SequencerClient | undefined,
|
|
153
|
+
protected readonly slasherClient: SlasherClientInterface | undefined,
|
|
154
|
+
protected readonly validatorsSentinel: Sentinel | undefined,
|
|
155
|
+
protected readonly epochPruneWatcher: EpochPruneWatcher | undefined,
|
|
105
156
|
protected readonly l1ChainId: number,
|
|
106
157
|
protected readonly version: number,
|
|
107
|
-
protected readonly globalVariableBuilder:
|
|
158
|
+
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
159
|
+
protected readonly epochCache: EpochCacheInterface,
|
|
160
|
+
protected readonly packageVersion: string,
|
|
108
161
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
109
162
|
private telemetry: TelemetryClient = getTelemetryClient(),
|
|
110
163
|
private log = createLogger('node'),
|
|
111
164
|
) {
|
|
112
|
-
this.packageVersion = getPackageVersion();
|
|
113
165
|
this.metrics = new NodeMetrics(telemetry, 'AztecNodeService');
|
|
114
166
|
this.tracer = telemetry.getTracer('AztecNodeService');
|
|
115
167
|
|
|
@@ -132,31 +184,121 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
132
184
|
* @returns - A fully synced Aztec Node for use in development/testing.
|
|
133
185
|
*/
|
|
134
186
|
public static async createAndSync(
|
|
135
|
-
|
|
187
|
+
inputConfig: AztecNodeConfig,
|
|
136
188
|
deps: {
|
|
137
189
|
telemetry?: TelemetryClient;
|
|
138
190
|
logger?: Logger;
|
|
139
191
|
publisher?: SequencerPublisher;
|
|
140
192
|
dateProvider?: DateProvider;
|
|
141
|
-
|
|
193
|
+
blobClient?: BlobClientInterface;
|
|
194
|
+
p2pClientDeps?: P2PClientDeps<P2PClientType.Full>;
|
|
142
195
|
} = {},
|
|
143
196
|
options: {
|
|
144
197
|
prefilledPublicData?: PublicDataTreeLeaf[];
|
|
198
|
+
dontStartSequencer?: boolean;
|
|
145
199
|
} = {},
|
|
146
200
|
): Promise<AztecNodeService> {
|
|
147
|
-
const
|
|
201
|
+
const config = { ...inputConfig }; // Copy the config so we dont mutate the input object
|
|
148
202
|
const log = deps.logger ?? createLogger('node');
|
|
203
|
+
const packageVersion = getPackageVersion() ?? '';
|
|
204
|
+
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
149
205
|
const dateProvider = deps.dateProvider ?? new DateProvider();
|
|
150
|
-
const blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config);
|
|
151
206
|
const ethereumChain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
152
|
-
|
|
207
|
+
|
|
208
|
+
// Build a key store from file if given or from environment otherwise
|
|
209
|
+
let keyStoreManager: KeystoreManager | undefined;
|
|
210
|
+
const keyStoreProvided = config.keyStoreDirectory !== undefined && config.keyStoreDirectory.length > 0;
|
|
211
|
+
if (keyStoreProvided) {
|
|
212
|
+
const keyStores = loadKeystores(config.keyStoreDirectory!);
|
|
213
|
+
keyStoreManager = new KeystoreManager(mergeKeystores(keyStores));
|
|
214
|
+
} else {
|
|
215
|
+
const keyStore = createKeyStoreForValidator(config);
|
|
216
|
+
if (keyStore) {
|
|
217
|
+
keyStoreManager = new KeystoreManager(keyStore);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
await keyStoreManager?.validateSigners();
|
|
222
|
+
|
|
223
|
+
// If we are a validator, verify our configuration before doing too much more.
|
|
224
|
+
if (!config.disableValidator) {
|
|
225
|
+
if (keyStoreManager === undefined) {
|
|
226
|
+
throw new Error('Failed to create key store, a requirement for running a validator');
|
|
227
|
+
}
|
|
228
|
+
if (!keyStoreProvided) {
|
|
229
|
+
log.warn(
|
|
230
|
+
'KEY STORE CREATED FROM ENVIRONMENT, IT IS RECOMMENDED TO USE A FILE-BASED KEY STORE IN PRODUCTION ENVIRONMENTS',
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
ValidatorClient.validateKeyStoreConfiguration(keyStoreManager, log);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// validate that the actual chain id matches that specified in configuration
|
|
153
237
|
if (config.l1ChainId !== ethereumChain.chainInfo.id) {
|
|
154
238
|
throw new Error(
|
|
155
239
|
`RPC URL configured for chain id ${ethereumChain.chainInfo.id} but expected id ${config.l1ChainId}`,
|
|
156
240
|
);
|
|
157
241
|
}
|
|
158
242
|
|
|
159
|
-
const
|
|
243
|
+
const publicClient = createPublicClient({
|
|
244
|
+
chain: ethereumChain.chainInfo,
|
|
245
|
+
transport: fallback(config.l1RpcUrls.map((url: string) => http(url, { batch: false }))),
|
|
246
|
+
pollingInterval: config.viemPollingIntervalMS,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const l1ContractsAddresses = await RegistryContract.collectAddresses(
|
|
250
|
+
publicClient,
|
|
251
|
+
config.l1Contracts.registryAddress,
|
|
252
|
+
config.rollupVersion ?? 'canonical',
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Overwrite the passed in vars.
|
|
256
|
+
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
|
|
257
|
+
|
|
258
|
+
const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
|
|
259
|
+
const [l1GenesisTime, slotDuration, rollupVersionFromRollup] = await Promise.all([
|
|
260
|
+
rollupContract.getL1GenesisTime(),
|
|
261
|
+
rollupContract.getSlotDuration(),
|
|
262
|
+
rollupContract.getVersion(),
|
|
263
|
+
] as const);
|
|
264
|
+
|
|
265
|
+
config.rollupVersion ??= Number(rollupVersionFromRollup);
|
|
266
|
+
|
|
267
|
+
if (config.rollupVersion !== Number(rollupVersionFromRollup)) {
|
|
268
|
+
log.warn(
|
|
269
|
+
`Registry looked up and returned a rollup with version (${config.rollupVersion}), but this does not match with version detected from the rollup directly: (${rollupVersionFromRollup}).`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const blobFileStoreMetadata: BlobFileStoreMetadata = {
|
|
274
|
+
l1ChainId: config.l1ChainId,
|
|
275
|
+
rollupVersion: config.rollupVersion,
|
|
276
|
+
rollupAddress: config.l1Contracts.rollupAddress.toString(),
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const [fileStoreClients, fileStoreUploadClient] = await Promise.all([
|
|
280
|
+
createReadOnlyFileStoreBlobClients(config.blobFileStoreUrls, blobFileStoreMetadata, log),
|
|
281
|
+
createWritableFileStoreBlobClient(config.blobFileStoreUploadUrl, blobFileStoreMetadata, log),
|
|
282
|
+
]);
|
|
283
|
+
|
|
284
|
+
const blobClient =
|
|
285
|
+
deps.blobClient ??
|
|
286
|
+
createBlobClient(config, {
|
|
287
|
+
logger: createLogger('node:blob-client:client'),
|
|
288
|
+
fileStoreClients,
|
|
289
|
+
fileStoreUploadClient,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// attempt snapshot sync if possible
|
|
293
|
+
await trySnapshotSync(config, log);
|
|
294
|
+
|
|
295
|
+
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
296
|
+
|
|
297
|
+
const archiver = await createArchiver(
|
|
298
|
+
config,
|
|
299
|
+
{ blobClient, epochCache, telemetry, dateProvider },
|
|
300
|
+
{ blockUntilSync: !config.skipArchiverInitialSync },
|
|
301
|
+
);
|
|
160
302
|
|
|
161
303
|
// now create the merkle trees and the world state synchronizer
|
|
162
304
|
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
@@ -165,12 +307,14 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
165
307
|
options.prefilledPublicData,
|
|
166
308
|
telemetry,
|
|
167
309
|
);
|
|
168
|
-
const
|
|
310
|
+
const circuitVerifier =
|
|
311
|
+
config.realProofs || config.debugForceTxProofVerification
|
|
312
|
+
? await BBCircuitVerifier.new(config)
|
|
313
|
+
: new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
169
314
|
if (!config.realProofs) {
|
|
170
315
|
log.warn(`Aztec node is accepting fake proofs`);
|
|
171
316
|
}
|
|
172
|
-
|
|
173
|
-
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
317
|
+
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
174
318
|
|
|
175
319
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
176
320
|
const p2pClient = await createP2PClient(
|
|
@@ -180,33 +324,183 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
180
324
|
proofVerifier,
|
|
181
325
|
worldStateSynchronizer,
|
|
182
326
|
epochCache,
|
|
327
|
+
packageVersion,
|
|
328
|
+
dateProvider,
|
|
183
329
|
telemetry,
|
|
330
|
+
deps.p2pClientDeps,
|
|
184
331
|
);
|
|
185
332
|
|
|
186
|
-
|
|
333
|
+
// We should really not be modifying the config object
|
|
334
|
+
config.txPublicSetupAllowList = config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
|
|
187
335
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
336
|
+
const blockBuilder = new BlockBuilder(
|
|
337
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
|
|
338
|
+
worldStateSynchronizer,
|
|
339
|
+
archiver,
|
|
340
|
+
dateProvider,
|
|
341
|
+
telemetry,
|
|
342
|
+
);
|
|
191
343
|
|
|
192
|
-
|
|
344
|
+
// We'll accumulate sentinel watchers here
|
|
345
|
+
const watchers: Watcher[] = [];
|
|
193
346
|
|
|
194
|
-
//
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
347
|
+
// Create validator client if required
|
|
348
|
+
const validatorClient = createValidatorClient(config, {
|
|
349
|
+
p2pClient,
|
|
350
|
+
telemetry,
|
|
351
|
+
dateProvider,
|
|
352
|
+
epochCache,
|
|
353
|
+
blockBuilder,
|
|
354
|
+
blockSource: archiver,
|
|
355
|
+
l1ToL2MessageSource: archiver,
|
|
356
|
+
keyStoreManager,
|
|
357
|
+
fileStoreBlobUploadClient: fileStoreUploadClient,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
361
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
362
|
+
// like attestations or auths will fail.
|
|
363
|
+
if (validatorClient) {
|
|
364
|
+
watchers.push(validatorClient);
|
|
365
|
+
if (!options.dontStartSequencer) {
|
|
366
|
+
await validatorClient.registerHandlers();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// If there's no validator client but alwaysReexecuteBlockProposals is enabled,
|
|
371
|
+
// create a BlockProposalHandler to reexecute block proposals for monitoring
|
|
372
|
+
if (!validatorClient && config.alwaysReexecuteBlockProposals) {
|
|
373
|
+
log.info('Setting up block proposal reexecution for monitoring');
|
|
374
|
+
createBlockProposalHandler(config, {
|
|
375
|
+
blockBuilder,
|
|
376
|
+
epochCache,
|
|
377
|
+
blockSource: archiver,
|
|
378
|
+
l1ToL2MessageSource: archiver,
|
|
379
|
+
p2pClient,
|
|
380
|
+
dateProvider,
|
|
381
|
+
telemetry,
|
|
382
|
+
}).registerForReexecution(p2pClient);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Start world state and wait for it to sync to the archiver.
|
|
386
|
+
await worldStateSynchronizer.start();
|
|
387
|
+
|
|
388
|
+
// Start p2p. Note that it depends on world state to be running.
|
|
389
|
+
await p2pClient.start();
|
|
390
|
+
|
|
391
|
+
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
392
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
393
|
+
watchers.push(validatorsSentinel);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
397
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
398
|
+
epochPruneWatcher = new EpochPruneWatcher(
|
|
399
|
+
archiver,
|
|
400
|
+
archiver,
|
|
401
|
+
epochCache,
|
|
402
|
+
p2pClient.getTxProvider(),
|
|
403
|
+
blockBuilder,
|
|
404
|
+
config,
|
|
405
|
+
);
|
|
406
|
+
watchers.push(epochPruneWatcher);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
410
|
+
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
411
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
412
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
413
|
+
watchers.push(attestationsBlockWatcher);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Start p2p-related services once the archiver has completed sync
|
|
417
|
+
void archiver
|
|
418
|
+
.waitForInitialSync()
|
|
419
|
+
.then(async () => {
|
|
420
|
+
await p2pClient.start();
|
|
421
|
+
await validatorsSentinel?.start();
|
|
422
|
+
await epochPruneWatcher?.start();
|
|
423
|
+
await attestationsBlockWatcher?.start();
|
|
424
|
+
log.info(`All p2p services started`);
|
|
425
|
+
})
|
|
426
|
+
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
427
|
+
|
|
428
|
+
// Validator enabled, create/start relevant service
|
|
429
|
+
let sequencer: SequencerClient | undefined;
|
|
430
|
+
let slasherClient: SlasherClientInterface | undefined;
|
|
431
|
+
if (!config.disableValidator && validatorClient) {
|
|
432
|
+
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
433
|
+
// as they are executed when the node is selected as proposer.
|
|
434
|
+
const validatorAddresses = keyStoreManager
|
|
435
|
+
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
436
|
+
: [];
|
|
437
|
+
|
|
438
|
+
slasherClient = await createSlasher(
|
|
439
|
+
config,
|
|
440
|
+
config.l1Contracts,
|
|
441
|
+
getPublicClient(config),
|
|
442
|
+
watchers,
|
|
443
|
+
dateProvider,
|
|
444
|
+
epochCache,
|
|
445
|
+
validatorAddresses,
|
|
446
|
+
undefined, // logger
|
|
447
|
+
);
|
|
448
|
+
await slasherClient.start();
|
|
449
|
+
|
|
450
|
+
const l1TxUtils = config.publisherForwarderAddress
|
|
451
|
+
? await createForwarderL1TxUtilsFromEthSigner(
|
|
452
|
+
publicClient,
|
|
453
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
454
|
+
config.publisherForwarderAddress,
|
|
455
|
+
{ ...config, scope: 'sequencer' },
|
|
456
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider },
|
|
457
|
+
)
|
|
458
|
+
: await createL1TxUtilsWithBlobsFromEthSigner(
|
|
459
|
+
publicClient,
|
|
460
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
461
|
+
{ ...config, scope: 'sequencer' },
|
|
462
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider },
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
// Create and start the sequencer client
|
|
466
|
+
const checkpointsBuilder = new CheckpointsBuilder(
|
|
467
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
|
|
468
|
+
archiver,
|
|
469
|
+
dateProvider,
|
|
470
|
+
telemetry,
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
sequencer = await SequencerClient.new(config, {
|
|
474
|
+
...deps,
|
|
475
|
+
epochCache,
|
|
476
|
+
l1TxUtils,
|
|
477
|
+
validatorClient,
|
|
478
|
+
p2pClient,
|
|
479
|
+
worldStateSynchronizer,
|
|
480
|
+
slasherClient,
|
|
481
|
+
checkpointsBuilder,
|
|
482
|
+
l2BlockSource: archiver,
|
|
483
|
+
l1ToL2MessageSource: archiver,
|
|
484
|
+
telemetry,
|
|
485
|
+
dateProvider,
|
|
486
|
+
blobClient,
|
|
487
|
+
nodeKeyStore: keyStoreManager!,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!options.dontStartSequencer && sequencer) {
|
|
492
|
+
await sequencer.start();
|
|
493
|
+
log.verbose(`Sequencer started`);
|
|
494
|
+
} else if (sequencer) {
|
|
495
|
+
log.warn(`Sequencer created but not started`);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const globalVariableBuilder = new GlobalVariableBuilder({
|
|
499
|
+
...config,
|
|
500
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
501
|
+
l1GenesisTime,
|
|
502
|
+
slotDuration: Number(slotDuration),
|
|
503
|
+
});
|
|
210
504
|
|
|
211
505
|
return new AztecNodeService(
|
|
212
506
|
config,
|
|
@@ -215,12 +509,16 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
215
509
|
archiver,
|
|
216
510
|
archiver,
|
|
217
511
|
archiver,
|
|
218
|
-
archiver,
|
|
219
512
|
worldStateSynchronizer,
|
|
220
513
|
sequencer,
|
|
514
|
+
slasherClient,
|
|
515
|
+
validatorsSentinel,
|
|
516
|
+
epochPruneWatcher,
|
|
221
517
|
ethereumChain.chainInfo.id,
|
|
222
|
-
config.
|
|
223
|
-
|
|
518
|
+
config.rollupVersion,
|
|
519
|
+
globalVariableBuilder,
|
|
520
|
+
epochCache,
|
|
521
|
+
packageVersion,
|
|
224
522
|
proofVerifier,
|
|
225
523
|
telemetry,
|
|
226
524
|
log,
|
|
@@ -259,6 +557,10 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
259
557
|
return Promise.resolve(this.p2pClient.getEnr()?.encodeTxt());
|
|
260
558
|
}
|
|
261
559
|
|
|
560
|
+
public async getAllowedPublicSetup(): Promise<AllowedElement[]> {
|
|
561
|
+
return this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
|
|
562
|
+
}
|
|
563
|
+
|
|
262
564
|
/**
|
|
263
565
|
* Method to determine if the node is ready to accept transactions.
|
|
264
566
|
* @returns - Flag indicating the readiness for tx submission.
|
|
@@ -268,20 +570,19 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
268
570
|
}
|
|
269
571
|
|
|
270
572
|
public async getNodeInfo(): Promise<NodeInfo> {
|
|
271
|
-
const [nodeVersion,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
]);
|
|
573
|
+
const [nodeVersion, rollupVersion, chainId, enr, contractAddresses, protocolContractAddresses] = await Promise.all([
|
|
574
|
+
this.getNodeVersion(),
|
|
575
|
+
this.getVersion(),
|
|
576
|
+
this.getChainId(),
|
|
577
|
+
this.getEncodedEnr(),
|
|
578
|
+
this.getL1ContractAddresses(),
|
|
579
|
+
this.getProtocolContractAddresses(),
|
|
580
|
+
]);
|
|
280
581
|
|
|
281
582
|
const nodeInfo: NodeInfo = {
|
|
282
583
|
nodeVersion,
|
|
283
584
|
l1ChainId: chainId,
|
|
284
|
-
|
|
585
|
+
rollupVersion,
|
|
285
586
|
enr,
|
|
286
587
|
l1ContractAddresses: contractAddresses,
|
|
287
588
|
protocolContractAddresses: protocolContractAddresses,
|
|
@@ -295,8 +596,29 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
295
596
|
* @param number - The block number being requested.
|
|
296
597
|
* @returns The requested block.
|
|
297
598
|
*/
|
|
298
|
-
public async getBlock(number:
|
|
299
|
-
|
|
599
|
+
public async getBlock(number: BlockParameter): Promise<L2Block | undefined> {
|
|
600
|
+
const blockNumber = number === 'latest' ? await this.getBlockNumber() : (number as BlockNumber);
|
|
601
|
+
return await this.blockSource.getBlock(blockNumber);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Get a block specified by its hash.
|
|
606
|
+
* @param blockHash - The block hash being requested.
|
|
607
|
+
* @returns The requested block.
|
|
608
|
+
*/
|
|
609
|
+
public async getBlockByHash(blockHash: Fr): Promise<L2Block | undefined> {
|
|
610
|
+
const publishedBlock = await this.blockSource.getPublishedBlockByHash(blockHash);
|
|
611
|
+
return publishedBlock?.block;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Get a block specified by its archive root.
|
|
616
|
+
* @param archive - The archive root being requested.
|
|
617
|
+
* @returns The requested block.
|
|
618
|
+
*/
|
|
619
|
+
public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
620
|
+
const publishedBlock = await this.blockSource.getPublishedBlockByArchive(archive);
|
|
621
|
+
return publishedBlock?.block;
|
|
300
622
|
}
|
|
301
623
|
|
|
302
624
|
/**
|
|
@@ -305,10 +627,14 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
305
627
|
* @param limit - The maximum number of blocks to obtain.
|
|
306
628
|
* @returns The blocks requested.
|
|
307
629
|
*/
|
|
308
|
-
public async getBlocks(from:
|
|
630
|
+
public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
309
631
|
return (await this.blockSource.getBlocks(from, limit)) ?? [];
|
|
310
632
|
}
|
|
311
633
|
|
|
634
|
+
public async getPublishedBlocks(from: BlockNumber, limit: number): Promise<PublishedL2Block[]> {
|
|
635
|
+
return (await this.blockSource.getPublishedBlocks(from, limit)) ?? [];
|
|
636
|
+
}
|
|
637
|
+
|
|
312
638
|
/**
|
|
313
639
|
* Method to fetch the current base fees.
|
|
314
640
|
* @returns The current base fees.
|
|
@@ -317,15 +643,23 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
317
643
|
return await this.globalVariableBuilder.getCurrentBaseFees();
|
|
318
644
|
}
|
|
319
645
|
|
|
646
|
+
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
647
|
+
for await (const tx of this.p2pClient.iteratePendingTxs()) {
|
|
648
|
+
return tx.getGasSettings().maxPriorityFeesPerGas;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return GasFees.from({ feePerDaGas: 0n, feePerL2Gas: 0n });
|
|
652
|
+
}
|
|
653
|
+
|
|
320
654
|
/**
|
|
321
|
-
* Method to fetch the
|
|
655
|
+
* Method to fetch the latest block number synchronized by the node.
|
|
322
656
|
* @returns The block number.
|
|
323
657
|
*/
|
|
324
|
-
public async getBlockNumber(): Promise<
|
|
658
|
+
public async getBlockNumber(): Promise<BlockNumber> {
|
|
325
659
|
return await this.blockSource.getBlockNumber();
|
|
326
660
|
}
|
|
327
661
|
|
|
328
|
-
public async getProvenBlockNumber(): Promise<
|
|
662
|
+
public async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
329
663
|
return await this.blockSource.getProvenBlockNumber();
|
|
330
664
|
}
|
|
331
665
|
|
|
@@ -353,49 +687,20 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
353
687
|
return Promise.resolve(this.l1ChainId);
|
|
354
688
|
}
|
|
355
689
|
|
|
356
|
-
public
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// TODO(#10007): Remove this check. This is needed only because we're manually registering
|
|
360
|
-
// some contracts in the archiver so they are available to all nodes (see `registerCommonContracts`
|
|
361
|
-
// in `archiver/src/factory.ts`), but we still want clients to send the registration tx in order
|
|
362
|
-
// to emit the corresponding nullifier, which is now being checked. Note that this method
|
|
363
|
-
// is only called by the PXE to check if a contract is publicly registered.
|
|
364
|
-
if (klazz) {
|
|
365
|
-
const classNullifier = await siloNullifier(AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS), id);
|
|
366
|
-
const worldState = await this.#getWorldState('latest');
|
|
367
|
-
const [index] = await worldState.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [classNullifier.toBuffer()]);
|
|
368
|
-
this.log.debug(`Registration nullifier ${classNullifier} for contract class ${id} found at index ${index}`);
|
|
369
|
-
if (index === undefined) {
|
|
370
|
-
return undefined;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
return klazz;
|
|
690
|
+
public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
691
|
+
return this.contractDataSource.getContractClass(id);
|
|
375
692
|
}
|
|
376
693
|
|
|
377
694
|
public getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
|
|
378
695
|
return this.contractDataSource.getContract(address);
|
|
379
696
|
}
|
|
380
697
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
* @param from - The block number from which to begin retrieving logs.
|
|
384
|
-
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
385
|
-
* @returns An array of private logs from the specified range of blocks.
|
|
386
|
-
*/
|
|
387
|
-
public getPrivateLogs(from: number, limit: number): Promise<PrivateLog[]> {
|
|
388
|
-
return this.logsSource.getPrivateLogs(from, limit);
|
|
698
|
+
public getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
|
|
699
|
+
return this.logsSource.getPrivateLogsByTags(tags);
|
|
389
700
|
}
|
|
390
701
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
* @param tags - The tags to filter the logs by.
|
|
394
|
-
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
395
|
-
* that tag.
|
|
396
|
-
*/
|
|
397
|
-
public getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
|
|
398
|
-
return this.logsSource.getLogsByTags(tags);
|
|
702
|
+
public getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
|
|
703
|
+
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
399
704
|
}
|
|
400
705
|
|
|
401
706
|
/**
|
|
@@ -421,17 +726,19 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
421
726
|
* @param tx - The transaction to be submitted.
|
|
422
727
|
*/
|
|
423
728
|
public async sendTx(tx: Tx) {
|
|
729
|
+
await this.#sendTx(tx);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
async #sendTx(tx: Tx) {
|
|
424
733
|
const timer = new Timer();
|
|
425
|
-
const txHash =
|
|
734
|
+
const txHash = tx.getTxHash().toString();
|
|
426
735
|
|
|
427
736
|
const valid = await this.isValidTx(tx);
|
|
428
737
|
if (valid.result !== 'valid') {
|
|
429
738
|
const reason = valid.reason.join(', ');
|
|
430
739
|
this.metrics.receivedTx(timer.ms(), false);
|
|
431
|
-
this.log.warn(`
|
|
432
|
-
|
|
433
|
-
// throw new Error(`Invalid tx: ${reason}`);
|
|
434
|
-
return;
|
|
740
|
+
this.log.warn(`Received invalid tx ${txHash}: ${reason}`, { txHash });
|
|
741
|
+
throw new Error(`Invalid tx: ${reason}`);
|
|
435
742
|
}
|
|
436
743
|
|
|
437
744
|
await this.p2pClient!.sendTx(tx);
|
|
@@ -457,7 +764,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
457
764
|
return txReceipt;
|
|
458
765
|
}
|
|
459
766
|
|
|
460
|
-
public getTxEffect(txHash: TxHash): Promise<
|
|
767
|
+
public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
461
768
|
return this.blockSource.getTxEffect(txHash);
|
|
462
769
|
}
|
|
463
770
|
|
|
@@ -465,89 +772,120 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
465
772
|
* Method to stop the aztec node.
|
|
466
773
|
*/
|
|
467
774
|
public async stop() {
|
|
468
|
-
this.log.info(`Stopping`);
|
|
469
|
-
await this.
|
|
470
|
-
await this.
|
|
471
|
-
await this.
|
|
775
|
+
this.log.info(`Stopping Aztec Node`);
|
|
776
|
+
await tryStop(this.validatorsSentinel);
|
|
777
|
+
await tryStop(this.epochPruneWatcher);
|
|
778
|
+
await tryStop(this.slasherClient);
|
|
779
|
+
await tryStop(this.proofVerifier);
|
|
780
|
+
await tryStop(this.sequencer);
|
|
781
|
+
await tryStop(this.p2pClient);
|
|
782
|
+
await tryStop(this.worldStateSynchronizer);
|
|
472
783
|
await tryStop(this.blockSource);
|
|
473
|
-
await this.telemetry
|
|
474
|
-
this.log.info(`Stopped`);
|
|
784
|
+
await tryStop(this.telemetry);
|
|
785
|
+
this.log.info(`Stopped Aztec Node`);
|
|
475
786
|
}
|
|
476
787
|
|
|
477
788
|
/**
|
|
478
789
|
* Method to retrieve pending txs.
|
|
790
|
+
* @param limit - The number of items to returns
|
|
791
|
+
* @param after - The last known pending tx. Used for pagination
|
|
479
792
|
* @returns - The pending txs.
|
|
480
793
|
*/
|
|
481
|
-
public getPendingTxs() {
|
|
482
|
-
return this.p2pClient!.getPendingTxs();
|
|
794
|
+
public getPendingTxs(limit?: number, after?: TxHash): Promise<Tx[]> {
|
|
795
|
+
return this.p2pClient!.getPendingTxs(limit, after);
|
|
483
796
|
}
|
|
484
797
|
|
|
485
|
-
public
|
|
486
|
-
|
|
487
|
-
return pendingTxs.length;
|
|
798
|
+
public getPendingTxCount(): Promise<number> {
|
|
799
|
+
return this.p2pClient!.getPendingTxCount();
|
|
488
800
|
}
|
|
489
801
|
|
|
490
802
|
/**
|
|
491
|
-
* Method to retrieve a single tx from the mempool or
|
|
803
|
+
* Method to retrieve a single tx from the mempool or unfinalized chain.
|
|
492
804
|
* @param txHash - The transaction hash to return.
|
|
493
805
|
* @returns - The tx if it exists.
|
|
494
806
|
*/
|
|
495
|
-
public getTxByHash(txHash: TxHash) {
|
|
807
|
+
public getTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
496
808
|
return Promise.resolve(this.p2pClient!.getTxByHashFromPool(txHash));
|
|
497
809
|
}
|
|
498
810
|
|
|
499
811
|
/**
|
|
500
|
-
* Method to retrieve txs from the mempool or
|
|
812
|
+
* Method to retrieve txs from the mempool or unfinalized chain.
|
|
501
813
|
* @param txHash - The transaction hash to return.
|
|
502
814
|
* @returns - The txs if it exists.
|
|
503
815
|
*/
|
|
504
|
-
public async getTxsByHash(txHashes: TxHash[]) {
|
|
816
|
+
public async getTxsByHash(txHashes: TxHash[]): Promise<Tx[]> {
|
|
505
817
|
return compactArray(await Promise.all(txHashes.map(txHash => this.getTxByHash(txHash))));
|
|
506
818
|
}
|
|
507
819
|
|
|
508
820
|
/**
|
|
509
|
-
* Find the indexes of the given leaves in the given tree
|
|
510
|
-
*
|
|
821
|
+
* Find the indexes of the given leaves in the given tree along with a block metadata pointing to the block in which
|
|
822
|
+
* the leaves were inserted.
|
|
823
|
+
* @param blockNumber - The block number at which to get the data or 'latest' for latest data.
|
|
511
824
|
* @param treeId - The tree to search in.
|
|
512
|
-
* @param
|
|
513
|
-
* @returns The
|
|
825
|
+
* @param leafValues - The values to search for.
|
|
826
|
+
* @returns The indices of leaves and the block metadata of a block in which the leaves were inserted.
|
|
514
827
|
*/
|
|
515
828
|
public async findLeavesIndexes(
|
|
516
|
-
blockNumber:
|
|
829
|
+
blockNumber: BlockParameter,
|
|
517
830
|
treeId: MerkleTreeId,
|
|
518
831
|
leafValues: Fr[],
|
|
519
|
-
): Promise<(bigint | undefined)[]> {
|
|
832
|
+
): Promise<(DataInBlock<bigint> | undefined)[]> {
|
|
520
833
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
521
|
-
|
|
834
|
+
const maybeIndices = await committedDb.findLeafIndices(
|
|
522
835
|
treeId,
|
|
523
836
|
leafValues.map(x => x.toBuffer()),
|
|
524
837
|
);
|
|
525
|
-
|
|
838
|
+
// We filter out undefined values
|
|
839
|
+
const indices = maybeIndices.filter(x => x !== undefined) as bigint[];
|
|
526
840
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const committedDb = await this.#getWorldState(blockNumber);
|
|
540
|
-
return await committedDb.getBlockNumbersForLeafIndices(treeId, leafIndices);
|
|
541
|
-
}
|
|
841
|
+
// Now we find the block numbers for the indices
|
|
842
|
+
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, indices);
|
|
843
|
+
|
|
844
|
+
// If any of the block numbers are undefined, we throw an error.
|
|
845
|
+
for (let i = 0; i < indices.length; i++) {
|
|
846
|
+
if (blockNumbers[i] === undefined) {
|
|
847
|
+
throw new Error(`Block number is undefined for leaf index ${indices[i]} in tree ${MerkleTreeId[treeId]}`);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Get unique block numbers in order to optimize num calls to getLeafValue function.
|
|
852
|
+
const uniqueBlockNumbers = [...new Set(blockNumbers.filter(x => x !== undefined))];
|
|
542
853
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
854
|
+
// Now we obtain the block hashes from the archive tree by calling await `committedDb.getLeafValue(treeId, index)`
|
|
855
|
+
// (note that block number corresponds to the leaf index in the archive tree).
|
|
856
|
+
const blockHashes = await Promise.all(
|
|
857
|
+
uniqueBlockNumbers.map(blockNumber => {
|
|
858
|
+
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
859
|
+
}),
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
// If any of the block hashes are undefined, we throw an error.
|
|
863
|
+
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
864
|
+
if (blockHashes[i] === undefined) {
|
|
865
|
+
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
866
|
+
}
|
|
549
867
|
}
|
|
550
|
-
|
|
868
|
+
|
|
869
|
+
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
870
|
+
return maybeIndices.map((index, i) => {
|
|
871
|
+
if (index === undefined) {
|
|
872
|
+
return undefined;
|
|
873
|
+
}
|
|
874
|
+
const blockNumber = blockNumbers[i];
|
|
875
|
+
if (blockNumber === undefined) {
|
|
876
|
+
return undefined;
|
|
877
|
+
}
|
|
878
|
+
const blockHashIndex = uniqueBlockNumbers.indexOf(blockNumber);
|
|
879
|
+
const blockHash = blockHashes[blockHashIndex];
|
|
880
|
+
if (!blockHash) {
|
|
881
|
+
return undefined;
|
|
882
|
+
}
|
|
883
|
+
return {
|
|
884
|
+
l2BlockNumber: BlockNumber(Number(blockNumber)),
|
|
885
|
+
l2BlockHash: L2BlockHash.fromField(blockHash),
|
|
886
|
+
data: index,
|
|
887
|
+
};
|
|
888
|
+
});
|
|
551
889
|
}
|
|
552
890
|
|
|
553
891
|
/**
|
|
@@ -557,7 +895,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
557
895
|
* @returns The sibling path for the leaf index.
|
|
558
896
|
*/
|
|
559
897
|
public async getNullifierSiblingPath(
|
|
560
|
-
blockNumber:
|
|
898
|
+
blockNumber: BlockParameter,
|
|
561
899
|
leafIndex: bigint,
|
|
562
900
|
): Promise<SiblingPath<typeof NULLIFIER_TREE_HEIGHT>> {
|
|
563
901
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -571,13 +909,38 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
571
909
|
* @returns The sibling path for the leaf index.
|
|
572
910
|
*/
|
|
573
911
|
public async getNoteHashSiblingPath(
|
|
574
|
-
blockNumber:
|
|
912
|
+
blockNumber: BlockParameter,
|
|
575
913
|
leafIndex: bigint,
|
|
576
914
|
): Promise<SiblingPath<typeof NOTE_HASH_TREE_HEIGHT>> {
|
|
577
915
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
578
916
|
return committedDb.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex);
|
|
579
917
|
}
|
|
580
918
|
|
|
919
|
+
public async getArchiveMembershipWitness(
|
|
920
|
+
blockNumber: BlockParameter,
|
|
921
|
+
archive: Fr,
|
|
922
|
+
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
923
|
+
const committedDb = await this.#getWorldState(blockNumber);
|
|
924
|
+
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [archive]);
|
|
925
|
+
return pathAndIndex === undefined
|
|
926
|
+
? undefined
|
|
927
|
+
: MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
public async getNoteHashMembershipWitness(
|
|
931
|
+
blockNumber: BlockParameter,
|
|
932
|
+
noteHash: Fr,
|
|
933
|
+
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
934
|
+
const committedDb = await this.#getWorldState(blockNumber);
|
|
935
|
+
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
|
|
936
|
+
MerkleTreeId.NOTE_HASH_TREE,
|
|
937
|
+
[noteHash],
|
|
938
|
+
);
|
|
939
|
+
return pathAndIndex === undefined
|
|
940
|
+
? undefined
|
|
941
|
+
: MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
942
|
+
}
|
|
943
|
+
|
|
581
944
|
/**
|
|
582
945
|
* Returns the index and a sibling path for a leaf in the committed l1 to l2 data tree.
|
|
583
946
|
* @param blockNumber - The block number at which to get the data.
|
|
@@ -585,19 +948,24 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
585
948
|
* @returns A tuple of the index and the sibling path of the L1ToL2Message (undefined if not found).
|
|
586
949
|
*/
|
|
587
950
|
public async getL1ToL2MessageMembershipWitness(
|
|
588
|
-
blockNumber:
|
|
951
|
+
blockNumber: BlockParameter,
|
|
589
952
|
l1ToL2Message: Fr,
|
|
590
953
|
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
|
|
591
|
-
const
|
|
592
|
-
|
|
954
|
+
const db = await this.#getWorldState(blockNumber);
|
|
955
|
+
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
|
|
956
|
+
if (!witness) {
|
|
593
957
|
return undefined;
|
|
594
958
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
959
|
+
|
|
960
|
+
// REFACTOR: Return a MembershipWitness object
|
|
961
|
+
return [witness.index, witness.path];
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
public async getL1ToL2MessageBlock(l1ToL2Message: Fr): Promise<BlockNumber | undefined> {
|
|
965
|
+
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
966
|
+
return messageIndex
|
|
967
|
+
? BlockNumber.fromCheckpointNumber(InboxLeaf.checkpointNumberFromIndex(messageIndex))
|
|
968
|
+
: undefined;
|
|
601
969
|
}
|
|
602
970
|
|
|
603
971
|
/**
|
|
@@ -606,89 +974,20 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
606
974
|
* @returns Whether the message is synced and ready to be included in a block.
|
|
607
975
|
*/
|
|
608
976
|
public async isL1ToL2MessageSynced(l1ToL2Message: Fr): Promise<boolean> {
|
|
609
|
-
|
|
977
|
+
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
978
|
+
return messageIndex !== undefined;
|
|
610
979
|
}
|
|
611
980
|
|
|
612
981
|
/**
|
|
613
|
-
* Returns the
|
|
614
|
-
* @remarks This tree is considered ephemeral because it is created on-demand by: taking all the l2ToL1 messages
|
|
615
|
-
* in a single block, and then using them to make a variable depth append-only tree with these messages as leaves.
|
|
616
|
-
* The tree is discarded immediately after calculating what we need from it.
|
|
617
|
-
* TODO: Handle the case where two messages in the same tx have the same hash.
|
|
982
|
+
* Returns all the L2 to L1 messages in a block.
|
|
618
983
|
* @param blockNumber - The block number at which to get the data.
|
|
619
|
-
* @
|
|
620
|
-
* @returns A tuple of the index and the sibling path of the L2ToL1Message.
|
|
984
|
+
* @returns The L2 to L1 messages (undefined if the block number is not found).
|
|
621
985
|
*/
|
|
622
|
-
public async
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
): Promise<[bigint, SiblingPath<number>]> {
|
|
626
|
-
const block = await this.blockSource.getBlock(blockNumber === 'latest' ? await this.getBlockNumber() : blockNumber);
|
|
627
|
-
|
|
628
|
-
if (block === undefined) {
|
|
629
|
-
throw new Error('Block is not defined');
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
const l2ToL1Messages = block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
|
|
633
|
-
|
|
634
|
-
// Find index of message
|
|
635
|
-
let indexOfMsgInSubtree = -1;
|
|
636
|
-
const indexOfMsgTx = l2ToL1Messages.findIndex(msgs => {
|
|
637
|
-
const idx = msgs.findIndex(msg => msg.equals(l2ToL1Message));
|
|
638
|
-
indexOfMsgInSubtree = Math.max(indexOfMsgInSubtree, idx);
|
|
639
|
-
return idx !== -1;
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
if (indexOfMsgTx === -1) {
|
|
643
|
-
throw new Error('The L2ToL1Message you are trying to prove inclusion of does not exist');
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
const tempStores: AztecKVStore[] = [];
|
|
647
|
-
|
|
648
|
-
// Construct message subtrees
|
|
649
|
-
const l2toL1Subtrees = await Promise.all(
|
|
650
|
-
l2ToL1Messages.map(async (msgs, i) => {
|
|
651
|
-
const store = openTmpStore(true);
|
|
652
|
-
tempStores.push(store);
|
|
653
|
-
const treeHeight = msgs.length <= 1 ? 1 : Math.ceil(Math.log2(msgs.length));
|
|
654
|
-
const tree = new StandardTree(store, new SHA256Trunc(), `temp_msgs_subtrees_${i}`, treeHeight, 0n, Fr);
|
|
655
|
-
await tree.appendLeaves(msgs);
|
|
656
|
-
return tree;
|
|
657
|
-
}),
|
|
658
|
-
);
|
|
659
|
-
|
|
660
|
-
// path of the input msg from leaf -> first out hash calculated in base rolllup
|
|
661
|
-
const subtreePathOfL2ToL1Message = await l2toL1Subtrees[indexOfMsgTx].getSiblingPath(
|
|
662
|
-
BigInt(indexOfMsgInSubtree),
|
|
663
|
-
true,
|
|
664
|
-
);
|
|
665
|
-
|
|
666
|
-
const numTxs = block.body.txEffects.length;
|
|
667
|
-
if (numTxs === 1) {
|
|
668
|
-
return [BigInt(indexOfMsgInSubtree), subtreePathOfL2ToL1Message];
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const l2toL1SubtreeRoots = l2toL1Subtrees.map(t => Fr.fromBuffer(t.getRoot(true)));
|
|
672
|
-
const maxTreeHeight = Math.ceil(Math.log2(l2toL1SubtreeRoots.length));
|
|
673
|
-
// The root of this tree is the out_hash calculated in Noir => we truncate to match Noir's SHA
|
|
674
|
-
const outHashTree = new UnbalancedTree(new SHA256Trunc(), 'temp_outhash_sibling_path', maxTreeHeight, Fr);
|
|
675
|
-
await outHashTree.appendLeaves(l2toL1SubtreeRoots);
|
|
676
|
-
|
|
677
|
-
const pathOfTxInOutHashTree = await outHashTree.getSiblingPath(l2toL1SubtreeRoots[indexOfMsgTx].toBigInt());
|
|
678
|
-
// Append subtree path to out hash tree path
|
|
679
|
-
const mergedPath = subtreePathOfL2ToL1Message.toBufferArray().concat(pathOfTxInOutHashTree.toBufferArray());
|
|
680
|
-
// Append binary index of subtree path to binary index of out hash tree path
|
|
681
|
-
const mergedIndex = parseInt(
|
|
682
|
-
indexOfMsgTx
|
|
683
|
-
.toString(2)
|
|
684
|
-
.concat(indexOfMsgInSubtree.toString(2).padStart(l2toL1Subtrees[indexOfMsgTx].getDepth(), '0')),
|
|
685
|
-
2,
|
|
986
|
+
public async getL2ToL1Messages(blockNumber: BlockParameter): Promise<Fr[][] | undefined> {
|
|
987
|
+
const block = await this.blockSource.getBlock(
|
|
988
|
+
blockNumber === 'latest' ? await this.getBlockNumber() : (blockNumber as BlockNumber),
|
|
686
989
|
);
|
|
687
|
-
|
|
688
|
-
// clear the tmp stores
|
|
689
|
-
await Promise.all(tempStores.map(store => store.delete()));
|
|
690
|
-
|
|
691
|
-
return [BigInt(mergedIndex), new SiblingPath(mergedPath.length, mergedPath)];
|
|
990
|
+
return block?.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
|
|
692
991
|
}
|
|
693
992
|
|
|
694
993
|
/**
|
|
@@ -698,7 +997,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
698
997
|
* @returns The sibling path.
|
|
699
998
|
*/
|
|
700
999
|
public async getArchiveSiblingPath(
|
|
701
|
-
blockNumber:
|
|
1000
|
+
blockNumber: BlockParameter,
|
|
702
1001
|
leafIndex: bigint,
|
|
703
1002
|
): Promise<SiblingPath<typeof ARCHIVE_HEIGHT>> {
|
|
704
1003
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -712,7 +1011,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
712
1011
|
* @returns The sibling path.
|
|
713
1012
|
*/
|
|
714
1013
|
public async getPublicDataSiblingPath(
|
|
715
|
-
blockNumber:
|
|
1014
|
+
blockNumber: BlockParameter,
|
|
716
1015
|
leafIndex: bigint,
|
|
717
1016
|
): Promise<SiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>> {
|
|
718
1017
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -726,28 +1025,22 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
726
1025
|
* @returns The nullifier membership witness (if found).
|
|
727
1026
|
*/
|
|
728
1027
|
public async getNullifierMembershipWitness(
|
|
729
|
-
blockNumber:
|
|
1028
|
+
blockNumber: BlockParameter,
|
|
730
1029
|
nullifier: Fr,
|
|
731
1030
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
732
1031
|
const db = await this.#getWorldState(blockNumber);
|
|
733
|
-
const
|
|
734
|
-
if (!
|
|
1032
|
+
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
|
|
1033
|
+
if (!witness) {
|
|
735
1034
|
return undefined;
|
|
736
1035
|
}
|
|
737
1036
|
|
|
738
|
-
const
|
|
739
|
-
const
|
|
740
|
-
MerkleTreeId.NULLIFIER_TREE,
|
|
741
|
-
BigInt(index),
|
|
742
|
-
);
|
|
743
|
-
|
|
744
|
-
const [leafPreimage, siblingPath] = await Promise.all([leafPreimagePromise, siblingPathPromise]);
|
|
745
|
-
|
|
1037
|
+
const { index, path } = witness;
|
|
1038
|
+
const leafPreimage = await db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
|
|
746
1039
|
if (!leafPreimage) {
|
|
747
1040
|
return undefined;
|
|
748
1041
|
}
|
|
749
1042
|
|
|
750
|
-
return new NullifierMembershipWitness(
|
|
1043
|
+
return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path);
|
|
751
1044
|
}
|
|
752
1045
|
|
|
753
1046
|
/**
|
|
@@ -765,7 +1058,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
765
1058
|
* TODO: This is a confusing behavior and we should eventually address that.
|
|
766
1059
|
*/
|
|
767
1060
|
public async getLowNullifierMembershipWitness(
|
|
768
|
-
blockNumber:
|
|
1061
|
+
blockNumber: BlockParameter,
|
|
769
1062
|
nullifier: Fr,
|
|
770
1063
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
771
1064
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -779,14 +1072,11 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
779
1072
|
}
|
|
780
1073
|
const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
|
|
781
1074
|
|
|
782
|
-
const siblingPath = await committedDb.getSiblingPath
|
|
783
|
-
MerkleTreeId.NULLIFIER_TREE,
|
|
784
|
-
BigInt(index),
|
|
785
|
-
);
|
|
1075
|
+
const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
|
|
786
1076
|
return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
|
|
787
1077
|
}
|
|
788
1078
|
|
|
789
|
-
async
|
|
1079
|
+
async getPublicDataWitness(blockNumber: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
790
1080
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
791
1081
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
792
1082
|
if (!lowLeafResult) {
|
|
@@ -796,10 +1086,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
796
1086
|
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
797
1087
|
lowLeafResult.index,
|
|
798
1088
|
)) as PublicDataTreeLeafPreimage;
|
|
799
|
-
const path = await committedDb.getSiblingPath
|
|
800
|
-
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
801
|
-
lowLeafResult.index,
|
|
802
|
-
);
|
|
1089
|
+
const path = await committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
|
|
803
1090
|
return new PublicDataWitness(lowLeafResult.index, preimage, path);
|
|
804
1091
|
}
|
|
805
1092
|
}
|
|
@@ -815,7 +1102,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
815
1102
|
* @param blockNumber - The block number at which to get the data or 'latest'.
|
|
816
1103
|
* @returns Storage value at the given contract slot.
|
|
817
1104
|
*/
|
|
818
|
-
public async getPublicStorageAt(blockNumber:
|
|
1105
|
+
public async getPublicStorageAt(blockNumber: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
819
1106
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
820
1107
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
821
1108
|
|
|
@@ -827,36 +1114,69 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
827
1114
|
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
828
1115
|
lowLeafResult.index,
|
|
829
1116
|
)) as PublicDataTreeLeafPreimage;
|
|
830
|
-
return preimage.value;
|
|
1117
|
+
return preimage.leaf.value;
|
|
831
1118
|
}
|
|
832
1119
|
|
|
833
1120
|
/**
|
|
834
1121
|
* Returns the currently committed block header, or the initial header if no blocks have been produced.
|
|
835
1122
|
* @returns The current committed block header.
|
|
836
1123
|
*/
|
|
837
|
-
public async getBlockHeader(blockNumber:
|
|
838
|
-
return blockNumber ===
|
|
1124
|
+
public async getBlockHeader(blockNumber: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
|
|
1125
|
+
return blockNumber === BlockNumber.ZERO ||
|
|
1126
|
+
(blockNumber === 'latest' && (await this.blockSource.getBlockNumber()) === BlockNumber.ZERO)
|
|
839
1127
|
? this.worldStateSynchronizer.getCommitted().getInitialHeader()
|
|
840
|
-
: this.blockSource.getBlockHeader(blockNumber);
|
|
1128
|
+
: this.blockSource.getBlockHeader(blockNumber === 'latest' ? blockNumber : (blockNumber as BlockNumber));
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Get a block header specified by its hash.
|
|
1133
|
+
* @param blockHash - The block hash being requested.
|
|
1134
|
+
* @returns The requested block header.
|
|
1135
|
+
*/
|
|
1136
|
+
public async getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
|
|
1137
|
+
return await this.blockSource.getBlockHeaderByHash(blockHash);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Get a block header specified by its archive root.
|
|
1142
|
+
* @param archive - The archive root being requested.
|
|
1143
|
+
* @returns The requested block header.
|
|
1144
|
+
*/
|
|
1145
|
+
public async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1146
|
+
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
841
1147
|
}
|
|
842
1148
|
|
|
843
1149
|
/**
|
|
844
1150
|
* Simulates the public part of a transaction with the current state.
|
|
845
1151
|
* @param tx - The transaction to simulate.
|
|
846
1152
|
**/
|
|
847
|
-
@trackSpan('AztecNodeService.simulatePublicCalls',
|
|
848
|
-
[Attributes.TX_HASH]:
|
|
1153
|
+
@trackSpan('AztecNodeService.simulatePublicCalls', (tx: Tx) => ({
|
|
1154
|
+
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
849
1155
|
}))
|
|
850
1156
|
public async simulatePublicCalls(tx: Tx, skipFeeEnforcement = false): Promise<PublicSimulationOutput> {
|
|
851
|
-
|
|
852
|
-
const
|
|
1157
|
+
// Check total gas limit for simulation
|
|
1158
|
+
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
1159
|
+
const txGasLimit = gasSettings.gasLimits.l2Gas;
|
|
1160
|
+
const teardownGasLimit = gasSettings.teardownGasLimits.l2Gas;
|
|
1161
|
+
if (txGasLimit + teardownGasLimit > this.config.rpcSimulatePublicMaxGasLimit) {
|
|
1162
|
+
throw new BadRequestError(
|
|
1163
|
+
`Transaction total gas limit ${
|
|
1164
|
+
txGasLimit + teardownGasLimit
|
|
1165
|
+
} (${txGasLimit} + ${teardownGasLimit}) exceeds maximum gas limit ${
|
|
1166
|
+
this.config.rpcSimulatePublicMaxGasLimit
|
|
1167
|
+
} for simulation`,
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
const txHash = tx.getTxHash();
|
|
1172
|
+
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
853
1173
|
|
|
854
1174
|
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
855
|
-
const coinbase =
|
|
856
|
-
const feeRecipient =
|
|
1175
|
+
const coinbase = EthAddress.ZERO;
|
|
1176
|
+
const feeRecipient = AztecAddress.ZERO;
|
|
857
1177
|
|
|
858
1178
|
const newGlobalVariables = await this.globalVariableBuilder.buildGlobalVariables(
|
|
859
|
-
|
|
1179
|
+
blockNumber,
|
|
860
1180
|
coinbase,
|
|
861
1181
|
feeRecipient,
|
|
862
1182
|
);
|
|
@@ -865,7 +1185,6 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
865
1185
|
new DateProvider(),
|
|
866
1186
|
this.telemetry,
|
|
867
1187
|
);
|
|
868
|
-
const fork = await this.worldStateSynchronizer.fork();
|
|
869
1188
|
|
|
870
1189
|
this.log.verbose(`Simulating public calls for tx ${txHash}`, {
|
|
871
1190
|
globalVariables: newGlobalVariables.toInspect(),
|
|
@@ -873,11 +1192,22 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
873
1192
|
blockNumber,
|
|
874
1193
|
});
|
|
875
1194
|
|
|
1195
|
+
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
876
1196
|
try {
|
|
877
|
-
const
|
|
1197
|
+
const config = PublicSimulatorConfig.from({
|
|
1198
|
+
skipFeeEnforcement,
|
|
1199
|
+
collectDebugLogs: true,
|
|
1200
|
+
collectHints: false,
|
|
1201
|
+
collectCallMetadata: true,
|
|
1202
|
+
collectStatistics: false,
|
|
1203
|
+
collectionLimits: CollectionLimitsConfig.from({
|
|
1204
|
+
maxDebugLogMemoryReads: this.config.rpcSimulatePublicMaxDebugLogMemoryReads,
|
|
1205
|
+
}),
|
|
1206
|
+
});
|
|
1207
|
+
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
878
1208
|
|
|
879
1209
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
880
|
-
const [processedTxs, failedTxs, returns] = await processor.process([tx]);
|
|
1210
|
+
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([tx]);
|
|
881
1211
|
// REFACTOR: Consider returning the error rather than throwing
|
|
882
1212
|
if (failedTxs.length) {
|
|
883
1213
|
this.log.warn(`Simulated tx ${txHash} fails: ${failedTxs[0].error}`, { txHash });
|
|
@@ -887,13 +1217,13 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
887
1217
|
const [processedTx] = processedTxs;
|
|
888
1218
|
return new PublicSimulationOutput(
|
|
889
1219
|
processedTx.revertReason,
|
|
890
|
-
processedTx.
|
|
1220
|
+
processedTx.globalVariables,
|
|
891
1221
|
processedTx.txEffect,
|
|
892
1222
|
returns,
|
|
893
1223
|
processedTx.gasUsed,
|
|
894
1224
|
);
|
|
895
1225
|
} finally {
|
|
896
|
-
await
|
|
1226
|
+
await merkleTreeFork.close();
|
|
897
1227
|
}
|
|
898
1228
|
}
|
|
899
1229
|
|
|
@@ -901,24 +1231,42 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
901
1231
|
tx: Tx,
|
|
902
1232
|
{ isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {},
|
|
903
1233
|
): Promise<TxValidationResult> {
|
|
904
|
-
const blockNumber = (await this.blockSource.getBlockNumber()) + 1;
|
|
905
1234
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
906
1235
|
const verifier = isSimulation ? undefined : this.proofVerifier;
|
|
1236
|
+
|
|
1237
|
+
// We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
|
|
1238
|
+
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1239
|
+
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
907
1240
|
const validator = createValidatorForAcceptingTxs(db, this.contractDataSource, verifier, {
|
|
1241
|
+
timestamp: nextSlotTimestamp,
|
|
908
1242
|
blockNumber,
|
|
909
1243
|
l1ChainId: this.l1ChainId,
|
|
910
|
-
|
|
1244
|
+
rollupVersion: this.version,
|
|
1245
|
+
setupAllowList: this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()),
|
|
911
1246
|
gasFees: await this.getCurrentBaseFees(),
|
|
912
1247
|
skipFeeEnforcement,
|
|
1248
|
+
txsPermitted: !this.config.disableTransactions,
|
|
913
1249
|
});
|
|
914
1250
|
|
|
915
1251
|
return await validator.validateTx(tx);
|
|
916
1252
|
}
|
|
917
1253
|
|
|
918
|
-
public
|
|
919
|
-
const
|
|
920
|
-
|
|
1254
|
+
public getConfig(): Promise<AztecNodeAdminConfig> {
|
|
1255
|
+
const schema = AztecNodeAdminConfigSchema;
|
|
1256
|
+
const keys = schema.keyof().options;
|
|
1257
|
+
return Promise.resolve(pick(this.config, ...keys));
|
|
1258
|
+
}
|
|
921
1259
|
|
|
1260
|
+
public async setConfig(config: Partial<AztecNodeAdminConfig>): Promise<void> {
|
|
1261
|
+
const newConfig = { ...this.config, ...config };
|
|
1262
|
+
this.sequencer?.updateConfig(config);
|
|
1263
|
+
this.slasherClient?.updateConfig(config);
|
|
1264
|
+
this.validatorsSentinel?.updateConfig(config);
|
|
1265
|
+
await this.p2pClient.updateP2PConfig(config);
|
|
1266
|
+
const archiver = this.blockSource as Archiver;
|
|
1267
|
+
if ('updateConfig' in archiver) {
|
|
1268
|
+
archiver.updateConfig(config);
|
|
1269
|
+
}
|
|
922
1270
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
923
1271
|
this.proofVerifier = config.realProofs ? await BBCircuitVerifier.new(newConfig) : new TestCircuitVerifier();
|
|
924
1272
|
}
|
|
@@ -928,42 +1276,151 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
928
1276
|
|
|
929
1277
|
public getProtocolContractAddresses(): Promise<ProtocolContractAddresses> {
|
|
930
1278
|
return Promise.resolve({
|
|
931
|
-
|
|
1279
|
+
classRegistry: ProtocolContractAddress.ContractClassRegistry,
|
|
932
1280
|
feeJuice: ProtocolContractAddress.FeeJuice,
|
|
933
|
-
|
|
1281
|
+
instanceRegistry: ProtocolContractAddress.ContractInstanceRegistry,
|
|
934
1282
|
multiCallEntrypoint: ProtocolContractAddress.MultiCallEntrypoint,
|
|
935
1283
|
});
|
|
936
1284
|
}
|
|
937
1285
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1286
|
+
public registerContractFunctionSignatures(signatures: string[]): Promise<void> {
|
|
1287
|
+
return this.contractDataSource.registerContractFunctionSignatures(signatures);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
public getValidatorsStats(): Promise<ValidatorsStats> {
|
|
1291
|
+
return this.validatorsSentinel?.computeStats() ?? Promise.resolve({ stats: {}, slotWindow: 0 });
|
|
942
1292
|
}
|
|
943
1293
|
|
|
944
|
-
public
|
|
945
|
-
|
|
1294
|
+
public getValidatorStats(
|
|
1295
|
+
validatorAddress: EthAddress,
|
|
1296
|
+
fromSlot?: SlotNumber,
|
|
1297
|
+
toSlot?: SlotNumber,
|
|
1298
|
+
): Promise<SingleValidatorStats | undefined> {
|
|
1299
|
+
return this.validatorsSentinel?.getValidatorStats(validatorAddress, fromSlot, toSlot) ?? Promise.resolve(undefined);
|
|
946
1300
|
}
|
|
947
1301
|
|
|
948
|
-
public
|
|
949
|
-
|
|
950
|
-
|
|
1302
|
+
public async startSnapshotUpload(location: string): Promise<void> {
|
|
1303
|
+
// Note that we are forcefully casting the blocksource as an archiver
|
|
1304
|
+
// We break support for archiver running remotely to the node
|
|
1305
|
+
const archiver = this.blockSource as Archiver;
|
|
1306
|
+
if (!('backupTo' in archiver)) {
|
|
1307
|
+
this.metrics.recordSnapshotError();
|
|
1308
|
+
throw new Error('Archiver implementation does not support backups. Cannot generate snapshot.');
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// Test that the archiver has done an initial sync.
|
|
1312
|
+
if (!archiver.isInitialSyncComplete()) {
|
|
1313
|
+
this.metrics.recordSnapshotError();
|
|
1314
|
+
throw new Error(`Archiver initial sync not complete. Cannot start snapshot.`);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// And it has an L2 block hash
|
|
1318
|
+
const l2BlockHash = await archiver.getL2Tips().then(tips => tips.latest.hash);
|
|
1319
|
+
if (!l2BlockHash) {
|
|
1320
|
+
this.metrics.recordSnapshotError();
|
|
1321
|
+
throw new Error(`Archiver has no latest L2 block hash downloaded. Cannot start snapshot.`);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
if (this.isUploadingSnapshot) {
|
|
1325
|
+
this.metrics.recordSnapshotError();
|
|
1326
|
+
throw new Error(`Snapshot upload already in progress. Cannot start another one until complete.`);
|
|
951
1327
|
}
|
|
952
|
-
|
|
1328
|
+
|
|
1329
|
+
// Do not wait for the upload to be complete to return to the caller, but flag that an operation is in progress
|
|
1330
|
+
this.isUploadingSnapshot = true;
|
|
1331
|
+
const timer = new Timer();
|
|
1332
|
+
void uploadSnapshot(location, this.blockSource as Archiver, this.worldStateSynchronizer, this.config, this.log)
|
|
1333
|
+
.then(() => {
|
|
1334
|
+
this.isUploadingSnapshot = false;
|
|
1335
|
+
this.metrics.recordSnapshot(timer.ms());
|
|
1336
|
+
})
|
|
1337
|
+
.catch(err => {
|
|
1338
|
+
this.isUploadingSnapshot = false;
|
|
1339
|
+
this.metrics.recordSnapshotError();
|
|
1340
|
+
this.log.error(`Error uploading snapshot: ${err}`);
|
|
1341
|
+
});
|
|
1342
|
+
|
|
953
1343
|
return Promise.resolve();
|
|
954
1344
|
}
|
|
955
1345
|
|
|
1346
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1347
|
+
const archiver = this.blockSource as Archiver;
|
|
1348
|
+
if (!('rollbackTo' in archiver)) {
|
|
1349
|
+
throw new Error('Archiver implementation does not support rollbacks.');
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
const finalizedBlock = await archiver.getL2Tips().then(tips => tips.finalized.number);
|
|
1353
|
+
if (targetBlock < finalizedBlock) {
|
|
1354
|
+
if (force) {
|
|
1355
|
+
this.log.warn(`Clearing world state database to allow rolling back behind finalized block ${finalizedBlock}`);
|
|
1356
|
+
await this.worldStateSynchronizer.clear();
|
|
1357
|
+
await this.p2pClient.clear();
|
|
1358
|
+
} else {
|
|
1359
|
+
throw new Error(`Cannot rollback to block ${targetBlock} as it is before finalized ${finalizedBlock}`);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
try {
|
|
1364
|
+
this.log.info(`Pausing archiver and world state sync to start rollback`);
|
|
1365
|
+
await archiver.stop();
|
|
1366
|
+
await this.worldStateSynchronizer.stopSync();
|
|
1367
|
+
const currentBlock = await archiver.getBlockNumber();
|
|
1368
|
+
const blocksToUnwind = currentBlock - targetBlock;
|
|
1369
|
+
this.log.info(`Unwinding ${count(blocksToUnwind, 'block')} from L2 block ${currentBlock} to ${targetBlock}`);
|
|
1370
|
+
await archiver.rollbackTo(targetBlock);
|
|
1371
|
+
this.log.info(`Unwinding complete.`);
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
this.log.error(`Error during rollback`, err);
|
|
1374
|
+
throw err;
|
|
1375
|
+
} finally {
|
|
1376
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1377
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1378
|
+
archiver.resume();
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
public async pauseSync(): Promise<void> {
|
|
1383
|
+
this.log.info(`Pausing archiver and world state sync`);
|
|
1384
|
+
await (this.blockSource as Archiver).stop();
|
|
1385
|
+
await this.worldStateSynchronizer.stopSync();
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
public resumeSync(): Promise<void> {
|
|
1389
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1390
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1391
|
+
(this.blockSource as Archiver).resume();
|
|
1392
|
+
return Promise.resolve();
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
public getSlashPayloads(): Promise<SlashPayloadRound[]> {
|
|
1396
|
+
if (!this.slasherClient) {
|
|
1397
|
+
throw new Error(`Slasher client not enabled`);
|
|
1398
|
+
}
|
|
1399
|
+
return this.slasherClient.getSlashPayloads();
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
1403
|
+
if (!this.slasherClient) {
|
|
1404
|
+
throw new Error(`Slasher client not enabled`);
|
|
1405
|
+
}
|
|
1406
|
+
if (round === 'all') {
|
|
1407
|
+
return this.slasherClient.getPendingOffenses();
|
|
1408
|
+
} else {
|
|
1409
|
+
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
956
1413
|
/**
|
|
957
1414
|
* Returns an instance of MerkleTreeOperations having first ensured the world state is fully synched
|
|
958
1415
|
* @param blockNumber - The block number at which to get the data.
|
|
959
1416
|
* @returns An instance of a committed MerkleTreeOperations
|
|
960
1417
|
*/
|
|
961
|
-
async #getWorldState(blockNumber:
|
|
1418
|
+
async #getWorldState(blockNumber: BlockParameter) {
|
|
962
1419
|
if (typeof blockNumber === 'number' && blockNumber < INITIAL_L2_BLOCK_NUM - 1) {
|
|
963
1420
|
throw new Error('Invalid block number to get world state for: ' + blockNumber);
|
|
964
1421
|
}
|
|
965
1422
|
|
|
966
|
-
let blockSyncedTo:
|
|
1423
|
+
let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
|
|
967
1424
|
try {
|
|
968
1425
|
// Attempt to sync the world state if necessary
|
|
969
1426
|
blockSyncedTo = await this.#syncWorldState();
|
|
@@ -977,7 +1434,7 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
977
1434
|
return this.worldStateSynchronizer.getCommitted();
|
|
978
1435
|
} else if (blockNumber <= blockSyncedTo) {
|
|
979
1436
|
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
980
|
-
return this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1437
|
+
return this.worldStateSynchronizer.getSnapshot(blockNumber as BlockNumber);
|
|
981
1438
|
} else {
|
|
982
1439
|
throw new Error(`Block ${blockNumber} not yet synced`);
|
|
983
1440
|
}
|
|
@@ -987,8 +1444,8 @@ export class AztecNodeService implements AztecNode, Traceable {
|
|
|
987
1444
|
* Ensure we fully sync the world state
|
|
988
1445
|
* @returns A promise that fulfils once the world state is synced
|
|
989
1446
|
*/
|
|
990
|
-
async #syncWorldState(): Promise<
|
|
1447
|
+
async #syncWorldState(): Promise<BlockNumber> {
|
|
991
1448
|
const blockSourceHeight = await this.blockSource.getBlockNumber();
|
|
992
|
-
return this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
|
|
1449
|
+
return await this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
|
|
993
1450
|
}
|
|
994
1451
|
}
|