@aztec/aztec-node 5.0.0-private.20260319 → 5.0.0-rc.2

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.
Files changed (71) hide show
  1. package/dest/aztec-node/block_response_helpers.d.ts +25 -0
  2. package/dest/aztec-node/block_response_helpers.d.ts.map +1 -0
  3. package/dest/aztec-node/block_response_helpers.js +112 -0
  4. package/dest/aztec-node/config.d.ts +16 -4
  5. package/dest/aztec-node/config.d.ts.map +1 -1
  6. package/dest/aztec-node/config.js +15 -5
  7. package/dest/aztec-node/node_public_calls_simulator.d.ts +90 -0
  8. package/dest/aztec-node/node_public_calls_simulator.d.ts.map +1 -0
  9. package/dest/aztec-node/node_public_calls_simulator.js +346 -0
  10. package/dest/aztec-node/public_data_overrides.d.ts +13 -0
  11. package/dest/aztec-node/public_data_overrides.d.ts.map +1 -0
  12. package/dest/aztec-node/public_data_overrides.js +21 -0
  13. package/dest/aztec-node/register_node_rpc_handlers.d.ts +10 -0
  14. package/dest/aztec-node/register_node_rpc_handlers.d.ts.map +1 -0
  15. package/dest/aztec-node/register_node_rpc_handlers.js +31 -0
  16. package/dest/aztec-node/server.d.ts +128 -134
  17. package/dest/aztec-node/server.d.ts.map +1 -1
  18. package/dest/aztec-node/server.js +393 -820
  19. package/dest/bin/index.js +15 -10
  20. package/dest/factory.d.ts +33 -0
  21. package/dest/factory.d.ts.map +1 -0
  22. package/dest/factory.js +496 -0
  23. package/dest/index.d.ts +3 -1
  24. package/dest/index.d.ts.map +1 -1
  25. package/dest/index.js +2 -0
  26. package/dest/modules/block_parameter.d.ts +25 -0
  27. package/dest/modules/block_parameter.d.ts.map +1 -0
  28. package/dest/modules/block_parameter.js +100 -0
  29. package/dest/modules/node_block_provider.d.ts +19 -0
  30. package/dest/modules/node_block_provider.d.ts.map +1 -0
  31. package/dest/modules/node_block_provider.js +112 -0
  32. package/dest/modules/node_tx_receipt.d.ts +24 -0
  33. package/dest/modules/node_tx_receipt.d.ts.map +1 -0
  34. package/dest/modules/node_tx_receipt.js +70 -0
  35. package/dest/modules/node_world_state_queries.d.ts +61 -0
  36. package/dest/modules/node_world_state_queries.d.ts.map +1 -0
  37. package/dest/modules/node_world_state_queries.js +257 -0
  38. package/dest/sentinel/config.d.ts +3 -2
  39. package/dest/sentinel/config.d.ts.map +1 -1
  40. package/dest/sentinel/config.js +15 -5
  41. package/dest/sentinel/factory.d.ts +5 -3
  42. package/dest/sentinel/factory.d.ts.map +1 -1
  43. package/dest/sentinel/factory.js +12 -5
  44. package/dest/sentinel/sentinel.d.ts +145 -21
  45. package/dest/sentinel/sentinel.d.ts.map +1 -1
  46. package/dest/sentinel/sentinel.js +227 -105
  47. package/dest/sentinel/store.d.ts +8 -8
  48. package/dest/sentinel/store.d.ts.map +1 -1
  49. package/dest/sentinel/store.js +25 -17
  50. package/dest/test/index.d.ts +3 -3
  51. package/dest/test/index.d.ts.map +1 -1
  52. package/package.json +28 -26
  53. package/src/aztec-node/block_response_helpers.ts +161 -0
  54. package/src/aztec-node/config.ts +30 -7
  55. package/src/aztec-node/node_public_calls_simulator.ts +383 -0
  56. package/src/aztec-node/public_data_overrides.ts +35 -0
  57. package/src/aztec-node/register_node_rpc_handlers.ts +29 -0
  58. package/src/aztec-node/server.ts +514 -1070
  59. package/src/bin/index.ts +19 -12
  60. package/src/factory.ts +656 -0
  61. package/src/index.ts +2 -0
  62. package/src/modules/block_parameter.ts +93 -0
  63. package/src/modules/node_block_provider.ts +149 -0
  64. package/src/modules/node_tx_receipt.ts +115 -0
  65. package/src/modules/node_world_state_queries.ts +360 -0
  66. package/src/sentinel/README.md +103 -0
  67. package/src/sentinel/config.ts +18 -6
  68. package/src/sentinel/factory.ts +21 -6
  69. package/src/sentinel/sentinel.ts +277 -130
  70. package/src/sentinel/store.ts +26 -18
  71. package/src/test/index.ts +2 -2
@@ -0,0 +1,93 @@
1
+ import type { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
+ import { BadRequestError } from '@aztec/foundation/json-rpc';
3
+ import {
4
+ BlockHash,
5
+ type BlockParameter,
6
+ BlockTag,
7
+ type L2BlockSource,
8
+ type NormalizedBlockParameter,
9
+ } from '@aztec/stdlib/block';
10
+ import type { CheckpointParameter, CheckpointTag } from '@aztec/stdlib/interfaces/client';
11
+
12
+ /** True when `value` is a {@link BlockTag}. */
13
+ export function isBlockTag(value: string): value is BlockTag {
14
+ return BlockTag.includes(value as BlockTag);
15
+ }
16
+
17
+ /** True when `value` is a {@link CheckpointTag}. */
18
+ export function isCheckpointTag(value: unknown): value is CheckpointTag {
19
+ return value === 'checkpointed' || value === 'proven' || value === 'finalized';
20
+ }
21
+
22
+ /**
23
+ * Normalizes a {@link BlockParameter} (which may be a bare value) into a {@link NormalizedBlockParameter}
24
+ * object form. Performs no chain-tip resolution — tag lookups are deferred to the underlying block source.
25
+ */
26
+ export function normalizeBlockParameter(param: BlockParameter): NormalizedBlockParameter {
27
+ if (BlockHash.isBlockHash(param)) {
28
+ return { hash: param };
29
+ }
30
+ if (typeof param === 'number') {
31
+ return { number: param as BlockNumber };
32
+ }
33
+ if (typeof param === 'string') {
34
+ if (isBlockTag(param)) {
35
+ return { tag: param === 'latest' ? 'proposed' : param };
36
+ }
37
+ throw new BadRequestError(`Invalid BlockParameter tag: ${param}`);
38
+ }
39
+ if (typeof param === 'object' && param !== null) {
40
+ if ('number' in param) {
41
+ return { number: param.number };
42
+ }
43
+ if ('hash' in param) {
44
+ return { hash: param.hash };
45
+ }
46
+ if ('archive' in param) {
47
+ return { archive: param.archive };
48
+ }
49
+ if ('tag' in param) {
50
+ if (isBlockTag(param.tag)) {
51
+ return { tag: param.tag };
52
+ }
53
+ throw new BadRequestError(`Invalid BlockParameter tag: ${param.tag}`);
54
+ }
55
+ }
56
+ throw new BadRequestError(`Invalid BlockParameter: ${JSON.stringify(param)}`);
57
+ }
58
+
59
+ /**
60
+ * Resolves a {@link CheckpointParameter} into a concrete `{ number }` or `{ slot }` query.
61
+ *
62
+ * Tag-based parameters (`'checkpointed'`, `'proven'`, `'finalized'`) are translated up-front to the
63
+ * corresponding tip's checkpoint number via {@link L2BlockSource.getL2Tips}. After resolution the unified
64
+ * `getCheckpoint` flow can perform a single confirmed→proposed lookup against either store.
65
+ */
66
+ export async function resolveCheckpointParameter(
67
+ param: CheckpointParameter,
68
+ blockSource: L2BlockSource,
69
+ ): Promise<{ number: CheckpointNumber } | { slot: SlotNumber }> {
70
+ if (typeof param === 'number') {
71
+ return { number: param as CheckpointNumber };
72
+ }
73
+ if (isCheckpointTag(param)) {
74
+ const tips = await blockSource.getL2Tips();
75
+ switch (param) {
76
+ case 'checkpointed':
77
+ return { number: tips.checkpointed.checkpoint.number };
78
+ case 'proven':
79
+ return { number: tips.proven.checkpoint.number };
80
+ case 'finalized':
81
+ return { number: tips.finalized.checkpoint.number };
82
+ }
83
+ }
84
+ if (typeof param === 'object' && param !== null) {
85
+ if ('number' in param) {
86
+ return { number: param.number };
87
+ }
88
+ if ('slot' in param) {
89
+ return { slot: param.slot };
90
+ }
91
+ }
92
+ throw new BadRequestError(`Invalid CheckpointParameter: ${JSON.stringify(param)}`);
93
+ }
@@ -0,0 +1,149 @@
1
+ import type { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
+ import { BadRequestError } from '@aztec/foundation/json-rpc';
3
+ import type { BlockData, BlockParameter, CommitteeAttestation, L2BlockSource } from '@aztec/stdlib/block';
4
+ import type { CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
5
+ import type {
6
+ BlockIncludeOptions,
7
+ BlockResponse,
8
+ BlocksIncludeOptions,
9
+ CheckpointIncludeOptions,
10
+ CheckpointParameter,
11
+ CheckpointResponse,
12
+ } from '@aztec/stdlib/interfaces/client';
13
+
14
+ import {
15
+ blockResponseFromBlockData,
16
+ blockResponseFromL2Block,
17
+ checkpointResponseFromCheckpointData,
18
+ checkpointResponseFromPublishedCheckpoint,
19
+ projectProposedToCheckpointResponse,
20
+ } from '../aztec-node/block_response_helpers.js';
21
+ import { normalizeBlockParameter, resolveCheckpointParameter } from './block_parameter.js';
22
+
23
+ /**
24
+ * Serves the node's block and checkpoint read queries, assembling RPC responses (optionally including
25
+ * transactions, L1 publish info, and attestations) from the underlying block source. Extracted from
26
+ * `AztecNodeService` to keep `server.ts` smaller.
27
+ */
28
+ export class NodeBlockProvider {
29
+ constructor(private readonly blockSource: L2BlockSource) {}
30
+
31
+ public async getBlock<Opts extends BlockIncludeOptions = {}>(
32
+ param: BlockParameter,
33
+ options: Opts = {} as Opts,
34
+ ): Promise<BlockResponse<Opts> | undefined> {
35
+ const query = normalizeBlockParameter(param);
36
+ const wantTxs = !!options.includeTransactions;
37
+ const wantContext = !!options.includeL1PublishInfo || !!options.includeAttestations;
38
+
39
+ if (wantTxs) {
40
+ const block = await this.blockSource.getBlock(query);
41
+ if (!block) {
42
+ return undefined;
43
+ }
44
+ const ctx = wantContext ? await this.#getCheckpointContext(block.checkpointNumber) : undefined;
45
+ return (await blockResponseFromL2Block(block, options, ctx)) as BlockResponse<Opts>;
46
+ }
47
+ const data = await this.blockSource.getBlockData(query);
48
+ if (!data) {
49
+ return undefined;
50
+ }
51
+ const ctx = wantContext ? await this.#getCheckpointContext(data.checkpointNumber) : undefined;
52
+ return blockResponseFromBlockData(data, options, ctx) as BlockResponse<Opts>;
53
+ }
54
+
55
+ public getBlockData(param: BlockParameter): Promise<BlockData | undefined> {
56
+ const query = normalizeBlockParameter(param);
57
+ return this.blockSource.getBlockData(query);
58
+ }
59
+
60
+ public async getBlocks<Opts extends BlocksIncludeOptions = {}>(
61
+ from: BlockNumber,
62
+ limit: number,
63
+ options: Opts = {} as Opts,
64
+ ): Promise<BlockResponse<Opts>[]> {
65
+ const wantTxs = !!options.includeTransactions;
66
+ const wantContext = !!options.includeL1PublishInfo || !!options.includeAttestations;
67
+ const onlyCheckpointed = !!options.onlyCheckpointed;
68
+ if (wantTxs) {
69
+ const blocks = await this.blockSource.getBlocks({ from, limit, onlyCheckpointed });
70
+ const ctxByCheckpoint = await this.#getCheckpointContextsForBlocks(wantContext ? blocks : []);
71
+ return (await Promise.all(
72
+ blocks.map(block => blockResponseFromL2Block(block, options, ctxByCheckpoint.get(block.checkpointNumber))),
73
+ )) as BlockResponse<Opts>[];
74
+ }
75
+ const dataItems = await this.blockSource.getBlocksData({ from, limit, onlyCheckpointed });
76
+ const ctxByCheckpoint = await this.#getCheckpointContextsForBlocks(wantContext ? dataItems : []);
77
+ return (await Promise.all(
78
+ dataItems.map(data => blockResponseFromBlockData(data, options, ctxByCheckpoint.get(data.checkpointNumber))),
79
+ )) as BlockResponse<Opts>[];
80
+ }
81
+
82
+ public async getCheckpoint<Opts extends CheckpointIncludeOptions = {}>(
83
+ param: CheckpointParameter,
84
+ options: Opts = {} as Opts,
85
+ ): Promise<CheckpointResponse<Opts> | undefined> {
86
+ const query = await resolveCheckpointParameter(param, this.blockSource);
87
+
88
+ // Try the confirmed store first.
89
+ const confirmed = options.includeBlocks
90
+ ? await this.blockSource.getCheckpoint(query)
91
+ : await this.blockSource.getCheckpointData(query);
92
+ if (confirmed) {
93
+ return (await (options.includeBlocks
94
+ ? checkpointResponseFromPublishedCheckpoint(confirmed as PublishedCheckpoint, options)
95
+ : checkpointResponseFromCheckpointData(confirmed as CheckpointData, options))) as CheckpointResponse<Opts>;
96
+ }
97
+
98
+ // Fall back to the proposed store.
99
+ const proposed = await this.blockSource.getProposedCheckpointData(query);
100
+ if (proposed) {
101
+ if (options.includeAttestations || options.includeL1PublishInfo) {
102
+ throw new BadRequestError(
103
+ `Options includeL1PublishInfo or includeAttestations cannot be satisfied for a proposed checkpoint`,
104
+ );
105
+ }
106
+ const blocks = options.includeBlocks
107
+ ? await this.blockSource.getBlocks({ from: proposed.startBlock, limit: proposed.blockCount })
108
+ : undefined;
109
+ return (await projectProposedToCheckpointResponse(proposed, options, blocks)) as CheckpointResponse<Opts>;
110
+ }
111
+
112
+ return undefined;
113
+ }
114
+
115
+ public async getCheckpoints<Opts extends CheckpointIncludeOptions = {}>(
116
+ from: CheckpointNumber,
117
+ limit: number,
118
+ options: Opts = {} as Opts,
119
+ ): Promise<CheckpointResponse<Opts>[]> {
120
+ if (options.includeBlocks) {
121
+ const checkpoints = await this.blockSource.getCheckpoints({ from, limit });
122
+ return (await Promise.all(
123
+ checkpoints.map(cp => checkpointResponseFromPublishedCheckpoint(cp, options)),
124
+ )) as CheckpointResponse<Opts>[];
125
+ }
126
+ const datas = await this.blockSource.getCheckpointsData({ from, limit });
127
+ return datas.map(d => checkpointResponseFromCheckpointData(d, options)) as CheckpointResponse<Opts>[];
128
+ }
129
+
130
+ /** Fetches checkpoint-level L1 and attestation data for use as block response context. */
131
+ async #getCheckpointContext(
132
+ checkpointNumber: CheckpointNumber,
133
+ ): Promise<{ l1?: L1PublishedData; attestations?: CommitteeAttestation[] } | undefined> {
134
+ const checkpoint = await this.blockSource.getCheckpointData({ number: checkpointNumber });
135
+ if (!checkpoint) {
136
+ return undefined;
137
+ }
138
+ return { l1: checkpoint.l1, attestations: checkpoint.attestations };
139
+ }
140
+
141
+ /** Fetches checkpoint context for a set of blocks, deduplicating shared checkpoints. */
142
+ async #getCheckpointContextsForBlocks(
143
+ blocks: { checkpointNumber: CheckpointNumber }[],
144
+ ): Promise<Map<CheckpointNumber, { l1?: L1PublishedData; attestations?: CommitteeAttestation[] } | undefined>> {
145
+ const unique = Array.from(new Set(blocks.map(b => b.checkpointNumber)));
146
+ const entries = await Promise.all(unique.map(async n => [n, await this.#getCheckpointContext(n)] as const));
147
+ return new Map(entries);
148
+ }
149
+ }
@@ -0,0 +1,115 @@
1
+ import type { BlockNumber } from '@aztec/foundation/branded-types';
2
+ import type { P2P } from '@aztec/p2p';
3
+ import type { L2BlockSource, L2Tips } from '@aztec/stdlib/block';
4
+ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
5
+ import type { DebugLogStore } from '@aztec/stdlib/logs';
6
+ import {
7
+ DroppedTxReceipt,
8
+ type GetTxReceiptOptions,
9
+ type IndexedTxEffect,
10
+ MinedTxReceipt,
11
+ type MinedTxStatus,
12
+ PendingTxReceipt,
13
+ type Tx,
14
+ type TxHash,
15
+ type TxReceipt,
16
+ TxStatus,
17
+ } from '@aztec/stdlib/tx';
18
+
19
+ /** Dependencies required to build a {@link NodeTxReceiptBuilder}. */
20
+ export interface NodeTxReceiptBuilderDeps {
21
+ p2pClient: P2P;
22
+ blockSource: L2BlockSource;
23
+ debugLogStore: DebugLogStore;
24
+ }
25
+
26
+ /**
27
+ * Builds transaction receipts by reconciling the archiver's mined tx effects with the tx pool's view,
28
+ * deriving the finalization status from the cached L2 tips. Extracted from `AztecNodeService` to keep
29
+ * `server.ts` smaller.
30
+ */
31
+ export class NodeTxReceiptBuilder {
32
+ private readonly p2pClient: P2P;
33
+ private readonly blockSource: L2BlockSource;
34
+ private readonly debugLogStore: DebugLogStore;
35
+
36
+ constructor(deps: NodeTxReceiptBuilderDeps) {
37
+ this.p2pClient = deps.p2pClient;
38
+ this.blockSource = deps.blockSource;
39
+ this.debugLogStore = deps.debugLogStore;
40
+ }
41
+
42
+ public async getTxReceipt<TGetTxReceiptOptions extends GetTxReceiptOptions = {}>(
43
+ txHash: TxHash,
44
+ options?: TGetTxReceiptOptions,
45
+ ): Promise<TxReceipt<TGetTxReceiptOptions>> {
46
+ // Check the tx pool status first. If the tx is known to the pool (pending or mined), we'll use that
47
+ // as a fallback if we don't find a mined tx effect in the archiver.
48
+ const txPoolStatus = await this.p2pClient.getTxStatus(txHash);
49
+ const isKnownToPool = txPoolStatus === 'pending' || txPoolStatus === 'mined';
50
+
51
+ // Then get the raw tx effect from the archiver, which tracks every tx in a mined block.
52
+ const indexed = await this.blockSource.getTxEffect(txHash);
53
+
54
+ let receipt: TxReceipt;
55
+ if (indexed) {
56
+ receipt = await this.#assembleMinedReceipt(indexed, options);
57
+ } else if (isKnownToPool) {
58
+ // If the tx is in the pool but not in the archiver, it's pending.
59
+ // This handles race conditions between archiver and p2p, where the archiver
60
+ // has pruned the block in which a tx was mined, but p2p has not caught up yet.
61
+ let tx: Tx | undefined;
62
+ if (options?.includePendingTx) {
63
+ // The tx may have left the pool since we checked its status (mined or dropped); in that case we
64
+ // leave `tx` unset and still return a pending receipt.
65
+ tx = await this.p2pClient.getTxByHashFromPool(txHash, { includeProof: !!options.includeProof });
66
+ }
67
+ receipt = new PendingTxReceipt(txHash, tx);
68
+ } else {
69
+ // Otherwise, if we don't know the tx, we consider it dropped.
70
+ receipt = new DroppedTxReceipt(txHash, 'Tx dropped by P2P node');
71
+ }
72
+
73
+ this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
74
+
75
+ return receipt;
76
+ }
77
+
78
+ /**
79
+ * Assembles a {@link MinedTxReceipt} from a raw {@link IndexedTxEffect}, deriving the finalization status from the
80
+ * cached L2 tips and the epoch from the block's slot number.
81
+ */
82
+ async #assembleMinedReceipt(indexed: IndexedTxEffect, options?: GetTxReceiptOptions): Promise<MinedTxReceipt> {
83
+ const blockNumber = indexed.l2BlockNumber;
84
+ const [tips, l1Constants] = await Promise.all([this.blockSource.getL2Tips(), this.blockSource.getL1Constants()]);
85
+
86
+ const status = this.#deriveMinedStatus(blockNumber, tips);
87
+ const epochNumber = getEpochAtSlot(indexed.slotNumber, l1Constants);
88
+
89
+ return new MinedTxReceipt(
90
+ indexed.data.txHash,
91
+ status,
92
+ MinedTxReceipt.executionResultFromRevertCode(indexed.data.revertCode),
93
+ indexed.data.transactionFee.toBigInt(),
94
+ indexed.l2BlockHash,
95
+ blockNumber,
96
+ indexed.slotNumber,
97
+ indexed.txIndexInBlock,
98
+ epochNumber,
99
+ options?.includeTxEffect ? indexed.data : undefined,
100
+ /*debugLogs=*/ undefined,
101
+ );
102
+ }
103
+
104
+ #deriveMinedStatus(blockNumber: BlockNumber, tips: L2Tips): MinedTxStatus {
105
+ if (blockNumber <= tips.finalized.block.number) {
106
+ return TxStatus.FINALIZED;
107
+ } else if (blockNumber <= tips.proven.block.number) {
108
+ return TxStatus.PROVEN;
109
+ } else if (blockNumber <= tips.checkpointed.block.number) {
110
+ return TxStatus.CHECKPOINTED;
111
+ } else {
112
+ return TxStatus.PROPOSED;
113
+ }
114
+ }
115
+ }