@aztec/archiver 0.0.0-test.0 → 0.0.1-commit.24de95ac
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/README.md +27 -6
- package/dest/archiver/archiver.d.ts +126 -46
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +683 -261
- package/dest/archiver/archiver_store.d.ts +84 -49
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +707 -213
- package/dest/archiver/config.d.ts +4 -20
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +16 -12
- package/dest/archiver/data_retrieval.d.ts +25 -20
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +147 -68
- package/dest/archiver/errors.d.ts +8 -0
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/errors.js +12 -0
- package/dest/archiver/index.d.ts +2 -3
- package/dest/archiver/index.d.ts.map +1 -1
- package/dest/archiver/index.js +1 -2
- package/dest/archiver/instrumentation.d.ts +9 -3
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/instrumentation.js +58 -17
- package/dest/archiver/kv_archiver_store/block_store.d.ts +47 -10
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +216 -63
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +12 -18
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +10 -7
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +30 -16
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +49 -34
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +88 -46
- package/dest/archiver/kv_archiver_store/log_store.d.ts +1 -1
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +18 -46
- package/dest/archiver/kv_archiver_store/message_store.d.ts +22 -16
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +150 -48
- package/dest/archiver/structs/inbox_message.d.ts +15 -0
- package/dest/archiver/structs/inbox_message.d.ts.map +1 -0
- package/dest/archiver/structs/inbox_message.js +38 -0
- package/dest/archiver/structs/published.d.ts +1 -10
- package/dest/archiver/structs/published.d.ts.map +1 -1
- package/dest/archiver/structs/published.js +1 -1
- package/dest/archiver/validation.d.ts +11 -0
- package/dest/archiver/validation.d.ts.map +1 -0
- package/dest/archiver/validation.js +90 -0
- package/dest/factory.d.ts +7 -12
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +18 -49
- package/dest/rpc/index.d.ts +1 -2
- package/dest/rpc/index.d.ts.map +1 -1
- package/dest/rpc/index.js +1 -4
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts +4 -2
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +14 -1
- package/dest/test/mock_l2_block_source.d.ts +32 -5
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +118 -7
- package/dest/test/mock_structs.d.ts +9 -0
- package/dest/test/mock_structs.d.ts.map +1 -0
- package/dest/test/mock_structs.js +37 -0
- package/package.json +25 -27
- package/src/archiver/archiver.ts +858 -317
- package/src/archiver/archiver_store.ts +97 -55
- package/src/archiver/archiver_store_test_suite.ts +663 -210
- package/src/archiver/config.ts +23 -41
- package/src/archiver/data_retrieval.ts +215 -92
- package/src/archiver/errors.ts +21 -0
- package/src/archiver/index.ts +2 -3
- package/src/archiver/instrumentation.ts +75 -20
- package/src/archiver/kv_archiver_store/block_store.ts +270 -72
- package/src/archiver/kv_archiver_store/contract_class_store.ts +13 -23
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +35 -27
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +127 -63
- package/src/archiver/kv_archiver_store/log_store.ts +24 -62
- package/src/archiver/kv_archiver_store/message_store.ts +209 -53
- package/src/archiver/structs/inbox_message.ts +41 -0
- package/src/archiver/structs/published.ts +1 -11
- package/src/archiver/validation.ts +99 -0
- package/src/factory.ts +24 -66
- package/src/rpc/index.ts +1 -5
- package/src/test/mock_archiver.ts +1 -1
- package/src/test/mock_l1_to_l2_message_source.ts +14 -3
- package/src/test/mock_l2_block_source.ts +152 -8
- package/src/test/mock_structs.ts +49 -0
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +0 -12
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/nullifier_store.js +0 -73
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +0 -23
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +0 -49
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +0 -175
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +0 -636
- package/src/archiver/kv_archiver_store/nullifier_store.ts +0 -97
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +0 -61
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +0 -801
|
@@ -18,32 +18,43 @@ export class ArchiverInstrumentation {
|
|
|
18
18
|
|
|
19
19
|
private blockHeight: Gauge;
|
|
20
20
|
private txCount: UpDownCounter;
|
|
21
|
-
private syncDuration: Histogram;
|
|
22
|
-
private l1BlocksSynced: UpDownCounter;
|
|
23
21
|
private l1BlockHeight: Gauge;
|
|
24
22
|
private proofsSubmittedDelay: Histogram;
|
|
25
23
|
private proofsSubmittedCount: UpDownCounter;
|
|
26
24
|
private dbMetrics: LmdbMetrics;
|
|
25
|
+
|
|
26
|
+
private pruneDuration: Histogram;
|
|
27
27
|
private pruneCount: UpDownCounter;
|
|
28
28
|
|
|
29
|
+
private syncDurationPerBlock: Histogram;
|
|
30
|
+
private syncBlockCount: UpDownCounter;
|
|
31
|
+
private manaPerBlock: Histogram;
|
|
32
|
+
private txsPerBlock: Histogram;
|
|
33
|
+
|
|
34
|
+
private syncDurationPerMessage: Histogram;
|
|
35
|
+
private syncMessageCount: UpDownCounter;
|
|
36
|
+
|
|
29
37
|
private log = createLogger('archiver:instrumentation');
|
|
30
38
|
|
|
31
|
-
private constructor(
|
|
39
|
+
private constructor(
|
|
40
|
+
private telemetry: TelemetryClient,
|
|
41
|
+
lmdbStats?: LmdbStatsCallback,
|
|
42
|
+
) {
|
|
32
43
|
this.tracer = telemetry.getTracer('Archiver');
|
|
33
44
|
const meter = telemetry.getMeter('Archiver');
|
|
45
|
+
|
|
34
46
|
this.blockHeight = meter.createGauge(Metrics.ARCHIVER_BLOCK_HEIGHT, {
|
|
35
47
|
description: 'The height of the latest block processed by the archiver',
|
|
36
48
|
valueType: ValueType.INT,
|
|
37
49
|
});
|
|
38
50
|
|
|
39
|
-
this.
|
|
40
|
-
description: 'The
|
|
51
|
+
this.l1BlockHeight = meter.createGauge(Metrics.ARCHIVER_L1_BLOCK_HEIGHT, {
|
|
52
|
+
description: 'The height of the latest L1 block processed by the archiver',
|
|
41
53
|
valueType: ValueType.INT,
|
|
42
54
|
});
|
|
43
55
|
|
|
44
|
-
this.
|
|
45
|
-
|
|
46
|
-
description: 'Duration to sync a block',
|
|
56
|
+
this.txCount = meter.createUpDownCounter(Metrics.ARCHIVER_TOTAL_TXS, {
|
|
57
|
+
description: 'The total number of transactions',
|
|
47
58
|
valueType: ValueType.INT,
|
|
48
59
|
});
|
|
49
60
|
|
|
@@ -58,13 +69,48 @@ export class ArchiverInstrumentation {
|
|
|
58
69
|
valueType: ValueType.INT,
|
|
59
70
|
});
|
|
60
71
|
|
|
61
|
-
this.
|
|
72
|
+
this.syncDurationPerBlock = meter.createHistogram(Metrics.ARCHIVER_SYNC_PER_BLOCK, {
|
|
73
|
+
unit: 'ms',
|
|
74
|
+
description: 'Duration to sync a block',
|
|
75
|
+
valueType: ValueType.INT,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.syncBlockCount = meter.createUpDownCounter(Metrics.ARCHIVER_SYNC_BLOCK_COUNT, {
|
|
62
79
|
description: 'Number of blocks synced from L1',
|
|
63
80
|
valueType: ValueType.INT,
|
|
64
81
|
});
|
|
65
82
|
|
|
66
|
-
this.
|
|
67
|
-
description: 'The
|
|
83
|
+
this.manaPerBlock = meter.createHistogram(Metrics.ARCHIVER_MANA_PER_BLOCK, {
|
|
84
|
+
description: 'The mana consumed by blocks',
|
|
85
|
+
valueType: ValueType.DOUBLE,
|
|
86
|
+
unit: 'Mmana',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
this.txsPerBlock = meter.createHistogram(Metrics.ARCHIVER_TXS_PER_BLOCK, {
|
|
90
|
+
description: 'The block tx count',
|
|
91
|
+
valueType: ValueType.INT,
|
|
92
|
+
unit: 'tx',
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
this.syncDurationPerMessage = meter.createHistogram(Metrics.ARCHIVER_SYNC_PER_MESSAGE, {
|
|
96
|
+
unit: 'ms',
|
|
97
|
+
description: 'Duration to sync a message',
|
|
98
|
+
valueType: ValueType.INT,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.syncMessageCount = meter.createUpDownCounter(Metrics.ARCHIVER_SYNC_MESSAGE_COUNT, {
|
|
102
|
+
description: 'Number of L1 to L2 messages synced',
|
|
103
|
+
valueType: ValueType.INT,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this.pruneDuration = meter.createHistogram(Metrics.ARCHIVER_PRUNE_DURATION, {
|
|
107
|
+
unit: 'ms',
|
|
108
|
+
description: 'Duration to sync a message',
|
|
109
|
+
valueType: ValueType.INT,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
this.pruneCount = meter.createUpDownCounter(Metrics.ARCHIVER_PRUNE_COUNT, {
|
|
113
|
+
description: 'Number of prunes detected',
|
|
68
114
|
valueType: ValueType.INT,
|
|
69
115
|
});
|
|
70
116
|
|
|
@@ -75,17 +121,14 @@ export class ArchiverInstrumentation {
|
|
|
75
121
|
},
|
|
76
122
|
lmdbStats,
|
|
77
123
|
);
|
|
78
|
-
|
|
79
|
-
this.pruneCount = meter.createUpDownCounter(Metrics.ARCHIVER_PRUNE_COUNT, {
|
|
80
|
-
description: 'Number of prunes detected',
|
|
81
|
-
valueType: ValueType.INT,
|
|
82
|
-
});
|
|
83
124
|
}
|
|
84
125
|
|
|
85
126
|
public static async new(telemetry: TelemetryClient, lmdbStats?: LmdbStatsCallback) {
|
|
86
127
|
const instance = new ArchiverInstrumentation(telemetry, lmdbStats);
|
|
87
128
|
|
|
88
|
-
instance.
|
|
129
|
+
instance.syncBlockCount.add(0);
|
|
130
|
+
instance.syncMessageCount.add(0);
|
|
131
|
+
instance.pruneCount.add(0);
|
|
89
132
|
|
|
90
133
|
await instance.telemetry.flush();
|
|
91
134
|
|
|
@@ -97,16 +140,28 @@ export class ArchiverInstrumentation {
|
|
|
97
140
|
}
|
|
98
141
|
|
|
99
142
|
public processNewBlocks(syncTimePerBlock: number, blocks: L2Block[]) {
|
|
100
|
-
this.
|
|
143
|
+
this.syncDurationPerBlock.record(Math.ceil(syncTimePerBlock));
|
|
101
144
|
this.blockHeight.record(Math.max(...blocks.map(b => b.number)));
|
|
102
|
-
this.
|
|
145
|
+
this.syncBlockCount.add(blocks.length);
|
|
146
|
+
|
|
103
147
|
for (const block of blocks) {
|
|
104
148
|
this.txCount.add(block.body.txEffects.length);
|
|
149
|
+
this.txsPerBlock.record(block.body.txEffects.length);
|
|
150
|
+
this.manaPerBlock.record(block.header.totalManaUsed.toNumber() / 1e6);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public processNewMessages(count: number, syncPerMessageMs: number) {
|
|
155
|
+
if (count === 0) {
|
|
156
|
+
return;
|
|
105
157
|
}
|
|
158
|
+
this.syncMessageCount.add(count);
|
|
159
|
+
this.syncDurationPerMessage.record(Math.ceil(syncPerMessageMs));
|
|
106
160
|
}
|
|
107
161
|
|
|
108
|
-
public processPrune() {
|
|
162
|
+
public processPrune(duration: number) {
|
|
109
163
|
this.pruneCount.add(1);
|
|
164
|
+
this.pruneDuration.record(Math.ceil(duration));
|
|
110
165
|
}
|
|
111
166
|
|
|
112
167
|
public updateLastProvenBlock(blockNumber: number) {
|
|
@@ -1,22 +1,44 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
2
3
|
import { toArray } from '@aztec/foundation/iterable';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import { BufferReader } from '@aztec/foundation/serialize';
|
|
6
|
+
import { bufferToHex } from '@aztec/foundation/string';
|
|
4
7
|
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
|
|
5
8
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Body,
|
|
11
|
+
CommitteeAttestation,
|
|
12
|
+
L2Block,
|
|
13
|
+
L2BlockHash,
|
|
14
|
+
PublishedL2Block,
|
|
15
|
+
type ValidateBlockResult,
|
|
16
|
+
} from '@aztec/stdlib/block';
|
|
17
|
+
import { L2BlockHeader, deserializeValidateBlockResult, serializeValidateBlockResult } from '@aztec/stdlib/block';
|
|
7
18
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
8
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
BlockHeader,
|
|
21
|
+
type IndexedTxEffect,
|
|
22
|
+
TxEffect,
|
|
23
|
+
TxHash,
|
|
24
|
+
TxReceipt,
|
|
25
|
+
deserializeIndexedTxEffect,
|
|
26
|
+
serializeIndexedTxEffect,
|
|
27
|
+
} from '@aztec/stdlib/tx';
|
|
9
28
|
|
|
10
|
-
import
|
|
29
|
+
import { BlockNumberNotSequentialError, InitialBlockNumberNotSequentialError } from '../errors.js';
|
|
30
|
+
import type { L1PublishedData } from '../structs/published.js';
|
|
11
31
|
|
|
12
|
-
export { type TxEffect, type TxHash
|
|
32
|
+
export { TxReceipt, type TxEffect, type TxHash } from '@aztec/stdlib/tx';
|
|
13
33
|
|
|
14
34
|
type BlockIndexValue = [blockNumber: number, index: number];
|
|
15
35
|
|
|
16
36
|
type BlockStorage = {
|
|
17
37
|
header: Buffer;
|
|
38
|
+
blockHash: Buffer;
|
|
18
39
|
archive: Buffer;
|
|
19
40
|
l1: L1PublishedData;
|
|
41
|
+
attestations: Buffer[];
|
|
20
42
|
};
|
|
21
43
|
|
|
22
44
|
/**
|
|
@@ -26,8 +48,11 @@ export class BlockStore {
|
|
|
26
48
|
/** Map block number to block data */
|
|
27
49
|
#blocks: AztecAsyncMap<number, BlockStorage>;
|
|
28
50
|
|
|
29
|
-
/** Map block hash to
|
|
30
|
-
#
|
|
51
|
+
/** Map block hash to list of tx hashes */
|
|
52
|
+
#blockTxs: AztecAsyncMap<string, Buffer>;
|
|
53
|
+
|
|
54
|
+
/** Tx hash to serialized IndexedTxEffect */
|
|
55
|
+
#txEffects: AztecAsyncMap<string, Buffer>;
|
|
31
56
|
|
|
32
57
|
/** Stores L1 block number in which the last processed L2 block was included */
|
|
33
58
|
#lastSynchedL1Block: AztecAsyncSingleton<bigint>;
|
|
@@ -35,25 +60,30 @@ export class BlockStore {
|
|
|
35
60
|
/** Stores l2 block number of the last proven block */
|
|
36
61
|
#lastProvenL2Block: AztecAsyncSingleton<number>;
|
|
37
62
|
|
|
38
|
-
/** Stores
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
/** Index mapping transaction hash (as a string) to its location in a block */
|
|
42
|
-
#txIndex: AztecAsyncMap<string, BlockIndexValue>;
|
|
63
|
+
/** Stores the pending chain validation status */
|
|
64
|
+
#pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
|
|
43
65
|
|
|
44
66
|
/** Index mapping a contract's address (as a string) to its location in a block */
|
|
45
67
|
#contractIndex: AztecAsyncMap<string, BlockIndexValue>;
|
|
46
68
|
|
|
69
|
+
/** Index mapping block hash to block number */
|
|
70
|
+
#blockHashIndex: AztecAsyncMap<string, number>;
|
|
71
|
+
|
|
72
|
+
/** Index mapping block archive to block number */
|
|
73
|
+
#blockArchiveIndex: AztecAsyncMap<string, number>;
|
|
74
|
+
|
|
47
75
|
#log = createLogger('archiver:block_store');
|
|
48
76
|
|
|
49
77
|
constructor(private db: AztecAsyncKVStore) {
|
|
50
78
|
this.#blocks = db.openMap('archiver_blocks');
|
|
51
|
-
this.#
|
|
52
|
-
this.#
|
|
79
|
+
this.#blockTxs = db.openMap('archiver_block_txs');
|
|
80
|
+
this.#txEffects = db.openMap('archiver_tx_effects');
|
|
53
81
|
this.#contractIndex = db.openMap('archiver_contract_index');
|
|
82
|
+
this.#blockHashIndex = db.openMap('archiver_block_hash_index');
|
|
83
|
+
this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
|
|
54
84
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
55
85
|
this.#lastProvenL2Block = db.openSingleton('archiver_last_proven_l2_block');
|
|
56
|
-
this.#
|
|
86
|
+
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
57
87
|
}
|
|
58
88
|
|
|
59
89
|
/**
|
|
@@ -61,25 +91,59 @@ export class BlockStore {
|
|
|
61
91
|
* @param blocks - The L2 blocks to be added to the store.
|
|
62
92
|
* @returns True if the operation is successful.
|
|
63
93
|
*/
|
|
64
|
-
async addBlocks(blocks:
|
|
94
|
+
async addBlocks(blocks: PublishedL2Block[], opts: { force?: boolean } = {}): Promise<boolean> {
|
|
65
95
|
if (blocks.length === 0) {
|
|
66
96
|
return true;
|
|
67
97
|
}
|
|
68
98
|
|
|
69
99
|
return await this.db.transactionAsync(async () => {
|
|
100
|
+
// Check that the block immediately before the first block to be added is present in the store.
|
|
101
|
+
const firstBlockNumber = blocks[0].block.number;
|
|
102
|
+
const [previousBlockNumber] = await toArray(
|
|
103
|
+
this.#blocks.keysAsync({ reverse: true, limit: 1, end: firstBlockNumber - 1 }),
|
|
104
|
+
);
|
|
105
|
+
const hasPreviousBlock =
|
|
106
|
+
firstBlockNumber === INITIAL_L2_BLOCK_NUM ||
|
|
107
|
+
(previousBlockNumber !== undefined && previousBlockNumber === firstBlockNumber - 1);
|
|
108
|
+
if (!opts.force && !hasPreviousBlock) {
|
|
109
|
+
throw new InitialBlockNumberNotSequentialError(firstBlockNumber, previousBlockNumber);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Iterate over blocks array and insert them, checking that the block numbers are sequential.
|
|
113
|
+
let previousBlock: PublishedL2Block | undefined = undefined;
|
|
70
114
|
for (const block of blocks) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
115
|
+
if (!opts.force && previousBlock && previousBlock.block.number + 1 !== block.block.number) {
|
|
116
|
+
throw new BlockNumberNotSequentialError(block.block.number, previousBlock.block.number);
|
|
117
|
+
}
|
|
118
|
+
previousBlock = block;
|
|
119
|
+
const blockHash = L2BlockHash.fromField(await block.block.hash());
|
|
120
|
+
|
|
121
|
+
await this.#blocks.set(block.block.number, {
|
|
122
|
+
header: block.block.header.toBuffer(),
|
|
123
|
+
blockHash: blockHash.toBuffer(),
|
|
124
|
+
archive: block.block.archive.toBuffer(),
|
|
74
125
|
l1: block.l1,
|
|
126
|
+
attestations: block.attestations.map(attestation => attestation.toBuffer()),
|
|
75
127
|
});
|
|
76
128
|
|
|
77
|
-
for (let i = 0; i < block.
|
|
78
|
-
const txEffect =
|
|
79
|
-
|
|
129
|
+
for (let i = 0; i < block.block.body.txEffects.length; i++) {
|
|
130
|
+
const txEffect: IndexedTxEffect = {
|
|
131
|
+
data: block.block.body.txEffects[i],
|
|
132
|
+
l2BlockNumber: block.block.number,
|
|
133
|
+
l2BlockHash: blockHash,
|
|
134
|
+
txIndexInBlock: i,
|
|
135
|
+
};
|
|
136
|
+
await this.#txEffects.set(txEffect.data.txHash.toString(), serializeIndexedTxEffect(txEffect));
|
|
80
137
|
}
|
|
81
138
|
|
|
82
|
-
await this.#
|
|
139
|
+
await this.#blockTxs.set(
|
|
140
|
+
blockHash.toString(),
|
|
141
|
+
Buffer.concat(block.block.body.txEffects.map(tx => tx.txHash.toBuffer())),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Update indices for block hash and archive
|
|
145
|
+
await this.#blockHashIndex.set(blockHash.toString(), block.block.number);
|
|
146
|
+
await this.#blockArchiveIndex.set(block.block.archive.root.toString(), block.block.number);
|
|
83
147
|
}
|
|
84
148
|
|
|
85
149
|
await this.#lastSynchedL1Block.set(blocks[blocks.length - 1].l1.blockNumber);
|
|
@@ -101,6 +165,11 @@ export class BlockStore {
|
|
|
101
165
|
throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`);
|
|
102
166
|
}
|
|
103
167
|
|
|
168
|
+
const proven = await this.getProvenL2BlockNumber();
|
|
169
|
+
if (from - blocksToUnwind < proven) {
|
|
170
|
+
await this.setProvenL2BlockNumber(from - blocksToUnwind);
|
|
171
|
+
}
|
|
172
|
+
|
|
104
173
|
for (let i = 0; i < blocksToUnwind; i++) {
|
|
105
174
|
const blockNumber = from - i;
|
|
106
175
|
const block = await this.getBlock(blockNumber);
|
|
@@ -109,10 +178,15 @@ export class BlockStore {
|
|
|
109
178
|
this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
|
|
110
179
|
continue;
|
|
111
180
|
}
|
|
112
|
-
await this.#blocks.delete(block.
|
|
113
|
-
await Promise.all(block.
|
|
114
|
-
const blockHash = (await block.
|
|
115
|
-
await this.#
|
|
181
|
+
await this.#blocks.delete(block.block.number);
|
|
182
|
+
await Promise.all(block.block.body.txEffects.map(tx => this.#txEffects.delete(tx.txHash.toString())));
|
|
183
|
+
const blockHash = (await block.block.hash()).toString();
|
|
184
|
+
await this.#blockTxs.delete(blockHash);
|
|
185
|
+
|
|
186
|
+
// Clean up indices
|
|
187
|
+
await this.#blockHashIndex.delete(blockHash);
|
|
188
|
+
await this.#blockArchiveIndex.delete(block.block.archive.root.toString());
|
|
189
|
+
|
|
116
190
|
this.#log.debug(`Unwound block ${blockNumber} ${blockHash}`);
|
|
117
191
|
}
|
|
118
192
|
|
|
@@ -126,10 +200,12 @@ export class BlockStore {
|
|
|
126
200
|
* @param limit - The number of blocks to return.
|
|
127
201
|
* @returns The requested L2 blocks
|
|
128
202
|
*/
|
|
129
|
-
async *getBlocks(start: number, limit: number): AsyncIterableIterator<
|
|
130
|
-
for await (const blockStorage of this
|
|
131
|
-
const block = await this.getBlockFromBlockStorage(blockStorage);
|
|
132
|
-
|
|
203
|
+
async *getBlocks(start: number, limit: number): AsyncIterableIterator<PublishedL2Block> {
|
|
204
|
+
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
205
|
+
const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
206
|
+
if (block) {
|
|
207
|
+
yield block;
|
|
208
|
+
}
|
|
133
209
|
}
|
|
134
210
|
}
|
|
135
211
|
|
|
@@ -138,13 +214,72 @@ export class BlockStore {
|
|
|
138
214
|
* @param blockNumber - The number of the block to return.
|
|
139
215
|
* @returns The requested L2 block.
|
|
140
216
|
*/
|
|
141
|
-
async getBlock(blockNumber: number): Promise<
|
|
217
|
+
async getBlock(blockNumber: number): Promise<PublishedL2Block | undefined> {
|
|
142
218
|
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
143
219
|
if (!blockStorage || !blockStorage.header) {
|
|
144
220
|
return Promise.resolve(undefined);
|
|
145
221
|
}
|
|
222
|
+
return this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Gets an L2 block by its hash.
|
|
227
|
+
* @param blockHash - The hash of the block to return.
|
|
228
|
+
* @returns The requested L2 block.
|
|
229
|
+
*/
|
|
230
|
+
async getBlockByHash(blockHash: L2BlockHash): Promise<PublishedL2Block | undefined> {
|
|
231
|
+
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
232
|
+
if (blockNumber === undefined) {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
return this.getBlock(blockNumber);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Gets an L2 block by its archive root.
|
|
240
|
+
* @param archive - The archive root of the block to return.
|
|
241
|
+
* @returns The requested L2 block.
|
|
242
|
+
*/
|
|
243
|
+
async getBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
|
|
244
|
+
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
245
|
+
if (blockNumber === undefined) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
return this.getBlock(blockNumber);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Gets a block header by its hash.
|
|
253
|
+
* @param blockHash - The hash of the block to return.
|
|
254
|
+
* @returns The requested block header.
|
|
255
|
+
*/
|
|
256
|
+
async getBlockHeaderByHash(blockHash: L2BlockHash): Promise<BlockHeader | undefined> {
|
|
257
|
+
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
258
|
+
if (blockNumber === undefined) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
262
|
+
if (!blockStorage || !blockStorage.header) {
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
return L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
|
|
266
|
+
}
|
|
146
267
|
|
|
147
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Gets a block header by its archive root.
|
|
270
|
+
* @param archive - The archive root of the block to return.
|
|
271
|
+
* @returns The requested block header.
|
|
272
|
+
*/
|
|
273
|
+
async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
274
|
+
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
275
|
+
if (blockNumber === undefined) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
279
|
+
if (!blockStorage || !blockStorage.header) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
return L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
|
|
148
283
|
}
|
|
149
284
|
|
|
150
285
|
/**
|
|
@@ -154,48 +289,80 @@ export class BlockStore {
|
|
|
154
289
|
* @returns The requested L2 block headers
|
|
155
290
|
*/
|
|
156
291
|
async *getBlockHeaders(start: number, limit: number): AsyncIterableIterator<BlockHeader> {
|
|
157
|
-
for await (const blockStorage of this
|
|
158
|
-
|
|
292
|
+
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
293
|
+
const header = L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
|
|
294
|
+
if (header.getBlockNumber() !== blockNumber) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`Block number mismatch when retrieving block header from archive (expected ${blockNumber} but got ${header.getBlockNumber()})`,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
yield header;
|
|
159
300
|
}
|
|
160
301
|
}
|
|
161
302
|
|
|
162
|
-
private async
|
|
163
|
-
|
|
303
|
+
private async *getBlockStorages(start: number, limit: number) {
|
|
304
|
+
let expectedBlockNumber = start;
|
|
305
|
+
for await (const [blockNumber, blockStorage] of this.#blocks.entriesAsync(this.#computeBlockRange(start, limit))) {
|
|
306
|
+
if (blockNumber !== expectedBlockNumber) {
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Block number mismatch when iterating blocks from archive (expected ${expectedBlockNumber} but got ${blockNumber})`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
expectedBlockNumber++;
|
|
312
|
+
yield [blockNumber, blockStorage] as const;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private async getBlockFromBlockStorage(
|
|
317
|
+
blockNumber: number,
|
|
318
|
+
blockStorage: BlockStorage,
|
|
319
|
+
): Promise<PublishedL2Block | undefined> {
|
|
320
|
+
const header = L2BlockHeader.fromBuffer(blockStorage.header);
|
|
164
321
|
const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
|
|
165
|
-
const blockHash =
|
|
166
|
-
const
|
|
167
|
-
|
|
322
|
+
const blockHash = blockStorage.blockHash;
|
|
323
|
+
const blockHashString = bufferToHex(blockHash);
|
|
324
|
+
const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
|
|
325
|
+
if (blockTxsBuffer === undefined) {
|
|
326
|
+
this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const txEffects: TxEffect[] = [];
|
|
331
|
+
const reader = BufferReader.asReader(blockTxsBuffer);
|
|
332
|
+
while (!reader.isEmpty()) {
|
|
333
|
+
const txHash = reader.readObject(TxHash);
|
|
334
|
+
const txEffect = await this.#txEffects.getAsync(txHash.toString());
|
|
335
|
+
if (txEffect === undefined) {
|
|
336
|
+
this.#log.warn(`Could not find tx effect for tx ${txHash} in block ${blockNumber}`);
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
txEffects.push(deserializeIndexedTxEffect(txEffect).data);
|
|
340
|
+
}
|
|
341
|
+
const body = new Body(txEffects);
|
|
342
|
+
const block = new L2Block(archive, header, body, Fr.fromBuffer(blockHash));
|
|
343
|
+
|
|
344
|
+
if (block.number !== blockNumber) {
|
|
168
345
|
throw new Error(
|
|
169
|
-
`
|
|
346
|
+
`Block number mismatch when retrieving block from archive (expected ${blockNumber} but got ${
|
|
347
|
+
block.number
|
|
348
|
+
} with hash ${blockHashString})`,
|
|
170
349
|
);
|
|
171
350
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
const l2Block = new L2Block(archive, header, body);
|
|
175
|
-
return { data: l2Block, l1: blockStorage.l1 };
|
|
351
|
+
const attestations = blockStorage.attestations.map(CommitteeAttestation.fromBuffer);
|
|
352
|
+
return PublishedL2Block.fromFields({ block, l1: blockStorage.l1, attestations });
|
|
176
353
|
}
|
|
177
354
|
|
|
178
355
|
/**
|
|
179
356
|
* Gets a tx effect.
|
|
180
|
-
* @param txHash - The
|
|
181
|
-
* @returns The requested tx effect (or undefined if not found).
|
|
357
|
+
* @param txHash - The hash of the tx corresponding to the tx effect.
|
|
358
|
+
* @returns The requested tx effect with block info (or undefined if not found).
|
|
182
359
|
*/
|
|
183
|
-
async getTxEffect(txHash: TxHash): Promise<
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
360
|
+
async getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
361
|
+
const buffer = await this.#txEffects.getAsync(txHash.toString());
|
|
362
|
+
if (!buffer) {
|
|
186
363
|
return undefined;
|
|
187
364
|
}
|
|
188
|
-
|
|
189
|
-
const block = await this.getBlock(blockNumber);
|
|
190
|
-
if (!block) {
|
|
191
|
-
return undefined;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
data: block.data.body.txEffects[txIndex],
|
|
196
|
-
l2BlockNumber: block.data.number,
|
|
197
|
-
l2BlockHash: (await block.data.hash()).toString(),
|
|
198
|
-
};
|
|
365
|
+
return deserializeIndexedTxEffect(buffer);
|
|
199
366
|
}
|
|
200
367
|
|
|
201
368
|
/**
|
|
@@ -204,21 +371,18 @@ export class BlockStore {
|
|
|
204
371
|
* @returns The requested tx receipt (or undefined if not found).
|
|
205
372
|
*/
|
|
206
373
|
async getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
374
|
+
const txEffect = await this.getTxEffect(txHash);
|
|
375
|
+
if (!txEffect) {
|
|
209
376
|
return undefined;
|
|
210
377
|
}
|
|
211
378
|
|
|
212
|
-
const block = (await this.getBlock(blockNumber))!;
|
|
213
|
-
const tx = block.data.body.txEffects[txIndex];
|
|
214
|
-
|
|
215
379
|
return new TxReceipt(
|
|
216
380
|
txHash,
|
|
217
|
-
TxReceipt.statusFromRevertCode(
|
|
381
|
+
TxReceipt.statusFromRevertCode(txEffect.data.revertCode),
|
|
218
382
|
'',
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
383
|
+
txEffect.data.transactionFee.toBigInt(),
|
|
384
|
+
txEffect.l2BlockHash,
|
|
385
|
+
txEffect.l2BlockNumber,
|
|
222
386
|
);
|
|
223
387
|
}
|
|
224
388
|
|
|
@@ -227,8 +391,13 @@ export class BlockStore {
|
|
|
227
391
|
* @param txHash - The txHash of the tx.
|
|
228
392
|
* @returns The block number and index of the tx.
|
|
229
393
|
*/
|
|
230
|
-
getTxLocation(txHash: TxHash): Promise<[blockNumber: number, txIndex: number] | undefined> {
|
|
231
|
-
|
|
394
|
+
public async getTxLocation(txHash: TxHash): Promise<[blockNumber: number, txIndex: number] | undefined> {
|
|
395
|
+
const txEffect = await this.#txEffects.getAsync(txHash.toString());
|
|
396
|
+
if (!txEffect) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
const { l2BlockNumber, txIndexInBlock } = deserializeIndexedTxEffect(txEffect);
|
|
400
|
+
return [l2BlockNumber, txIndexInBlock];
|
|
232
401
|
}
|
|
233
402
|
|
|
234
403
|
/**
|
|
@@ -262,7 +431,11 @@ export class BlockStore {
|
|
|
262
431
|
}
|
|
263
432
|
|
|
264
433
|
async getProvenL2BlockNumber(): Promise<number> {
|
|
265
|
-
|
|
434
|
+
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
|
|
435
|
+
this.getSynchedL2BlockNumber(),
|
|
436
|
+
this.#lastProvenL2Block.getAsync(),
|
|
437
|
+
]);
|
|
438
|
+
return (provenBlockNumber ?? 0) > latestBlockNumber ? latestBlockNumber : (provenBlockNumber ?? 0);
|
|
266
439
|
}
|
|
267
440
|
|
|
268
441
|
setProvenL2BlockNumber(blockNumber: number) {
|
|
@@ -280,4 +453,29 @@ export class BlockStore {
|
|
|
280
453
|
|
|
281
454
|
return { start, limit };
|
|
282
455
|
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Gets the pending chain validation status.
|
|
459
|
+
* @returns The validation status or undefined if not set.
|
|
460
|
+
*/
|
|
461
|
+
async getPendingChainValidationStatus(): Promise<ValidateBlockResult | undefined> {
|
|
462
|
+
const buffer = await this.#pendingChainValidationStatus.getAsync();
|
|
463
|
+
if (!buffer) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
return deserializeValidateBlockResult(buffer);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Sets the pending chain validation status.
|
|
471
|
+
* @param status - The validation status to store.
|
|
472
|
+
*/
|
|
473
|
+
async setPendingChainValidationStatus(status: ValidateBlockResult | undefined): Promise<void> {
|
|
474
|
+
if (status) {
|
|
475
|
+
const buffer = serializeValidateBlockResult(status);
|
|
476
|
+
await this.#pendingChainValidationStatus.set(buffer);
|
|
477
|
+
} else {
|
|
478
|
+
await this.#pendingChainValidationStatus.delete();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
283
481
|
}
|