@aztec/archiver 0.48.0 → 0.50.1

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 (31) hide show
  1. package/dest/archiver/archiver.d.ts +4 -0
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +26 -10
  4. package/dest/archiver/archiver_store.d.ts +4 -2
  5. package/dest/archiver/archiver_store.d.ts.map +1 -1
  6. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.js +21 -5
  8. package/dest/archiver/data_retrieval.d.ts +4 -4
  9. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  10. package/dest/archiver/data_retrieval.js +8 -5
  11. package/dest/archiver/eth_log_handlers.js +6 -6
  12. package/dest/archiver/instrumentation.js +2 -2
  13. package/dest/archiver/kv_archiver_store/block_body_store.d.ts +7 -1
  14. package/dest/archiver/kv_archiver_store/block_body_store.d.ts.map +1 -1
  15. package/dest/archiver/kv_archiver_store/block_body_store.js +15 -4
  16. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +1 -1
  17. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  18. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +2 -1
  19. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +2 -1
  20. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  21. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +5 -2
  22. package/package.json +10 -10
  23. package/src/archiver/archiver.ts +32 -12
  24. package/src/archiver/archiver_store.ts +4 -2
  25. package/src/archiver/archiver_store_test_suite.ts +26 -9
  26. package/src/archiver/data_retrieval.ts +14 -6
  27. package/src/archiver/eth_log_handlers.ts +5 -5
  28. package/src/archiver/instrumentation.ts +1 -1
  29. package/src/archiver/kv_archiver_store/block_body_store.ts +18 -4
  30. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +2 -1
  31. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +5 -3
@@ -1,4 +1,4 @@
1
- import { InboxLeaf, L2Block, LogId, LogType, TxHash } from '@aztec/circuit-types';
1
+ import { type Body, InboxLeaf, L2Block, LogId, LogType, TxHash } from '@aztec/circuit-types';
2
2
  import '@aztec/circuit-types/jest';
3
3
  import { AztecAddress, Fr, INITIAL_L2_BLOCK_NUM, L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js';
4
4
  import {
@@ -14,7 +14,7 @@ import {
14
14
  SerializableContractInstance,
15
15
  } from '@aztec/types/contracts';
16
16
 
17
- import { type ArchiverDataStore } from './archiver_store.js';
17
+ import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
18
18
  import { type DataRetrieval } from './data_retrieval.js';
19
19
 
20
20
  /**
@@ -25,6 +25,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
25
25
  describe(testName, () => {
26
26
  let store: ArchiverDataStore;
27
27
  let blocks: DataRetrieval<L2Block>;
28
+ let blockBodies: DataRetrieval<Body>;
28
29
  const blockTests: [number, number, () => L2Block[]][] = [
29
30
  [1, 1, () => blocks.retrievedData.slice(0, 1)],
30
31
  [10, 1, () => blocks.retrievedData.slice(9, 10)],
@@ -39,11 +40,15 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
39
40
  lastProcessedL1BlockNumber: 5n,
40
41
  retrievedData: Array.from({ length: 10 }).map((_, i) => L2Block.random(i + 1)),
41
42
  };
43
+ blockBodies = {
44
+ retrievedData: blocks.retrievedData.map(block => block.body),
45
+ lastProcessedL1BlockNumber: 4n,
46
+ };
42
47
  });
43
48
 
44
49
  describe('addBlocks', () => {
45
50
  it('returns success when adding block bodies', async () => {
46
- await expect(store.addBlockBodies(blocks.retrievedData.map(block => block.body))).resolves.toBe(true);
51
+ await expect(store.addBlockBodies(blockBodies)).resolves.toBe(true);
47
52
  });
48
53
 
49
54
  it('returns success when adding blocks', async () => {
@@ -59,7 +64,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
59
64
  describe('getBlocks', () => {
60
65
  beforeEach(async () => {
61
66
  await store.addBlocks(blocks);
62
- await store.addBlockBodies(blocks.retrievedData.map(block => block.body));
67
+ await store.addBlockBodies(blockBodies);
63
68
  });
64
69
 
65
70
  it.each(blockTests)('retrieves previously stored blocks', async (start, limit, getExpectedBlocks) => {
@@ -95,7 +100,8 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
95
100
  await expect(store.getSynchPoint()).resolves.toEqual({
96
101
  blocksSynchedTo: 0n,
97
102
  messagesSynchedTo: 0n,
98
- });
103
+ blockBodiesSynchedTo: 0n,
104
+ } satisfies ArchiverL1SynchPoint);
99
105
  });
100
106
 
101
107
  it('returns the L1 block number in which the most recent L2 block was published', async () => {
@@ -103,7 +109,17 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
103
109
  await expect(store.getSynchPoint()).resolves.toEqual({
104
110
  blocksSynchedTo: blocks.lastProcessedL1BlockNumber,
105
111
  messagesSynchedTo: 0n,
106
- });
112
+ blockBodiesSynchedTo: 0n,
113
+ } satisfies ArchiverL1SynchPoint);
114
+ });
115
+
116
+ it('returns the L1 block number in which the most recent L2 block body was published', async () => {
117
+ await store.addBlockBodies(blockBodies);
118
+ await expect(store.getSynchPoint()).resolves.toEqual({
119
+ blocksSynchedTo: 0n,
120
+ messagesSynchedTo: 0n,
121
+ blockBodiesSynchedTo: blockBodies.lastProcessedL1BlockNumber,
122
+ } satisfies ArchiverL1SynchPoint);
107
123
  });
108
124
 
109
125
  it('returns the L1 block number that most recently added messages from inbox', async () => {
@@ -114,7 +130,8 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
114
130
  await expect(store.getSynchPoint()).resolves.toEqual({
115
131
  blocksSynchedTo: 0n,
116
132
  messagesSynchedTo: 1n,
117
- });
133
+ blockBodiesSynchedTo: 0n,
134
+ } satisfies ArchiverL1SynchPoint);
118
135
  });
119
136
  });
120
137
 
@@ -179,7 +196,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
179
196
  ),
180
197
  );
181
198
  await store.addBlocks(blocks);
182
- await store.addBlockBodies(blocks.retrievedData.map(block => block.body));
199
+ await store.addBlockBodies(blockBodies);
183
200
  });
184
201
 
185
202
  it.each([
@@ -335,7 +352,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
335
352
  };
336
353
 
337
354
  await store.addBlocks(blocks);
338
- await store.addBlockBodies(blocks.retrievedData.map(block => block.body));
355
+ await store.addBlockBodies(blockBodies);
339
356
 
340
357
  await Promise.all(
341
358
  blocks.retrievedData.map(block =>
@@ -1,6 +1,7 @@
1
1
  import { type Body, type InboxLeaf } from '@aztec/circuit-types';
2
2
  import { type AppendOnlyTreeSnapshot, Fr, type Header } from '@aztec/circuits.js';
3
3
  import { type EthAddress } from '@aztec/foundation/eth-address';
4
+ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
4
5
  import { RollupAbi } from '@aztec/l1-artifacts';
5
6
 
6
7
  import { type PublicClient, getAbiItem } from 'viem';
@@ -45,6 +46,7 @@ export async function retrieveBlockMetadataFromRollup(
45
46
  searchStartBlock: bigint,
46
47
  searchEndBlock: bigint,
47
48
  expectedNextL2BlockNum: bigint,
49
+ logger: DebugLogger = createDebugLogger('aztec:archiver'),
48
50
  ): Promise<DataRetrieval<[Header, AppendOnlyTreeSnapshot]>> {
49
51
  const retrievedBlockMetadata: [Header, AppendOnlyTreeSnapshot][] = [];
50
52
  do {
@@ -61,13 +63,18 @@ export async function retrieveBlockMetadataFromRollup(
61
63
  break;
62
64
  }
63
65
 
66
+ const lastLog = l2BlockProcessedLogs[l2BlockProcessedLogs.length - 1];
67
+ logger.debug(
68
+ `Got L2 block processed logs for ${l2BlockProcessedLogs[0].blockNumber}-${lastLog.blockNumber} between ${searchStartBlock}-${searchEndBlock} L1 blocks`,
69
+ );
70
+
64
71
  const newBlockMetadata = await processL2BlockProcessedLogs(
65
72
  publicClient,
66
73
  expectedNextL2BlockNum,
67
74
  l2BlockProcessedLogs,
68
75
  );
69
76
  retrievedBlockMetadata.push(...newBlockMetadata);
70
- searchStartBlock = l2BlockProcessedLogs[l2BlockProcessedLogs.length - 1].blockNumber! + 1n;
77
+ searchStartBlock = lastLog.blockNumber! + 1n;
71
78
  expectedNextL2BlockNum += BigInt(newBlockMetadata.length);
72
79
  } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
73
80
  return { lastProcessedL1BlockNumber: searchStartBlock - 1n, retrievedData: retrievedBlockMetadata };
@@ -80,7 +87,7 @@ export async function retrieveBlockMetadataFromRollup(
80
87
  * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
81
88
  * @param searchStartBlock - The block number to use for starting the search.
82
89
  * @param searchEndBlock - The highest block number that we should search up to.
83
- * @returns A array of tuples of L2 block bodies and their associated hash as well as the next eth block to search from
90
+ * @returns A array of L2 block bodies as well as the next eth block to search from
84
91
  */
85
92
  export async function retrieveBlockBodiesFromAvailabilityOracle(
86
93
  publicClient: PublicClient,
@@ -88,8 +95,8 @@ export async function retrieveBlockBodiesFromAvailabilityOracle(
88
95
  blockUntilSynced: boolean,
89
96
  searchStartBlock: bigint,
90
97
  searchEndBlock: bigint,
91
- ): Promise<DataRetrieval<[Body, Buffer]>> {
92
- const retrievedBlockBodies: [Body, Buffer][] = [];
98
+ ): Promise<DataRetrieval<Body>> {
99
+ const retrievedBlockBodies: Body[] = [];
93
100
 
94
101
  do {
95
102
  if (searchStartBlock > searchEndBlock) {
@@ -106,9 +113,10 @@ export async function retrieveBlockBodiesFromAvailabilityOracle(
106
113
  }
107
114
 
108
115
  const newBlockBodies = await processTxsPublishedLogs(publicClient, l2TxsPublishedLogs);
109
- retrievedBlockBodies.push(...newBlockBodies);
110
- searchStartBlock = l2TxsPublishedLogs[l2TxsPublishedLogs.length - 1].blockNumber! + 1n;
116
+ retrievedBlockBodies.push(...newBlockBodies.map(([body]) => body));
117
+ searchStartBlock = l2TxsPublishedLogs[l2TxsPublishedLogs.length - 1].blockNumber + 1n;
111
118
  } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
119
+
112
120
  return { lastProcessedL1BlockNumber: searchStartBlock - 1n, retrievedData: retrievedBlockBodies };
113
121
  }
114
122
 
@@ -100,7 +100,7 @@ async function getBlockMetadataFromRollupTx(
100
100
  if (!(functionName === 'process' || functionName === 'publishAndProcess')) {
101
101
  throw new Error(`Unexpected method called ${functionName}`);
102
102
  }
103
- const [headerHex, archiveRootHex] = args! as readonly [Hex, Hex];
103
+ const [headerHex, archiveRootHex, _] = args! as readonly [Hex, Hex, Hex];
104
104
 
105
105
  const header = Header.fromBuffer(Buffer.from(hexToBytes(headerHex)));
106
106
 
@@ -133,16 +133,16 @@ async function getBlockBodiesFromAvailabilityOracleTx(
133
133
  txHash: `0x${string}`,
134
134
  ): Promise<Body> {
135
135
  const { input: data } = await publicClient.getTransaction({ hash: txHash });
136
- const DATA_INDEX = [3, 2, 0];
136
+ const DATA_INDEX = [4, 3, 0];
137
137
 
138
138
  // @note Use `forge inspect Rollup methodIdentifiers to get this,
139
139
  // If using `forge sig` you will get an INVALID value for the case with a struct.
140
140
  // [
141
- // "publishAndProcess(bytes calldata _header,bytes32 _archive,SignatureLib.Signature[] memory _signatures,bytes calldata _body)",
142
- // "publishAndProcess(bytes calldata _header,bytes32 _archive,bytes calldata _body)",
141
+ // "publishAndProcess(bytes calldata _header,bytes32 _archive,bytes32 _blockHash,SignatureLib.Signature[] memory _signatures,bytes calldata _body)",
142
+ // "publishAndProcess(bytes calldata _header,bytes32 _archive,bytes32 _blockHash,bytes calldata _body)",
143
143
  // "publish(bytes calldata _body)"
144
144
  // ]
145
- const SUPPORTED_SIGS = ['0xe4e90c26', '0xe86e3595', '0x7fd28346'];
145
+ const SUPPORTED_SIGS = ['0x64450c6c', '0xde36c478', '0x7fd28346'];
146
146
 
147
147
  const signature = slice(data, 0, 4);
148
148
 
@@ -37,7 +37,7 @@ export class ArchiverInstrumentation {
37
37
  }
38
38
 
39
39
  public processNewBlocks(syncTimePerBlock: number, blocks: L2Block[]) {
40
- this.syncDuration.record(syncTimePerBlock);
40
+ this.syncDuration.record(Math.ceil(syncTimePerBlock));
41
41
  this.blockHeight.record(Math.max(...blocks.map(b => b.number)));
42
42
  for (const block of blocks) {
43
43
  this.blockSize.record(block.body.txEffects.length);
@@ -1,13 +1,19 @@
1
1
  import { Body } from '@aztec/circuit-types';
2
2
  import { createDebugLogger } from '@aztec/foundation/log';
3
- import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
3
+ import { type AztecKVStore, type AztecMap, type AztecSingleton } from '@aztec/kv-store';
4
+
5
+ import { type DataRetrieval } from '../data_retrieval.js';
4
6
 
5
7
  export class BlockBodyStore {
6
8
  /** Map block body hash to block body */
7
9
  #blockBodies: AztecMap<string, Buffer>;
8
10
 
11
+ /** Stores L1 block number in which the last processed L2 block body was included */
12
+ #lastSynchedL1Block: AztecSingleton<bigint>;
13
+
9
14
  constructor(private db: AztecKVStore, private log = createDebugLogger('aztec:archiver:block_body_store')) {
10
15
  this.#blockBodies = db.openMap('archiver_block_bodies');
16
+ this.#lastSynchedL1Block = db.openSingleton('archiver_block_bodies_last_synched_l1_block');
11
17
  }
12
18
 
13
19
  /**
@@ -15,12 +21,12 @@ export class BlockBodyStore {
15
21
  * @param blockBodies - The L2 block bodies to be added to the store.
16
22
  * @returns True if the operation is successful.
17
23
  */
18
- addBlockBodies(blockBodies: Body[]): Promise<boolean> {
24
+ addBlockBodies(blockBodies: DataRetrieval<Body>): Promise<boolean> {
19
25
  return this.db.transaction(() => {
20
- for (const body of blockBodies) {
26
+ for (const body of blockBodies.retrievedData) {
21
27
  void this.#blockBodies.set(body.getTxsEffectsHash().toString('hex'), body.toBuffer());
22
28
  }
23
-
29
+ void this.#lastSynchedL1Block.set(blockBodies.lastProcessedL1BlockNumber);
24
30
  return true;
25
31
  });
26
32
  }
@@ -57,4 +63,12 @@ export class BlockBodyStore {
57
63
 
58
64
  return blockBody && Body.fromBuffer(blockBody);
59
65
  }
66
+
67
+ /**
68
+ * Gets the last L1 block number in which a L2 block body was included
69
+ * @returns The L1 block number
70
+ */
71
+ getSynchedL1BlockNumber(): bigint {
72
+ return this.#lastSynchedL1Block.get() ?? 0n;
73
+ }
60
74
  }
@@ -101,7 +101,7 @@ export class KVArchiverDataStore implements ArchiverDataStore {
101
101
  * @param blockBodies - The L2 block bodies to be added to the store.
102
102
  * @returns True if the operation is successful.
103
103
  */
104
- addBlockBodies(blockBodies: Body[]): Promise<boolean> {
104
+ addBlockBodies(blockBodies: DataRetrieval<Body>): Promise<boolean> {
105
105
  return this.#blockBodyStore.addBlockBodies(blockBodies);
106
106
  }
107
107
 
@@ -260,6 +260,7 @@ export class KVArchiverDataStore implements ArchiverDataStore {
260
260
  getSynchPoint(): Promise<ArchiverL1SynchPoint> {
261
261
  return Promise.resolve({
262
262
  blocksSynchedTo: this.#blockStore.getSynchedL1BlockNumber(),
263
+ blockBodiesSynchedTo: this.#blockBodyStore.getSynchedL1BlockNumber(),
263
264
  messagesSynchedTo: this.#messageStore.getSynchedL1BlockNumber(),
264
265
  });
265
266
  }
@@ -83,6 +83,7 @@ export class MemoryArchiverStore implements ArchiverDataStore {
83
83
  private contractInstances: Map<string, ContractInstanceWithAddress> = new Map();
84
84
 
85
85
  private lastL1BlockNewBlocks: bigint = 0n;
86
+ private lastL1BlockNewBlockBodies: bigint = 0n;
86
87
  private lastL1BlockNewMessages: bigint = 0n;
87
88
  private lastProvenL2BlockNumber: number = 0;
88
89
 
@@ -163,11 +164,11 @@ export class MemoryArchiverStore implements ArchiverDataStore {
163
164
  * @param blockBodies - The L2 block bodies to be added to the store.
164
165
  * @returns True if the operation is successful.
165
166
  */
166
- addBlockBodies(blockBodies: Body[]): Promise<boolean> {
167
- for (const body of blockBodies) {
167
+ addBlockBodies(blockBodies: DataRetrieval<Body>): Promise<boolean> {
168
+ for (const body of blockBodies.retrievedData) {
168
169
  void this.l2BlockBodies.set(body.getTxsEffectsHash().toString('hex'), body);
169
170
  }
170
-
171
+ this.lastL1BlockNewBlockBodies = blockBodies.lastProcessedL1BlockNumber;
171
172
  return Promise.resolve(true);
172
173
  }
173
174
 
@@ -443,6 +444,7 @@ export class MemoryArchiverStore implements ArchiverDataStore {
443
444
  return Promise.resolve({
444
445
  blocksSynchedTo: this.lastL1BlockNewBlocks,
445
446
  messagesSynchedTo: this.lastL1BlockNewMessages,
447
+ blockBodiesSynchedTo: this.lastL1BlockNewBlockBodies,
446
448
  });
447
449
  }
448
450