@aztec/aztec-node 0.0.1-commit.a89ec08 → 0.0.1-commit.aa0c64f
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/block_response_helpers.d.ts +25 -0
- package/dest/aztec-node/block_response_helpers.d.ts.map +1 -0
- package/dest/aztec-node/block_response_helpers.js +112 -0
- package/dest/aztec-node/config.d.ts +17 -5
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +16 -6
- package/dest/aztec-node/node_public_calls_simulator.d.ts +97 -0
- package/dest/aztec-node/node_public_calls_simulator.d.ts.map +1 -0
- package/dest/aztec-node/node_public_calls_simulator.js +351 -0
- package/dest/aztec-node/public_data_overrides.d.ts +13 -0
- package/dest/aztec-node/public_data_overrides.d.ts.map +1 -0
- package/dest/aztec-node/public_data_overrides.js +21 -0
- package/dest/aztec-node/register_node_rpc_handlers.d.ts +10 -0
- package/dest/aztec-node/register_node_rpc_handlers.d.ts.map +1 -0
- package/dest/aztec-node/register_node_rpc_handlers.js +31 -0
- package/dest/aztec-node/server.d.ts +130 -133
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +373 -827
- package/dest/bin/index.js +15 -10
- package/dest/factory.d.ts +33 -0
- package/dest/factory.d.ts.map +1 -0
- package/dest/factory.js +523 -0
- package/dest/index.d.ts +3 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -0
- package/dest/modules/block_parameter.d.ts +25 -0
- package/dest/modules/block_parameter.d.ts.map +1 -0
- package/dest/modules/block_parameter.js +100 -0
- package/dest/modules/node_block_provider.d.ts +19 -0
- package/dest/modules/node_block_provider.d.ts.map +1 -0
- package/dest/modules/node_block_provider.js +112 -0
- package/dest/modules/node_tx_receipt.d.ts +24 -0
- package/dest/modules/node_tx_receipt.d.ts.map +1 -0
- package/dest/modules/node_tx_receipt.js +70 -0
- package/dest/modules/node_world_state_queries.d.ts +65 -0
- package/dest/modules/node_world_state_queries.d.ts.map +1 -0
- package/dest/modules/node_world_state_queries.js +272 -0
- package/dest/sentinel/config.d.ts +3 -2
- package/dest/sentinel/config.d.ts.map +1 -1
- package/dest/sentinel/config.js +15 -5
- package/dest/sentinel/factory.d.ts +6 -4
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/factory.js +12 -5
- package/dest/sentinel/sentinel.d.ts +145 -21
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +227 -105
- package/dest/sentinel/store.d.ts +8 -8
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +25 -17
- package/dest/test/index.d.ts +3 -3
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +28 -27
- package/src/aztec-node/block_response_helpers.ts +161 -0
- package/src/aztec-node/config.ts +31 -8
- package/src/aztec-node/node_public_calls_simulator.ts +394 -0
- package/src/aztec-node/public_data_overrides.ts +35 -0
- package/src/aztec-node/register_node_rpc_handlers.ts +29 -0
- package/src/aztec-node/server.ts +497 -1082
- package/src/bin/index.ts +19 -12
- package/src/factory.ts +688 -0
- package/src/index.ts +2 -0
- package/src/modules/block_parameter.ts +93 -0
- package/src/modules/node_block_provider.ts +149 -0
- package/src/modules/node_tx_receipt.ts +115 -0
- package/src/modules/node_world_state_queries.ts +374 -0
- package/src/sentinel/README.md +103 -0
- package/src/sentinel/config.ts +18 -6
- package/src/sentinel/factory.ts +22 -7
- package/src/sentinel/sentinel.ts +277 -130
- package/src/sentinel/store.ts +26 -18
- package/src/test/index.ts +2 -2
package/src/aztec-node/server.ts
CHANGED
|
@@ -1,56 +1,49 @@
|
|
|
1
|
-
import { Archiver
|
|
2
|
-
import { BBCircuitVerifier,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { Archiver } from '@aztec/archiver';
|
|
2
|
+
import { BBCircuitVerifier, BatchChonkVerifier, QueuedIVCVerifier } from '@aztec/bb-prover';
|
|
3
|
+
import { TestCircuitVerifier } from '@aztec/bb-prover/test';
|
|
4
|
+
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
5
5
|
import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
7
|
+
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
8
|
+
import { type L1ContractAddresses, pickL1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
9
|
+
import {
|
|
10
|
+
BlockNumber,
|
|
11
|
+
CheckpointNumber,
|
|
12
|
+
type CheckpointProposalHash,
|
|
13
|
+
EpochNumber,
|
|
14
|
+
SlotNumber,
|
|
15
|
+
} from '@aztec/foundation/branded-types';
|
|
16
|
+
import { compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
13
17
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
14
18
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
15
19
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
16
20
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
17
21
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
18
22
|
import { count } from '@aztec/foundation/string';
|
|
19
|
-
import {
|
|
23
|
+
import { Timer } from '@aztec/foundation/timer';
|
|
20
24
|
import { MembershipWitness, SiblingPath } from '@aztec/foundation/trees';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
type P2P,
|
|
26
|
-
type P2PClientDeps,
|
|
27
|
-
createP2PClient,
|
|
28
|
-
createTxValidatorForAcceptingTxsOverRPC,
|
|
29
|
-
getDefaultAllowedSetupFunctions,
|
|
30
|
-
} from '@aztec/p2p';
|
|
25
|
+
import { KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
|
|
26
|
+
import { uploadSnapshot } from '@aztec/node-lib/actions';
|
|
27
|
+
import { type P2P, createTxValidatorForAcceptingTxsOverRPC, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
|
|
31
28
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
EpochPruneWatcher,
|
|
39
|
-
type SlasherClientInterface,
|
|
40
|
-
type Watcher,
|
|
41
|
-
createSlasher,
|
|
42
|
-
} from '@aztec/slasher';
|
|
43
|
-
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
29
|
+
import type { ProverNode } from '@aztec/prover-node';
|
|
30
|
+
import { SequencerClient } from '@aztec/sequencer-client';
|
|
31
|
+
import { AutomineSequencer } from '@aztec/sequencer-client/automine';
|
|
32
|
+
import type { AvmSimulator } from '@aztec/simulator/server';
|
|
33
|
+
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
34
|
+
import { STANDARD_MULTI_CALL_ENTRYPOINT_ADDRESS } from '@aztec/standard-contracts/multi-call-entrypoint';
|
|
44
35
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
45
36
|
import {
|
|
46
37
|
type BlockData,
|
|
47
38
|
BlockHash,
|
|
48
39
|
type BlockParameter,
|
|
40
|
+
type CheckpointsQuery,
|
|
49
41
|
type DataInBlock,
|
|
50
|
-
L2Block,
|
|
51
42
|
type L2BlockSource,
|
|
43
|
+
type L2BlockTag,
|
|
44
|
+
type L2Tips,
|
|
45
|
+
inspectBlockParameter,
|
|
52
46
|
} from '@aztec/stdlib/block';
|
|
53
|
-
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
54
47
|
import type {
|
|
55
48
|
ContractClassPublic,
|
|
56
49
|
ContractDataSource,
|
|
@@ -58,17 +51,25 @@ import type {
|
|
|
58
51
|
NodeInfo,
|
|
59
52
|
ProtocolContractAddresses,
|
|
60
53
|
} from '@aztec/stdlib/contract';
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
54
|
+
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
55
|
+
import { GasFees, type ManaUsageEstimate, getNetworkTxGasLimits } from '@aztec/stdlib/gas';
|
|
56
|
+
import type {
|
|
57
|
+
AztecNode,
|
|
58
|
+
AztecNodeAdmin,
|
|
59
|
+
AztecNodeAdminConfig,
|
|
60
|
+
AztecNodeDebug,
|
|
61
|
+
BlockIncludeOptions,
|
|
62
|
+
BlockResponse,
|
|
63
|
+
BlocksIncludeOptions,
|
|
64
|
+
CheckpointIncludeOptions,
|
|
65
|
+
CheckpointParameter,
|
|
66
|
+
CheckpointResponse,
|
|
67
|
+
CheckpointTag,
|
|
68
|
+
GetTxByHashOptions,
|
|
69
|
+
PeerInfo,
|
|
70
|
+
ProposalsForSlot,
|
|
71
71
|
} from '@aztec/stdlib/interfaces/client';
|
|
72
|
+
import { AztecNodeAdminConfigSchema } from '@aztec/stdlib/interfaces/client';
|
|
72
73
|
import {
|
|
73
74
|
type AllowedElement,
|
|
74
75
|
type ClientProtocolCircuitVerifier,
|
|
@@ -78,24 +79,24 @@ import {
|
|
|
78
79
|
type WorldStateSynchronizer,
|
|
79
80
|
tryStop,
|
|
80
81
|
} from '@aztec/stdlib/interfaces/server';
|
|
81
|
-
import type { DebugLogStore,
|
|
82
|
-
import {
|
|
83
|
-
import {
|
|
84
|
-
import type {
|
|
85
|
-
import type {
|
|
82
|
+
import type { DebugLogStore, LogResult, PrivateLogsQuery, PublicLogsQuery } from '@aztec/stdlib/logs';
|
|
83
|
+
import { NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
84
|
+
import type { L1ToL2MessageSource, L2ToL1MembershipWitness } from '@aztec/stdlib/messaging';
|
|
85
|
+
import type { CheckpointAttestation } from '@aztec/stdlib/p2p';
|
|
86
|
+
import type { Offense } from '@aztec/stdlib/slashing';
|
|
86
87
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
87
88
|
import {
|
|
88
|
-
type
|
|
89
|
+
type FeeProvider,
|
|
90
|
+
type GetTxReceiptOptions,
|
|
89
91
|
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
90
92
|
type IndexedTxEffect,
|
|
91
93
|
PublicSimulationOutput,
|
|
94
|
+
type SimulationOverrides,
|
|
92
95
|
Tx,
|
|
93
96
|
type TxHash,
|
|
94
|
-
TxReceipt,
|
|
95
|
-
TxStatus,
|
|
97
|
+
type TxReceipt,
|
|
96
98
|
type TxValidationResult,
|
|
97
99
|
} from '@aztec/stdlib/tx';
|
|
98
|
-
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
99
100
|
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
100
101
|
import {
|
|
101
102
|
Attributes,
|
|
@@ -105,495 +106,265 @@ import {
|
|
|
105
106
|
getTelemetryClient,
|
|
106
107
|
trackSpan,
|
|
107
108
|
} from '@aztec/telemetry-client';
|
|
108
|
-
import {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
createProposalHandler,
|
|
114
|
-
createValidatorClient,
|
|
115
|
-
} from '@aztec/validator-client';
|
|
116
|
-
import type { SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
117
|
-
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
118
|
-
|
|
119
|
-
import { createPublicClient } from 'viem';
|
|
120
|
-
|
|
121
|
-
import { createSentinel } from '../sentinel/factory.js';
|
|
109
|
+
import { NodeKeystoreAdapter, ValidatorClient } from '@aztec/validator-client';
|
|
110
|
+
|
|
111
|
+
import { NodeBlockProvider } from '../modules/node_block_provider.js';
|
|
112
|
+
import { NodeTxReceiptBuilder } from '../modules/node_tx_receipt.js';
|
|
113
|
+
import { NodeWorldStateQueries } from '../modules/node_world_state_queries.js';
|
|
122
114
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
123
|
-
import {
|
|
115
|
+
import type { AztecNodeConfig } from './config.js';
|
|
124
116
|
import { NodeMetrics } from './node_metrics.js';
|
|
117
|
+
import { NodePublicCallsSimulator } from './node_public_calls_simulator.js';
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Fully-constructed collaborators and settings an {@link AztecNodeService} owns. Built by `createAztecNodeService`
|
|
121
|
+
* (see `factory.ts`); passed as a single object so call sites name each dependency instead of relying on
|
|
122
|
+
* positional order.
|
|
123
|
+
*/
|
|
124
|
+
export interface AztecNodeServiceDeps {
|
|
125
|
+
config: AztecNodeConfig;
|
|
126
|
+
p2pClient: P2P;
|
|
127
|
+
blockSource: L2BlockSource & Partial<Service>;
|
|
128
|
+
logsSource: L2LogsSource;
|
|
129
|
+
contractDataSource: ContractDataSource;
|
|
130
|
+
l1ToL2MessageSource: L1ToL2MessageSource;
|
|
131
|
+
worldStateSynchronizer: WorldStateSynchronizer;
|
|
132
|
+
sequencer: SequencerClient | undefined;
|
|
133
|
+
proverNode: ProverNode | undefined;
|
|
134
|
+
slasherClient: SlasherClientInterface | undefined;
|
|
135
|
+
validatorsSentinel: Sentinel | undefined;
|
|
136
|
+
stopStartedWatchers: () => Promise<void>;
|
|
137
|
+
l1ChainId: number;
|
|
138
|
+
version: number;
|
|
139
|
+
globalVariableBuilder: GlobalVariableBuilderInterface;
|
|
140
|
+
rollupContract: RollupContract | undefined;
|
|
141
|
+
feeProvider: FeeProvider;
|
|
142
|
+
epochCache: EpochCacheInterface;
|
|
143
|
+
packageVersion: string;
|
|
144
|
+
peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
145
|
+
rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
146
|
+
telemetry?: TelemetryClient;
|
|
147
|
+
log?: Logger;
|
|
148
|
+
blobClient?: BlobClientInterface;
|
|
149
|
+
validatorClient?: ValidatorClient;
|
|
150
|
+
keyStoreManager?: KeystoreManager;
|
|
151
|
+
debugLogStore?: DebugLogStore;
|
|
152
|
+
automineSequencer?: AutomineSequencer;
|
|
153
|
+
// AVM execution backend for public simulation. Wired in production (factory.ts); absent in unit/TXE nodes
|
|
154
|
+
// that don't drive public execution, hence optional and asserted at the simulation call site. Owned by the
|
|
155
|
+
// node (disposed on stop), so it must be disposable — a spawned process pool + CDB IPC server.
|
|
156
|
+
avmSimulator?: AvmSimulator & AsyncDisposable;
|
|
157
|
+
}
|
|
125
158
|
|
|
126
159
|
/**
|
|
127
160
|
* The aztec node.
|
|
128
161
|
*/
|
|
129
162
|
export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDebug, Traceable {
|
|
130
163
|
private metrics: NodeMetrics;
|
|
131
|
-
private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
|
|
132
|
-
|
|
133
164
|
// Prevent two snapshot operations to happen simultaneously
|
|
134
165
|
private isUploadingSnapshot = false;
|
|
166
|
+
// Saved minTxsPerBlock used by `pauseSequencer` to restore production-sequencer config on resume.
|
|
167
|
+
private sequencerPausedMinTxsPerBlock: number | undefined;
|
|
168
|
+
private readonly nodePublicCallsSimulator: NodePublicCallsSimulator;
|
|
169
|
+
private readonly worldStateQueries: NodeWorldStateQueries;
|
|
170
|
+
private readonly blockProvider: NodeBlockProvider;
|
|
171
|
+
private readonly txReceiptBuilder: NodeTxReceiptBuilder;
|
|
135
172
|
|
|
136
173
|
public readonly tracer: Tracer;
|
|
137
174
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
175
|
+
protected config: AztecNodeConfig;
|
|
176
|
+
protected readonly p2pClient: P2P;
|
|
177
|
+
protected readonly blockSource: L2BlockSource & Partial<Service>;
|
|
178
|
+
protected readonly logsSource: L2LogsSource;
|
|
179
|
+
protected readonly contractDataSource: ContractDataSource;
|
|
180
|
+
protected readonly l1ToL2MessageSource: L1ToL2MessageSource;
|
|
181
|
+
protected readonly worldStateSynchronizer: WorldStateSynchronizer;
|
|
182
|
+
protected readonly sequencer: SequencerClient | undefined;
|
|
183
|
+
protected readonly proverNode: ProverNode | undefined;
|
|
184
|
+
protected readonly slasherClient: SlasherClientInterface | undefined;
|
|
185
|
+
protected readonly validatorsSentinel: Sentinel | undefined;
|
|
186
|
+
private readonly stopStartedWatchers: () => Promise<void>;
|
|
187
|
+
protected readonly l1ChainId: number;
|
|
188
|
+
protected readonly version: number;
|
|
189
|
+
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface;
|
|
190
|
+
protected readonly rollupContract: RollupContract | undefined;
|
|
191
|
+
protected readonly feeProvider: FeeProvider;
|
|
192
|
+
protected readonly epochCache: EpochCacheInterface;
|
|
193
|
+
protected readonly packageVersion: string;
|
|
194
|
+
private peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
195
|
+
private rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
196
|
+
private telemetry: TelemetryClient;
|
|
197
|
+
private log: Logger;
|
|
198
|
+
private blobClient?: BlobClientInterface;
|
|
199
|
+
private validatorClient?: ValidatorClient;
|
|
200
|
+
private keyStoreManager?: KeystoreManager;
|
|
201
|
+
private debugLogStore: DebugLogStore;
|
|
202
|
+
private readonly automineSequencer?: AutomineSequencer;
|
|
203
|
+
private readonly avmSimulator?: AvmSimulator & AsyncDisposable;
|
|
204
|
+
|
|
205
|
+
constructor(deps: AztecNodeServiceDeps) {
|
|
206
|
+
this.config = deps.config;
|
|
207
|
+
this.p2pClient = deps.p2pClient;
|
|
208
|
+
this.blockSource = deps.blockSource;
|
|
209
|
+
this.logsSource = deps.logsSource;
|
|
210
|
+
this.contractDataSource = deps.contractDataSource;
|
|
211
|
+
this.l1ToL2MessageSource = deps.l1ToL2MessageSource;
|
|
212
|
+
this.worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
213
|
+
this.sequencer = deps.sequencer;
|
|
214
|
+
this.proverNode = deps.proverNode;
|
|
215
|
+
this.slasherClient = deps.slasherClient;
|
|
216
|
+
this.validatorsSentinel = deps.validatorsSentinel;
|
|
217
|
+
this.stopStartedWatchers = deps.stopStartedWatchers;
|
|
218
|
+
this.l1ChainId = deps.l1ChainId;
|
|
219
|
+
this.version = deps.version;
|
|
220
|
+
this.globalVariableBuilder = deps.globalVariableBuilder;
|
|
221
|
+
this.rollupContract = deps.rollupContract;
|
|
222
|
+
this.feeProvider = deps.feeProvider;
|
|
223
|
+
this.epochCache = deps.epochCache;
|
|
224
|
+
this.packageVersion = deps.packageVersion;
|
|
225
|
+
this.peerProofVerifier = deps.peerProofVerifier;
|
|
226
|
+
this.rpcProofVerifier = deps.rpcProofVerifier;
|
|
227
|
+
this.telemetry = deps.telemetry ?? getTelemetryClient();
|
|
228
|
+
this.log = deps.log ?? createLogger('node');
|
|
229
|
+
this.blobClient = deps.blobClient;
|
|
230
|
+
this.validatorClient = deps.validatorClient;
|
|
231
|
+
this.keyStoreManager = deps.keyStoreManager;
|
|
232
|
+
this.debugLogStore = deps.debugLogStore ?? new NullDebugLogStore();
|
|
233
|
+
this.automineSequencer = deps.automineSequencer;
|
|
234
|
+
this.avmSimulator = deps.avmSimulator;
|
|
235
|
+
|
|
236
|
+
this.metrics = new NodeMetrics(this.telemetry, 'AztecNodeService');
|
|
237
|
+
this.tracer = this.telemetry.getTracer('AztecNodeService');
|
|
238
|
+
|
|
239
|
+
// The node never represents a proposer's payout addresses, so the simulator zeroes coinbase and
|
|
240
|
+
// fee recipient. The signature context only needs chain id + rollup address (see signature_utils).
|
|
241
|
+
this.nodePublicCallsSimulator = new NodePublicCallsSimulator({
|
|
242
|
+
blockSource: this.blockSource,
|
|
243
|
+
worldStateSynchronizer: this.worldStateSynchronizer,
|
|
244
|
+
l1ToL2MessageSource: this.l1ToL2MessageSource,
|
|
245
|
+
contractDataSource: this.contractDataSource,
|
|
246
|
+
globalVariableBuilder: this.globalVariableBuilder,
|
|
247
|
+
rollupContract: this.rollupContract,
|
|
248
|
+
epochCache: this.epochCache,
|
|
249
|
+
signatureContext: { chainId: this.l1ChainId, rollupAddress: this.config.rollupAddress },
|
|
250
|
+
config: this.config,
|
|
251
|
+
avmSimulator: this.avmSimulator,
|
|
252
|
+
telemetry: this.telemetry,
|
|
253
|
+
log: this.log.createChild('public-calls-simulator'),
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
this.worldStateQueries = new NodeWorldStateQueries({
|
|
257
|
+
worldStateSynchronizer: this.worldStateSynchronizer,
|
|
258
|
+
blockSource: this.blockSource,
|
|
259
|
+
l1ToL2MessageSource: this.l1ToL2MessageSource,
|
|
260
|
+
log: this.log.createChild('world-state-queries'),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
this.blockProvider = new NodeBlockProvider(this.blockSource);
|
|
264
|
+
|
|
265
|
+
this.txReceiptBuilder = new NodeTxReceiptBuilder({
|
|
266
|
+
p2pClient: this.p2pClient,
|
|
267
|
+
blockSource: this.blockSource,
|
|
268
|
+
debugLogStore: this.debugLogStore,
|
|
269
|
+
});
|
|
166
270
|
|
|
167
271
|
this.log.info(`Aztec Node version: ${this.packageVersion}`);
|
|
168
|
-
this.log.info(`Aztec Node started on chain 0x${l1ChainId.toString(16)}`, config
|
|
272
|
+
this.log.info(`Aztec Node started on chain 0x${this.l1ChainId.toString(16)}`, pickL1ContractAddresses(this.config));
|
|
169
273
|
|
|
170
|
-
// A defensive check that protects us against introducing a bug in the complex
|
|
274
|
+
// A defensive check that protects us against introducing a bug in the complex node creation flow. We must
|
|
171
275
|
// never have debugLogStore enabled when not in test mode because then we would be accumulating debug logs in
|
|
172
276
|
// memory which could be a DoS vector on the sequencer (since no fees are paid for debug logs).
|
|
173
|
-
if (debugLogStore.isEnabled && config.realProofs) {
|
|
277
|
+
if (this.debugLogStore.isEnabled && this.config.realProofs) {
|
|
174
278
|
throw new Error('debugLogStore should never be enabled when realProofs are set');
|
|
175
279
|
}
|
|
176
280
|
}
|
|
177
281
|
|
|
282
|
+
/** @internal Exposed for testing — returns the RPC proof verifier. */
|
|
283
|
+
public getProofVerifier(): ClientProtocolCircuitVerifier {
|
|
284
|
+
return this.rpcProofVerifier;
|
|
285
|
+
}
|
|
286
|
+
|
|
178
287
|
public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
|
|
179
288
|
const status = await this.worldStateSynchronizer.status();
|
|
180
289
|
return status.syncSummary;
|
|
181
290
|
}
|
|
182
291
|
|
|
183
|
-
public
|
|
292
|
+
public getChainTips(): Promise<L2Tips> {
|
|
184
293
|
return this.blockSource.getL2Tips();
|
|
185
294
|
}
|
|
186
295
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* @returns - A fully synced Aztec Node for use in development/testing.
|
|
191
|
-
*/
|
|
192
|
-
public static async createAndSync(
|
|
193
|
-
inputConfig: AztecNodeConfig,
|
|
194
|
-
deps: {
|
|
195
|
-
telemetry?: TelemetryClient;
|
|
196
|
-
logger?: Logger;
|
|
197
|
-
publisher?: SequencerPublisher;
|
|
198
|
-
dateProvider?: DateProvider;
|
|
199
|
-
p2pClientDeps?: P2PClientDeps;
|
|
200
|
-
proverNodeDeps?: Partial<ProverNodeDeps>;
|
|
201
|
-
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
202
|
-
} = {},
|
|
203
|
-
options: {
|
|
204
|
-
prefilledPublicData?: PublicDataTreeLeaf[];
|
|
205
|
-
dontStartSequencer?: boolean;
|
|
206
|
-
dontStartProverNode?: boolean;
|
|
207
|
-
} = {},
|
|
208
|
-
): Promise<AztecNodeService> {
|
|
209
|
-
const config = { ...inputConfig }; // Copy the config so we dont mutate the input object
|
|
210
|
-
const log = deps.logger ?? createLogger('node');
|
|
211
|
-
const packageVersion = getPackageVersion() ?? '';
|
|
212
|
-
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
213
|
-
const dateProvider = deps.dateProvider ?? new DateProvider();
|
|
214
|
-
const ethereumChain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
215
|
-
|
|
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.
|
|
218
|
-
let keyStoreManager: KeystoreManager | undefined;
|
|
219
|
-
const keyStoreProvided = config.keyStoreDirectory !== undefined && config.keyStoreDirectory.length > 0;
|
|
220
|
-
if (keyStoreProvided) {
|
|
221
|
-
const keyStores = loadKeystores(config.keyStoreDirectory!);
|
|
222
|
-
keyStoreManager = new KeystoreManager(mergeKeystores(keyStores));
|
|
223
|
-
} else {
|
|
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
|
-
);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
await keyStoreManager?.validateSigners();
|
|
243
|
-
|
|
244
|
-
// If we are a validator, verify our configuration before doing too much more.
|
|
245
|
-
if (!config.disableValidator) {
|
|
246
|
-
if (keyStoreManager === undefined) {
|
|
247
|
-
throw new Error('Failed to create key store, a requirement for running a validator');
|
|
248
|
-
}
|
|
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");
|
|
251
|
-
}
|
|
252
|
-
ValidatorClient.validateKeyStoreConfiguration(keyStoreManager, log);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// validate that the actual chain id matches that specified in configuration
|
|
256
|
-
if (config.l1ChainId !== ethereumChain.chainInfo.id) {
|
|
257
|
-
throw new Error(
|
|
258
|
-
`RPC URL configured for chain id ${ethereumChain.chainInfo.id} but expected id ${config.l1ChainId}`,
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const publicClient = createPublicClient({
|
|
263
|
-
chain: ethereumChain.chainInfo,
|
|
264
|
-
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
|
|
265
|
-
pollingInterval: config.viemPollingIntervalMS,
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
const l1ContractsAddresses = await RegistryContract.collectAddresses(
|
|
269
|
-
publicClient,
|
|
270
|
-
config.l1Contracts.registryAddress,
|
|
271
|
-
config.rollupVersion ?? 'canonical',
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
// Overwrite the passed in vars.
|
|
275
|
-
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
|
|
276
|
-
|
|
277
|
-
const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
|
|
278
|
-
const [l1GenesisTime, slotDuration, rollupVersionFromRollup, rollupManaLimit] = await Promise.all([
|
|
279
|
-
rollupContract.getL1GenesisTime(),
|
|
280
|
-
rollupContract.getSlotDuration(),
|
|
281
|
-
rollupContract.getVersion(),
|
|
282
|
-
rollupContract.getManaLimit().then(Number),
|
|
283
|
-
] as const);
|
|
284
|
-
|
|
285
|
-
config.rollupVersion ??= Number(rollupVersionFromRollup);
|
|
286
|
-
|
|
287
|
-
if (config.rollupVersion !== Number(rollupVersionFromRollup)) {
|
|
288
|
-
log.warn(
|
|
289
|
-
`Registry looked up and returned a rollup with version (${config.rollupVersion}), but this does not match with version detected from the rollup directly: (${rollupVersionFromRollup}).`,
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const blobClient = await createBlobClientWithFileStores(config, log.createChild('blob-client'));
|
|
294
|
-
|
|
295
|
-
// attempt snapshot sync if possible
|
|
296
|
-
await trySnapshotSync(config, log);
|
|
297
|
-
|
|
298
|
-
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
299
|
-
|
|
300
|
-
const archiver = await createArchiver(
|
|
301
|
-
config,
|
|
302
|
-
{ blobClient, epochCache, telemetry, dateProvider },
|
|
303
|
-
{ blockUntilSync: !config.skipArchiverInitialSync },
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
// now create the merkle trees and the world state synchronizer
|
|
307
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
308
|
-
config,
|
|
309
|
-
archiver,
|
|
310
|
-
options.prefilledPublicData,
|
|
311
|
-
telemetry,
|
|
312
|
-
);
|
|
313
|
-
const circuitVerifier =
|
|
314
|
-
config.realProofs || config.debugForceTxProofVerification
|
|
315
|
-
? await BBCircuitVerifier.new(config)
|
|
316
|
-
: new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
317
|
-
|
|
318
|
-
let debugLogStore: DebugLogStore;
|
|
319
|
-
if (!config.realProofs) {
|
|
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();
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
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
|
-
|
|
337
|
-
// create the tx pool and the p2p client, which will need the l2 block source
|
|
338
|
-
const p2pClient = await createP2PClient(
|
|
339
|
-
config,
|
|
340
|
-
archiver,
|
|
341
|
-
proofVerifier,
|
|
342
|
-
worldStateSynchronizer,
|
|
343
|
-
epochCache,
|
|
344
|
-
packageVersion,
|
|
345
|
-
dateProvider,
|
|
346
|
-
telemetry,
|
|
347
|
-
deps.p2pClientDeps,
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
// We'll accumulate sentinel watchers here
|
|
351
|
-
const watchers: Watcher[] = [];
|
|
352
|
-
|
|
353
|
-
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
354
|
-
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
355
|
-
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
|
|
356
|
-
{
|
|
357
|
-
...config,
|
|
358
|
-
l1GenesisTime,
|
|
359
|
-
slotDuration: Number(slotDuration),
|
|
360
|
-
rollupManaLimit,
|
|
361
|
-
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
362
|
-
},
|
|
363
|
-
worldStateSynchronizer,
|
|
364
|
-
archiver,
|
|
365
|
-
dateProvider,
|
|
366
|
-
telemetry,
|
|
367
|
-
);
|
|
368
|
-
|
|
369
|
-
let validatorClient: ValidatorClient | undefined;
|
|
370
|
-
|
|
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
|
-
});
|
|
386
|
-
|
|
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
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
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, {
|
|
406
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
407
|
-
worldState: worldStateSynchronizer,
|
|
408
|
-
epochCache,
|
|
409
|
-
blockSource: archiver,
|
|
410
|
-
l1ToL2MessageSource: archiver,
|
|
411
|
-
p2pClient,
|
|
412
|
-
blobClient,
|
|
413
|
-
dateProvider,
|
|
414
|
-
telemetry,
|
|
415
|
-
}).register(p2pClient, reexecute);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Start world state and wait for it to sync to the archiver.
|
|
419
|
-
await worldStateSynchronizer.start();
|
|
296
|
+
public getL1Constants(): Promise<L1RollupConstants> {
|
|
297
|
+
return this.blockSource.getL1Constants();
|
|
298
|
+
}
|
|
420
299
|
|
|
421
|
-
|
|
422
|
-
|
|
300
|
+
public getSyncedL2SlotNumber() {
|
|
301
|
+
return this.blockSource.getSyncedL2SlotNumber();
|
|
302
|
+
}
|
|
423
303
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
304
|
+
public getSyncedL2EpochNumber() {
|
|
305
|
+
return this.blockSource.getSyncedL2EpochNumber();
|
|
306
|
+
}
|
|
427
307
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
watchers.push(validatorsSentinel);
|
|
432
|
-
}
|
|
308
|
+
public getSyncedL1Timestamp() {
|
|
309
|
+
return this.blockSource.getL1Timestamp();
|
|
310
|
+
}
|
|
433
311
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
archiver,
|
|
438
|
-
epochCache,
|
|
439
|
-
p2pClient.getTxProvider(),
|
|
440
|
-
validatorCheckpointsBuilder,
|
|
441
|
-
config,
|
|
442
|
-
);
|
|
443
|
-
watchers.push(epochPruneWatcher);
|
|
444
|
-
}
|
|
312
|
+
public getCheckpointsData(query: CheckpointsQuery) {
|
|
313
|
+
return this.blockSource.getCheckpointsData(query);
|
|
314
|
+
}
|
|
445
315
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
watchers.push(attestationsBlockWatcher);
|
|
450
|
-
}
|
|
316
|
+
public async getBlockNumber(tip?: L2BlockTag): Promise<BlockNumber> {
|
|
317
|
+
if (tip === undefined || tip === 'proposed') {
|
|
318
|
+
return this.blockSource.getBlockNumber();
|
|
451
319
|
}
|
|
320
|
+
return (await this.blockSource.getBlockNumber({ tag: tip })) ?? BlockNumber.ZERO;
|
|
321
|
+
}
|
|
452
322
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
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
|
-
|
|
473
|
-
// Validator enabled, create/start relevant service
|
|
474
|
-
let sequencer: SequencerClient | undefined;
|
|
475
|
-
let slasherClient: SlasherClientInterface | undefined;
|
|
476
|
-
if (!config.disableValidator && validatorClient) {
|
|
477
|
-
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
478
|
-
// as they are executed when the node is selected as proposer.
|
|
479
|
-
const validatorAddresses = keyStoreManager
|
|
480
|
-
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
481
|
-
: [];
|
|
482
|
-
|
|
483
|
-
slasherClient = await createSlasher(
|
|
484
|
-
config,
|
|
485
|
-
config.l1Contracts,
|
|
486
|
-
getPublicClient(config),
|
|
487
|
-
watchers,
|
|
488
|
-
dateProvider,
|
|
489
|
-
epochCache,
|
|
490
|
-
validatorAddresses,
|
|
491
|
-
undefined, // logger
|
|
492
|
-
);
|
|
493
|
-
await slasherClient.start();
|
|
494
|
-
|
|
495
|
-
const l1TxUtils = config.sequencerPublisherForwarderAddress
|
|
496
|
-
? await createForwarderL1TxUtilsFromSigners(
|
|
497
|
-
publicClient,
|
|
498
|
-
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
499
|
-
config.sequencerPublisherForwarderAddress,
|
|
500
|
-
{ ...config, scope: 'sequencer' },
|
|
501
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
502
|
-
)
|
|
503
|
-
: await createL1TxUtilsFromSigners(
|
|
504
|
-
publicClient,
|
|
505
|
-
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
506
|
-
{ ...config, scope: 'sequencer' },
|
|
507
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
508
|
-
);
|
|
509
|
-
|
|
510
|
-
// Create and start the sequencer client
|
|
511
|
-
const checkpointsBuilder = new CheckpointsBuilder(
|
|
512
|
-
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
513
|
-
worldStateSynchronizer,
|
|
514
|
-
archiver,
|
|
515
|
-
dateProvider,
|
|
516
|
-
telemetry,
|
|
517
|
-
debugLogStore,
|
|
518
|
-
);
|
|
519
|
-
|
|
520
|
-
sequencer = await SequencerClient.new(config, {
|
|
521
|
-
...deps,
|
|
522
|
-
epochCache,
|
|
523
|
-
l1TxUtils,
|
|
524
|
-
validatorClient,
|
|
525
|
-
p2pClient,
|
|
526
|
-
worldStateSynchronizer,
|
|
527
|
-
slasherClient,
|
|
528
|
-
checkpointsBuilder,
|
|
529
|
-
l2BlockSource: archiver,
|
|
530
|
-
l1ToL2MessageSource: archiver,
|
|
531
|
-
telemetry,
|
|
532
|
-
dateProvider,
|
|
533
|
-
blobClient,
|
|
534
|
-
nodeKeyStore: keyStoreManager!,
|
|
535
|
-
globalVariableBuilder,
|
|
536
|
-
});
|
|
323
|
+
public async getCheckpointNumber(tip?: CheckpointTag): Promise<CheckpointNumber> {
|
|
324
|
+
const tips = await this.blockSource.getL2Tips();
|
|
325
|
+
switch (tip) {
|
|
326
|
+
case undefined:
|
|
327
|
+
case 'checkpointed':
|
|
328
|
+
return tips.checkpointed.checkpoint.number;
|
|
329
|
+
case 'proven':
|
|
330
|
+
return tips.proven.checkpoint.number;
|
|
331
|
+
case 'finalized':
|
|
332
|
+
return tips.finalized.checkpoint.number;
|
|
537
333
|
}
|
|
334
|
+
}
|
|
538
335
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
336
|
+
public getBlock<Opts extends BlockIncludeOptions = {}>(
|
|
337
|
+
param: BlockParameter,
|
|
338
|
+
options: Opts = {} as Opts,
|
|
339
|
+
): Promise<BlockResponse<Opts> | undefined> {
|
|
340
|
+
return this.blockProvider.getBlock(param, options);
|
|
341
|
+
}
|
|
545
342
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
});
|
|
343
|
+
public getBlockData(param: BlockParameter): Promise<BlockData | undefined> {
|
|
344
|
+
return this.blockProvider.getBlockData(param);
|
|
345
|
+
}
|
|
560
346
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
347
|
+
public getBlocks<Opts extends BlocksIncludeOptions = {}>(
|
|
348
|
+
from: BlockNumber,
|
|
349
|
+
limit: number,
|
|
350
|
+
options: Opts = {} as Opts,
|
|
351
|
+
): Promise<BlockResponse<Opts>[]> {
|
|
352
|
+
return this.blockProvider.getBlocks(from, limit, options);
|
|
353
|
+
}
|
|
568
354
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
archiver,
|
|
576
|
-
worldStateSynchronizer,
|
|
577
|
-
sequencer,
|
|
578
|
-
proverNode,
|
|
579
|
-
slasherClient,
|
|
580
|
-
validatorsSentinel,
|
|
581
|
-
epochPruneWatcher,
|
|
582
|
-
ethereumChain.chainInfo.id,
|
|
583
|
-
config.rollupVersion,
|
|
584
|
-
globalVariableBuilder,
|
|
585
|
-
epochCache,
|
|
586
|
-
packageVersion,
|
|
587
|
-
proofVerifier,
|
|
588
|
-
telemetry,
|
|
589
|
-
log,
|
|
590
|
-
blobClient,
|
|
591
|
-
validatorClient,
|
|
592
|
-
keyStoreManager,
|
|
593
|
-
debugLogStore,
|
|
594
|
-
);
|
|
355
|
+
public getCheckpoint<Opts extends CheckpointIncludeOptions = {}>(
|
|
356
|
+
param: CheckpointParameter,
|
|
357
|
+
options: Opts = {} as Opts,
|
|
358
|
+
): Promise<CheckpointResponse<Opts> | undefined> {
|
|
359
|
+
return this.blockProvider.getCheckpoint(param, options);
|
|
360
|
+
}
|
|
595
361
|
|
|
596
|
-
|
|
362
|
+
public getCheckpoints<Opts extends CheckpointIncludeOptions = {}>(
|
|
363
|
+
from: CheckpointNumber,
|
|
364
|
+
limit: number,
|
|
365
|
+
options: Opts = {} as Opts,
|
|
366
|
+
): Promise<CheckpointResponse<Opts>[]> {
|
|
367
|
+
return this.blockProvider.getCheckpoints(from, limit, options);
|
|
597
368
|
}
|
|
598
369
|
|
|
599
370
|
/**
|
|
@@ -604,6 +375,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
604
375
|
return this.sequencer;
|
|
605
376
|
}
|
|
606
377
|
|
|
378
|
+
/** Test-only: returns the AutomineSequencer when wired via `useAutomineSequencer`. */
|
|
379
|
+
public getAutomineSequencer(): AutomineSequencer | undefined {
|
|
380
|
+
return this.automineSequencer;
|
|
381
|
+
}
|
|
382
|
+
|
|
607
383
|
/** Returns the prover node subsystem, if enabled. */
|
|
608
384
|
public getProverNode(): ProverNode | undefined {
|
|
609
385
|
return this.proverNode;
|
|
@@ -626,7 +402,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
626
402
|
* @returns - The currently deployed L1 contract addresses.
|
|
627
403
|
*/
|
|
628
404
|
public getL1ContractAddresses(): Promise<L1ContractAddresses> {
|
|
629
|
-
return Promise.resolve(this.config
|
|
405
|
+
return Promise.resolve(pickL1ContractAddresses(this.config));
|
|
630
406
|
}
|
|
631
407
|
|
|
632
408
|
public getEncodedEnr(): Promise<string | undefined> {
|
|
@@ -646,14 +422,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
646
422
|
}
|
|
647
423
|
|
|
648
424
|
public async getNodeInfo(): Promise<NodeInfo> {
|
|
649
|
-
const [nodeVersion, rollupVersion, chainId, enr, contractAddresses, protocolContractAddresses] =
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
425
|
+
const [nodeVersion, rollupVersion, chainId, enr, contractAddresses, protocolContractAddresses, l1Constants] =
|
|
426
|
+
await Promise.all([
|
|
427
|
+
this.getNodeVersion(),
|
|
428
|
+
this.getVersion(),
|
|
429
|
+
this.getChainId(),
|
|
430
|
+
this.getEncodedEnr(),
|
|
431
|
+
this.getL1ContractAddresses(),
|
|
432
|
+
this.getProtocolContractAddresses(),
|
|
433
|
+
this.blockSource.getL1Constants(),
|
|
434
|
+
]);
|
|
435
|
+
|
|
436
|
+
// Gas limits a single tx may declare on this network, derived from network-wide constants only (the
|
|
437
|
+
// timetable's blocks-per-checkpoint and the network-minimum per-block multipliers) — never this node's
|
|
438
|
+
// local caps or configured multipliers, which can make the node stricter at block-building time but
|
|
439
|
+
// cannot define what the network accepts for relay. Clients read txsLimits to set fallback gas limits.
|
|
440
|
+
const maxTxGas = getNetworkTxGasLimits(this.config, l1Constants);
|
|
657
441
|
|
|
658
442
|
const nodeInfo: NodeInfo = {
|
|
659
443
|
nodeVersion,
|
|
@@ -663,112 +447,29 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
663
447
|
l1ContractAddresses: contractAddresses,
|
|
664
448
|
protocolContractAddresses: protocolContractAddresses,
|
|
665
449
|
realProofs: !!this.config.realProofs,
|
|
450
|
+
txsLimits: { gas: { daGas: maxTxGas.daGas, l2Gas: maxTxGas.l2Gas } },
|
|
666
451
|
};
|
|
667
452
|
|
|
668
453
|
return nodeInfo;
|
|
669
454
|
}
|
|
670
455
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
* @param block - The block parameter (block number, block hash, or 'latest').
|
|
674
|
-
* @returns The requested block.
|
|
675
|
-
*/
|
|
676
|
-
public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
|
|
677
|
-
if (BlockHash.isBlockHash(block)) {
|
|
678
|
-
return this.getBlockByHash(block);
|
|
679
|
-
}
|
|
680
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
681
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
682
|
-
return this.buildInitialBlock();
|
|
683
|
-
}
|
|
684
|
-
return await this.blockSource.getL2Block(blockNumber);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/**
|
|
688
|
-
* Get a block specified by its hash.
|
|
689
|
-
* @param blockHash - The block hash being requested.
|
|
690
|
-
* @returns The requested block.
|
|
691
|
-
*/
|
|
692
|
-
public async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
693
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
694
|
-
if (blockHash.equals(initialBlockHash)) {
|
|
695
|
-
return this.buildInitialBlock();
|
|
696
|
-
}
|
|
697
|
-
return await this.blockSource.getL2BlockByHash(blockHash);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
private buildInitialBlock(): L2Block {
|
|
701
|
-
const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
702
|
-
return L2Block.empty(initialHeader);
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Get a block specified by its archive root.
|
|
707
|
-
* @param archive - The archive root being requested.
|
|
708
|
-
* @returns The requested block.
|
|
709
|
-
*/
|
|
710
|
-
public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
711
|
-
return await this.blockSource.getL2BlockByArchive(archive);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
716
|
-
* @param from - The start of the range of blocks to return.
|
|
717
|
-
* @param limit - The maximum number of blocks to obtain.
|
|
718
|
-
* @returns The blocks requested.
|
|
719
|
-
*/
|
|
720
|
-
public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
721
|
-
return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
|
|
725
|
-
return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
|
|
729
|
-
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
456
|
+
public async getCurrentMinFees(): Promise<GasFees> {
|
|
457
|
+
return await this.feeProvider.getCurrentMinFees();
|
|
730
458
|
}
|
|
731
459
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* Method to fetch the current min L2 fees.
|
|
738
|
-
* @returns The current min L2 fees.
|
|
739
|
-
*/
|
|
740
|
-
public async getCurrentMinFees(): Promise<GasFees> {
|
|
741
|
-
return await this.globalVariableBuilder.getCurrentMinFees();
|
|
460
|
+
/** Returns predicted min fees for the current slot and next N slots. */
|
|
461
|
+
public async getPredictedMinFees(manaUsage?: ManaUsageEstimate): Promise<GasFees[]> {
|
|
462
|
+
return await this.feeProvider.getPredictedMinFees(manaUsage);
|
|
742
463
|
}
|
|
743
464
|
|
|
744
465
|
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
745
|
-
for await (const tx of this.p2pClient.iteratePendingTxs()) {
|
|
466
|
+
for await (const tx of this.p2pClient.iteratePendingTxs({ includeProof: false })) {
|
|
746
467
|
return tx.getGasSettings().maxPriorityFeesPerGas;
|
|
747
468
|
}
|
|
748
469
|
|
|
749
470
|
return GasFees.from({ feePerDaGas: 0n, feePerL2Gas: 0n });
|
|
750
471
|
}
|
|
751
472
|
|
|
752
|
-
/**
|
|
753
|
-
* Method to fetch the latest block number synchronized by the node.
|
|
754
|
-
* @returns The block number.
|
|
755
|
-
*/
|
|
756
|
-
public async getBlockNumber(): Promise<BlockNumber> {
|
|
757
|
-
return await this.blockSource.getBlockNumber();
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
public async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
761
|
-
return await this.blockSource.getProvenBlockNumber();
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
765
|
-
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
769
|
-
return this.blockSource.getCheckpointNumber();
|
|
770
|
-
}
|
|
771
|
-
|
|
772
473
|
/**
|
|
773
474
|
* Method to fetch the version of the package.
|
|
774
475
|
* @returns The node package version
|
|
@@ -797,65 +498,25 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
797
498
|
return this.contractDataSource.getContractClass(id);
|
|
798
499
|
}
|
|
799
500
|
|
|
800
|
-
public getContract(
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
if (referenceBlock) {
|
|
810
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
811
|
-
if (!referenceBlock.equals(initialBlockHash)) {
|
|
812
|
-
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
813
|
-
if (!header) {
|
|
814
|
-
throw new Error(
|
|
815
|
-
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
816
|
-
);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
return this.logsSource.getPrivateLogsByTags(tags, page);
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
public async getPublicLogsByTagsFromContract(
|
|
824
|
-
contractAddress: AztecAddress,
|
|
825
|
-
tags: Tag[],
|
|
826
|
-
page?: number,
|
|
827
|
-
referenceBlock?: BlockHash,
|
|
828
|
-
): Promise<TxScopedL2Log[][]> {
|
|
829
|
-
if (referenceBlock) {
|
|
830
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
831
|
-
if (!referenceBlock.equals(initialBlockHash)) {
|
|
832
|
-
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
833
|
-
if (!header) {
|
|
834
|
-
throw new Error(
|
|
835
|
-
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
836
|
-
);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
501
|
+
public async getContract(
|
|
502
|
+
address: AztecAddress,
|
|
503
|
+
referenceBlock: BlockParameter = 'latest',
|
|
504
|
+
): Promise<ContractInstanceWithAddress | undefined> {
|
|
505
|
+
const blockData = await this.getBlockData(referenceBlock);
|
|
506
|
+
if (!blockData) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
`Reference block ${inspectBlockParameter(referenceBlock)} not found when querying contract ${address}. If the node API has been queried with an anchor block hash, possibly a reorg has occurred.`,
|
|
509
|
+
);
|
|
839
510
|
}
|
|
840
|
-
return this.
|
|
511
|
+
return this.contractDataSource.getContract(address, blockData.header.globalVariables.timestamp);
|
|
841
512
|
}
|
|
842
513
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
* @param filter - The filter to apply to the logs.
|
|
846
|
-
* @returns The requested logs.
|
|
847
|
-
*/
|
|
848
|
-
getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
849
|
-
return this.logsSource.getPublicLogs(filter);
|
|
514
|
+
public getPrivateLogsByTags(query: PrivateLogsQuery): Promise<LogResult[][]> {
|
|
515
|
+
return this.logsSource.getPrivateLogsByTags(query);
|
|
850
516
|
}
|
|
851
517
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
* @param filter - The filter to apply to the logs.
|
|
855
|
-
* @returns The requested logs.
|
|
856
|
-
*/
|
|
857
|
-
getContractClassLogs(filter: LogFilter): Promise<GetContractClassLogsResponse> {
|
|
858
|
-
return this.logsSource.getContractClassLogs(filter);
|
|
518
|
+
public getPublicLogsByTags(query: PublicLogsQuery): Promise<LogResult[][]> {
|
|
519
|
+
return this.logsSource.getPublicLogsByTags(query);
|
|
859
520
|
}
|
|
860
521
|
|
|
861
522
|
/**
|
|
@@ -878,37 +539,23 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
878
539
|
throw new Error(`Invalid tx: ${reason}`);
|
|
879
540
|
}
|
|
880
541
|
|
|
881
|
-
|
|
542
|
+
try {
|
|
543
|
+
await this.p2pClient!.sendTx(tx);
|
|
544
|
+
} catch (err) {
|
|
545
|
+
this.metrics.receivedTx(timer.ms(), false);
|
|
546
|
+
this.log.warn(`Mempool rejected tx ${txHash}: ${(err as Error).message}`, { txHash });
|
|
547
|
+
throw err;
|
|
548
|
+
}
|
|
882
549
|
const duration = timer.ms();
|
|
883
550
|
this.metrics.receivedTx(duration, true);
|
|
884
551
|
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
885
552
|
}
|
|
886
553
|
|
|
887
|
-
public
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
// Then get the actual tx from the archiver, which tracks every tx in a mined block.
|
|
894
|
-
const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
|
|
895
|
-
|
|
896
|
-
let receipt: TxReceipt;
|
|
897
|
-
if (settledTxReceipt) {
|
|
898
|
-
receipt = settledTxReceipt;
|
|
899
|
-
} else if (isKnownToPool) {
|
|
900
|
-
// If the tx is in the pool but not in the archiver, it's pending.
|
|
901
|
-
// This handles race conditions between archiver and p2p, where the archiver
|
|
902
|
-
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
903
|
-
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
904
|
-
} else {
|
|
905
|
-
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
906
|
-
receipt = new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
910
|
-
|
|
911
|
-
return receipt;
|
|
554
|
+
public getTxReceipt<TGetTxReceiptOptions extends GetTxReceiptOptions = {}>(
|
|
555
|
+
txHash: TxHash,
|
|
556
|
+
options?: TGetTxReceiptOptions,
|
|
557
|
+
): Promise<TxReceipt<TGetTxReceiptOptions>> {
|
|
558
|
+
return this.txReceiptBuilder.getTxReceipt(txHash, options);
|
|
912
559
|
}
|
|
913
560
|
|
|
914
561
|
public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
@@ -920,13 +567,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
920
567
|
*/
|
|
921
568
|
public async stop() {
|
|
922
569
|
this.log.info(`Stopping Aztec Node`);
|
|
923
|
-
await
|
|
924
|
-
await tryStop(this.epochPruneWatcher);
|
|
570
|
+
await this.stopStartedWatchers();
|
|
925
571
|
await tryStop(this.slasherClient);
|
|
926
|
-
await tryStop(this.
|
|
572
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
927
573
|
await tryStop(this.sequencer);
|
|
574
|
+
await tryStop(this.automineSequencer);
|
|
928
575
|
await tryStop(this.proverNode);
|
|
929
576
|
await tryStop(this.p2pClient);
|
|
577
|
+
// Dispose the AVM backend before world state: it kills the bb-avm-sim processes and closes the CDB IPC
|
|
578
|
+
// server, releasing their connections to the WSDB so it shuts down cleanly (and freeing the
|
|
579
|
+
// Server/Socket/ChildProcess handles that would otherwise keep the process alive after teardown).
|
|
580
|
+
await this.avmSimulator?.[Symbol.asyncDispose]();
|
|
930
581
|
await tryStop(this.worldStateSynchronizer);
|
|
931
582
|
await tryStop(this.blockSource);
|
|
932
583
|
await tryStop(this.blobClient);
|
|
@@ -948,374 +599,146 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
948
599
|
* @param after - The last known pending tx. Used for pagination
|
|
949
600
|
* @returns - The pending txs.
|
|
950
601
|
*/
|
|
951
|
-
public getPendingTxs(limit?: number, after?: TxHash): Promise<Tx[]> {
|
|
952
|
-
return this.p2pClient!.getPendingTxs(limit, after);
|
|
602
|
+
public getPendingTxs(limit?: number, after?: TxHash, options?: GetTxByHashOptions): Promise<Tx[]> {
|
|
603
|
+
return this.p2pClient!.getPendingTxs(limit, after, options);
|
|
953
604
|
}
|
|
954
605
|
|
|
955
606
|
public getPendingTxCount(): Promise<number> {
|
|
956
607
|
return this.p2pClient!.getPendingTxCount();
|
|
957
608
|
}
|
|
958
609
|
|
|
610
|
+
public getPeers(includePending?: boolean): Promise<PeerInfo[]> {
|
|
611
|
+
return this.p2pClient!.getPeers(includePending);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
public getCheckpointAttestationsForSlot(
|
|
615
|
+
slot: SlotNumber,
|
|
616
|
+
proposalPayloadHash?: CheckpointProposalHash,
|
|
617
|
+
): Promise<CheckpointAttestation[]> {
|
|
618
|
+
return this.p2pClient!.getCheckpointAttestationsForSlot(slot, proposalPayloadHash);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
public getProposalsForSlot(slot: SlotNumber): Promise<ProposalsForSlot> {
|
|
622
|
+
return this.p2pClient!.getProposalsForSlot(slot);
|
|
623
|
+
}
|
|
624
|
+
|
|
959
625
|
/**
|
|
960
|
-
* Method to retrieve a single tx from the mempool or unfinalized chain.
|
|
626
|
+
* Method to retrieve a single tx from the mempool or unfinalized chain. The tx's proof is only loaded and returned
|
|
627
|
+
* when `includeProof` is set.
|
|
961
628
|
* @param txHash - The transaction hash to return.
|
|
629
|
+
* @param options - Options for the returned tx (eg whether to include its proof).
|
|
962
630
|
* @returns - The tx if it exists.
|
|
963
631
|
*/
|
|
964
|
-
public getTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
965
|
-
return
|
|
632
|
+
public getTxByHash(txHash: TxHash, options?: GetTxByHashOptions): Promise<Tx | undefined> {
|
|
633
|
+
return this.p2pClient!.getTxByHashFromPool(txHash, { includeProof: !!options?.includeProof });
|
|
966
634
|
}
|
|
967
635
|
|
|
968
636
|
/**
|
|
969
|
-
* Method to retrieve txs from the mempool or unfinalized chain.
|
|
637
|
+
* Method to retrieve txs from the mempool or unfinalized chain. The txs' proofs are only loaded and returned when
|
|
638
|
+
* `includeProof` is set.
|
|
970
639
|
* @param txHash - The transaction hash to return.
|
|
640
|
+
* @param options - Options for the returned txs (eg whether to include their proofs).
|
|
971
641
|
* @returns - The txs if it exists.
|
|
972
642
|
*/
|
|
973
|
-
public async getTxsByHash(txHashes: TxHash[]): Promise<Tx[]> {
|
|
974
|
-
|
|
643
|
+
public async getTxsByHash(txHashes: TxHash[], options?: GetTxByHashOptions): Promise<Tx[]> {
|
|
644
|
+
const txs = await this.p2pClient!.getTxsByHashFromPool(txHashes, { includeProof: !!options?.includeProof });
|
|
645
|
+
return compactArray(txs);
|
|
975
646
|
}
|
|
976
647
|
|
|
977
|
-
public
|
|
648
|
+
public findLeavesIndexes(
|
|
978
649
|
referenceBlock: BlockParameter,
|
|
979
650
|
treeId: MerkleTreeId,
|
|
980
651
|
leafValues: Fr[],
|
|
981
652
|
): Promise<(DataInBlock<bigint> | undefined)[]> {
|
|
982
|
-
|
|
983
|
-
const maybeIndices = await committedDb.findLeafIndices(
|
|
984
|
-
treeId,
|
|
985
|
-
leafValues.map(x => x.toBuffer()),
|
|
986
|
-
);
|
|
987
|
-
// Filter out undefined values to query block numbers only for found leaves
|
|
988
|
-
const definedIndices = maybeIndices.filter(x => x !== undefined);
|
|
989
|
-
|
|
990
|
-
// Now we find the block numbers for the defined indices
|
|
991
|
-
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, definedIndices);
|
|
992
|
-
|
|
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
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
indexToBlockNumber.set(definedIndices[i], blockNumber);
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
// Get unique block numbers in order to optimize num calls to getLeafValue function.
|
|
1006
|
-
const uniqueBlockNumbers = [...new Set(indexToBlockNumber.values())];
|
|
1007
|
-
|
|
1008
|
-
// Now we obtain the block hashes from the archive tree (block number = leaf index in archive tree).
|
|
1009
|
-
const blockHashes = await Promise.all(
|
|
1010
|
-
uniqueBlockNumbers.map(blockNumber => {
|
|
1011
|
-
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1012
|
-
}),
|
|
1013
|
-
);
|
|
1014
|
-
|
|
1015
|
-
// Build a map from block number to block hash
|
|
1016
|
-
const blockNumberToHash = new Map<BlockNumber, Fr>();
|
|
1017
|
-
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
1018
|
-
const blockHash = blockHashes[i];
|
|
1019
|
-
if (blockHash === undefined) {
|
|
1020
|
-
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
1021
|
-
}
|
|
1022
|
-
blockNumberToHash.set(uniqueBlockNumbers[i], blockHash);
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
1026
|
-
return maybeIndices.map(index => {
|
|
1027
|
-
if (index === undefined) {
|
|
1028
|
-
return undefined;
|
|
1029
|
-
}
|
|
1030
|
-
const blockNumber = indexToBlockNumber.get(index);
|
|
1031
|
-
if (blockNumber === undefined) {
|
|
1032
|
-
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1033
|
-
}
|
|
1034
|
-
const blockHash = blockNumberToHash.get(blockNumber);
|
|
1035
|
-
if (blockHash === undefined) {
|
|
1036
|
-
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1037
|
-
}
|
|
1038
|
-
return {
|
|
1039
|
-
l2BlockNumber: blockNumber,
|
|
1040
|
-
l2BlockHash: new BlockHash(blockHash),
|
|
1041
|
-
data: index,
|
|
1042
|
-
};
|
|
1043
|
-
});
|
|
653
|
+
return this.worldStateQueries.findLeavesIndexes(referenceBlock, treeId, leafValues);
|
|
1044
654
|
}
|
|
1045
655
|
|
|
1046
|
-
public
|
|
656
|
+
public getBlockHashMembershipWitness(
|
|
1047
657
|
referenceBlock: BlockParameter,
|
|
1048
658
|
blockHash: BlockHash,
|
|
1049
659
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
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]);
|
|
1066
|
-
return pathAndIndex === undefined
|
|
1067
|
-
? undefined
|
|
1068
|
-
: MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
public async getNoteHashMembershipWitness(
|
|
660
|
+
return this.worldStateQueries.getBlockHashMembershipWitness(referenceBlock, blockHash);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
public getNoteHashMembershipWitness(
|
|
1072
664
|
referenceBlock: BlockParameter,
|
|
1073
665
|
noteHash: Fr,
|
|
1074
666
|
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
1075
|
-
|
|
1076
|
-
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
|
|
1077
|
-
MerkleTreeId.NOTE_HASH_TREE,
|
|
1078
|
-
[noteHash],
|
|
1079
|
-
);
|
|
1080
|
-
return pathAndIndex === undefined
|
|
1081
|
-
? undefined
|
|
1082
|
-
: MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
667
|
+
return this.worldStateQueries.getNoteHashMembershipWitness(referenceBlock, noteHash);
|
|
1083
668
|
}
|
|
1084
669
|
|
|
1085
|
-
public
|
|
670
|
+
public getL1ToL2MessageMembershipWitness(
|
|
1086
671
|
referenceBlock: BlockParameter,
|
|
1087
672
|
l1ToL2Message: Fr,
|
|
1088
673
|
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
|
|
1089
|
-
|
|
1090
|
-
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
|
|
1091
|
-
if (!witness) {
|
|
1092
|
-
return undefined;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// REFACTOR: Return a MembershipWitness object
|
|
1096
|
-
return [witness.index, witness.path];
|
|
674
|
+
return this.worldStateQueries.getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message);
|
|
1097
675
|
}
|
|
1098
676
|
|
|
1099
|
-
public
|
|
1100
|
-
|
|
1101
|
-
return messageIndex ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined;
|
|
677
|
+
public getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise<CheckpointNumber | undefined> {
|
|
678
|
+
return this.worldStateQueries.getL1ToL2MessageCheckpoint(l1ToL2Message);
|
|
1102
679
|
}
|
|
1103
680
|
|
|
1104
681
|
/**
|
|
1105
|
-
* Returns
|
|
1106
|
-
*
|
|
1107
|
-
* @
|
|
682
|
+
* Returns all the L2 to L1 messages in an epoch.
|
|
683
|
+
*
|
|
684
|
+
* @deprecated Use {@link getL2ToL1MembershipWitness} to get an L2-to-L1 message witness directly.
|
|
685
|
+
*
|
|
686
|
+
* @param epoch - The epoch at which to get the data.
|
|
687
|
+
* @returns The L2 to L1 messages (empty array if the epoch is not found).
|
|
1108
688
|
*/
|
|
1109
|
-
public
|
|
1110
|
-
|
|
1111
|
-
return messageIndex !== undefined;
|
|
689
|
+
public getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
|
|
690
|
+
return this.worldStateQueries.getL2ToL1Messages(epoch);
|
|
1112
691
|
}
|
|
1113
692
|
|
|
1114
693
|
/**
|
|
1115
|
-
* Returns
|
|
1116
|
-
*
|
|
1117
|
-
* @returns The L2 to L1 messages (empty array if the epoch is not found).
|
|
694
|
+
* Returns the L2-to-L1 membership witness for a message in `txHash`. Passthrough to the
|
|
695
|
+
* archiver's locally-cached resolver — see {@link Archiver.getL2ToL1MembershipWitness}.
|
|
1118
696
|
*/
|
|
1119
|
-
public
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
);
|
|
1125
|
-
return blocksInCheckpoints.map(blocks =>
|
|
1126
|
-
blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
|
|
1127
|
-
);
|
|
697
|
+
public getL2ToL1MembershipWitness(
|
|
698
|
+
txHash: TxHash,
|
|
699
|
+
message: Fr,
|
|
700
|
+
messageIndexInTx?: number,
|
|
701
|
+
): Promise<L2ToL1MembershipWitness | undefined> {
|
|
702
|
+
return this.worldStateQueries.getL2ToL1MembershipWitness(txHash, message, messageIndexInTx);
|
|
1128
703
|
}
|
|
1129
704
|
|
|
1130
|
-
public
|
|
705
|
+
public getNullifierMembershipWitness(
|
|
1131
706
|
referenceBlock: BlockParameter,
|
|
1132
707
|
nullifier: Fr,
|
|
1133
708
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1134
|
-
|
|
1135
|
-
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
|
|
1136
|
-
if (!witness) {
|
|
1137
|
-
return undefined;
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
const { index, path } = witness;
|
|
1141
|
-
const leafPreimage = await db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
|
|
1142
|
-
if (!leafPreimage) {
|
|
1143
|
-
return undefined;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path);
|
|
709
|
+
return this.worldStateQueries.getNullifierMembershipWitness(referenceBlock, nullifier);
|
|
1147
710
|
}
|
|
1148
711
|
|
|
1149
|
-
public
|
|
712
|
+
public getLowNullifierMembershipWitness(
|
|
1150
713
|
referenceBlock: BlockParameter,
|
|
1151
714
|
nullifier: Fr,
|
|
1152
715
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1153
|
-
|
|
1154
|
-
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1155
|
-
if (!findResult) {
|
|
1156
|
-
return undefined;
|
|
1157
|
-
}
|
|
1158
|
-
const { index, alreadyPresent } = findResult;
|
|
1159
|
-
if (alreadyPresent) {
|
|
1160
|
-
throw new Error(
|
|
1161
|
-
`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`,
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
|
-
const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
|
|
1165
|
-
|
|
1166
|
-
const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
|
|
1167
|
-
return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
|
|
716
|
+
return this.worldStateQueries.getLowNullifierMembershipWitness(referenceBlock, nullifier);
|
|
1168
717
|
}
|
|
1169
718
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1173
|
-
if (!lowLeafResult) {
|
|
1174
|
-
return undefined;
|
|
1175
|
-
} else {
|
|
1176
|
-
const preimage = (await committedDb.getLeafPreimage(
|
|
1177
|
-
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
1178
|
-
lowLeafResult.index,
|
|
1179
|
-
)) as PublicDataTreeLeafPreimage;
|
|
1180
|
-
const path = await committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
|
|
1181
|
-
return new PublicDataWitness(lowLeafResult.index, preimage, path);
|
|
1182
|
-
}
|
|
719
|
+
public getPublicDataWitness(referenceBlock: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
720
|
+
return this.worldStateQueries.getPublicDataWitness(referenceBlock, leafSlot);
|
|
1183
721
|
}
|
|
1184
722
|
|
|
1185
|
-
public
|
|
1186
|
-
|
|
1187
|
-
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1188
|
-
|
|
1189
|
-
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1190
|
-
if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
|
|
1191
|
-
return Fr.ZERO;
|
|
1192
|
-
}
|
|
1193
|
-
const preimage = (await committedDb.getLeafPreimage(
|
|
1194
|
-
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
1195
|
-
lowLeafResult.index,
|
|
1196
|
-
)) as PublicDataTreeLeafPreimage;
|
|
1197
|
-
return preimage.leaf.value;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
|
|
1201
|
-
if (BlockHash.isBlockHash(block)) {
|
|
1202
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1203
|
-
if (block.equals(initialBlockHash)) {
|
|
1204
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1205
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1206
|
-
}
|
|
1207
|
-
return this.blockSource.getBlockHeaderByHash(block);
|
|
1208
|
-
} else {
|
|
1209
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1210
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
1211
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
1212
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1213
|
-
}
|
|
1214
|
-
return this.blockSource.getBlockHeader(block);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
/**
|
|
1219
|
-
* Get a block header specified by its archive root.
|
|
1220
|
-
* @param archive - The archive root being requested.
|
|
1221
|
-
* @returns The requested block header.
|
|
1222
|
-
*/
|
|
1223
|
-
public async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1224
|
-
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1225
|
-
}
|
|
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);
|
|
723
|
+
public getPublicStorageAt(referenceBlock: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
724
|
+
return this.worldStateQueries.getPublicStorageAt(referenceBlock, contract, slot);
|
|
1233
725
|
}
|
|
1234
726
|
|
|
1235
727
|
/**
|
|
1236
728
|
* Simulates the public part of a transaction with the current state.
|
|
1237
729
|
* @param tx - The transaction to simulate.
|
|
730
|
+
* @param skipFeeEnforcement - If true, fee enforcement is skipped.
|
|
731
|
+
* @param overrides - Optional pre-simulation overrides applied to the ephemeral fork and contract DB.
|
|
1238
732
|
**/
|
|
1239
733
|
@trackSpan('AztecNodeService.simulatePublicCalls', (tx: Tx) => ({
|
|
1240
734
|
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
1241
735
|
}))
|
|
1242
|
-
public
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
throw new BadRequestError(
|
|
1249
|
-
`Transaction total gas limit ${
|
|
1250
|
-
txGasLimit + teardownGasLimit
|
|
1251
|
-
} (${txGasLimit} + ${teardownGasLimit}) exceeds maximum gas limit ${
|
|
1252
|
-
this.config.rpcSimulatePublicMaxGasLimit
|
|
1253
|
-
} for simulation`,
|
|
1254
|
-
);
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
const txHash = tx.getTxHash();
|
|
1258
|
-
const latestBlockNumber = await this.blockSource.getBlockNumber();
|
|
1259
|
-
const blockNumber = BlockNumber.add(latestBlockNumber, 1);
|
|
1260
|
-
|
|
1261
|
-
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
1262
|
-
const coinbase = EthAddress.ZERO;
|
|
1263
|
-
const feeRecipient = AztecAddress.ZERO;
|
|
1264
|
-
|
|
1265
|
-
const newGlobalVariables = await this.globalVariableBuilder.buildGlobalVariables(
|
|
1266
|
-
blockNumber,
|
|
1267
|
-
coinbase,
|
|
1268
|
-
feeRecipient,
|
|
1269
|
-
);
|
|
1270
|
-
const publicProcessorFactory = new PublicProcessorFactory(
|
|
1271
|
-
this.contractDataSource,
|
|
1272
|
-
new DateProvider(),
|
|
1273
|
-
this.telemetry,
|
|
1274
|
-
this.log.getBindings(),
|
|
1275
|
-
);
|
|
1276
|
-
|
|
1277
|
-
this.log.verbose(`Simulating public calls for tx ${txHash}`, {
|
|
1278
|
-
globalVariables: newGlobalVariables.toInspect(),
|
|
1279
|
-
txHash,
|
|
1280
|
-
blockNumber,
|
|
1281
|
-
});
|
|
1282
|
-
|
|
1283
|
-
// Ensure world-state has caught up with the latest block we loaded from the archiver
|
|
1284
|
-
await this.worldStateSynchronizer.syncImmediate(latestBlockNumber);
|
|
1285
|
-
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
1286
|
-
try {
|
|
1287
|
-
const config = PublicSimulatorConfig.from({
|
|
1288
|
-
skipFeeEnforcement,
|
|
1289
|
-
collectDebugLogs: true,
|
|
1290
|
-
collectHints: false,
|
|
1291
|
-
collectCallMetadata: true,
|
|
1292
|
-
collectStatistics: false,
|
|
1293
|
-
collectionLimits: CollectionLimitsConfig.from({
|
|
1294
|
-
maxDebugLogMemoryReads: this.config.rpcSimulatePublicMaxDebugLogMemoryReads,
|
|
1295
|
-
}),
|
|
1296
|
-
});
|
|
1297
|
-
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1298
|
-
|
|
1299
|
-
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1300
|
-
const [processedTxs, failedTxs, _usedTxs, returns, debugLogs] = await processor.process([tx]);
|
|
1301
|
-
// REFACTOR: Consider returning the error rather than throwing
|
|
1302
|
-
if (failedTxs.length) {
|
|
1303
|
-
this.log.warn(`Simulated tx ${txHash} fails: ${failedTxs[0].error}`, { txHash });
|
|
1304
|
-
throw failedTxs[0].error;
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
const [processedTx] = processedTxs;
|
|
1308
|
-
return new PublicSimulationOutput(
|
|
1309
|
-
processedTx.revertReason,
|
|
1310
|
-
processedTx.globalVariables,
|
|
1311
|
-
processedTx.txEffect,
|
|
1312
|
-
returns,
|
|
1313
|
-
processedTx.gasUsed,
|
|
1314
|
-
debugLogs,
|
|
1315
|
-
);
|
|
1316
|
-
} finally {
|
|
1317
|
-
await merkleTreeFork.close();
|
|
1318
|
-
}
|
|
736
|
+
public simulatePublicCalls(
|
|
737
|
+
tx: Tx,
|
|
738
|
+
skipFeeEnforcement = false,
|
|
739
|
+
overrides?: SimulationOverrides,
|
|
740
|
+
): Promise<PublicSimulationOutput> {
|
|
741
|
+
return this.nodePublicCallsSimulator.simulate(tx, skipFeeEnforcement, overrides);
|
|
1319
742
|
}
|
|
1320
743
|
|
|
1321
744
|
public async isValidTx(
|
|
@@ -1323,12 +746,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1323
746
|
{ isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {},
|
|
1324
747
|
): Promise<TxValidationResult> {
|
|
1325
748
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1326
|
-
const verifier = isSimulation ? undefined : this.
|
|
749
|
+
const verifier = isSimulation ? undefined : this.rpcProofVerifier;
|
|
1327
750
|
|
|
1328
751
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1329
752
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1330
753
|
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
1331
754
|
const l1Constants = await this.blockSource.getL1Constants();
|
|
755
|
+
// Enforce the same network admission limit the node advertises in getNodeInfo (network-wide, not this
|
|
756
|
+
// node's local caps), so a tx the wallet sized against txsLimits is not rejected here.
|
|
757
|
+
const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
|
|
1332
758
|
const validator = createTxValidatorForAcceptingTxsOverRPC(
|
|
1333
759
|
db,
|
|
1334
760
|
this.contractDataSource,
|
|
@@ -1344,10 +770,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1344
770
|
],
|
|
1345
771
|
gasFees: await this.getCurrentMinFees(),
|
|
1346
772
|
skipFeeEnforcement,
|
|
773
|
+
isSimulation,
|
|
1347
774
|
txsPermitted: !this.config.disableTransactions,
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
775
|
+
maxTxL2Gas: networkTxGasLimits.l2Gas,
|
|
776
|
+
maxTxDAGas: networkTxGasLimits.daGas,
|
|
1351
777
|
},
|
|
1352
778
|
this.log.getBindings(),
|
|
1353
779
|
);
|
|
@@ -1363,7 +789,18 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1363
789
|
|
|
1364
790
|
public async setConfig(config: Partial<AztecNodeAdminConfig>): Promise<void> {
|
|
1365
791
|
const newConfig = { ...this.config, ...config };
|
|
1366
|
-
|
|
792
|
+
// If the sequencer is currently paused via pauseSequencer(), record the caller's desired
|
|
793
|
+
// minTxsPerBlock as the restore value (so resumeSequencer applies it) and keep the freeze
|
|
794
|
+
// (MAX_SAFE_INTEGER) applied to the underlying sequencer. Without this guard, forwarding
|
|
795
|
+
// the new minTxsPerBlock to the sequencer would silently unpause block production while
|
|
796
|
+
// pauseSequencer() still considers it paused.
|
|
797
|
+
const sequencerUpdate = { ...config };
|
|
798
|
+
if (this.sequencerPausedMinTxsPerBlock !== undefined && sequencerUpdate.minTxsPerBlock !== undefined) {
|
|
799
|
+
this.sequencerPausedMinTxsPerBlock = sequencerUpdate.minTxsPerBlock;
|
|
800
|
+
delete sequencerUpdate.minTxsPerBlock;
|
|
801
|
+
}
|
|
802
|
+
this.sequencer?.updateConfig(sequencerUpdate);
|
|
803
|
+
this.automineSequencer?.updateConfig(sequencerUpdate);
|
|
1367
804
|
this.slasherClient?.updateConfig(config);
|
|
1368
805
|
this.validatorsSentinel?.updateConfig(config);
|
|
1369
806
|
await this.p2pClient.updateP2PConfig(config);
|
|
@@ -1372,7 +809,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1372
809
|
archiver.updateConfig(config);
|
|
1373
810
|
}
|
|
1374
811
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
1375
|
-
|
|
812
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
813
|
+
if (newConfig.realProofs) {
|
|
814
|
+
this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
|
|
815
|
+
const rpcVerifier = await BBCircuitVerifier.new(newConfig);
|
|
816
|
+
this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
|
|
817
|
+
} else {
|
|
818
|
+
this.peerProofVerifier = new TestCircuitVerifier();
|
|
819
|
+
this.rpcProofVerifier = new TestCircuitVerifier();
|
|
820
|
+
}
|
|
1376
821
|
}
|
|
1377
822
|
|
|
1378
823
|
this.config = newConfig;
|
|
@@ -1383,7 +828,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1383
828
|
classRegistry: ProtocolContractAddress.ContractClassRegistry,
|
|
1384
829
|
feeJuice: ProtocolContractAddress.FeeJuice,
|
|
1385
830
|
instanceRegistry: ProtocolContractAddress.ContractInstanceRegistry,
|
|
1386
|
-
multiCallEntrypoint:
|
|
831
|
+
multiCallEntrypoint: STANDARD_MULTI_CALL_ENTRYPOINT_ADDRESS,
|
|
1387
832
|
});
|
|
1388
833
|
}
|
|
1389
834
|
|
|
@@ -1447,7 +892,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1447
892
|
return Promise.resolve();
|
|
1448
893
|
}
|
|
1449
894
|
|
|
1450
|
-
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
895
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
|
|
1451
896
|
const archiver = this.blockSource as Archiver;
|
|
1452
897
|
if (!('rollbackTo' in archiver)) {
|
|
1453
898
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1477,9 +922,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1477
922
|
this.log.error(`Error during rollback`, err);
|
|
1478
923
|
throw err;
|
|
1479
924
|
} finally {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
925
|
+
if (resumeSync) {
|
|
926
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
927
|
+
this.worldStateSynchronizer.resumeSync();
|
|
928
|
+
archiver.resume();
|
|
929
|
+
} else {
|
|
930
|
+
this.log.info(`Sync left paused after rollback (resumeSync=false).`);
|
|
931
|
+
}
|
|
1483
932
|
}
|
|
1484
933
|
}
|
|
1485
934
|
|
|
@@ -1496,11 +945,39 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1496
945
|
return Promise.resolve();
|
|
1497
946
|
}
|
|
1498
947
|
|
|
1499
|
-
public
|
|
1500
|
-
if (
|
|
1501
|
-
|
|
948
|
+
public pauseSequencer(): Promise<void> {
|
|
949
|
+
if (this.automineSequencer) {
|
|
950
|
+
this.automineSequencer.pause();
|
|
951
|
+
return Promise.resolve();
|
|
952
|
+
}
|
|
953
|
+
if (this.sequencer) {
|
|
954
|
+
if (this.sequencerPausedMinTxsPerBlock === undefined) {
|
|
955
|
+
this.sequencerPausedMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock ?? 0;
|
|
956
|
+
this.sequencer.updateConfig({ minTxsPerBlock: Number.MAX_SAFE_INTEGER });
|
|
957
|
+
this.log.info(`Sequencer paused (minTxsPerBlock set to MAX_SAFE_INTEGER)`, {
|
|
958
|
+
previousMinTxsPerBlock: this.sequencerPausedMinTxsPerBlock,
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
return Promise.resolve();
|
|
962
|
+
}
|
|
963
|
+
throw new BadRequestError('Cannot pause sequencer: no sequencer is running');
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
public resumeSequencer(): Promise<void> {
|
|
967
|
+
if (this.automineSequencer) {
|
|
968
|
+
this.automineSequencer.resume();
|
|
969
|
+
return Promise.resolve();
|
|
1502
970
|
}
|
|
1503
|
-
|
|
971
|
+
if (this.sequencer) {
|
|
972
|
+
if (this.sequencerPausedMinTxsPerBlock !== undefined) {
|
|
973
|
+
const restored = this.sequencerPausedMinTxsPerBlock;
|
|
974
|
+
this.sequencerPausedMinTxsPerBlock = undefined;
|
|
975
|
+
this.sequencer.updateConfig({ minTxsPerBlock: restored });
|
|
976
|
+
this.log.info(`Sequencer resumed (minTxsPerBlock restored)`, { minTxsPerBlock: restored });
|
|
977
|
+
}
|
|
978
|
+
return Promise.resolve();
|
|
979
|
+
}
|
|
980
|
+
throw new BadRequestError('Cannot resume sequencer: no sequencer is running');
|
|
1504
981
|
}
|
|
1505
982
|
|
|
1506
983
|
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
@@ -1508,7 +985,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1508
985
|
throw new Error(`Slasher client not enabled`);
|
|
1509
986
|
}
|
|
1510
987
|
if (round === 'all') {
|
|
1511
|
-
return this.slasherClient.
|
|
988
|
+
return this.slasherClient.getOffenses();
|
|
1512
989
|
} else {
|
|
1513
990
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1514
991
|
}
|
|
@@ -1603,6 +1080,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1603
1080
|
}
|
|
1604
1081
|
|
|
1605
1082
|
public async mineBlock(): Promise<void> {
|
|
1083
|
+
if (this.automineSequencer) {
|
|
1084
|
+
await this.automineSequencer.buildEmptyBlock();
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1606
1087
|
if (!this.sequencer) {
|
|
1607
1088
|
throw new BadRequestError('Cannot mine block: no sequencer is running');
|
|
1608
1089
|
}
|
|
@@ -1636,99 +1117,33 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1636
1117
|
}
|
|
1637
1118
|
}
|
|
1638
1119
|
|
|
1639
|
-
|
|
1640
|
-
if (!this.
|
|
1641
|
-
|
|
1120
|
+
public async prove(upToCheckpoint?: CheckpointNumber): Promise<CheckpointNumber> {
|
|
1121
|
+
if (!this.automineSequencer) {
|
|
1122
|
+
throw new BadRequestError('Cannot prove checkpoint: no automine sequencer is running');
|
|
1642
1123
|
}
|
|
1643
|
-
return this.
|
|
1124
|
+
return await this.automineSequencer.prove(upToCheckpoint);
|
|
1644
1125
|
}
|
|
1645
1126
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
* @returns An instance of a committed MerkleTreeOperations
|
|
1650
|
-
*/
|
|
1651
|
-
protected async getWorldState(block: BlockParameter) {
|
|
1652
|
-
let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
|
|
1653
|
-
try {
|
|
1654
|
-
// Attempt to sync the world state if necessary
|
|
1655
|
-
blockSyncedTo = await this.#syncWorldState();
|
|
1656
|
-
} catch (err) {
|
|
1657
|
-
this.log.error(`Error getting world state: ${err}`);
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
if (block === 'latest') {
|
|
1661
|
-
this.log.debug(`Using committed db for block 'latest', world state synced upto ${blockSyncedTo}`);
|
|
1662
|
-
return this.worldStateSynchronizer.getCommitted();
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
// Get the block number, either directly from the parameter or by quering the archiver with the block hash
|
|
1666
|
-
let blockNumber: BlockNumber;
|
|
1667
|
-
if (BlockHash.isBlockHash(block)) {
|
|
1668
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1669
|
-
if (block.equals(initialBlockHash)) {
|
|
1670
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1671
|
-
return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1675
|
-
if (!header) {
|
|
1676
|
-
throw new Error(
|
|
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.`,
|
|
1678
|
-
);
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
blockNumber = header.getBlockNumber();
|
|
1682
|
-
} else {
|
|
1683
|
-
blockNumber = block as BlockNumber;
|
|
1684
|
-
}
|
|
1685
|
-
|
|
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}`);
|
|
1691
|
-
|
|
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
|
-
);
|
|
1701
|
-
}
|
|
1127
|
+
public async warpL2TimeAtLeastTo(targetTimestamp: number): Promise<void> {
|
|
1128
|
+
if (!this.automineSequencer) {
|
|
1129
|
+
throw new BadRequestError('Cannot warp L2 time: no automine sequencer is running');
|
|
1702
1130
|
}
|
|
1703
|
-
|
|
1704
|
-
return snapshot;
|
|
1131
|
+
await this.automineSequencer.warpTo(targetTimestamp);
|
|
1705
1132
|
}
|
|
1706
1133
|
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
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();
|
|
1134
|
+
public async warpL2TimeAtLeastBy(duration: number): Promise<void> {
|
|
1135
|
+
if (!this.automineSequencer) {
|
|
1136
|
+
throw new BadRequestError('Cannot warp L2 time: no automine sequencer is running');
|
|
1722
1137
|
}
|
|
1723
|
-
|
|
1138
|
+
await this.automineSequencer.warpBy(duration);
|
|
1724
1139
|
}
|
|
1725
1140
|
|
|
1726
1141
|
/**
|
|
1727
|
-
*
|
|
1728
|
-
* @
|
|
1142
|
+
* Returns a committed world-state view at `block`, driving sync first. Delegates to
|
|
1143
|
+
* {@link NodeWorldStateQueries.getWorldState}; kept as a protected method so subclasses and tests can
|
|
1144
|
+
* exercise the node's block-resolution and reorg-aware sync behavior.
|
|
1729
1145
|
*/
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
return await this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
|
|
1146
|
+
protected getWorldState(block: BlockParameter) {
|
|
1147
|
+
return this.worldStateQueries.getWorldState(block);
|
|
1733
1148
|
}
|
|
1734
1149
|
}
|