@aztec/archiver 0.0.1-commit.b655e406 → 0.0.1-commit.c7c42ec
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/archiver/archiver.d.ts +110 -83
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +672 -349
- package/dest/archiver/archiver_store.d.ts +100 -47
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +1871 -389
- package/dest/archiver/config.d.ts +5 -4
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +15 -3
- package/dest/archiver/errors.d.ts +25 -1
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/errors.js +37 -0
- package/dest/archiver/index.d.ts +2 -2
- package/dest/archiver/index.d.ts.map +1 -1
- package/dest/archiver/instrumentation.d.ts +5 -3
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/instrumentation.js +11 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +51 -18
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +324 -87
- 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 +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +47 -57
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +65 -48
- package/dest/archiver/kv_archiver_store/log_store.d.ts +12 -16
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +149 -84
- package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -5
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +15 -14
- package/dest/archiver/l1/bin/retrieve-calldata.d.ts +3 -0
- package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +1 -0
- package/dest/archiver/l1/bin/retrieve-calldata.js +149 -0
- package/dest/archiver/l1/calldata_retriever.d.ts +112 -0
- package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -0
- package/dest/archiver/l1/calldata_retriever.js +471 -0
- package/dest/archiver/l1/data_retrieval.d.ts +90 -0
- package/dest/archiver/l1/data_retrieval.d.ts.map +1 -0
- package/dest/archiver/l1/data_retrieval.js +331 -0
- package/dest/archiver/l1/debug_tx.d.ts +19 -0
- package/dest/archiver/l1/debug_tx.d.ts.map +1 -0
- package/dest/archiver/l1/debug_tx.js +73 -0
- package/dest/archiver/l1/spire_proposer.d.ts +70 -0
- package/dest/archiver/l1/spire_proposer.d.ts.map +1 -0
- package/dest/archiver/l1/spire_proposer.js +157 -0
- package/dest/archiver/l1/trace_tx.d.ts +97 -0
- package/dest/archiver/l1/trace_tx.d.ts.map +1 -0
- package/dest/archiver/l1/trace_tx.js +91 -0
- package/dest/archiver/l1/types.d.ts +12 -0
- package/dest/archiver/l1/types.d.ts.map +1 -0
- package/dest/archiver/l1/types.js +3 -0
- package/dest/archiver/l1/validate_trace.d.ts +29 -0
- package/dest/archiver/l1/validate_trace.d.ts.map +1 -0
- package/dest/archiver/l1/validate_trace.js +150 -0
- package/dest/archiver/structs/data_retrieval.d.ts +1 -1
- package/dest/archiver/structs/inbox_message.d.ts +4 -4
- package/dest/archiver/structs/inbox_message.d.ts.map +1 -1
- package/dest/archiver/structs/inbox_message.js +6 -5
- package/dest/archiver/structs/published.d.ts +2 -2
- package/dest/archiver/structs/published.d.ts.map +1 -1
- package/dest/archiver/validation.d.ts +10 -4
- package/dest/archiver/validation.d.ts.map +1 -1
- package/dest/archiver/validation.js +29 -21
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +4 -3
- package/dest/index.d.ts +2 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/rpc/index.d.ts +2 -2
- package/dest/test/index.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts +16 -8
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +19 -14
- package/dest/test/mock_l1_to_l2_message_source.d.ts +7 -6
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +10 -9
- package/dest/test/mock_l2_block_source.d.ts +23 -11
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +38 -24
- package/dest/test/mock_structs.d.ts +3 -2
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +9 -8
- package/package.json +18 -17
- package/src/archiver/archiver.ts +884 -449
- package/src/archiver/archiver_store.ts +113 -46
- package/src/archiver/archiver_store_test_suite.ts +1936 -356
- package/src/archiver/config.ts +20 -10
- package/src/archiver/errors.ts +64 -0
- package/src/archiver/index.ts +1 -1
- package/src/archiver/instrumentation.ts +16 -2
- package/src/archiver/kv_archiver_store/block_store.ts +442 -101
- package/src/archiver/kv_archiver_store/contract_class_store.ts +1 -1
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -1
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +87 -71
- package/src/archiver/kv_archiver_store/log_store.ts +209 -99
- package/src/archiver/kv_archiver_store/message_store.ts +21 -18
- package/src/archiver/l1/README.md +98 -0
- package/src/archiver/l1/bin/retrieve-calldata.ts +182 -0
- package/src/archiver/l1/calldata_retriever.ts +641 -0
- package/src/archiver/l1/data_retrieval.ts +512 -0
- package/src/archiver/l1/debug_tx.ts +99 -0
- package/src/archiver/l1/spire_proposer.ts +160 -0
- package/src/archiver/l1/trace_tx.ts +128 -0
- package/src/archiver/l1/types.ts +13 -0
- package/src/archiver/l1/validate_trace.ts +211 -0
- package/src/archiver/structs/inbox_message.ts +8 -8
- package/src/archiver/structs/published.ts +1 -1
- package/src/archiver/validation.ts +52 -27
- package/src/factory.ts +4 -3
- package/src/index.ts +1 -1
- package/src/test/fixtures/debug_traceTransaction-multicall3.json +88 -0
- package/src/test/fixtures/debug_traceTransaction-multiplePropose.json +153 -0
- package/src/test/fixtures/debug_traceTransaction-proxied.json +122 -0
- package/src/test/fixtures/trace_transaction-multicall3.json +65 -0
- package/src/test/fixtures/trace_transaction-multiplePropose.json +319 -0
- package/src/test/fixtures/trace_transaction-proxied.json +128 -0
- package/src/test/fixtures/trace_transaction-randomRevert.json +216 -0
- package/src/test/mock_archiver.ts +22 -16
- package/src/test/mock_l1_to_l2_message_source.ts +10 -9
- package/src/test/mock_l2_block_source.ts +51 -30
- package/src/test/mock_structs.ts +10 -9
- package/dest/archiver/data_retrieval.d.ts +0 -79
- package/dest/archiver/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/data_retrieval.js +0 -362
- package/src/archiver/data_retrieval.ts +0 -545
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import { INITIAL_L2_BLOCK_NUM, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants';
|
|
1
|
+
import { INITIAL_CHECKPOINT_NUMBER, INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants';
|
|
2
2
|
import { makeTuple } from '@aztec/foundation/array';
|
|
3
|
+
import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
3
4
|
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
4
5
|
import { times, timesParallel } from '@aztec/foundation/collection';
|
|
5
|
-
import { randomInt } from '@aztec/foundation/crypto';
|
|
6
|
-
import { Fr } from '@aztec/foundation/
|
|
6
|
+
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
7
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
7
8
|
import { toArray } from '@aztec/foundation/iterable';
|
|
8
9
|
import { sleep } from '@aztec/foundation/sleep';
|
|
9
10
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
10
|
-
import { CommitteeAttestation, EthAddress,
|
|
11
|
+
import { CommitteeAttestation, EthAddress, L2BlockHash, L2BlockNew, randomBlockInfo } from '@aztec/stdlib/block';
|
|
12
|
+
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
11
13
|
import { SerializableContractInstance, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
|
|
12
|
-
import { LogId, PrivateLog, PublicLog } from '@aztec/stdlib/logs';
|
|
14
|
+
import { ContractClassLog, LogId, PrivateLog, PublicLog, SiloedTag, Tag } from '@aztec/stdlib/logs';
|
|
13
15
|
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
16
|
+
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
14
17
|
import { makeContractClassPublic, makeExecutablePrivateFunctionWithMembershipProof, makeUtilityFunctionWithMembershipProof } from '@aztec/stdlib/testing';
|
|
15
18
|
import '@aztec/stdlib/testing/jest';
|
|
16
|
-
import {
|
|
19
|
+
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
20
|
+
import { PartialStateReference, StateReference, TxEffect, TxHash } from '@aztec/stdlib/tx';
|
|
17
21
|
import { makeInboxMessage, makeInboxMessages } from '../test/mock_structs.js';
|
|
18
|
-
import { BlockNumberNotSequentialError, InitialBlockNumberNotSequentialError } from './errors.js';
|
|
22
|
+
import { BlockArchiveNotConsistentError, BlockIndexNotSequentialError, BlockNumberNotSequentialError, CheckpointNumberNotConsistentError, CheckpointNumberNotSequentialError, InitialBlockNumberNotSequentialError, InitialCheckpointNumberNotSequentialError } from './errors.js';
|
|
19
23
|
import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
20
24
|
/**
|
|
21
25
|
* @param testName - The name of the test suite.
|
|
@@ -23,100 +27,256 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
23
27
|
*/ export function describeArchiverDataStore(testName, getStore) {
|
|
24
28
|
describe(testName, ()=>{
|
|
25
29
|
let store;
|
|
26
|
-
let
|
|
27
|
-
const
|
|
30
|
+
let publishedCheckpoints;
|
|
31
|
+
const blockNumberTests = [
|
|
28
32
|
[
|
|
29
33
|
1,
|
|
30
|
-
|
|
31
|
-
()=>blocks.slice(0, 1)
|
|
32
|
-
],
|
|
33
|
-
[
|
|
34
|
-
10,
|
|
35
|
-
1,
|
|
36
|
-
()=>blocks.slice(9, 10)
|
|
34
|
+
()=>publishedCheckpoints[0].checkpoint.blocks[0]
|
|
37
35
|
],
|
|
38
36
|
[
|
|
39
|
-
1,
|
|
40
37
|
10,
|
|
41
|
-
()=>blocks
|
|
38
|
+
()=>publishedCheckpoints[9].checkpoint.blocks[0]
|
|
42
39
|
],
|
|
43
40
|
[
|
|
44
|
-
2,
|
|
45
41
|
5,
|
|
46
|
-
()=>blocks
|
|
47
|
-
],
|
|
48
|
-
[
|
|
49
|
-
5,
|
|
50
|
-
2,
|
|
51
|
-
()=>blocks.slice(4, 6)
|
|
42
|
+
()=>publishedCheckpoints[4].checkpoint.blocks[0]
|
|
52
43
|
]
|
|
53
44
|
];
|
|
54
45
|
const makeBlockHash = (blockNumber)=>`0x${blockNumber.toString(16).padStart(64, '0')}`;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
46
|
+
// Create a state reference with properly calculated noteHashTree.nextAvailableLeafIndex
|
|
47
|
+
// This is needed because the log store calculates dataStartIndexForBlock as:
|
|
48
|
+
// noteHashTree.nextAvailableLeafIndex - txEffects.length * MAX_NOTE_HASHES_PER_TX
|
|
49
|
+
// If nextAvailableLeafIndex is too small (random values 0-1000), this becomes negative
|
|
50
|
+
const makeStateForBlock = (blockNumber, txsPerBlock)=>{
|
|
51
|
+
// Ensure nextAvailableLeafIndex is large enough for all blocks up to this point
|
|
52
|
+
const noteHashIndex = blockNumber * txsPerBlock * MAX_NOTE_HASHES_PER_TX;
|
|
53
|
+
return new StateReference(AppendOnlyTreeSnapshot.random(), new PartialStateReference(new AppendOnlyTreeSnapshot(Fr.random(), noteHashIndex), AppendOnlyTreeSnapshot.random(), AppendOnlyTreeSnapshot.random()));
|
|
54
|
+
};
|
|
55
|
+
const makePublishedCheckpoint = (checkpoint, l1BlockNumber)=>{
|
|
56
|
+
return new PublishedCheckpoint(checkpoint, new L1PublishedData(BigInt(l1BlockNumber), BigInt(l1BlockNumber * 1000), makeBlockHash(l1BlockNumber)), times(3, CommitteeAttestation.random));
|
|
57
|
+
};
|
|
58
|
+
const expectCheckpointedBlockEquals = (actual, expectedBlock, expectedCheckpoint)=>{
|
|
59
|
+
expect(actual.l1).toEqual(expectedCheckpoint.l1);
|
|
60
|
+
expect(actual.block.header.equals(expectedBlock.header)).toBe(true);
|
|
61
|
+
expect(actual.checkpointNumber).toEqual(expectedCheckpoint.checkpoint.number);
|
|
62
|
+
expect(actual.attestations.every((a, i)=>a.equals(expectedCheckpoint.attestations[i]))).toBe(true);
|
|
73
63
|
};
|
|
74
64
|
beforeEach(async ()=>{
|
|
75
65
|
store = await getStore();
|
|
76
|
-
|
|
66
|
+
// Create checkpoints sequentially to ensure archive roots are chained properly.
|
|
67
|
+
// Each block's header.lastArchive must equal the previous block's archive.
|
|
68
|
+
publishedCheckpoints = [];
|
|
69
|
+
const txsPerBlock = 4;
|
|
70
|
+
for(let i = 0; i < 10; i++){
|
|
71
|
+
const blockNumber = i + 1;
|
|
72
|
+
const previousArchive = i > 0 ? publishedCheckpoints[i - 1].checkpoint.blocks[0].archive : undefined;
|
|
73
|
+
const checkpoint = await Checkpoint.random(CheckpointNumber(i + 1), {
|
|
74
|
+
numBlocks: 1,
|
|
75
|
+
startBlockNumber: blockNumber,
|
|
76
|
+
previousArchive,
|
|
77
|
+
txsPerBlock,
|
|
78
|
+
state: makeStateForBlock(blockNumber, txsPerBlock),
|
|
79
|
+
// Ensure each tx has public logs for getPublicLogs tests
|
|
80
|
+
txOptions: {
|
|
81
|
+
numPublicCallsPerTx: 2,
|
|
82
|
+
numPublicLogsPerCall: 2
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
publishedCheckpoints.push(makePublishedCheckpoint(checkpoint, i + 10));
|
|
86
|
+
}
|
|
77
87
|
});
|
|
78
|
-
describe('
|
|
79
|
-
it('returns success when adding
|
|
80
|
-
await expect(store.
|
|
88
|
+
describe('addCheckpoints', ()=>{
|
|
89
|
+
it('returns success when adding checkpoints', async ()=>{
|
|
90
|
+
await expect(store.addCheckpoints(publishedCheckpoints)).resolves.toBe(true);
|
|
81
91
|
});
|
|
82
|
-
it('
|
|
83
|
-
await store.
|
|
84
|
-
await expect(store.
|
|
92
|
+
it('throws on duplicate checkpoints', async ()=>{
|
|
93
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
94
|
+
await expect(store.addCheckpoints(publishedCheckpoints)).rejects.toThrow(InitialCheckpointNumberNotSequentialError);
|
|
85
95
|
});
|
|
86
96
|
it('throws an error if the previous block does not exist in the store', async ()=>{
|
|
87
|
-
const
|
|
88
|
-
|
|
97
|
+
const checkpoint = await Checkpoint.random(CheckpointNumber(2), {
|
|
98
|
+
numBlocks: 1,
|
|
99
|
+
startBlockNumber: 2
|
|
100
|
+
});
|
|
101
|
+
const block = makePublishedCheckpoint(checkpoint, 2);
|
|
102
|
+
await expect(store.addCheckpoints([
|
|
89
103
|
block
|
|
90
|
-
])).rejects.toThrow(
|
|
91
|
-
await expect(store.
|
|
104
|
+
])).rejects.toThrow(InitialCheckpointNumberNotSequentialError);
|
|
105
|
+
await expect(store.getCheckpointedBlock(1)).resolves.toBeUndefined();
|
|
92
106
|
});
|
|
93
107
|
it('throws an error if there is a gap in the blocks being added', async ()=>{
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
108
|
+
const checkpoint1 = await Checkpoint.random(CheckpointNumber(1), {
|
|
109
|
+
numBlocks: 1,
|
|
110
|
+
startBlockNumber: 1
|
|
111
|
+
});
|
|
112
|
+
const checkpoint3 = await Checkpoint.random(CheckpointNumber(3), {
|
|
113
|
+
numBlocks: 1,
|
|
114
|
+
startBlockNumber: 3
|
|
115
|
+
});
|
|
116
|
+
const checkpoints = [
|
|
117
|
+
makePublishedCheckpoint(checkpoint1, 1),
|
|
118
|
+
makePublishedCheckpoint(checkpoint3, 3)
|
|
97
119
|
];
|
|
98
|
-
await expect(store.
|
|
99
|
-
await expect(store.
|
|
120
|
+
await expect(store.addCheckpoints(checkpoints)).rejects.toThrow(CheckpointNumberNotSequentialError);
|
|
121
|
+
await expect(store.getCheckpointedBlock(1)).resolves.toBeUndefined();
|
|
122
|
+
});
|
|
123
|
+
it('throws an error if blocks within a checkpoint are not sequential', async ()=>{
|
|
124
|
+
// Create a checkpoint with non-sequential block numbers (block 1 and block 3, skipping block 2)
|
|
125
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
126
|
+
checkpointNumber: CheckpointNumber(1)
|
|
127
|
+
});
|
|
128
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
129
|
+
checkpointNumber: CheckpointNumber(1)
|
|
130
|
+
});
|
|
131
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
132
|
+
block1,
|
|
133
|
+
block3
|
|
134
|
+
], CheckpointNumber(1));
|
|
135
|
+
const publishedCheckpoint = makePublishedCheckpoint(checkpoint, 10);
|
|
136
|
+
await expect(store.addCheckpoints([
|
|
137
|
+
publishedCheckpoint
|
|
138
|
+
])).rejects.toThrow(BlockNumberNotSequentialError);
|
|
139
|
+
await expect(store.getCheckpointedBlock(1)).resolves.toBeUndefined();
|
|
140
|
+
});
|
|
141
|
+
it('throws an error if blocks within a checkpoint do not have sequential indexes', async ()=>{
|
|
142
|
+
// Create a checkpoint with non-sequential indexes
|
|
143
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
144
|
+
checkpointNumber: CheckpointNumber(1),
|
|
145
|
+
indexWithinCheckpoint: 0
|
|
146
|
+
});
|
|
147
|
+
const block3 = await L2BlockNew.random(BlockNumber(2), {
|
|
148
|
+
checkpointNumber: CheckpointNumber(1),
|
|
149
|
+
indexWithinCheckpoint: 2
|
|
150
|
+
});
|
|
151
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
152
|
+
block1,
|
|
153
|
+
block3
|
|
154
|
+
], CheckpointNumber(1));
|
|
155
|
+
const publishedCheckpoint = makePublishedCheckpoint(checkpoint, 10);
|
|
156
|
+
await expect(store.addCheckpoints([
|
|
157
|
+
publishedCheckpoint
|
|
158
|
+
])).rejects.toThrow(BlockIndexNotSequentialError);
|
|
159
|
+
await expect(store.getCheckpointedBlock(1)).resolves.toBeUndefined();
|
|
160
|
+
});
|
|
161
|
+
it('throws an error if blocks within a checkpoint do not start from index 0', async ()=>{
|
|
162
|
+
// Create a checkpoint with non-sequential indexes
|
|
163
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
164
|
+
checkpointNumber: CheckpointNumber(1),
|
|
165
|
+
indexWithinCheckpoint: 1
|
|
166
|
+
});
|
|
167
|
+
const block3 = await L2BlockNew.random(BlockNumber(2), {
|
|
168
|
+
checkpointNumber: CheckpointNumber(1),
|
|
169
|
+
indexWithinCheckpoint: 2
|
|
170
|
+
});
|
|
171
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
172
|
+
block1,
|
|
173
|
+
block3
|
|
174
|
+
], CheckpointNumber(1));
|
|
175
|
+
const publishedCheckpoint = makePublishedCheckpoint(checkpoint, 10);
|
|
176
|
+
await expect(store.addCheckpoints([
|
|
177
|
+
publishedCheckpoint
|
|
178
|
+
])).rejects.toThrow(BlockIndexNotSequentialError);
|
|
179
|
+
await expect(store.getCheckpointedBlock(1)).resolves.toBeUndefined();
|
|
180
|
+
});
|
|
181
|
+
it('throws an error if block has invalid checkpoint index', async ()=>{
|
|
182
|
+
// Create a block wit an invalid checkpoint index
|
|
183
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
184
|
+
checkpointNumber: CheckpointNumber(1),
|
|
185
|
+
indexWithinCheckpoint: -1
|
|
186
|
+
});
|
|
187
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
188
|
+
block1
|
|
189
|
+
], CheckpointNumber(1));
|
|
190
|
+
const publishedCheckpoint = makePublishedCheckpoint(checkpoint, 10);
|
|
191
|
+
await expect(store.addCheckpoints([
|
|
192
|
+
publishedCheckpoint
|
|
193
|
+
])).rejects.toThrow(BlockIndexNotSequentialError);
|
|
194
|
+
await expect(store.getCheckpointedBlock(1)).resolves.toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
it('throws an error if checkpoint has invalid initial number', async ()=>{
|
|
197
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
198
|
+
checkpointNumber: CheckpointNumber(2),
|
|
199
|
+
indexWithinCheckpoint: 0
|
|
200
|
+
});
|
|
201
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
202
|
+
block1
|
|
203
|
+
], CheckpointNumber(2));
|
|
204
|
+
const publishedCheckpoint = makePublishedCheckpoint(checkpoint, 10);
|
|
205
|
+
await expect(store.addCheckpoints([
|
|
206
|
+
publishedCheckpoint
|
|
207
|
+
])).rejects.toThrow(InitialCheckpointNumberNotSequentialError);
|
|
208
|
+
});
|
|
209
|
+
it('allows the correct initial checkpoint', async ()=>{
|
|
210
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
211
|
+
checkpointNumber: CheckpointNumber(1),
|
|
212
|
+
indexWithinCheckpoint: 0
|
|
213
|
+
});
|
|
214
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
215
|
+
block1
|
|
216
|
+
], CheckpointNumber(1));
|
|
217
|
+
const publishedCheckpoint = makePublishedCheckpoint(checkpoint, 10);
|
|
218
|
+
await expect(store.addCheckpoints([
|
|
219
|
+
publishedCheckpoint
|
|
220
|
+
])).resolves.toBe(true);
|
|
221
|
+
});
|
|
222
|
+
it('throws on duplicate initial checkpoint', async ()=>{
|
|
223
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
224
|
+
checkpointNumber: CheckpointNumber(1),
|
|
225
|
+
indexWithinCheckpoint: 0
|
|
226
|
+
});
|
|
227
|
+
const block2 = await L2BlockNew.random(BlockNumber(1), {
|
|
228
|
+
checkpointNumber: CheckpointNumber(1),
|
|
229
|
+
indexWithinCheckpoint: 0
|
|
230
|
+
});
|
|
231
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
232
|
+
block1
|
|
233
|
+
], CheckpointNumber(1));
|
|
234
|
+
const publishedCheckpoint = makePublishedCheckpoint(checkpoint, 10);
|
|
235
|
+
const checkpoint2 = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
236
|
+
block2
|
|
237
|
+
], CheckpointNumber(1));
|
|
238
|
+
const publishedCheckpoint2 = makePublishedCheckpoint(checkpoint2, 10);
|
|
239
|
+
await expect(store.addCheckpoints([
|
|
240
|
+
publishedCheckpoint
|
|
241
|
+
])).resolves.toBe(true);
|
|
242
|
+
await expect(store.addCheckpoints([
|
|
243
|
+
publishedCheckpoint2
|
|
244
|
+
])).rejects.toThrow(InitialCheckpointNumberNotSequentialError);
|
|
100
245
|
});
|
|
101
246
|
});
|
|
102
|
-
describe('
|
|
103
|
-
it('unwinding
|
|
104
|
-
await store.
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
await store.
|
|
110
|
-
expect(
|
|
111
|
-
expect(
|
|
247
|
+
describe('unwindcheckpoints', ()=>{
|
|
248
|
+
it('unwinding checkpoints will remove checkpoints from the chain', async ()=>{
|
|
249
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
250
|
+
const checkpointNumber = await store.getSynchedCheckpointNumber();
|
|
251
|
+
const lastCheckpoint = publishedCheckpoints.at(-1);
|
|
252
|
+
const lastBlockNumber = lastCheckpoint.checkpoint.blocks[0].number;
|
|
253
|
+
// Verify block exists before unwinding
|
|
254
|
+
const retrievedBlock = await store.getCheckpointedBlock(lastBlockNumber);
|
|
255
|
+
expect(retrievedBlock).toBeDefined();
|
|
256
|
+
expect(retrievedBlock.block.header.equals(lastCheckpoint.checkpoint.blocks[0].header)).toBe(true);
|
|
257
|
+
expect(retrievedBlock.checkpointNumber).toEqual(checkpointNumber);
|
|
258
|
+
await store.unwindCheckpoints(checkpointNumber, 1);
|
|
259
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(checkpointNumber - 1);
|
|
260
|
+
await expect(store.getCheckpointedBlock(lastBlockNumber)).resolves.toBeUndefined();
|
|
112
261
|
});
|
|
113
262
|
it('can unwind multiple empty blocks', async ()=>{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
263
|
+
// Create checkpoints sequentially to chain archive roots
|
|
264
|
+
const emptyCheckpoints = [];
|
|
265
|
+
for(let i = 0; i < 10; i++){
|
|
266
|
+
const previousArchive = i > 0 ? emptyCheckpoints[i - 1].checkpoint.blocks[0].archive : undefined;
|
|
267
|
+
const checkpoint = await Checkpoint.random(CheckpointNumber(i + 1), {
|
|
268
|
+
numBlocks: 1,
|
|
269
|
+
startBlockNumber: i + 1,
|
|
270
|
+
txsPerBlock: 0,
|
|
271
|
+
previousArchive
|
|
272
|
+
});
|
|
273
|
+
emptyCheckpoints.push(makePublishedCheckpoint(checkpoint, i + 10));
|
|
274
|
+
}
|
|
275
|
+
await store.addCheckpoints(emptyCheckpoints);
|
|
276
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(10);
|
|
277
|
+
await store.unwindCheckpoints(CheckpointNumber(10), 3);
|
|
278
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(7);
|
|
279
|
+
expect((await store.getRangeOfCheckpoints(CheckpointNumber(1), 10)).map((b)=>b.checkpointNumber)).toEqual([
|
|
120
280
|
1,
|
|
121
281
|
2,
|
|
122
282
|
3,
|
|
@@ -126,113 +286,1308 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
126
286
|
7
|
|
127
287
|
]);
|
|
128
288
|
});
|
|
129
|
-
it('refuses to unwind
|
|
130
|
-
await store.
|
|
131
|
-
await expect(store.
|
|
289
|
+
it('refuses to unwind checkpoints if the tip is not the last checkpoint', async ()=>{
|
|
290
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
291
|
+
await expect(store.unwindCheckpoints(CheckpointNumber(5), 1)).rejects.toThrow(/can only unwind checkpoints from the tip/i);
|
|
132
292
|
});
|
|
133
293
|
it('unwound blocks and headers cannot be retrieved by hash or archive', async ()=>{
|
|
134
|
-
await store.
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
const
|
|
294
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
295
|
+
const lastCheckpoint = publishedCheckpoints[publishedCheckpoints.length - 1];
|
|
296
|
+
const lastBlock = lastCheckpoint.checkpoint.blocks[0];
|
|
297
|
+
const blockHash = await lastBlock.header.hash();
|
|
298
|
+
const archive = lastBlock.archive.root;
|
|
138
299
|
// Verify block and header exist before unwinding
|
|
139
|
-
|
|
140
|
-
expect(
|
|
141
|
-
expect(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
300
|
+
const retrievedByHash = await store.getCheckpointedBlockByHash(blockHash);
|
|
301
|
+
expect(retrievedByHash).toBeDefined();
|
|
302
|
+
expect(retrievedByHash.block.header.equals(lastBlock.header)).toBe(true);
|
|
303
|
+
const retrievedByArchive = await store.getCheckpointedBlockByArchive(archive);
|
|
304
|
+
expect(retrievedByArchive).toBeDefined();
|
|
305
|
+
expect(retrievedByArchive.block.header.equals(lastBlock.header)).toBe(true);
|
|
306
|
+
const headerByHash = await store.getBlockHeaderByHash(blockHash);
|
|
307
|
+
expect(headerByHash).toBeDefined();
|
|
308
|
+
expect(headerByHash.equals(lastBlock.header)).toBe(true);
|
|
309
|
+
const headerByArchive = await store.getBlockHeaderByArchive(archive);
|
|
310
|
+
expect(headerByArchive).toBeDefined();
|
|
311
|
+
expect(headerByArchive.equals(lastBlock.header)).toBe(true);
|
|
312
|
+
// Unwind the checkpoint
|
|
313
|
+
await store.unwindCheckpoints(lastCheckpoint.checkpoint.number, 1);
|
|
145
314
|
// Verify neither block nor header can be retrieved after unwinding
|
|
146
|
-
expect(await store.
|
|
147
|
-
expect(await store.
|
|
315
|
+
expect(await store.getCheckpointedBlockByHash(blockHash)).toBeUndefined();
|
|
316
|
+
expect(await store.getCheckpointedBlockByArchive(archive)).toBeUndefined();
|
|
148
317
|
expect(await store.getBlockHeaderByHash(blockHash)).toBeUndefined();
|
|
149
318
|
expect(await store.getBlockHeaderByArchive(archive)).toBeUndefined();
|
|
150
319
|
});
|
|
151
320
|
});
|
|
152
|
-
describe('
|
|
153
|
-
|
|
154
|
-
|
|
321
|
+
describe('multi-block checkpoints', ()=>{
|
|
322
|
+
it('block number increases correctly when adding checkpoints with multiple blocks', async ()=>{
|
|
323
|
+
// Create 3 checkpoints: first with 2 blocks, second with 3 blocks, third with 1 block
|
|
324
|
+
// Total blocks: 6, spanning block numbers 1-6
|
|
325
|
+
// Chain archive roots across checkpoints
|
|
326
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
327
|
+
numBlocks: 2,
|
|
328
|
+
startBlockNumber: 1
|
|
329
|
+
});
|
|
330
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
331
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
332
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
333
|
+
numBlocks: 3,
|
|
334
|
+
startBlockNumber: 3,
|
|
335
|
+
previousArchive: previousArchive1
|
|
336
|
+
});
|
|
337
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
338
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
339
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
340
|
+
numBlocks: 1,
|
|
341
|
+
startBlockNumber: 6,
|
|
342
|
+
previousArchive: previousArchive2
|
|
343
|
+
});
|
|
344
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
345
|
+
await store.addCheckpoints([
|
|
346
|
+
checkpoint1,
|
|
347
|
+
checkpoint2,
|
|
348
|
+
checkpoint3
|
|
349
|
+
]);
|
|
350
|
+
// Checkpoint number should be 3 (the last checkpoint number)
|
|
351
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(3);
|
|
352
|
+
// Block number should be 6 (the last block number across all checkpoints)
|
|
353
|
+
expect(await store.getLatestBlockNumber()).toBe(6);
|
|
354
|
+
});
|
|
355
|
+
it('block number decreases correctly when unwinding checkpoints with multiple blocks', async ()=>{
|
|
356
|
+
// Create 3 checkpoints with varying block counts, chaining archive roots
|
|
357
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
358
|
+
numBlocks: 2,
|
|
359
|
+
startBlockNumber: 1
|
|
360
|
+
});
|
|
361
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
362
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
363
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
364
|
+
numBlocks: 3,
|
|
365
|
+
startBlockNumber: 3,
|
|
366
|
+
previousArchive: previousArchive1
|
|
367
|
+
});
|
|
368
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
369
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
370
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
371
|
+
numBlocks: 2,
|
|
372
|
+
startBlockNumber: 6,
|
|
373
|
+
previousArchive: previousArchive2
|
|
374
|
+
});
|
|
375
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
376
|
+
await store.addCheckpoints([
|
|
377
|
+
checkpoint1,
|
|
378
|
+
checkpoint2,
|
|
379
|
+
checkpoint3
|
|
380
|
+
]);
|
|
381
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(3);
|
|
382
|
+
expect(await store.getLatestBlockNumber()).toBe(7);
|
|
383
|
+
// Unwind the last checkpoint (which has 2 blocks)
|
|
384
|
+
await store.unwindCheckpoints(CheckpointNumber(3), 1);
|
|
385
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(2);
|
|
386
|
+
expect(await store.getLatestBlockNumber()).toBe(5);
|
|
387
|
+
// Unwind another checkpoint (which has 3 blocks)
|
|
388
|
+
await store.unwindCheckpoints(CheckpointNumber(2), 1);
|
|
389
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(1);
|
|
390
|
+
expect(await store.getLatestBlockNumber()).toBe(2);
|
|
391
|
+
});
|
|
392
|
+
it('unwinding multiple checkpoints with multiple blocks in one go', async ()=>{
|
|
393
|
+
// Create 4 checkpoints with varying block counts, chaining archive roots
|
|
394
|
+
// Checkpoint 1: blocks 1-2 (2 blocks)
|
|
395
|
+
// Checkpoint 2: blocks 3-5 (3 blocks)
|
|
396
|
+
// Checkpoint 3: blocks 6-7 (2 blocks)
|
|
397
|
+
// Checkpoint 4: blocks 8-10 (3 blocks)
|
|
398
|
+
// Total: 10 blocks across 4 checkpoints
|
|
399
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
400
|
+
numBlocks: 2,
|
|
401
|
+
startBlockNumber: 1
|
|
402
|
+
});
|
|
403
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
404
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
405
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
406
|
+
numBlocks: 3,
|
|
407
|
+
startBlockNumber: 3,
|
|
408
|
+
previousArchive: previousArchive1
|
|
409
|
+
});
|
|
410
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
411
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
412
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
413
|
+
numBlocks: 2,
|
|
414
|
+
startBlockNumber: 6,
|
|
415
|
+
previousArchive: previousArchive2
|
|
416
|
+
});
|
|
417
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
418
|
+
const previousArchive3 = checkpoint3Cp.blocks.at(-1).archive;
|
|
419
|
+
const checkpoint4Cp = await Checkpoint.random(CheckpointNumber(4), {
|
|
420
|
+
numBlocks: 3,
|
|
421
|
+
startBlockNumber: 8,
|
|
422
|
+
previousArchive: previousArchive3
|
|
423
|
+
});
|
|
424
|
+
const checkpoint4 = makePublishedCheckpoint(checkpoint4Cp, 13);
|
|
425
|
+
await store.addCheckpoints([
|
|
426
|
+
checkpoint1,
|
|
427
|
+
checkpoint2,
|
|
428
|
+
checkpoint3,
|
|
429
|
+
checkpoint4
|
|
430
|
+
]);
|
|
431
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(4);
|
|
432
|
+
expect(await store.getLatestBlockNumber()).toBe(10);
|
|
433
|
+
// Unwind 2 checkpoints at once (checkpoints 3 and 4, which together have 5 blocks)
|
|
434
|
+
await store.unwindCheckpoints(CheckpointNumber(4), 2);
|
|
435
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(2);
|
|
436
|
+
expect(await store.getLatestBlockNumber()).toBe(5);
|
|
437
|
+
// Verify blocks 1-5 still exist (from checkpoints 1 and 2)
|
|
438
|
+
for(let blockNumber = 1; blockNumber <= 5; blockNumber++){
|
|
439
|
+
expect(await store.getCheckpointedBlock(blockNumber)).toBeDefined();
|
|
440
|
+
}
|
|
441
|
+
// Verify blocks 6-10 are gone (from checkpoints 3 and 4)
|
|
442
|
+
for(let blockNumber = 6; blockNumber <= 10; blockNumber++){
|
|
443
|
+
expect(await store.getCheckpointedBlock(blockNumber)).toBeUndefined();
|
|
444
|
+
}
|
|
445
|
+
// Unwind remaining 2 checkpoints at once (checkpoints 1 and 2, which together have 5 blocks)
|
|
446
|
+
await store.unwindCheckpoints(CheckpointNumber(2), 2);
|
|
447
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(0);
|
|
448
|
+
expect(await store.getLatestBlockNumber()).toBe(0);
|
|
449
|
+
// Verify all blocks are gone
|
|
450
|
+
for(let blockNumber = 1; blockNumber <= 10; blockNumber++){
|
|
451
|
+
expect(await store.getCheckpointedBlock(blockNumber)).toBeUndefined();
|
|
452
|
+
}
|
|
155
453
|
});
|
|
156
|
-
it
|
|
157
|
-
|
|
454
|
+
it('getCheckpointedBlock returns correct checkpoint info for blocks within multi-block checkpoints', async ()=>{
|
|
455
|
+
// Create checkpoints with chained archive roots
|
|
456
|
+
// Create a checkpoint with 3 blocks
|
|
457
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
458
|
+
numBlocks: 3,
|
|
459
|
+
startBlockNumber: 1
|
|
460
|
+
});
|
|
461
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
462
|
+
// Create another checkpoint with 2 blocks
|
|
463
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
464
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
465
|
+
numBlocks: 2,
|
|
466
|
+
startBlockNumber: 4,
|
|
467
|
+
previousArchive: previousArchive1
|
|
468
|
+
});
|
|
469
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
470
|
+
await store.addCheckpoints([
|
|
471
|
+
checkpoint1,
|
|
472
|
+
checkpoint2
|
|
473
|
+
]);
|
|
474
|
+
// Check blocks from the first checkpoint (blocks 1, 2, 3)
|
|
475
|
+
for(let i = 0; i < 3; i++){
|
|
476
|
+
const blockNumber = i + 1;
|
|
477
|
+
const retrievedBlock = await store.getCheckpointedBlock(blockNumber);
|
|
478
|
+
expect(retrievedBlock).toBeDefined();
|
|
479
|
+
expect(retrievedBlock.checkpointNumber).toBe(1);
|
|
480
|
+
expect(retrievedBlock.block.number).toBe(blockNumber);
|
|
481
|
+
expect(retrievedBlock.l1).toEqual(checkpoint1.l1);
|
|
482
|
+
expect(retrievedBlock.attestations.every((a, j)=>a.equals(checkpoint1.attestations[j]))).toBe(true);
|
|
483
|
+
}
|
|
484
|
+
// Check blocks from the second checkpoint (blocks 4, 5)
|
|
485
|
+
for(let i = 0; i < 2; i++){
|
|
486
|
+
const blockNumber = i + 4;
|
|
487
|
+
const retrievedBlock = await store.getCheckpointedBlock(blockNumber);
|
|
488
|
+
expect(retrievedBlock).toBeDefined();
|
|
489
|
+
expect(retrievedBlock.checkpointNumber).toBe(2);
|
|
490
|
+
expect(retrievedBlock.block.number).toBe(blockNumber);
|
|
491
|
+
expect(retrievedBlock.l1).toEqual(checkpoint2.l1);
|
|
492
|
+
expect(retrievedBlock.attestations.every((a, j)=>a.equals(checkpoint2.attestations[j]))).toBe(true);
|
|
493
|
+
}
|
|
158
494
|
});
|
|
159
|
-
it('returns
|
|
160
|
-
await
|
|
495
|
+
it('getCheckpointedBlockByHash returns correct checkpoint info for blocks within multi-block checkpoints', async ()=>{
|
|
496
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
497
|
+
numBlocks: 3,
|
|
498
|
+
startBlockNumber: 1
|
|
499
|
+
}), 10);
|
|
500
|
+
await store.addCheckpoints([
|
|
501
|
+
checkpoint
|
|
502
|
+
]);
|
|
503
|
+
// Check each block by its hash
|
|
504
|
+
for(let i = 0; i < checkpoint.checkpoint.blocks.length; i++){
|
|
505
|
+
const block = checkpoint.checkpoint.blocks[i];
|
|
506
|
+
const blockHash = await block.header.hash();
|
|
507
|
+
const retrievedBlock = await store.getCheckpointedBlockByHash(blockHash);
|
|
508
|
+
expect(retrievedBlock).toBeDefined();
|
|
509
|
+
expect(retrievedBlock.checkpointNumber).toBe(1);
|
|
510
|
+
expect(retrievedBlock.block.number).toBe(i + 1);
|
|
511
|
+
expect(retrievedBlock.l1).toEqual(checkpoint.l1);
|
|
512
|
+
}
|
|
161
513
|
});
|
|
162
|
-
it('
|
|
163
|
-
await
|
|
514
|
+
it('getCheckpointedBlockByArchive returns correct checkpoint info for blocks within multi-block checkpoints', async ()=>{
|
|
515
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
516
|
+
numBlocks: 3,
|
|
517
|
+
startBlockNumber: 1
|
|
518
|
+
}), 10);
|
|
519
|
+
await store.addCheckpoints([
|
|
520
|
+
checkpoint
|
|
521
|
+
]);
|
|
522
|
+
// Check each block by its archive root
|
|
523
|
+
for(let i = 0; i < checkpoint.checkpoint.blocks.length; i++){
|
|
524
|
+
const block = checkpoint.checkpoint.blocks[i];
|
|
525
|
+
const archive = block.archive.root;
|
|
526
|
+
const retrievedBlock = await store.getCheckpointedBlockByArchive(archive);
|
|
527
|
+
expect(retrievedBlock).toBeDefined();
|
|
528
|
+
expect(retrievedBlock.checkpointNumber).toBe(1);
|
|
529
|
+
expect(retrievedBlock.block.number).toBe(i + 1);
|
|
530
|
+
expect(retrievedBlock.l1).toEqual(checkpoint.l1);
|
|
531
|
+
}
|
|
164
532
|
});
|
|
165
|
-
it('
|
|
166
|
-
await
|
|
533
|
+
it('unwinding a multi-block checkpoint removes all its blocks', async ()=>{
|
|
534
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
535
|
+
numBlocks: 3,
|
|
536
|
+
startBlockNumber: 1
|
|
537
|
+
}), 10);
|
|
538
|
+
await store.addCheckpoints([
|
|
539
|
+
checkpoint
|
|
540
|
+
]);
|
|
541
|
+
// Verify all 3 blocks exist
|
|
542
|
+
for(let blockNumber = 1; blockNumber <= 3; blockNumber++){
|
|
543
|
+
expect(await store.getCheckpointedBlock(blockNumber)).toBeDefined();
|
|
544
|
+
}
|
|
545
|
+
// Unwind the checkpoint
|
|
546
|
+
await store.unwindCheckpoints(CheckpointNumber(1), 1);
|
|
547
|
+
// Verify all 3 blocks are removed
|
|
548
|
+
for(let blockNumber = 1; blockNumber <= 3; blockNumber++){
|
|
549
|
+
expect(await store.getCheckpointedBlock(blockNumber)).toBeUndefined();
|
|
550
|
+
}
|
|
551
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(0);
|
|
552
|
+
expect(await store.getLatestBlockNumber()).toBe(0);
|
|
167
553
|
});
|
|
168
|
-
|
|
554
|
+
});
|
|
555
|
+
describe('uncheckpointed blocks', ()=>{
|
|
556
|
+
it('can add blocks independently before a checkpoint arrives', async ()=>{
|
|
557
|
+
// First, establish some checkpointed blocks (checkpoint 1 with blocks 1-3)
|
|
558
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
559
|
+
numBlocks: 3,
|
|
560
|
+
startBlockNumber: 1
|
|
561
|
+
}), 10);
|
|
562
|
+
await store.addCheckpoints([
|
|
563
|
+
checkpoint1
|
|
564
|
+
]);
|
|
565
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(1);
|
|
566
|
+
expect(await store.getLatestBlockNumber()).toBe(3);
|
|
567
|
+
// Now add blocks 4, 5, 6 independently (without a checkpoint) for upcoming checkpoint 2
|
|
568
|
+
// Chain archive roots from the last block of checkpoint 1
|
|
569
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
570
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
571
|
+
checkpointNumber: CheckpointNumber(2),
|
|
572
|
+
indexWithinCheckpoint: 0,
|
|
573
|
+
lastArchive: lastBlockArchive
|
|
574
|
+
});
|
|
575
|
+
const block5 = await L2BlockNew.random(BlockNumber(5), {
|
|
576
|
+
checkpointNumber: CheckpointNumber(2),
|
|
577
|
+
indexWithinCheckpoint: 1,
|
|
578
|
+
lastArchive: block4.archive
|
|
579
|
+
});
|
|
580
|
+
const block6 = await L2BlockNew.random(BlockNumber(6), {
|
|
581
|
+
checkpointNumber: CheckpointNumber(2),
|
|
582
|
+
indexWithinCheckpoint: 2,
|
|
583
|
+
lastArchive: block5.archive
|
|
584
|
+
});
|
|
169
585
|
await store.addBlocks([
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
586
|
+
block4,
|
|
587
|
+
block5,
|
|
588
|
+
block6
|
|
589
|
+
]);
|
|
590
|
+
// Checkpoint number should still be 1 (no new checkpoint added)
|
|
591
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(1);
|
|
592
|
+
// But latest block number should be 6
|
|
593
|
+
expect(await store.getLatestBlockNumber()).toBe(6);
|
|
594
|
+
});
|
|
595
|
+
it('getBlock retrieves uncheckpointed blocks', async ()=>{
|
|
596
|
+
// First, establish some checkpointed blocks
|
|
597
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
598
|
+
numBlocks: 2,
|
|
599
|
+
startBlockNumber: 1
|
|
600
|
+
}), 10);
|
|
601
|
+
await store.addCheckpoints([
|
|
602
|
+
checkpoint1
|
|
603
|
+
]);
|
|
604
|
+
// Add uncheckpointed blocks for upcoming checkpoint 2, chaining archive roots
|
|
605
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
606
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
607
|
+
checkpointNumber: CheckpointNumber(2),
|
|
608
|
+
indexWithinCheckpoint: 0,
|
|
609
|
+
lastArchive: lastBlockArchive
|
|
610
|
+
});
|
|
611
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
612
|
+
checkpointNumber: CheckpointNumber(2),
|
|
613
|
+
indexWithinCheckpoint: 1,
|
|
614
|
+
lastArchive: block3.archive
|
|
615
|
+
});
|
|
616
|
+
await store.addBlocks([
|
|
617
|
+
block3,
|
|
618
|
+
block4
|
|
619
|
+
]);
|
|
620
|
+
// getBlock should work for both checkpointed and uncheckpointed blocks
|
|
621
|
+
expect((await store.getBlock(1))?.number).toBe(1);
|
|
622
|
+
expect((await store.getBlock(2))?.number).toBe(2);
|
|
623
|
+
expect((await store.getBlock(3))?.equals(block3)).toBe(true);
|
|
624
|
+
expect((await store.getBlock(4))?.equals(block4)).toBe(true);
|
|
625
|
+
expect(await store.getBlock(5)).toBeUndefined();
|
|
626
|
+
const block5 = await L2BlockNew.random(BlockNumber(5), {
|
|
627
|
+
checkpointNumber: CheckpointNumber(2),
|
|
628
|
+
indexWithinCheckpoint: 2,
|
|
629
|
+
lastArchive: block4.archive
|
|
630
|
+
});
|
|
631
|
+
await store.addBlocks([
|
|
632
|
+
block5
|
|
633
|
+
]);
|
|
634
|
+
// Verify the uncheckpointed blocks have correct data
|
|
635
|
+
const retrieved3 = await store.getBlock(3);
|
|
636
|
+
expect(retrieved3.number).toBe(3);
|
|
637
|
+
expect(retrieved3.equals(block3)).toBe(true);
|
|
638
|
+
const retrieved4 = await store.getBlock(4);
|
|
639
|
+
expect(retrieved4.number).toBe(4);
|
|
640
|
+
expect(retrieved4.equals(block4)).toBe(true);
|
|
641
|
+
const retrieved5 = await store.getBlock(5);
|
|
642
|
+
expect(retrieved5.number).toBe(5);
|
|
643
|
+
expect(retrieved5.equals(block5)).toBe(true);
|
|
644
|
+
});
|
|
645
|
+
it('getBlockByHash retrieves uncheckpointed blocks', async ()=>{
|
|
646
|
+
// Add uncheckpointed blocks (no checkpoints at all) for initial checkpoint 1, chaining archive roots
|
|
647
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
648
|
+
checkpointNumber: CheckpointNumber(1),
|
|
649
|
+
indexWithinCheckpoint: 0
|
|
650
|
+
});
|
|
651
|
+
const block2 = await L2BlockNew.random(BlockNumber(2), {
|
|
652
|
+
checkpointNumber: CheckpointNumber(1),
|
|
653
|
+
indexWithinCheckpoint: 1,
|
|
654
|
+
lastArchive: block1.archive
|
|
173
655
|
});
|
|
174
|
-
await
|
|
656
|
+
await store.addBlocks([
|
|
657
|
+
block1,
|
|
658
|
+
block2
|
|
659
|
+
]);
|
|
660
|
+
// getBlockByHash should work for uncheckpointed blocks
|
|
661
|
+
const hash1 = await block1.header.hash();
|
|
662
|
+
const hash2 = await block2.header.hash();
|
|
663
|
+
const retrieved1 = await store.getBlockByHash(hash1);
|
|
664
|
+
expect(retrieved1.equals(block1)).toBe(true);
|
|
665
|
+
const retrieved2 = await store.getBlockByHash(hash2);
|
|
666
|
+
expect(retrieved2.equals(block2)).toBe(true);
|
|
667
|
+
});
|
|
668
|
+
it('getBlockByArchive retrieves uncheckpointed blocks', async ()=>{
|
|
669
|
+
// Add uncheckpointed blocks for initial checkpoint 1, chaining archive roots
|
|
670
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
671
|
+
checkpointNumber: CheckpointNumber(1),
|
|
672
|
+
indexWithinCheckpoint: 0
|
|
673
|
+
});
|
|
674
|
+
const block2 = await L2BlockNew.random(BlockNumber(2), {
|
|
675
|
+
checkpointNumber: CheckpointNumber(1),
|
|
676
|
+
indexWithinCheckpoint: 1,
|
|
677
|
+
lastArchive: block1.archive
|
|
678
|
+
});
|
|
679
|
+
await store.addBlocks([
|
|
680
|
+
block1,
|
|
681
|
+
block2
|
|
682
|
+
]);
|
|
683
|
+
// getBlockByArchive should work for uncheckpointed blocks
|
|
684
|
+
const archive1 = block1.archive.root;
|
|
685
|
+
const archive2 = block2.archive.root;
|
|
686
|
+
const retrieved1 = await store.getBlockByArchive(archive1);
|
|
687
|
+
expect(retrieved1.equals(block1)).toBe(true);
|
|
688
|
+
const retrieved2 = await store.getBlockByArchive(archive2);
|
|
689
|
+
expect(retrieved2.equals(block2)).toBe(true);
|
|
690
|
+
});
|
|
691
|
+
it('getCheckpointedBlock returns undefined for uncheckpointed blocks', async ()=>{
|
|
692
|
+
// Add a checkpoint with blocks 1-2
|
|
693
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
694
|
+
numBlocks: 2,
|
|
695
|
+
startBlockNumber: 1
|
|
696
|
+
}), 10);
|
|
697
|
+
await store.addCheckpoints([
|
|
698
|
+
checkpoint1
|
|
699
|
+
]);
|
|
700
|
+
// Add uncheckpointed blocks 3-4 for upcoming checkpoint 2, chaining archive roots
|
|
701
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
702
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
703
|
+
checkpointNumber: CheckpointNumber(2),
|
|
704
|
+
indexWithinCheckpoint: 0,
|
|
705
|
+
lastArchive: lastBlockArchive
|
|
706
|
+
});
|
|
707
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
708
|
+
checkpointNumber: CheckpointNumber(2),
|
|
709
|
+
indexWithinCheckpoint: 1,
|
|
710
|
+
lastArchive: block3.archive
|
|
711
|
+
});
|
|
712
|
+
await store.addBlocks([
|
|
713
|
+
block3,
|
|
714
|
+
block4
|
|
715
|
+
]);
|
|
716
|
+
// getCheckpointedBlock should work for checkpointed blocks
|
|
717
|
+
expect((await store.getCheckpointedBlock(1))?.block.number).toBe(1);
|
|
718
|
+
expect((await store.getCheckpointedBlock(2))?.block.number).toBe(2);
|
|
719
|
+
// getCheckpointedBlock should return undefined for uncheckpointed blocks
|
|
720
|
+
expect(await store.getCheckpointedBlock(3)).toBeUndefined();
|
|
721
|
+
expect(await store.getCheckpointedBlock(4)).toBeUndefined();
|
|
722
|
+
// But getBlock should work for all blocks
|
|
723
|
+
expect((await store.getBlock(3))?.equals(block3)).toBe(true);
|
|
724
|
+
expect((await store.getBlock(4))?.equals(block4)).toBe(true);
|
|
725
|
+
});
|
|
726
|
+
it('getCheckpointedBlockByHash returns undefined for uncheckpointed blocks', async ()=>{
|
|
727
|
+
// Add uncheckpointed blocks for initial checkpoint 1
|
|
728
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
729
|
+
checkpointNumber: CheckpointNumber(1),
|
|
730
|
+
indexWithinCheckpoint: 0
|
|
731
|
+
});
|
|
732
|
+
await store.addBlocks([
|
|
733
|
+
block1
|
|
734
|
+
]);
|
|
735
|
+
const hash = await block1.header.hash();
|
|
736
|
+
// getCheckpointedBlockByHash should return undefined
|
|
737
|
+
expect(await store.getCheckpointedBlockByHash(hash)).toBeUndefined();
|
|
738
|
+
// But getBlockByHash should work
|
|
739
|
+
expect((await store.getBlockByHash(hash))?.equals(block1)).toBe(true);
|
|
740
|
+
});
|
|
741
|
+
it('getCheckpointedBlockByArchive returns undefined for uncheckpointed blocks', async ()=>{
|
|
742
|
+
// Add uncheckpointed blocks for initial checkpoint 1
|
|
743
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
744
|
+
checkpointNumber: CheckpointNumber(1),
|
|
745
|
+
indexWithinCheckpoint: 0
|
|
746
|
+
});
|
|
747
|
+
await store.addBlocks([
|
|
748
|
+
block1
|
|
749
|
+
]);
|
|
750
|
+
const archive = block1.archive.root;
|
|
751
|
+
// getCheckpointedBlockByArchive should return undefined
|
|
752
|
+
expect(await store.getCheckpointedBlockByArchive(archive)).toBeUndefined();
|
|
753
|
+
// But getBlockByArchive should work
|
|
754
|
+
expect((await store.getBlockByArchive(archive))?.equals(block1)).toBe(true);
|
|
755
|
+
});
|
|
756
|
+
it('checkpoint adopts previously added uncheckpointed blocks', async ()=>{
|
|
757
|
+
// Add blocks 1-3 without a checkpoint (for initial checkpoint 1), chaining archive roots
|
|
758
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
759
|
+
checkpointNumber: CheckpointNumber(1),
|
|
760
|
+
indexWithinCheckpoint: 0
|
|
761
|
+
});
|
|
762
|
+
const block2 = await L2BlockNew.random(BlockNumber(2), {
|
|
763
|
+
checkpointNumber: CheckpointNumber(1),
|
|
764
|
+
indexWithinCheckpoint: 1,
|
|
765
|
+
lastArchive: block1.archive
|
|
766
|
+
});
|
|
767
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
768
|
+
checkpointNumber: CheckpointNumber(1),
|
|
769
|
+
indexWithinCheckpoint: 2,
|
|
770
|
+
lastArchive: block2.archive
|
|
771
|
+
});
|
|
772
|
+
await store.addBlocks([
|
|
773
|
+
block1,
|
|
774
|
+
block2,
|
|
775
|
+
block3
|
|
776
|
+
]);
|
|
777
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(0);
|
|
778
|
+
expect(await store.getLatestBlockNumber()).toBe(3);
|
|
779
|
+
// getCheckpointedBlock should return undefined for all
|
|
780
|
+
expect(await store.getCheckpointedBlock(1)).toBeUndefined();
|
|
781
|
+
expect(await store.getCheckpointedBlock(2)).toBeUndefined();
|
|
782
|
+
expect(await store.getCheckpointedBlock(3)).toBeUndefined();
|
|
783
|
+
// Now add a checkpoint that covers blocks 1-3
|
|
784
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
785
|
+
numBlocks: 3,
|
|
786
|
+
startBlockNumber: 1
|
|
787
|
+
}), 10);
|
|
788
|
+
await store.addCheckpoints([
|
|
789
|
+
checkpoint1
|
|
790
|
+
]);
|
|
791
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(1);
|
|
792
|
+
expect(await store.getLatestBlockNumber()).toBe(3);
|
|
793
|
+
// Now getCheckpointedBlock should work for all blocks
|
|
794
|
+
const checkpointed1 = await store.getCheckpointedBlock(1);
|
|
795
|
+
expect(checkpointed1).toBeDefined();
|
|
796
|
+
expect(checkpointed1.checkpointNumber).toBe(1);
|
|
797
|
+
expect(checkpointed1.l1).toEqual(checkpoint1.l1);
|
|
798
|
+
const checkpointed2 = await store.getCheckpointedBlock(2);
|
|
799
|
+
expect(checkpointed2).toBeDefined();
|
|
800
|
+
expect(checkpointed2.checkpointNumber).toBe(1);
|
|
801
|
+
const checkpointed3 = await store.getCheckpointedBlock(3);
|
|
802
|
+
expect(checkpointed3).toBeDefined();
|
|
803
|
+
expect(checkpointed3.checkpointNumber).toBe(1);
|
|
804
|
+
});
|
|
805
|
+
it('can add more uncheckpointed blocks after a checkpoint and then checkpoint them', async ()=>{
|
|
806
|
+
// Start with checkpoint 1 covering blocks 1-2
|
|
807
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
808
|
+
numBlocks: 2,
|
|
809
|
+
startBlockNumber: 1
|
|
810
|
+
}), 10);
|
|
811
|
+
await store.addCheckpoints([
|
|
812
|
+
checkpoint1
|
|
813
|
+
]);
|
|
814
|
+
// Add uncheckpointed blocks 3-5 for the upcoming checkpoint 2, chaining archive roots
|
|
815
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
816
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
817
|
+
checkpointNumber: CheckpointNumber(2),
|
|
818
|
+
indexWithinCheckpoint: 0,
|
|
819
|
+
lastArchive: lastBlockArchive
|
|
820
|
+
});
|
|
821
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
822
|
+
checkpointNumber: CheckpointNumber(2),
|
|
823
|
+
indexWithinCheckpoint: 1,
|
|
824
|
+
lastArchive: block3.archive
|
|
825
|
+
});
|
|
826
|
+
const block5 = await L2BlockNew.random(BlockNumber(5), {
|
|
827
|
+
checkpointNumber: CheckpointNumber(2),
|
|
828
|
+
indexWithinCheckpoint: 2,
|
|
829
|
+
lastArchive: block4.archive
|
|
830
|
+
});
|
|
831
|
+
await store.addBlocks([
|
|
832
|
+
block3,
|
|
833
|
+
block4,
|
|
834
|
+
block5
|
|
835
|
+
]);
|
|
836
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(1);
|
|
837
|
+
expect(await store.getLatestBlockNumber()).toBe(5);
|
|
838
|
+
// Blocks 3-5 are not checkpointed yet
|
|
839
|
+
expect(await store.getCheckpointedBlock(3)).toBeUndefined();
|
|
840
|
+
expect(await store.getCheckpointedBlock(4)).toBeUndefined();
|
|
841
|
+
expect(await store.getCheckpointedBlock(5)).toBeUndefined();
|
|
842
|
+
// Add checkpoint 2 covering blocks 3-5, chaining from checkpoint1
|
|
843
|
+
const checkpoint2 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(2), {
|
|
844
|
+
numBlocks: 3,
|
|
845
|
+
startBlockNumber: 3,
|
|
846
|
+
previousArchive: lastBlockArchive
|
|
847
|
+
}), 11);
|
|
848
|
+
await store.addCheckpoints([
|
|
849
|
+
checkpoint2
|
|
850
|
+
]);
|
|
851
|
+
expect(await store.getSynchedCheckpointNumber()).toBe(2);
|
|
852
|
+
expect(await store.getLatestBlockNumber()).toBe(5);
|
|
853
|
+
// Now blocks 3-5 should be checkpointed with checkpoint 2's info
|
|
854
|
+
const checkpointed3 = await store.getCheckpointedBlock(3);
|
|
855
|
+
expect(checkpointed3).toBeDefined();
|
|
856
|
+
expect(checkpointed3.checkpointNumber).toBe(2);
|
|
857
|
+
expect(checkpointed3.l1).toEqual(checkpoint2.l1);
|
|
858
|
+
const checkpointed4 = await store.getCheckpointedBlock(4);
|
|
859
|
+
expect(checkpointed4).toBeDefined();
|
|
860
|
+
expect(checkpointed4.checkpointNumber).toBe(2);
|
|
861
|
+
const checkpointed5 = await store.getCheckpointedBlock(5);
|
|
862
|
+
expect(checkpointed5).toBeDefined();
|
|
863
|
+
expect(checkpointed5.checkpointNumber).toBe(2);
|
|
864
|
+
});
|
|
865
|
+
it('getBlocks retrieves both checkpointed and uncheckpointed blocks', async ()=>{
|
|
866
|
+
// Add checkpoint with blocks 1-2
|
|
867
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
868
|
+
numBlocks: 2,
|
|
869
|
+
startBlockNumber: 1
|
|
870
|
+
}), 10);
|
|
871
|
+
await store.addCheckpoints([
|
|
872
|
+
checkpoint1
|
|
873
|
+
]);
|
|
874
|
+
// Add uncheckpointed blocks 3-4 for the upcoming checkpoint 2, chaining archive roots
|
|
875
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
876
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
877
|
+
checkpointNumber: CheckpointNumber(2),
|
|
878
|
+
indexWithinCheckpoint: 0,
|
|
879
|
+
lastArchive: lastBlockArchive
|
|
880
|
+
});
|
|
881
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
882
|
+
checkpointNumber: CheckpointNumber(2),
|
|
883
|
+
indexWithinCheckpoint: 1,
|
|
884
|
+
lastArchive: block3.archive
|
|
885
|
+
});
|
|
886
|
+
await store.addBlocks([
|
|
887
|
+
block3,
|
|
888
|
+
block4
|
|
889
|
+
]);
|
|
890
|
+
// getBlocks should retrieve all blocks
|
|
891
|
+
const allBlocks = await store.getBlocks(1, 10);
|
|
892
|
+
expect(allBlocks.length).toBe(4);
|
|
893
|
+
expect(allBlocks.map((b)=>b.number)).toEqual([
|
|
894
|
+
1,
|
|
895
|
+
2,
|
|
896
|
+
3,
|
|
897
|
+
4
|
|
898
|
+
]);
|
|
175
899
|
});
|
|
176
|
-
|
|
900
|
+
});
|
|
901
|
+
describe('addBlocks validation', ()=>{
|
|
902
|
+
it('throws if blocks have different checkpoint numbers', async ()=>{
|
|
903
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
904
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
905
|
+
numBlocks: 2,
|
|
906
|
+
startBlockNumber: 1
|
|
907
|
+
}), 10);
|
|
908
|
+
await store.addCheckpoints([
|
|
909
|
+
checkpoint1
|
|
910
|
+
]);
|
|
911
|
+
// Try to add blocks 3 and 4 with different checkpoint numbers
|
|
912
|
+
// Chain archives correctly to test the checkpoint number validation
|
|
913
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
914
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
915
|
+
checkpointNumber: CheckpointNumber(2),
|
|
916
|
+
indexWithinCheckpoint: 0,
|
|
917
|
+
lastArchive: lastBlockArchive
|
|
918
|
+
});
|
|
919
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
920
|
+
checkpointNumber: CheckpointNumber(3),
|
|
921
|
+
indexWithinCheckpoint: 1,
|
|
922
|
+
lastArchive: block3.archive
|
|
923
|
+
});
|
|
924
|
+
await expect(store.addBlocks([
|
|
925
|
+
block3,
|
|
926
|
+
block4
|
|
927
|
+
])).rejects.toThrow(CheckpointNumberNotConsistentError);
|
|
928
|
+
});
|
|
929
|
+
it('throws if checkpoint number is not the current checkpoint', async ()=>{
|
|
930
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
931
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
932
|
+
numBlocks: 2,
|
|
933
|
+
startBlockNumber: 1
|
|
934
|
+
}), 10);
|
|
935
|
+
await store.addCheckpoints([
|
|
936
|
+
checkpoint1
|
|
937
|
+
]);
|
|
938
|
+
// Try to add blocks for checkpoint 3 (skipping checkpoint 2)
|
|
939
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
940
|
+
checkpointNumber: CheckpointNumber(3),
|
|
941
|
+
indexWithinCheckpoint: 0
|
|
942
|
+
});
|
|
943
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
944
|
+
checkpointNumber: CheckpointNumber(3),
|
|
945
|
+
indexWithinCheckpoint: 1
|
|
946
|
+
});
|
|
947
|
+
await expect(store.addBlocks([
|
|
948
|
+
block3,
|
|
949
|
+
block4
|
|
950
|
+
])).rejects.toThrow(InitialCheckpointNumberNotSequentialError);
|
|
951
|
+
});
|
|
952
|
+
it('allows blocks with the same checkpoint number for the current checkpoint', async ()=>{
|
|
953
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
954
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
955
|
+
numBlocks: 2,
|
|
956
|
+
startBlockNumber: 1
|
|
957
|
+
}), 10);
|
|
958
|
+
await store.addCheckpoints([
|
|
959
|
+
checkpoint1
|
|
960
|
+
]);
|
|
961
|
+
// Add blocks 3 and 4 with consistent checkpoint number (2), chaining archive roots
|
|
962
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
963
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
964
|
+
checkpointNumber: CheckpointNumber(2),
|
|
965
|
+
indexWithinCheckpoint: 0,
|
|
966
|
+
lastArchive: lastBlockArchive
|
|
967
|
+
});
|
|
968
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
969
|
+
checkpointNumber: CheckpointNumber(2),
|
|
970
|
+
indexWithinCheckpoint: 1,
|
|
971
|
+
lastArchive: block3.archive
|
|
972
|
+
});
|
|
973
|
+
await expect(store.addBlocks([
|
|
974
|
+
block3,
|
|
975
|
+
block4
|
|
976
|
+
])).resolves.toBe(true);
|
|
977
|
+
// Verify blocks were added
|
|
978
|
+
expect((await store.getBlock(3))?.equals(block3)).toBe(true);
|
|
979
|
+
expect((await store.getBlock(4))?.equals(block4)).toBe(true);
|
|
980
|
+
});
|
|
981
|
+
it('allows blocks for the initial checkpoint when store is empty', async ()=>{
|
|
982
|
+
// Add blocks for the initial checkpoint (1), chaining archive roots
|
|
983
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
984
|
+
checkpointNumber: CheckpointNumber(1),
|
|
985
|
+
indexWithinCheckpoint: 0
|
|
986
|
+
});
|
|
987
|
+
const block2 = await L2BlockNew.random(BlockNumber(2), {
|
|
988
|
+
checkpointNumber: CheckpointNumber(1),
|
|
989
|
+
indexWithinCheckpoint: 1,
|
|
990
|
+
lastArchive: block1.archive
|
|
991
|
+
});
|
|
992
|
+
await expect(store.addBlocks([
|
|
993
|
+
block1,
|
|
994
|
+
block2
|
|
995
|
+
])).resolves.toBe(true);
|
|
996
|
+
// Verify blocks were added
|
|
997
|
+
expect((await store.getBlock(1))?.equals(block1)).toBe(true);
|
|
998
|
+
expect((await store.getBlock(2))?.equals(block2)).toBe(true);
|
|
999
|
+
expect(await store.getLatestBlockNumber()).toBe(2);
|
|
1000
|
+
});
|
|
1001
|
+
it('throws if initial block is duplicated across calls', async ()=>{
|
|
1002
|
+
// Add blocks for the initial checkpoint (1)
|
|
1003
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
1004
|
+
checkpointNumber: CheckpointNumber(1),
|
|
1005
|
+
indexWithinCheckpoint: 0
|
|
1006
|
+
});
|
|
1007
|
+
const block2 = await L2BlockNew.random(BlockNumber(1), {
|
|
1008
|
+
checkpointNumber: CheckpointNumber(1),
|
|
1009
|
+
indexWithinCheckpoint: 0
|
|
1010
|
+
});
|
|
1011
|
+
await expect(store.addBlocks([
|
|
1012
|
+
block1
|
|
1013
|
+
])).resolves.toBe(true);
|
|
1014
|
+
await expect(store.addBlocks([
|
|
1015
|
+
block2
|
|
1016
|
+
])).rejects.toThrow(InitialBlockNumberNotSequentialError);
|
|
1017
|
+
});
|
|
1018
|
+
it('throws if first block has wrong checkpoint number when store is empty', async ()=>{
|
|
1019
|
+
// Try to add blocks for checkpoint 2 when store is empty (should start at 1)
|
|
1020
|
+
const block1 = await L2BlockNew.random(BlockNumber(1), {
|
|
1021
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1022
|
+
indexWithinCheckpoint: 0
|
|
1023
|
+
});
|
|
1024
|
+
const block2 = await L2BlockNew.random(BlockNumber(2), {
|
|
1025
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1026
|
+
indexWithinCheckpoint: 1
|
|
1027
|
+
});
|
|
1028
|
+
await expect(store.addBlocks([
|
|
1029
|
+
block1,
|
|
1030
|
+
block2
|
|
1031
|
+
])).rejects.toThrow(InitialCheckpointNumberNotSequentialError);
|
|
1032
|
+
});
|
|
1033
|
+
it('allows adding more blocks to the same checkpoint in separate calls', async ()=>{
|
|
1034
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
1035
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1036
|
+
numBlocks: 2,
|
|
1037
|
+
startBlockNumber: 1
|
|
1038
|
+
}), 10);
|
|
1039
|
+
await store.addCheckpoints([
|
|
1040
|
+
checkpoint1
|
|
1041
|
+
]);
|
|
1042
|
+
// Add block 3 for checkpoint 2, chaining archive roots
|
|
1043
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
1044
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
1045
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1046
|
+
indexWithinCheckpoint: 0,
|
|
1047
|
+
lastArchive: lastBlockArchive
|
|
1048
|
+
});
|
|
1049
|
+
await expect(store.addBlocks([
|
|
1050
|
+
block3
|
|
1051
|
+
])).resolves.toBe(true);
|
|
1052
|
+
// Add block 4 for the same checkpoint 2 in a separate call
|
|
1053
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
1054
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1055
|
+
indexWithinCheckpoint: 1,
|
|
1056
|
+
lastArchive: block3.archive
|
|
1057
|
+
});
|
|
1058
|
+
await expect(store.addBlocks([
|
|
1059
|
+
block4
|
|
1060
|
+
])).resolves.toBe(true);
|
|
1061
|
+
expect(await store.getLatestBlockNumber()).toBe(4);
|
|
1062
|
+
});
|
|
1063
|
+
it('throws if adding blocks in separate calls with non-consecutive indexes', async ()=>{
|
|
1064
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
1065
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1066
|
+
numBlocks: 2,
|
|
1067
|
+
startBlockNumber: 1
|
|
1068
|
+
}), 10);
|
|
1069
|
+
await store.addCheckpoints([
|
|
1070
|
+
checkpoint1
|
|
1071
|
+
]);
|
|
1072
|
+
// Add block 3 for checkpoint 2, chaining archive roots
|
|
1073
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
1074
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
1075
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1076
|
+
indexWithinCheckpoint: 0,
|
|
1077
|
+
lastArchive: lastBlockArchive
|
|
1078
|
+
});
|
|
1079
|
+
await expect(store.addBlocks([
|
|
1080
|
+
block3
|
|
1081
|
+
])).resolves.toBe(true);
|
|
1082
|
+
// Add block 4 for the same checkpoint 2 in a separate call but with a missing index
|
|
1083
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
1084
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1085
|
+
indexWithinCheckpoint: 2,
|
|
1086
|
+
lastArchive: block3.archive
|
|
1087
|
+
});
|
|
1088
|
+
await expect(store.addBlocks([
|
|
1089
|
+
block4
|
|
1090
|
+
])).rejects.toThrow(BlockIndexNotSequentialError);
|
|
1091
|
+
expect(await store.getLatestBlockNumber()).toBe(3);
|
|
1092
|
+
});
|
|
1093
|
+
it('throws if second batch of blocks has different checkpoint number than first batch', async ()=>{
|
|
1094
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
1095
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1096
|
+
numBlocks: 2,
|
|
1097
|
+
startBlockNumber: 1
|
|
1098
|
+
}), 10);
|
|
1099
|
+
await store.addCheckpoints([
|
|
1100
|
+
checkpoint1
|
|
1101
|
+
]);
|
|
1102
|
+
// Add block 3 for checkpoint 2, chaining archive roots
|
|
1103
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
1104
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
1105
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1106
|
+
indexWithinCheckpoint: 0,
|
|
1107
|
+
lastArchive: lastBlockArchive
|
|
1108
|
+
});
|
|
177
1109
|
await store.addBlocks([
|
|
178
|
-
|
|
179
|
-
|
|
1110
|
+
block3
|
|
1111
|
+
]);
|
|
1112
|
+
// Try to add block 4 for checkpoint 3 (should fail because current checkpoint is still 2)
|
|
1113
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
1114
|
+
checkpointNumber: CheckpointNumber(3),
|
|
1115
|
+
indexWithinCheckpoint: 0,
|
|
1116
|
+
lastArchive: block3.archive
|
|
1117
|
+
});
|
|
1118
|
+
await expect(store.addBlocks([
|
|
1119
|
+
block4
|
|
1120
|
+
])).rejects.toThrow(InitialCheckpointNumberNotSequentialError);
|
|
1121
|
+
});
|
|
1122
|
+
it('force option bypasses checkpoint number validation', async ()=>{
|
|
1123
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
1124
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1125
|
+
numBlocks: 2,
|
|
1126
|
+
startBlockNumber: 1
|
|
1127
|
+
}), 10);
|
|
1128
|
+
await store.addCheckpoints([
|
|
1129
|
+
checkpoint1
|
|
1130
|
+
]);
|
|
1131
|
+
// Add blocks with different checkpoint numbers using force option, chaining archive roots
|
|
1132
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
1133
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
1134
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1135
|
+
indexWithinCheckpoint: 0,
|
|
1136
|
+
lastArchive: lastBlockArchive
|
|
1137
|
+
});
|
|
1138
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
1139
|
+
checkpointNumber: CheckpointNumber(5),
|
|
1140
|
+
indexWithinCheckpoint: 0,
|
|
1141
|
+
lastArchive: block3.archive
|
|
1142
|
+
});
|
|
1143
|
+
await expect(store.addBlocks([
|
|
1144
|
+
block3,
|
|
1145
|
+
block4
|
|
1146
|
+
], {
|
|
1147
|
+
force: true
|
|
1148
|
+
})).resolves.toBe(true);
|
|
1149
|
+
});
|
|
1150
|
+
it('force option bypasses blockindex number validation', async ()=>{
|
|
1151
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
1152
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1153
|
+
numBlocks: 2,
|
|
1154
|
+
startBlockNumber: 1
|
|
1155
|
+
}), 10);
|
|
1156
|
+
await store.addCheckpoints([
|
|
1157
|
+
checkpoint1
|
|
1158
|
+
]);
|
|
1159
|
+
// Add blocks with different checkpoint numbers using force option, chaining archive roots
|
|
1160
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
1161
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
1162
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1163
|
+
indexWithinCheckpoint: 0,
|
|
1164
|
+
lastArchive: lastBlockArchive
|
|
1165
|
+
});
|
|
1166
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
1167
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1168
|
+
indexWithinCheckpoint: 2,
|
|
1169
|
+
lastArchive: block3.archive
|
|
1170
|
+
});
|
|
1171
|
+
await expect(store.addBlocks([
|
|
1172
|
+
block3,
|
|
1173
|
+
block4
|
|
180
1174
|
], {
|
|
181
1175
|
force: true
|
|
1176
|
+
})).resolves.toBe(true);
|
|
1177
|
+
});
|
|
1178
|
+
it('throws if adding blocks with non-consecutive archives', async ()=>{
|
|
1179
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
1180
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1181
|
+
numBlocks: 2,
|
|
1182
|
+
startBlockNumber: 1
|
|
1183
|
+
}), 10);
|
|
1184
|
+
await store.addCheckpoints([
|
|
1185
|
+
checkpoint1
|
|
1186
|
+
]);
|
|
1187
|
+
// Add block 3 for checkpoint 2 with incorrect archive
|
|
1188
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
1189
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1190
|
+
indexWithinCheckpoint: 0
|
|
182
1191
|
});
|
|
183
|
-
await expect(store.
|
|
1192
|
+
await expect(store.addBlocks([
|
|
1193
|
+
block3
|
|
1194
|
+
])).rejects.toThrow(BlockArchiveNotConsistentError);
|
|
1195
|
+
expect(await store.getLatestBlockNumber()).toBe(2);
|
|
1196
|
+
});
|
|
1197
|
+
it('throws if adding blocks with non-consecutive archives across calls', async ()=>{
|
|
1198
|
+
// First, establish checkpoint 1 with blocks 1-2
|
|
1199
|
+
const checkpoint1 = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1200
|
+
numBlocks: 2,
|
|
1201
|
+
startBlockNumber: 1
|
|
1202
|
+
}), 10);
|
|
1203
|
+
await store.addCheckpoints([
|
|
1204
|
+
checkpoint1
|
|
1205
|
+
]);
|
|
1206
|
+
// Add block 3 for checkpoint 2 with correct archive
|
|
1207
|
+
const lastBlockArchive = checkpoint1.checkpoint.blocks.at(-1).archive;
|
|
1208
|
+
const block3 = await L2BlockNew.random(BlockNumber(3), {
|
|
1209
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1210
|
+
indexWithinCheckpoint: 0,
|
|
1211
|
+
lastArchive: lastBlockArchive
|
|
1212
|
+
});
|
|
1213
|
+
await expect(store.addBlocks([
|
|
1214
|
+
block3
|
|
1215
|
+
])).resolves.toBe(true);
|
|
1216
|
+
// Add block 4 with incorrect archive (should fail)
|
|
1217
|
+
const block4 = await L2BlockNew.random(BlockNumber(4), {
|
|
1218
|
+
checkpointNumber: CheckpointNumber(2),
|
|
1219
|
+
indexWithinCheckpoint: 1,
|
|
1220
|
+
lastArchive: AppendOnlyTreeSnapshot.random()
|
|
1221
|
+
});
|
|
1222
|
+
await expect(store.addBlocks([
|
|
1223
|
+
block4
|
|
1224
|
+
])).rejects.toThrow(BlockArchiveNotConsistentError);
|
|
1225
|
+
expect(await store.getLatestBlockNumber()).toBe(3);
|
|
1226
|
+
});
|
|
1227
|
+
});
|
|
1228
|
+
describe('getBlocksForCheckpoint', ()=>{
|
|
1229
|
+
it('returns blocks for a single-block checkpoint', async ()=>{
|
|
1230
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1231
|
+
numBlocks: 1,
|
|
1232
|
+
startBlockNumber: 1
|
|
1233
|
+
}), 10);
|
|
1234
|
+
await store.addCheckpoints([
|
|
1235
|
+
checkpoint
|
|
1236
|
+
]);
|
|
1237
|
+
const blocks = await store.getBlocksForCheckpoint(CheckpointNumber(1));
|
|
1238
|
+
expect(blocks).toBeDefined();
|
|
1239
|
+
expect(blocks.length).toBe(1);
|
|
1240
|
+
expect(blocks[0].number).toBe(1);
|
|
1241
|
+
});
|
|
1242
|
+
it('returns all blocks for a multi-block checkpoint', async ()=>{
|
|
1243
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1244
|
+
numBlocks: 4,
|
|
1245
|
+
startBlockNumber: 1
|
|
1246
|
+
}), 10);
|
|
1247
|
+
await store.addCheckpoints([
|
|
1248
|
+
checkpoint
|
|
1249
|
+
]);
|
|
1250
|
+
const blocks = await store.getBlocksForCheckpoint(CheckpointNumber(1));
|
|
1251
|
+
expect(blocks).toBeDefined();
|
|
1252
|
+
expect(blocks.length).toBe(4);
|
|
1253
|
+
expect(blocks.map((b)=>b.number)).toEqual([
|
|
1254
|
+
1,
|
|
1255
|
+
2,
|
|
1256
|
+
3,
|
|
1257
|
+
4
|
|
1258
|
+
]);
|
|
1259
|
+
});
|
|
1260
|
+
it('returns correct blocks for different checkpoints', async ()=>{
|
|
1261
|
+
// Create checkpoints with chained archive roots
|
|
1262
|
+
// Checkpoint 1: blocks 1-2
|
|
1263
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
1264
|
+
numBlocks: 2,
|
|
1265
|
+
startBlockNumber: 1
|
|
1266
|
+
});
|
|
1267
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
1268
|
+
// Checkpoint 2: blocks 3-5
|
|
1269
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
1270
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
1271
|
+
numBlocks: 3,
|
|
1272
|
+
startBlockNumber: 3,
|
|
1273
|
+
previousArchive: previousArchive1
|
|
1274
|
+
});
|
|
1275
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
1276
|
+
// Checkpoint 3: blocks 6-7
|
|
1277
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
1278
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
1279
|
+
numBlocks: 2,
|
|
1280
|
+
startBlockNumber: 6,
|
|
1281
|
+
previousArchive: previousArchive2
|
|
1282
|
+
});
|
|
1283
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
1284
|
+
await store.addCheckpoints([
|
|
1285
|
+
checkpoint1,
|
|
1286
|
+
checkpoint2,
|
|
1287
|
+
checkpoint3
|
|
1288
|
+
]);
|
|
1289
|
+
const blocks1 = await store.getBlocksForCheckpoint(CheckpointNumber(1));
|
|
1290
|
+
expect(blocks1).toBeDefined();
|
|
1291
|
+
expect(blocks1.map((b)=>b.number)).toEqual([
|
|
1292
|
+
1,
|
|
1293
|
+
2
|
|
1294
|
+
]);
|
|
1295
|
+
const blocks2 = await store.getBlocksForCheckpoint(CheckpointNumber(2));
|
|
1296
|
+
expect(blocks2).toBeDefined();
|
|
1297
|
+
expect(blocks2.map((b)=>b.number)).toEqual([
|
|
1298
|
+
3,
|
|
1299
|
+
4,
|
|
1300
|
+
5
|
|
1301
|
+
]);
|
|
1302
|
+
const blocks3 = await store.getBlocksForCheckpoint(CheckpointNumber(3));
|
|
1303
|
+
expect(blocks3).toBeDefined();
|
|
1304
|
+
expect(blocks3.map((b)=>b.number)).toEqual([
|
|
1305
|
+
6,
|
|
1306
|
+
7
|
|
1307
|
+
]);
|
|
1308
|
+
});
|
|
1309
|
+
it('returns undefined for non-existent checkpoint', async ()=>{
|
|
1310
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1311
|
+
numBlocks: 2,
|
|
1312
|
+
startBlockNumber: 1
|
|
1313
|
+
}), 10);
|
|
1314
|
+
await store.addCheckpoints([
|
|
1315
|
+
checkpoint
|
|
1316
|
+
]);
|
|
1317
|
+
const blocks = await store.getBlocksForCheckpoint(CheckpointNumber(5));
|
|
1318
|
+
expect(blocks).toBeUndefined();
|
|
1319
|
+
});
|
|
1320
|
+
it('returns undefined when no checkpoints exist', async ()=>{
|
|
1321
|
+
const blocks = await store.getBlocksForCheckpoint(CheckpointNumber(1));
|
|
1322
|
+
expect(blocks).toBeUndefined();
|
|
184
1323
|
});
|
|
185
1324
|
});
|
|
186
|
-
describe('
|
|
1325
|
+
describe('getRangeOfCheckpoints', ()=>{
|
|
1326
|
+
it('returns empty array when no checkpoints exist', async ()=>{
|
|
1327
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(1), 10);
|
|
1328
|
+
expect(checkpoints).toEqual([]);
|
|
1329
|
+
});
|
|
1330
|
+
it('returns single checkpoint', async ()=>{
|
|
1331
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1332
|
+
numBlocks: 2,
|
|
1333
|
+
startBlockNumber: 1
|
|
1334
|
+
}), 10);
|
|
1335
|
+
await store.addCheckpoints([
|
|
1336
|
+
checkpoint
|
|
1337
|
+
]);
|
|
1338
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(1), 10);
|
|
1339
|
+
expect(checkpoints.length).toBe(1);
|
|
1340
|
+
expect(checkpoints[0].checkpointNumber).toBe(1);
|
|
1341
|
+
expect(checkpoints[0].startBlock).toBe(1);
|
|
1342
|
+
expect(checkpoints[0].numBlocks).toBe(2);
|
|
1343
|
+
});
|
|
1344
|
+
it('returns multiple checkpoints in order', async ()=>{
|
|
1345
|
+
// Create checkpoints with chained archive roots
|
|
1346
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
1347
|
+
numBlocks: 2,
|
|
1348
|
+
startBlockNumber: 1
|
|
1349
|
+
});
|
|
1350
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
1351
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
1352
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
1353
|
+
numBlocks: 3,
|
|
1354
|
+
startBlockNumber: 3,
|
|
1355
|
+
previousArchive: previousArchive1
|
|
1356
|
+
});
|
|
1357
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
1358
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
1359
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
1360
|
+
numBlocks: 1,
|
|
1361
|
+
startBlockNumber: 6,
|
|
1362
|
+
previousArchive: previousArchive2
|
|
1363
|
+
});
|
|
1364
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
1365
|
+
await store.addCheckpoints([
|
|
1366
|
+
checkpoint1,
|
|
1367
|
+
checkpoint2,
|
|
1368
|
+
checkpoint3
|
|
1369
|
+
]);
|
|
1370
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(1), 10);
|
|
1371
|
+
expect(checkpoints.length).toBe(3);
|
|
1372
|
+
expect(checkpoints.map((c)=>c.checkpointNumber)).toEqual([
|
|
1373
|
+
1,
|
|
1374
|
+
2,
|
|
1375
|
+
3
|
|
1376
|
+
]);
|
|
1377
|
+
expect(checkpoints.map((c)=>c.startBlock)).toEqual([
|
|
1378
|
+
1,
|
|
1379
|
+
3,
|
|
1380
|
+
6
|
|
1381
|
+
]);
|
|
1382
|
+
expect(checkpoints.map((c)=>c.numBlocks)).toEqual([
|
|
1383
|
+
2,
|
|
1384
|
+
3,
|
|
1385
|
+
1
|
|
1386
|
+
]);
|
|
1387
|
+
});
|
|
1388
|
+
it('respects the from parameter', async ()=>{
|
|
1389
|
+
// Create checkpoints with chained archive roots
|
|
1390
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
1391
|
+
numBlocks: 2,
|
|
1392
|
+
startBlockNumber: 1
|
|
1393
|
+
});
|
|
1394
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
1395
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
1396
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
1397
|
+
numBlocks: 2,
|
|
1398
|
+
startBlockNumber: 3,
|
|
1399
|
+
previousArchive: previousArchive1
|
|
1400
|
+
});
|
|
1401
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
1402
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
1403
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
1404
|
+
numBlocks: 2,
|
|
1405
|
+
startBlockNumber: 5,
|
|
1406
|
+
previousArchive: previousArchive2
|
|
1407
|
+
});
|
|
1408
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
1409
|
+
await store.addCheckpoints([
|
|
1410
|
+
checkpoint1,
|
|
1411
|
+
checkpoint2,
|
|
1412
|
+
checkpoint3
|
|
1413
|
+
]);
|
|
1414
|
+
// Start from checkpoint 2
|
|
1415
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(2), 10);
|
|
1416
|
+
expect(checkpoints.length).toBe(2);
|
|
1417
|
+
expect(checkpoints.map((c)=>c.checkpointNumber)).toEqual([
|
|
1418
|
+
2,
|
|
1419
|
+
3
|
|
1420
|
+
]);
|
|
1421
|
+
});
|
|
1422
|
+
it('respects the limit parameter', async ()=>{
|
|
1423
|
+
// Create checkpoints with chained archive roots
|
|
1424
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
1425
|
+
numBlocks: 1,
|
|
1426
|
+
startBlockNumber: 1
|
|
1427
|
+
});
|
|
1428
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
1429
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
1430
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
1431
|
+
numBlocks: 1,
|
|
1432
|
+
startBlockNumber: 2,
|
|
1433
|
+
previousArchive: previousArchive1
|
|
1434
|
+
});
|
|
1435
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
1436
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
1437
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
1438
|
+
numBlocks: 1,
|
|
1439
|
+
startBlockNumber: 3,
|
|
1440
|
+
previousArchive: previousArchive2
|
|
1441
|
+
});
|
|
1442
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
1443
|
+
const previousArchive3 = checkpoint3Cp.blocks.at(-1).archive;
|
|
1444
|
+
const checkpoint4Cp = await Checkpoint.random(CheckpointNumber(4), {
|
|
1445
|
+
numBlocks: 1,
|
|
1446
|
+
startBlockNumber: 4,
|
|
1447
|
+
previousArchive: previousArchive3
|
|
1448
|
+
});
|
|
1449
|
+
const checkpoint4 = makePublishedCheckpoint(checkpoint4Cp, 13);
|
|
1450
|
+
await store.addCheckpoints([
|
|
1451
|
+
checkpoint1,
|
|
1452
|
+
checkpoint2,
|
|
1453
|
+
checkpoint3,
|
|
1454
|
+
checkpoint4
|
|
1455
|
+
]);
|
|
1456
|
+
// Only get 2 checkpoints
|
|
1457
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(1), 2);
|
|
1458
|
+
expect(checkpoints.length).toBe(2);
|
|
1459
|
+
expect(checkpoints.map((c)=>c.checkpointNumber)).toEqual([
|
|
1460
|
+
1,
|
|
1461
|
+
2
|
|
1462
|
+
]);
|
|
1463
|
+
});
|
|
1464
|
+
it('returns correct checkpoint data including L1 info', async ()=>{
|
|
1465
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1466
|
+
numBlocks: 3,
|
|
1467
|
+
startBlockNumber: 1
|
|
1468
|
+
}), 42);
|
|
1469
|
+
await store.addCheckpoints([
|
|
1470
|
+
checkpoint
|
|
1471
|
+
]);
|
|
1472
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(1), 1);
|
|
1473
|
+
expect(checkpoints.length).toBe(1);
|
|
1474
|
+
const data = checkpoints[0];
|
|
1475
|
+
expect(data.checkpointNumber).toBe(1);
|
|
1476
|
+
expect(data.startBlock).toBe(1);
|
|
1477
|
+
expect(data.numBlocks).toBe(3);
|
|
1478
|
+
expect(data.l1.blockNumber).toBe(42n);
|
|
1479
|
+
expect(data.header.equals(checkpoint.checkpoint.header)).toBe(true);
|
|
1480
|
+
expect(data.archive.equals(checkpoint.checkpoint.archive)).toBe(true);
|
|
1481
|
+
});
|
|
1482
|
+
it('returns empty array when from is beyond available checkpoints', async ()=>{
|
|
1483
|
+
const checkpoint = makePublishedCheckpoint(await Checkpoint.random(CheckpointNumber(1), {
|
|
1484
|
+
numBlocks: 2,
|
|
1485
|
+
startBlockNumber: 1
|
|
1486
|
+
}), 10);
|
|
1487
|
+
await store.addCheckpoints([
|
|
1488
|
+
checkpoint
|
|
1489
|
+
]);
|
|
1490
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(5), 10);
|
|
1491
|
+
expect(checkpoints).toEqual([]);
|
|
1492
|
+
});
|
|
1493
|
+
it('works correctly after unwinding checkpoints', async ()=>{
|
|
1494
|
+
// Create checkpoints with chained archive roots
|
|
1495
|
+
const checkpoint1Cp = await Checkpoint.random(CheckpointNumber(1), {
|
|
1496
|
+
numBlocks: 2,
|
|
1497
|
+
startBlockNumber: 1
|
|
1498
|
+
});
|
|
1499
|
+
const checkpoint1 = makePublishedCheckpoint(checkpoint1Cp, 10);
|
|
1500
|
+
const previousArchive1 = checkpoint1Cp.blocks.at(-1).archive;
|
|
1501
|
+
const checkpoint2Cp = await Checkpoint.random(CheckpointNumber(2), {
|
|
1502
|
+
numBlocks: 2,
|
|
1503
|
+
startBlockNumber: 3,
|
|
1504
|
+
previousArchive: previousArchive1
|
|
1505
|
+
});
|
|
1506
|
+
const checkpoint2 = makePublishedCheckpoint(checkpoint2Cp, 11);
|
|
1507
|
+
const previousArchive2 = checkpoint2Cp.blocks.at(-1).archive;
|
|
1508
|
+
const checkpoint3Cp = await Checkpoint.random(CheckpointNumber(3), {
|
|
1509
|
+
numBlocks: 2,
|
|
1510
|
+
startBlockNumber: 5,
|
|
1511
|
+
previousArchive: previousArchive2
|
|
1512
|
+
});
|
|
1513
|
+
const checkpoint3 = makePublishedCheckpoint(checkpoint3Cp, 12);
|
|
1514
|
+
await store.addCheckpoints([
|
|
1515
|
+
checkpoint1,
|
|
1516
|
+
checkpoint2,
|
|
1517
|
+
checkpoint3
|
|
1518
|
+
]);
|
|
1519
|
+
// Unwind checkpoint 3
|
|
1520
|
+
await store.unwindCheckpoints(CheckpointNumber(3), 1);
|
|
1521
|
+
const checkpoints = await store.getRangeOfCheckpoints(CheckpointNumber(1), 10);
|
|
1522
|
+
expect(checkpoints.length).toBe(2);
|
|
1523
|
+
expect(checkpoints.map((c)=>c.checkpointNumber)).toEqual([
|
|
1524
|
+
1,
|
|
1525
|
+
2
|
|
1526
|
+
]);
|
|
1527
|
+
});
|
|
1528
|
+
});
|
|
1529
|
+
describe('getCheckpointedBlock', ()=>{
|
|
187
1530
|
beforeEach(async ()=>{
|
|
188
|
-
await store.
|
|
1531
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
1532
|
+
});
|
|
1533
|
+
it.each(blockNumberTests)('retrieves previously stored block %i', async (blockNumber, getExpectedBlock)=>{
|
|
1534
|
+
const retrievedBlock = await store.getCheckpointedBlock(blockNumber);
|
|
1535
|
+
const expectedBlock = getExpectedBlock();
|
|
1536
|
+
const expectedCheckpoint = publishedCheckpoints[blockNumber - 1];
|
|
1537
|
+
expect(retrievedBlock).toBeDefined();
|
|
1538
|
+
expectCheckpointedBlockEquals(retrievedBlock, expectedBlock, expectedCheckpoint);
|
|
1539
|
+
});
|
|
1540
|
+
it('returns undefined if block is not found', async ()=>{
|
|
1541
|
+
await expect(store.getCheckpointedBlock(12)).resolves.toBeUndefined();
|
|
1542
|
+
});
|
|
1543
|
+
it('returns undefined for block number 0', async ()=>{
|
|
1544
|
+
await expect(store.getCheckpointedBlock(0)).resolves.toBeUndefined();
|
|
1545
|
+
});
|
|
1546
|
+
});
|
|
1547
|
+
describe('getCheckpointedBlockByHash', ()=>{
|
|
1548
|
+
beforeEach(async ()=>{
|
|
1549
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
189
1550
|
});
|
|
190
1551
|
it('retrieves a block by its hash', async ()=>{
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const
|
|
1552
|
+
const expectedCheckpoint = publishedCheckpoints[5];
|
|
1553
|
+
const expectedBlock = expectedCheckpoint.checkpoint.blocks[0];
|
|
1554
|
+
const blockHash = await expectedBlock.header.hash();
|
|
1555
|
+
const retrievedBlock = await store.getCheckpointedBlockByHash(blockHash);
|
|
194
1556
|
expect(retrievedBlock).toBeDefined();
|
|
195
|
-
|
|
196
|
-
retrievedBlock
|
|
197
|
-
], [
|
|
198
|
-
expectedBlock
|
|
199
|
-
]);
|
|
1557
|
+
expectCheckpointedBlockEquals(retrievedBlock, expectedBlock, expectedCheckpoint);
|
|
200
1558
|
});
|
|
201
1559
|
it('returns undefined for non-existent block hash', async ()=>{
|
|
202
1560
|
const nonExistentHash = Fr.random();
|
|
203
|
-
await expect(store.
|
|
1561
|
+
await expect(store.getCheckpointedBlockByHash(nonExistentHash)).resolves.toBeUndefined();
|
|
204
1562
|
});
|
|
205
1563
|
});
|
|
206
|
-
describe('
|
|
1564
|
+
describe('getCheckpointedBlockByArchive', ()=>{
|
|
207
1565
|
beforeEach(async ()=>{
|
|
208
|
-
await store.
|
|
1566
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
209
1567
|
});
|
|
210
1568
|
it('retrieves a block by its archive root', async ()=>{
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
const
|
|
1569
|
+
const expectedCheckpoint = publishedCheckpoints[3];
|
|
1570
|
+
const expectedBlock = expectedCheckpoint.checkpoint.blocks[0];
|
|
1571
|
+
const archive = expectedBlock.archive.root;
|
|
1572
|
+
const retrievedBlock = await store.getCheckpointedBlockByArchive(archive);
|
|
214
1573
|
expect(retrievedBlock).toBeDefined();
|
|
215
|
-
|
|
216
|
-
retrievedBlock
|
|
217
|
-
], [
|
|
218
|
-
expectedBlock
|
|
219
|
-
]);
|
|
1574
|
+
expectCheckpointedBlockEquals(retrievedBlock, expectedBlock, expectedCheckpoint);
|
|
220
1575
|
});
|
|
221
1576
|
it('returns undefined for non-existent archive root', async ()=>{
|
|
222
1577
|
const nonExistentArchive = Fr.random();
|
|
223
|
-
await expect(store.
|
|
1578
|
+
await expect(store.getCheckpointedBlockByArchive(nonExistentArchive)).resolves.toBeUndefined();
|
|
224
1579
|
});
|
|
225
1580
|
});
|
|
226
1581
|
describe('getBlockHeaderByHash', ()=>{
|
|
227
1582
|
beforeEach(async ()=>{
|
|
228
|
-
await store.
|
|
1583
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
229
1584
|
});
|
|
230
1585
|
it('retrieves a block header by its hash', async ()=>{
|
|
231
|
-
const expectedBlock =
|
|
232
|
-
const blockHash = await expectedBlock.
|
|
1586
|
+
const expectedBlock = publishedCheckpoints[7].checkpoint.blocks[0];
|
|
1587
|
+
const blockHash = await expectedBlock.header.hash();
|
|
233
1588
|
const retrievedHeader = await store.getBlockHeaderByHash(blockHash);
|
|
234
1589
|
expect(retrievedHeader).toBeDefined();
|
|
235
|
-
expect(retrievedHeader.equals(expectedBlock.
|
|
1590
|
+
expect(retrievedHeader.equals(expectedBlock.header)).toBe(true);
|
|
236
1591
|
});
|
|
237
1592
|
it('returns undefined for non-existent block hash', async ()=>{
|
|
238
1593
|
const nonExistentHash = Fr.random();
|
|
@@ -241,27 +1596,27 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
241
1596
|
});
|
|
242
1597
|
describe('getBlockHeaderByArchive', ()=>{
|
|
243
1598
|
beforeEach(async ()=>{
|
|
244
|
-
await store.
|
|
1599
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
245
1600
|
});
|
|
246
1601
|
it('retrieves a block header by its archive root', async ()=>{
|
|
247
|
-
const expectedBlock =
|
|
248
|
-
const archive = expectedBlock.
|
|
1602
|
+
const expectedBlock = publishedCheckpoints[2].checkpoint.blocks[0];
|
|
1603
|
+
const archive = expectedBlock.archive.root;
|
|
249
1604
|
const retrievedHeader = await store.getBlockHeaderByArchive(archive);
|
|
250
1605
|
expect(retrievedHeader).toBeDefined();
|
|
251
|
-
expect(retrievedHeader.equals(expectedBlock.
|
|
1606
|
+
expect(retrievedHeader.equals(expectedBlock.header)).toBe(true);
|
|
252
1607
|
});
|
|
253
1608
|
it('returns undefined for non-existent archive root', async ()=>{
|
|
254
1609
|
const nonExistentArchive = Fr.random();
|
|
255
1610
|
await expect(store.getBlockHeaderByArchive(nonExistentArchive)).resolves.toBeUndefined();
|
|
256
1611
|
});
|
|
257
1612
|
});
|
|
258
|
-
describe('
|
|
259
|
-
it('returns the
|
|
260
|
-
await expect(store.
|
|
1613
|
+
describe('getSynchedCheckpointNumber', ()=>{
|
|
1614
|
+
it('returns the checkpoint number before INITIAL_CHECKPOINT_NUMBER if no checkpoints have been added', async ()=>{
|
|
1615
|
+
await expect(store.getSynchedCheckpointNumber()).resolves.toEqual(INITIAL_CHECKPOINT_NUMBER - 1);
|
|
261
1616
|
});
|
|
262
|
-
it(
|
|
263
|
-
await store.
|
|
264
|
-
await expect(store.
|
|
1617
|
+
it('returns the most recently added checkpoint number', async ()=>{
|
|
1618
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
1619
|
+
await expect(store.getSynchedCheckpointNumber()).resolves.toEqual(publishedCheckpoints.at(-1).checkpoint.number);
|
|
265
1620
|
});
|
|
266
1621
|
});
|
|
267
1622
|
describe('getSynchPoint', ()=>{
|
|
@@ -272,7 +1627,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
272
1627
|
});
|
|
273
1628
|
});
|
|
274
1629
|
it('returns the L1 block number in which the most recent L2 block was published', async ()=>{
|
|
275
|
-
await store.
|
|
1630
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
276
1631
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
277
1632
|
blocksSynchedTo: 19n,
|
|
278
1633
|
messagesSynchedTo: undefined
|
|
@@ -324,77 +1679,62 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
324
1679
|
});
|
|
325
1680
|
describe('addLogs', ()=>{
|
|
326
1681
|
it('adds private & public logs', async ()=>{
|
|
327
|
-
const
|
|
328
|
-
await
|
|
329
|
-
|
|
330
|
-
])).resolves.toEqual(true);
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
describe('deleteLogs', ()=>{
|
|
334
|
-
it('deletes private & public logs', async ()=>{
|
|
335
|
-
const block = blocks[0].block;
|
|
336
|
-
await store.addBlocks([
|
|
337
|
-
blocks[0]
|
|
338
|
-
]);
|
|
339
|
-
await expect(store.addLogs([
|
|
340
|
-
block
|
|
341
|
-
])).resolves.toEqual(true);
|
|
342
|
-
expect((await store.getPrivateLogs(1, 1)).length).toEqual(block.body.txEffects.map((txEffect)=>txEffect.privateLogs).flat().length);
|
|
343
|
-
expect((await store.getPublicLogs({
|
|
344
|
-
fromBlock: 1
|
|
345
|
-
})).logs.length).toEqual(block.body.txEffects.map((txEffect)=>txEffect.publicLogs).flat().length);
|
|
346
|
-
// This one is a pain for memory as we would never want to just delete memory in the middle.
|
|
347
|
-
await store.deleteLogs([
|
|
348
|
-
block
|
|
1682
|
+
const checkpoint = publishedCheckpoints[0];
|
|
1683
|
+
await store.addCheckpoints([
|
|
1684
|
+
checkpoint
|
|
349
1685
|
]);
|
|
350
|
-
expect(
|
|
351
|
-
expect((await store.getPublicLogs({
|
|
352
|
-
fromBlock: 1
|
|
353
|
-
})).logs.length).toEqual(0);
|
|
1686
|
+
await expect(store.addLogs(checkpoint.checkpoint.blocks)).resolves.toEqual(true);
|
|
354
1687
|
});
|
|
355
1688
|
});
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
1689
|
+
it('deleteLogs', async ()=>{
|
|
1690
|
+
const block = publishedCheckpoints[0].checkpoint.blocks[0];
|
|
1691
|
+
await store.addBlocks([
|
|
1692
|
+
block
|
|
1693
|
+
]);
|
|
1694
|
+
await expect(store.addLogs([
|
|
1695
|
+
block
|
|
1696
|
+
])).resolves.toEqual(true);
|
|
1697
|
+
expect((await store.getPublicLogs({
|
|
1698
|
+
fromBlock: BlockNumber(1)
|
|
1699
|
+
})).logs.length).toEqual(block.body.txEffects.map((txEffect)=>txEffect.publicLogs).flat().length);
|
|
1700
|
+
// This one is a pain for memory as we would never want to just delete memory in the middle.
|
|
1701
|
+
await store.deleteLogs([
|
|
1702
|
+
block
|
|
1703
|
+
]);
|
|
1704
|
+
expect((await store.getPublicLogs({
|
|
1705
|
+
fromBlock: BlockNumber(1)
|
|
1706
|
+
})).logs.length).toEqual(0);
|
|
368
1707
|
});
|
|
369
1708
|
describe('getTxEffect', ()=>{
|
|
1709
|
+
const getBlock = (i)=>publishedCheckpoints[i].checkpoint.blocks[0];
|
|
370
1710
|
beforeEach(async ()=>{
|
|
371
|
-
await store.addLogs(
|
|
372
|
-
await store.
|
|
1711
|
+
await store.addLogs(publishedCheckpoints.flatMap((x)=>x.checkpoint.blocks));
|
|
1712
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
373
1713
|
});
|
|
374
1714
|
it.each([
|
|
375
1715
|
()=>({
|
|
376
|
-
data:
|
|
377
|
-
block:
|
|
1716
|
+
data: getBlock(0).body.txEffects[0],
|
|
1717
|
+
block: getBlock(0),
|
|
378
1718
|
txIndexInBlock: 0
|
|
379
1719
|
}),
|
|
380
1720
|
()=>({
|
|
381
|
-
data:
|
|
382
|
-
block:
|
|
1721
|
+
data: getBlock(9).body.txEffects[3],
|
|
1722
|
+
block: getBlock(9),
|
|
383
1723
|
txIndexInBlock: 3
|
|
384
1724
|
}),
|
|
385
1725
|
()=>({
|
|
386
|
-
data:
|
|
387
|
-
block:
|
|
1726
|
+
data: getBlock(3).body.txEffects[1],
|
|
1727
|
+
block: getBlock(3),
|
|
388
1728
|
txIndexInBlock: 1
|
|
389
1729
|
}),
|
|
390
1730
|
()=>({
|
|
391
|
-
data:
|
|
392
|
-
block:
|
|
1731
|
+
data: getBlock(5).body.txEffects[2],
|
|
1732
|
+
block: getBlock(5),
|
|
393
1733
|
txIndexInBlock: 2
|
|
394
1734
|
}),
|
|
395
1735
|
()=>({
|
|
396
|
-
data:
|
|
397
|
-
block:
|
|
1736
|
+
data: getBlock(1).body.txEffects[0],
|
|
1737
|
+
block: getBlock(1),
|
|
398
1738
|
txIndexInBlock: 0
|
|
399
1739
|
})
|
|
400
1740
|
])('retrieves a previously stored transaction', async (getExpectedTx)=>{
|
|
@@ -402,7 +1742,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
402
1742
|
const expectedTx = {
|
|
403
1743
|
data,
|
|
404
1744
|
l2BlockNumber: block.number,
|
|
405
|
-
l2BlockHash: L2BlockHash.fromField(await block.hash()),
|
|
1745
|
+
l2BlockHash: L2BlockHash.fromField(await block.header.hash()),
|
|
406
1746
|
txIndexInBlock
|
|
407
1747
|
};
|
|
408
1748
|
const actualTx = await store.getTxEffect(data.txHash);
|
|
@@ -412,36 +1752,36 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
412
1752
|
await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
|
|
413
1753
|
});
|
|
414
1754
|
it.each([
|
|
415
|
-
()=>
|
|
416
|
-
()=>
|
|
417
|
-
()=>
|
|
418
|
-
()=>
|
|
419
|
-
()=>
|
|
420
|
-
])('tries to retrieves a previously stored transaction after deleted', async (
|
|
421
|
-
await store.
|
|
422
|
-
const
|
|
423
|
-
const actualTx = await store.getTxEffect(
|
|
1755
|
+
()=>getBlock(0).body.txEffects[0],
|
|
1756
|
+
()=>getBlock(9).body.txEffects[3],
|
|
1757
|
+
()=>getBlock(3).body.txEffects[1],
|
|
1758
|
+
()=>getBlock(5).body.txEffects[2],
|
|
1759
|
+
()=>getBlock(1).body.txEffects[0]
|
|
1760
|
+
])('tries to retrieves a previously stored transaction after deleted', async (getTxEffect)=>{
|
|
1761
|
+
await store.unwindCheckpoints(CheckpointNumber(publishedCheckpoints.length), publishedCheckpoints.length);
|
|
1762
|
+
const txEffect = getTxEffect();
|
|
1763
|
+
const actualTx = await store.getTxEffect(txEffect.txHash);
|
|
424
1764
|
expect(actualTx).toEqual(undefined);
|
|
425
1765
|
});
|
|
426
1766
|
it('returns undefined if tx is not found', async ()=>{
|
|
427
1767
|
await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
|
|
428
1768
|
});
|
|
429
1769
|
it('does not fail if the block is unwound while requesting a tx', async ()=>{
|
|
430
|
-
const
|
|
1770
|
+
const txEffect = getBlock(1).body.txEffects[0];
|
|
431
1771
|
let done = false;
|
|
432
1772
|
void (async ()=>{
|
|
433
1773
|
while(!done){
|
|
434
|
-
void store.getTxEffect(
|
|
1774
|
+
void store.getTxEffect(txEffect.txHash);
|
|
435
1775
|
await sleep(1);
|
|
436
1776
|
}
|
|
437
1777
|
})();
|
|
438
|
-
await store.
|
|
1778
|
+
await store.unwindCheckpoints(CheckpointNumber(publishedCheckpoints.length), publishedCheckpoints.length);
|
|
439
1779
|
done = true;
|
|
440
|
-
expect(await store.getTxEffect(
|
|
1780
|
+
expect(await store.getTxEffect(txEffect.txHash)).toEqual(undefined);
|
|
441
1781
|
});
|
|
442
1782
|
});
|
|
443
1783
|
describe('L1 to L2 Messages', ()=>{
|
|
444
|
-
const
|
|
1784
|
+
const initialCheckpointNumber = CheckpointNumber(13);
|
|
445
1785
|
const checkMessages = async (msgs)=>{
|
|
446
1786
|
expect(await store.getLastL1ToL2Message()).toEqual(msgs.at(-1));
|
|
447
1787
|
expect(await toArray(store.iterateL1ToL2Messages())).toEqual(msgs);
|
|
@@ -449,11 +1789,11 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
449
1789
|
};
|
|
450
1790
|
const makeInboxMessagesWithFullBlocks = (blockCount, opts = {})=>makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, {
|
|
451
1791
|
overrideFn: (msg, i)=>{
|
|
452
|
-
const
|
|
453
|
-
const index = InboxLeaf.
|
|
1792
|
+
const checkpointNumber = CheckpointNumber((opts.initialCheckpointNumber ?? initialCheckpointNumber) + Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP));
|
|
1793
|
+
const index = InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(i % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
454
1794
|
return {
|
|
455
1795
|
...msg,
|
|
456
|
-
|
|
1796
|
+
checkpointNumber,
|
|
457
1797
|
index
|
|
458
1798
|
};
|
|
459
1799
|
}
|
|
@@ -461,7 +1801,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
461
1801
|
it('stores first message ever', async ()=>{
|
|
462
1802
|
const msg = makeInboxMessage(Buffer16.ZERO, {
|
|
463
1803
|
index: 0n,
|
|
464
|
-
|
|
1804
|
+
checkpointNumber: CheckpointNumber(1)
|
|
465
1805
|
});
|
|
466
1806
|
await store.addL1ToL2Messages([
|
|
467
1807
|
msg
|
|
@@ -469,13 +1809,13 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
469
1809
|
await checkMessages([
|
|
470
1810
|
msg
|
|
471
1811
|
]);
|
|
472
|
-
expect(await store.getL1ToL2Messages(1)).toEqual([
|
|
1812
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toEqual([
|
|
473
1813
|
msg.leaf
|
|
474
1814
|
]);
|
|
475
1815
|
});
|
|
476
1816
|
it('stores single message', async ()=>{
|
|
477
1817
|
const msg = makeInboxMessage(Buffer16.ZERO, {
|
|
478
|
-
|
|
1818
|
+
checkpointNumber: CheckpointNumber(2)
|
|
479
1819
|
});
|
|
480
1820
|
await store.addL1ToL2Messages([
|
|
481
1821
|
msg
|
|
@@ -483,23 +1823,23 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
483
1823
|
await checkMessages([
|
|
484
1824
|
msg
|
|
485
1825
|
]);
|
|
486
|
-
expect(await store.getL1ToL2Messages(2)).toEqual([
|
|
1826
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(2))).toEqual([
|
|
487
1827
|
msg.leaf
|
|
488
1828
|
]);
|
|
489
1829
|
});
|
|
490
1830
|
it('stores and returns messages across different blocks', async ()=>{
|
|
491
1831
|
const msgs = makeInboxMessages(5, {
|
|
492
|
-
|
|
1832
|
+
initialCheckpointNumber
|
|
493
1833
|
});
|
|
494
1834
|
await store.addL1ToL2Messages(msgs);
|
|
495
1835
|
await checkMessages(msgs);
|
|
496
|
-
expect(await store.getL1ToL2Messages(
|
|
1836
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual([
|
|
497
1837
|
msgs[2]
|
|
498
1838
|
].map((m)=>m.leaf));
|
|
499
1839
|
});
|
|
500
1840
|
it('stores the same messages again', async ()=>{
|
|
501
1841
|
const msgs = makeInboxMessages(5, {
|
|
502
|
-
|
|
1842
|
+
initialCheckpointNumber
|
|
503
1843
|
});
|
|
504
1844
|
await store.addL1ToL2Messages(msgs);
|
|
505
1845
|
await store.addL1ToL2Messages(msgs.slice(2));
|
|
@@ -507,10 +1847,10 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
507
1847
|
});
|
|
508
1848
|
it('stores and returns messages across different blocks with gaps', async ()=>{
|
|
509
1849
|
const msgs1 = makeInboxMessages(3, {
|
|
510
|
-
|
|
1850
|
+
initialCheckpointNumber: CheckpointNumber(1)
|
|
511
1851
|
});
|
|
512
1852
|
const msgs2 = makeInboxMessages(3, {
|
|
513
|
-
|
|
1853
|
+
initialCheckpointNumber: CheckpointNumber(20),
|
|
514
1854
|
initialHash: msgs1.at(-1).rollingHash
|
|
515
1855
|
});
|
|
516
1856
|
await store.addL1ToL2Messages(msgs1);
|
|
@@ -519,22 +1859,22 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
519
1859
|
...msgs1,
|
|
520
1860
|
...msgs2
|
|
521
1861
|
]);
|
|
522
|
-
expect(await store.getL1ToL2Messages(1)).toEqual([
|
|
1862
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toEqual([
|
|
523
1863
|
msgs1[0].leaf
|
|
524
1864
|
]);
|
|
525
|
-
expect(await store.getL1ToL2Messages(4)).toEqual([]);
|
|
526
|
-
expect(await store.getL1ToL2Messages(20)).toEqual([
|
|
1865
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(4))).toEqual([]);
|
|
1866
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(20))).toEqual([
|
|
527
1867
|
msgs2[0].leaf
|
|
528
1868
|
]);
|
|
529
|
-
expect(await store.getL1ToL2Messages(24)).toEqual([]);
|
|
1869
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(24))).toEqual([]);
|
|
530
1870
|
});
|
|
531
1871
|
it('stores and returns messages with block numbers larger than a byte', async ()=>{
|
|
532
1872
|
const msgs = makeInboxMessages(5, {
|
|
533
|
-
|
|
1873
|
+
initialCheckpointNumber: CheckpointNumber(1000)
|
|
534
1874
|
});
|
|
535
1875
|
await store.addL1ToL2Messages(msgs);
|
|
536
1876
|
await checkMessages(msgs);
|
|
537
|
-
expect(await store.getL1ToL2Messages(1002)).toEqual([
|
|
1877
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1002))).toEqual([
|
|
538
1878
|
msgs[2]
|
|
539
1879
|
].map((m)=>m.leaf));
|
|
540
1880
|
});
|
|
@@ -542,27 +1882,27 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
542
1882
|
const msgs = makeInboxMessagesWithFullBlocks(4);
|
|
543
1883
|
await store.addL1ToL2Messages(msgs);
|
|
544
1884
|
await checkMessages(msgs);
|
|
545
|
-
const blockMessages = await store.getL1ToL2Messages(
|
|
1885
|
+
const blockMessages = await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 1));
|
|
546
1886
|
expect(blockMessages).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
547
1887
|
expect(blockMessages).toEqual(msgs.slice(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2).map((m)=>m.leaf));
|
|
548
1888
|
});
|
|
549
1889
|
it('stores messages in multiple operations', async ()=>{
|
|
550
1890
|
const msgs = makeInboxMessages(20, {
|
|
551
|
-
|
|
1891
|
+
initialCheckpointNumber
|
|
552
1892
|
});
|
|
553
1893
|
await store.addL1ToL2Messages(msgs.slice(0, 10));
|
|
554
1894
|
await store.addL1ToL2Messages(msgs.slice(10, 20));
|
|
555
|
-
expect(await store.getL1ToL2Messages(
|
|
1895
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual([
|
|
556
1896
|
msgs[2]
|
|
557
1897
|
].map((m)=>m.leaf));
|
|
558
|
-
expect(await store.getL1ToL2Messages(
|
|
1898
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 12))).toEqual([
|
|
559
1899
|
msgs[12]
|
|
560
1900
|
].map((m)=>m.leaf));
|
|
561
1901
|
await checkMessages(msgs);
|
|
562
1902
|
});
|
|
563
1903
|
it('iterates over messages from start index', async ()=>{
|
|
564
1904
|
const msgs = makeInboxMessages(10, {
|
|
565
|
-
|
|
1905
|
+
initialCheckpointNumber
|
|
566
1906
|
});
|
|
567
1907
|
await store.addL1ToL2Messages(msgs);
|
|
568
1908
|
const iterated = await toArray(store.iterateL1ToL2Messages({
|
|
@@ -572,9 +1912,10 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
572
1912
|
});
|
|
573
1913
|
it('iterates over messages in reverse', async ()=>{
|
|
574
1914
|
const msgs = makeInboxMessages(10, {
|
|
575
|
-
|
|
1915
|
+
initialCheckpointNumber
|
|
576
1916
|
});
|
|
577
1917
|
await store.addL1ToL2Messages(msgs);
|
|
1918
|
+
initialCheckpointNumber;
|
|
578
1919
|
const iterated = await toArray(store.iterateL1ToL2Messages({
|
|
579
1920
|
reverse: true,
|
|
580
1921
|
end: msgs[3].index
|
|
@@ -592,9 +1933,9 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
592
1933
|
});
|
|
593
1934
|
it('throws if block number for the first message is out of order', async ()=>{
|
|
594
1935
|
const msgs = makeInboxMessages(4, {
|
|
595
|
-
|
|
1936
|
+
initialCheckpointNumber
|
|
596
1937
|
});
|
|
597
|
-
msgs[2].
|
|
1938
|
+
msgs[2].checkpointNumber = CheckpointNumber(initialCheckpointNumber - 1);
|
|
598
1939
|
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
599
1940
|
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
600
1941
|
});
|
|
@@ -606,29 +1947,29 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
606
1947
|
it('throws if rolling hash for first message is not correct', async ()=>{
|
|
607
1948
|
const msgs = makeInboxMessages(4);
|
|
608
1949
|
msgs[2].rollingHash = Buffer16.random();
|
|
609
|
-
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
1950
|
+
await store.addL1ToL2Messages(msgs.slice(0, CheckpointNumber(2)));
|
|
610
1951
|
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
611
1952
|
});
|
|
612
1953
|
it('throws if index is not in the correct range', async ()=>{
|
|
613
1954
|
const msgs = makeInboxMessages(5, {
|
|
614
|
-
|
|
1955
|
+
initialCheckpointNumber
|
|
615
1956
|
});
|
|
616
1957
|
msgs.at(-1).index += 100n;
|
|
617
1958
|
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
618
1959
|
});
|
|
619
1960
|
it('throws if first index in block has gaps', async ()=>{
|
|
620
1961
|
const msgs = makeInboxMessages(4, {
|
|
621
|
-
|
|
1962
|
+
initialCheckpointNumber
|
|
622
1963
|
});
|
|
623
1964
|
msgs[2].index++;
|
|
624
1965
|
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
625
1966
|
});
|
|
626
1967
|
it('throws if index does not follow previous one', async ()=>{
|
|
627
1968
|
const msgs = makeInboxMessages(2, {
|
|
628
|
-
|
|
1969
|
+
initialCheckpointNumber,
|
|
629
1970
|
overrideFn: (msg, i)=>({
|
|
630
1971
|
...msg,
|
|
631
|
-
|
|
1972
|
+
checkpointNumber: CheckpointNumber(2),
|
|
632
1973
|
index: BigInt(i + NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2)
|
|
633
1974
|
})
|
|
634
1975
|
});
|
|
@@ -637,24 +1978,24 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
637
1978
|
});
|
|
638
1979
|
it('removes messages up to the given block number', async ()=>{
|
|
639
1980
|
const msgs = makeInboxMessagesWithFullBlocks(4, {
|
|
640
|
-
|
|
1981
|
+
initialCheckpointNumber: CheckpointNumber(1)
|
|
641
1982
|
});
|
|
642
1983
|
await store.addL1ToL2Messages(msgs);
|
|
643
1984
|
await checkMessages(msgs);
|
|
644
|
-
expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
645
|
-
expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
646
|
-
expect(await store.getL1ToL2Messages(3)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
647
|
-
expect(await store.getL1ToL2Messages(4)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
648
|
-
await store.
|
|
649
|
-
expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
650
|
-
expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
651
|
-
expect(await store.getL1ToL2Messages(3)).toHaveLength(0);
|
|
652
|
-
expect(await store.getL1ToL2Messages(4)).toHaveLength(0);
|
|
1985
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
1986
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
1987
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
1988
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
1989
|
+
await store.rollbackL1ToL2MessagesToCheckpoint(CheckpointNumber(2));
|
|
1990
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
1991
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
1992
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(0);
|
|
1993
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(0);
|
|
653
1994
|
await checkMessages(msgs.slice(0, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2));
|
|
654
1995
|
});
|
|
655
1996
|
it('removes messages starting with the given index', async ()=>{
|
|
656
1997
|
const msgs = makeInboxMessagesWithFullBlocks(4, {
|
|
657
|
-
|
|
1998
|
+
initialCheckpointNumber: CheckpointNumber(1)
|
|
658
1999
|
});
|
|
659
2000
|
await store.addL1ToL2Messages(msgs);
|
|
660
2001
|
await store.removeL1ToL2Messages(msgs[13].index);
|
|
@@ -677,7 +2018,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
677
2018
|
};
|
|
678
2019
|
await store.addContractInstances([
|
|
679
2020
|
contractInstance
|
|
680
|
-
], blockNum);
|
|
2021
|
+
], BlockNumber(blockNum));
|
|
681
2022
|
});
|
|
682
2023
|
it('returns previously stored contract instances', async ()=>{
|
|
683
2024
|
await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toMatchObject(contractInstance);
|
|
@@ -688,7 +2029,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
688
2029
|
it('returns undefined if previously stored contract instances was deleted', async ()=>{
|
|
689
2030
|
await store.deleteContractInstances([
|
|
690
2031
|
contractInstance
|
|
691
|
-
], blockNum);
|
|
2032
|
+
], BlockNumber(blockNum));
|
|
692
2033
|
await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toBeUndefined();
|
|
693
2034
|
});
|
|
694
2035
|
});
|
|
@@ -710,7 +2051,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
710
2051
|
};
|
|
711
2052
|
await store.addContractInstances([
|
|
712
2053
|
contractInstance
|
|
713
|
-
], 1);
|
|
2054
|
+
], BlockNumber(1));
|
|
714
2055
|
await store.addContractInstanceUpdates([
|
|
715
2056
|
{
|
|
716
2057
|
prevContractClassId: classId,
|
|
@@ -747,7 +2088,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
747
2088
|
};
|
|
748
2089
|
await store.addContractInstances([
|
|
749
2090
|
otherContractInstance
|
|
750
|
-
], 1);
|
|
2091
|
+
], BlockNumber(1));
|
|
751
2092
|
const fetchedInstance = await store.getContractInstance(otherContractInstance.address, timestampOfChange + 1n);
|
|
752
2093
|
expect(fetchedInstance?.originalContractClassId).toEqual(otherClassId);
|
|
753
2094
|
expect(fetchedInstance?.currentContractClassId).toEqual(otherClassId);
|
|
@@ -765,7 +2106,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
765
2106
|
};
|
|
766
2107
|
await store.addContractInstances([
|
|
767
2108
|
otherContractInstance
|
|
768
|
-
], 1);
|
|
2109
|
+
], BlockNumber(1));
|
|
769
2110
|
await store.addContractInstanceUpdates([
|
|
770
2111
|
{
|
|
771
2112
|
prevContractClassId: otherClassId,
|
|
@@ -788,7 +2129,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
788
2129
|
contractClass
|
|
789
2130
|
], [
|
|
790
2131
|
await computePublicBytecodeCommitment(contractClass.packedBytecode)
|
|
791
|
-
], blockNum);
|
|
2132
|
+
], BlockNumber(blockNum));
|
|
792
2133
|
});
|
|
793
2134
|
it('returns previously stored contract class', async ()=>{
|
|
794
2135
|
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
@@ -796,7 +2137,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
796
2137
|
it('returns undefined if the initial deployed contract class was deleted', async ()=>{
|
|
797
2138
|
await store.deleteContractClasses([
|
|
798
2139
|
contractClass
|
|
799
|
-
], blockNum);
|
|
2140
|
+
], BlockNumber(blockNum));
|
|
800
2141
|
await expect(store.getContractClass(contractClass.id)).resolves.toBeUndefined();
|
|
801
2142
|
});
|
|
802
2143
|
it('returns contract class if later "deployment" class was deleted', async ()=>{
|
|
@@ -804,10 +2145,10 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
804
2145
|
contractClass
|
|
805
2146
|
], [
|
|
806
2147
|
await computePublicBytecodeCommitment(contractClass.packedBytecode)
|
|
807
|
-
], blockNum + 1);
|
|
2148
|
+
], BlockNumber(blockNum + 1));
|
|
808
2149
|
await store.deleteContractClasses([
|
|
809
2150
|
contractClass
|
|
810
|
-
], blockNum + 1);
|
|
2151
|
+
], BlockNumber(blockNum + 1));
|
|
811
2152
|
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
812
2153
|
});
|
|
813
2154
|
it('returns undefined if contract class is not found', async ()=>{
|
|
@@ -840,180 +2181,268 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
840
2181
|
expect(stored?.utilityFunctions).toEqual(fns);
|
|
841
2182
|
});
|
|
842
2183
|
});
|
|
843
|
-
describe('
|
|
844
|
-
const
|
|
2184
|
+
describe('getPrivateLogsByTags', ()=>{
|
|
2185
|
+
const numBlocksForLogs = 3;
|
|
845
2186
|
const numTxsPerBlock = 4;
|
|
846
2187
|
const numPrivateLogsPerTx = 3;
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
: new Fr((blockNumber * 100 + txIndex * 10 + logIndex) * (isPublic ? 123 : 1));
|
|
2188
|
+
let logsCheckpoints;
|
|
2189
|
+
const makePrivateLogTag = (blockNumber, txIndex, logIndex)=>new SiloedTag(blockNumber === 1 && txIndex === 0 && logIndex === 0 ? Fr.ZERO // Shared tag
|
|
2190
|
+
: new Fr(blockNumber * 100 + txIndex * 10 + logIndex));
|
|
851
2191
|
const makePrivateLog = (tag)=>PrivateLog.from({
|
|
852
|
-
fields: makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, (i)=>!i ? tag : new Fr(tag.
|
|
2192
|
+
fields: makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, (i)=>!i ? tag.value : new Fr(tag.value.toBigInt() + BigInt(i))),
|
|
853
2193
|
emittedLength: PRIVATE_LOG_SIZE_IN_FIELDS
|
|
854
2194
|
});
|
|
855
|
-
const makePublicLog = (tag)=>PublicLog.from({
|
|
856
|
-
contractAddress: AztecAddress.fromNumber(1),
|
|
857
|
-
// Arbitrary length
|
|
858
|
-
fields: new Array(10).fill(null).map((_, i)=>!i ? tag : new Fr(tag.toNumber() + i))
|
|
859
|
-
});
|
|
860
2195
|
const mockPrivateLogs = (blockNumber, txIndex)=>{
|
|
861
2196
|
return times(numPrivateLogsPerTx, (logIndex)=>{
|
|
862
|
-
const tag =
|
|
2197
|
+
const tag = makePrivateLogTag(blockNumber, txIndex, logIndex);
|
|
863
2198
|
return makePrivateLog(tag);
|
|
864
2199
|
});
|
|
865
2200
|
};
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
2201
|
+
const mockCheckpointWithLogs = async (blockNumber, previousArchive)=>{
|
|
2202
|
+
const block = await L2BlockNew.random(BlockNumber(blockNumber), {
|
|
2203
|
+
checkpointNumber: CheckpointNumber(blockNumber),
|
|
2204
|
+
indexWithinCheckpoint: 0,
|
|
2205
|
+
state: makeStateForBlock(blockNumber, numTxsPerBlock),
|
|
2206
|
+
...previousArchive ? {
|
|
2207
|
+
lastArchive: previousArchive
|
|
2208
|
+
} : {}
|
|
870
2209
|
});
|
|
871
|
-
|
|
872
|
-
const mockBlockWithLogs = async (blockNumber)=>{
|
|
873
|
-
const block = await L2Block.random(blockNumber);
|
|
874
|
-
block.header.globalVariables.blockNumber = blockNumber;
|
|
2210
|
+
block.header.globalVariables.blockNumber = BlockNumber(blockNumber);
|
|
875
2211
|
block.body.txEffects = await timesParallel(numTxsPerBlock, async (txIndex)=>{
|
|
876
2212
|
const txEffect = await TxEffect.random();
|
|
877
2213
|
txEffect.privateLogs = mockPrivateLogs(blockNumber, txIndex);
|
|
878
|
-
txEffect.publicLogs =
|
|
2214
|
+
txEffect.publicLogs = []; // No public logs needed for private log tests
|
|
879
2215
|
return txEffect;
|
|
880
2216
|
});
|
|
881
|
-
|
|
882
|
-
block
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
blockNumber: BigInt(blockNumber),
|
|
886
|
-
blockHash: makeBlockHash(blockNumber),
|
|
887
|
-
timestamp: BigInt(blockNumber)
|
|
888
|
-
}
|
|
889
|
-
});
|
|
2217
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
2218
|
+
block
|
|
2219
|
+
], CheckpointNumber(blockNumber));
|
|
2220
|
+
return makePublishedCheckpoint(checkpoint, blockNumber);
|
|
890
2221
|
};
|
|
891
2222
|
beforeEach(async ()=>{
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
2223
|
+
// Create checkpoints sequentially to chain archive roots
|
|
2224
|
+
logsCheckpoints = [];
|
|
2225
|
+
for(let i = 0; i < numBlocksForLogs; i++){
|
|
2226
|
+
const previousArchive = i > 0 ? logsCheckpoints[i - 1].checkpoint.blocks[0].archive : undefined;
|
|
2227
|
+
logsCheckpoints.push(await mockCheckpointWithLogs(i + 1, previousArchive));
|
|
2228
|
+
}
|
|
2229
|
+
await store.addCheckpoints(logsCheckpoints);
|
|
2230
|
+
await store.addLogs(logsCheckpoints.flatMap((p)=>p.checkpoint.blocks));
|
|
895
2231
|
});
|
|
896
2232
|
it('is possible to batch request private logs via tags', async ()=>{
|
|
897
2233
|
const tags = [
|
|
898
|
-
|
|
899
|
-
|
|
2234
|
+
makePrivateLogTag(2, 1, 2),
|
|
2235
|
+
makePrivateLogTag(1, 2, 0)
|
|
900
2236
|
];
|
|
901
|
-
const logsByTags = await store.
|
|
2237
|
+
const logsByTags = await store.getPrivateLogsByTags(tags);
|
|
902
2238
|
expect(logsByTags).toEqual([
|
|
903
2239
|
[
|
|
904
2240
|
expect.objectContaining({
|
|
905
2241
|
blockNumber: 2,
|
|
906
|
-
|
|
907
|
-
isFromPublic: false
|
|
2242
|
+
logData: makePrivateLog(tags[0]).getEmittedFields()
|
|
908
2243
|
})
|
|
909
2244
|
],
|
|
910
2245
|
[
|
|
911
2246
|
expect.objectContaining({
|
|
912
2247
|
blockNumber: 1,
|
|
913
|
-
|
|
914
|
-
isFromPublic: false
|
|
2248
|
+
logData: makePrivateLog(tags[1]).getEmittedFields()
|
|
915
2249
|
})
|
|
916
2250
|
]
|
|
917
2251
|
]);
|
|
918
2252
|
});
|
|
919
|
-
it('is possible to batch request
|
|
920
|
-
// Tag(1, 0, 0) is shared with the first private log and the first public log.
|
|
2253
|
+
it('is possible to batch request logs that have the same tag but different content', async ()=>{
|
|
921
2254
|
const tags = [
|
|
922
|
-
|
|
2255
|
+
makePrivateLogTag(1, 2, 1)
|
|
923
2256
|
];
|
|
924
|
-
|
|
2257
|
+
// Create a checkpoint containing logs that have the same tag as the checkpoints before.
|
|
2258
|
+
// Chain from the last checkpoint's archive
|
|
2259
|
+
const newBlockNumber = numBlocksForLogs + 1;
|
|
2260
|
+
const previousArchive = logsCheckpoints[logsCheckpoints.length - 1].checkpoint.blocks[0].archive;
|
|
2261
|
+
const newCheckpoint = await mockCheckpointWithLogs(newBlockNumber, previousArchive);
|
|
2262
|
+
const newLog = newCheckpoint.checkpoint.blocks[0].body.txEffects[1].privateLogs[1];
|
|
2263
|
+
newLog.fields[0] = tags[0].value;
|
|
2264
|
+
newCheckpoint.checkpoint.blocks[0].body.txEffects[1].privateLogs[1] = newLog;
|
|
2265
|
+
await store.addCheckpoints([
|
|
2266
|
+
newCheckpoint
|
|
2267
|
+
]);
|
|
2268
|
+
await store.addLogs([
|
|
2269
|
+
newCheckpoint.checkpoint.blocks[0]
|
|
2270
|
+
]);
|
|
2271
|
+
const logsByTags = await store.getPrivateLogsByTags(tags);
|
|
925
2272
|
expect(logsByTags).toEqual([
|
|
926
2273
|
[
|
|
927
2274
|
expect.objectContaining({
|
|
928
2275
|
blockNumber: 1,
|
|
929
|
-
|
|
930
|
-
isFromPublic: false
|
|
2276
|
+
logData: makePrivateLog(tags[0]).getEmittedFields()
|
|
931
2277
|
}),
|
|
2278
|
+
expect.objectContaining({
|
|
2279
|
+
blockNumber: newBlockNumber,
|
|
2280
|
+
logData: newLog.getEmittedFields()
|
|
2281
|
+
})
|
|
2282
|
+
]
|
|
2283
|
+
]);
|
|
2284
|
+
});
|
|
2285
|
+
it('is possible to request logs for non-existing tags and determine their position', async ()=>{
|
|
2286
|
+
const tags = [
|
|
2287
|
+
makePrivateLogTag(99, 88, 77),
|
|
2288
|
+
makePrivateLogTag(1, 1, 1)
|
|
2289
|
+
];
|
|
2290
|
+
const logsByTags = await store.getPrivateLogsByTags(tags);
|
|
2291
|
+
expect(logsByTags).toEqual([
|
|
2292
|
+
[],
|
|
2293
|
+
[
|
|
932
2294
|
expect.objectContaining({
|
|
933
2295
|
blockNumber: 1,
|
|
934
|
-
|
|
935
|
-
|
|
2296
|
+
logData: makePrivateLog(tags[1]).getEmittedFields()
|
|
2297
|
+
})
|
|
2298
|
+
]
|
|
2299
|
+
]);
|
|
2300
|
+
});
|
|
2301
|
+
});
|
|
2302
|
+
describe('getPublicLogsByTagsFromContract', ()=>{
|
|
2303
|
+
const numBlocksForLogs = 3;
|
|
2304
|
+
const numTxsPerBlock = 4;
|
|
2305
|
+
const numPublicLogsPerTx = 2;
|
|
2306
|
+
const contractAddress = AztecAddress.fromNumber(543254);
|
|
2307
|
+
let logsCheckpoints;
|
|
2308
|
+
const makePublicLogTag = (blockNumber, txIndex, logIndex)=>new Tag(blockNumber === 1 && txIndex === 0 && logIndex === 0 ? Fr.ZERO // Shared tag
|
|
2309
|
+
: new Fr((blockNumber * 100 + txIndex * 10 + logIndex) * 123));
|
|
2310
|
+
const makePublicLog = (tag)=>PublicLog.from({
|
|
2311
|
+
contractAddress: contractAddress,
|
|
2312
|
+
// Arbitrary length
|
|
2313
|
+
fields: new Array(10).fill(null).map((_, i)=>!i ? tag.value : new Fr(tag.value.toBigInt() + BigInt(i)))
|
|
2314
|
+
});
|
|
2315
|
+
const mockPublicLogs = (blockNumber, txIndex)=>{
|
|
2316
|
+
return times(numPublicLogsPerTx, (logIndex)=>{
|
|
2317
|
+
const tag = makePublicLogTag(blockNumber, txIndex, logIndex);
|
|
2318
|
+
return makePublicLog(tag);
|
|
2319
|
+
});
|
|
2320
|
+
};
|
|
2321
|
+
const mockCheckpointWithLogs = async (blockNumber, previousArchive)=>{
|
|
2322
|
+
const block = await L2BlockNew.random(BlockNumber(blockNumber), {
|
|
2323
|
+
checkpointNumber: CheckpointNumber(blockNumber),
|
|
2324
|
+
indexWithinCheckpoint: 0,
|
|
2325
|
+
state: makeStateForBlock(blockNumber, numTxsPerBlock),
|
|
2326
|
+
...previousArchive ? {
|
|
2327
|
+
lastArchive: previousArchive
|
|
2328
|
+
} : {}
|
|
2329
|
+
});
|
|
2330
|
+
block.header.globalVariables.blockNumber = BlockNumber(blockNumber);
|
|
2331
|
+
block.body.txEffects = await timesParallel(numTxsPerBlock, async (txIndex)=>{
|
|
2332
|
+
const txEffect = await TxEffect.random();
|
|
2333
|
+
txEffect.privateLogs = []; // No private logs needed for public log tests
|
|
2334
|
+
txEffect.publicLogs = mockPublicLogs(blockNumber, txIndex);
|
|
2335
|
+
return txEffect;
|
|
2336
|
+
});
|
|
2337
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
2338
|
+
block
|
|
2339
|
+
], CheckpointNumber(blockNumber));
|
|
2340
|
+
return makePublishedCheckpoint(checkpoint, blockNumber);
|
|
2341
|
+
};
|
|
2342
|
+
beforeEach(async ()=>{
|
|
2343
|
+
// Create checkpoints sequentially to chain archive roots
|
|
2344
|
+
logsCheckpoints = [];
|
|
2345
|
+
for(let i = 0; i < numBlocksForLogs; i++){
|
|
2346
|
+
const previousArchive = i > 0 ? logsCheckpoints[i - 1].checkpoint.blocks[0].archive : undefined;
|
|
2347
|
+
logsCheckpoints.push(await mockCheckpointWithLogs(i + 1, previousArchive));
|
|
2348
|
+
}
|
|
2349
|
+
await store.addCheckpoints(logsCheckpoints);
|
|
2350
|
+
await store.addLogs(logsCheckpoints.flatMap((p)=>p.checkpoint.blocks));
|
|
2351
|
+
});
|
|
2352
|
+
it('is possible to batch request public logs via tags', async ()=>{
|
|
2353
|
+
const tags = [
|
|
2354
|
+
makePublicLogTag(2, 1, 1),
|
|
2355
|
+
makePublicLogTag(1, 2, 0)
|
|
2356
|
+
];
|
|
2357
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
2358
|
+
expect(logsByTags).toEqual([
|
|
2359
|
+
[
|
|
2360
|
+
expect.objectContaining({
|
|
2361
|
+
blockNumber: 2,
|
|
2362
|
+
logData: makePublicLog(tags[0]).getEmittedFields()
|
|
2363
|
+
})
|
|
2364
|
+
],
|
|
2365
|
+
[
|
|
2366
|
+
expect.objectContaining({
|
|
2367
|
+
blockNumber: 1,
|
|
2368
|
+
logData: makePublicLog(tags[1]).getEmittedFields()
|
|
936
2369
|
})
|
|
937
2370
|
]
|
|
938
2371
|
]);
|
|
939
2372
|
});
|
|
940
2373
|
it('is possible to batch request logs that have the same tag but different content', async ()=>{
|
|
941
2374
|
const tags = [
|
|
942
|
-
|
|
2375
|
+
makePublicLogTag(1, 2, 1)
|
|
943
2376
|
];
|
|
944
|
-
// Create a
|
|
945
|
-
|
|
946
|
-
const
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
2377
|
+
// Create a checkpoint containing logs that have the same tag as the checkpoints before.
|
|
2378
|
+
// Chain from the last checkpoint's archive
|
|
2379
|
+
const newBlockNumber = numBlocksForLogs + 1;
|
|
2380
|
+
const previousArchive = logsCheckpoints[logsCheckpoints.length - 1].checkpoint.blocks[0].archive;
|
|
2381
|
+
const newCheckpoint = await mockCheckpointWithLogs(newBlockNumber, previousArchive);
|
|
2382
|
+
const newLog = newCheckpoint.checkpoint.blocks[0].body.txEffects[1].publicLogs[1];
|
|
2383
|
+
newLog.fields[0] = tags[0].value;
|
|
2384
|
+
newCheckpoint.checkpoint.blocks[0].body.txEffects[1].publicLogs[1] = newLog;
|
|
2385
|
+
await store.addCheckpoints([
|
|
2386
|
+
newCheckpoint
|
|
952
2387
|
]);
|
|
953
2388
|
await store.addLogs([
|
|
954
|
-
|
|
2389
|
+
newCheckpoint.checkpoint.blocks[0]
|
|
955
2390
|
]);
|
|
956
|
-
const logsByTags = await store.
|
|
2391
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
957
2392
|
expect(logsByTags).toEqual([
|
|
958
2393
|
[
|
|
959
2394
|
expect.objectContaining({
|
|
960
2395
|
blockNumber: 1,
|
|
961
|
-
|
|
962
|
-
isFromPublic: false
|
|
2396
|
+
logData: makePublicLog(tags[0]).getEmittedFields()
|
|
963
2397
|
}),
|
|
964
2398
|
expect.objectContaining({
|
|
965
2399
|
blockNumber: newBlockNumber,
|
|
966
|
-
|
|
967
|
-
isFromPublic: false
|
|
2400
|
+
logData: newLog.getEmittedFields()
|
|
968
2401
|
})
|
|
969
2402
|
]
|
|
970
2403
|
]);
|
|
971
2404
|
});
|
|
972
2405
|
it('is possible to request logs for non-existing tags and determine their position', async ()=>{
|
|
973
2406
|
const tags = [
|
|
974
|
-
|
|
975
|
-
|
|
2407
|
+
makePublicLogTag(99, 88, 77),
|
|
2408
|
+
makePublicLogTag(1, 1, 0)
|
|
976
2409
|
];
|
|
977
|
-
const logsByTags = await store.
|
|
2410
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
978
2411
|
expect(logsByTags).toEqual([
|
|
979
2412
|
[],
|
|
980
2413
|
[
|
|
981
2414
|
expect.objectContaining({
|
|
982
2415
|
blockNumber: 1,
|
|
983
|
-
|
|
984
|
-
isFromPublic: false
|
|
2416
|
+
logData: makePublicLog(tags[1]).getEmittedFields()
|
|
985
2417
|
})
|
|
986
2418
|
]
|
|
987
2419
|
]);
|
|
988
2420
|
});
|
|
989
2421
|
});
|
|
990
2422
|
describe('getPublicLogs', ()=>{
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
2423
|
+
const numBlocksForPublicLogs = 10;
|
|
2424
|
+
// Helper to get total public logs per tx from a block
|
|
2425
|
+
const getPublicLogsPerTx = (block, txIndex)=>block.body.txEffects[txIndex].publicLogs.length;
|
|
2426
|
+
// Helper to get number of txs in a block
|
|
2427
|
+
const getTxsPerBlock = (block)=>block.body.txEffects.length;
|
|
996
2428
|
beforeEach(async ()=>{
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
attestations: times(3, CommitteeAttestation.random)
|
|
1005
|
-
}));
|
|
1006
|
-
await store.addBlocks(blocks);
|
|
1007
|
-
await store.addLogs(blocks.map((b)=>b.block));
|
|
2429
|
+
// Use the outer publishedCheckpoints for log tests
|
|
2430
|
+
for(let i = 0; i < numBlocksForPublicLogs; i++){
|
|
2431
|
+
await store.addCheckpoints([
|
|
2432
|
+
publishedCheckpoints[i]
|
|
2433
|
+
]);
|
|
2434
|
+
await store.addLogs(publishedCheckpoints[i].checkpoint.blocks);
|
|
2435
|
+
}
|
|
1008
2436
|
});
|
|
1009
2437
|
it('no logs returned if deleted ("txHash" filter param is respected variant)', async ()=>{
|
|
1010
2438
|
// get random tx
|
|
1011
|
-
const targetBlockIndex = randomInt(
|
|
1012
|
-
const
|
|
1013
|
-
const
|
|
2439
|
+
const targetBlockIndex = randomInt(numBlocksForPublicLogs);
|
|
2440
|
+
const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0];
|
|
2441
|
+
const targetTxIndex = randomInt(getTxsPerBlock(targetBlock));
|
|
2442
|
+
const targetTxHash = targetBlock.body.txEffects[targetTxIndex].txHash;
|
|
1014
2443
|
await Promise.all([
|
|
1015
|
-
store.
|
|
1016
|
-
store.deleteLogs(
|
|
2444
|
+
store.unwindCheckpoints(CheckpointNumber(numBlocksForPublicLogs), numBlocksForPublicLogs),
|
|
2445
|
+
store.deleteLogs(publishedCheckpoints.slice(0, numBlocksForPublicLogs).flatMap((b)=>b.checkpoint.blocks))
|
|
1017
2446
|
]);
|
|
1018
2447
|
const response = await store.getPublicLogs({
|
|
1019
2448
|
txHash: targetTxHash
|
|
@@ -1024,15 +2453,16 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1024
2453
|
});
|
|
1025
2454
|
it('"txHash" filter param is respected', async ()=>{
|
|
1026
2455
|
// get random tx
|
|
1027
|
-
const targetBlockIndex = randomInt(
|
|
1028
|
-
const
|
|
1029
|
-
const
|
|
2456
|
+
const targetBlockIndex = randomInt(numBlocksForPublicLogs);
|
|
2457
|
+
const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0];
|
|
2458
|
+
const targetTxIndex = randomInt(getTxsPerBlock(targetBlock));
|
|
2459
|
+
const targetTxHash = targetBlock.body.txEffects[targetTxIndex].txHash;
|
|
1030
2460
|
const response = await store.getPublicLogs({
|
|
1031
2461
|
txHash: targetTxHash
|
|
1032
2462
|
});
|
|
1033
2463
|
const logs = response.logs;
|
|
1034
2464
|
expect(response.maxLogsHit).toBeFalsy();
|
|
1035
|
-
const expectedNumLogs =
|
|
2465
|
+
const expectedNumLogs = getPublicLogsPerTx(targetBlock, targetTxIndex);
|
|
1036
2466
|
expect(logs.length).toEqual(expectedNumLogs);
|
|
1037
2467
|
const targeBlockNumber = targetBlockIndex + INITIAL_L2_BLOCK_NUM;
|
|
1038
2468
|
for (const log of logs){
|
|
@@ -1040,6 +2470,16 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1040
2470
|
expect(log.id.txIndex).toEqual(targetTxIndex);
|
|
1041
2471
|
}
|
|
1042
2472
|
});
|
|
2473
|
+
it('returns block hash on public log ids', async ()=>{
|
|
2474
|
+
const targetBlock = publishedCheckpoints[0].checkpoint.blocks[0];
|
|
2475
|
+
const expectedBlockHash = L2BlockHash.fromField(await targetBlock.header.hash());
|
|
2476
|
+
const logs = (await store.getPublicLogs({
|
|
2477
|
+
fromBlock: targetBlock.number,
|
|
2478
|
+
toBlock: targetBlock.number + 1
|
|
2479
|
+
})).logs;
|
|
2480
|
+
expect(logs.length).toBeGreaterThan(0);
|
|
2481
|
+
expect(logs.every((log)=>log.id.blockHash.equals(expectedBlockHash))).toBe(true);
|
|
2482
|
+
});
|
|
1043
2483
|
it('"fromBlock" and "toBlock" filter params are respected', async ()=>{
|
|
1044
2484
|
// Set "fromBlock" and "toBlock"
|
|
1045
2485
|
const fromBlock = 3;
|
|
@@ -1050,7 +2490,12 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1050
2490
|
});
|
|
1051
2491
|
const logs = response.logs;
|
|
1052
2492
|
expect(response.maxLogsHit).toBeFalsy();
|
|
1053
|
-
|
|
2493
|
+
// Compute expected logs from the blocks in range
|
|
2494
|
+
let expectedNumLogs = 0;
|
|
2495
|
+
for(let i = fromBlock - 1; i < toBlock - 1; i++){
|
|
2496
|
+
const block = publishedCheckpoints[i].checkpoint.blocks[0];
|
|
2497
|
+
expectedNumLogs += block.body.txEffects.reduce((sum, tx)=>sum + tx.publicLogs.length, 0);
|
|
2498
|
+
}
|
|
1054
2499
|
expect(logs.length).toEqual(expectedNumLogs);
|
|
1055
2500
|
for (const log of logs){
|
|
1056
2501
|
const blockNumber = log.id.blockNumber;
|
|
@@ -1060,10 +2505,11 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1060
2505
|
});
|
|
1061
2506
|
it('"contractAddress" filter param is respected', async ()=>{
|
|
1062
2507
|
// Get a random contract address from the logs
|
|
1063
|
-
const targetBlockIndex = randomInt(
|
|
1064
|
-
const
|
|
1065
|
-
const
|
|
1066
|
-
const
|
|
2508
|
+
const targetBlockIndex = randomInt(numBlocksForPublicLogs);
|
|
2509
|
+
const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0];
|
|
2510
|
+
const targetTxIndex = randomInt(getTxsPerBlock(targetBlock));
|
|
2511
|
+
const targetLogIndex = randomInt(getPublicLogsPerTx(targetBlock, targetTxIndex));
|
|
2512
|
+
const targetContractAddress = targetBlock.body.txEffects[targetTxIndex].publicLogs[targetLogIndex].contractAddress;
|
|
1067
2513
|
const response = await store.getPublicLogs({
|
|
1068
2514
|
contractAddress: targetContractAddress
|
|
1069
2515
|
});
|
|
@@ -1074,10 +2520,13 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1074
2520
|
});
|
|
1075
2521
|
it('"afterLog" filter param is respected', async ()=>{
|
|
1076
2522
|
// Get a random log as reference
|
|
1077
|
-
const targetBlockIndex = randomInt(
|
|
1078
|
-
const
|
|
1079
|
-
const
|
|
1080
|
-
const
|
|
2523
|
+
const targetBlockIndex = randomInt(numBlocksForPublicLogs);
|
|
2524
|
+
const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0];
|
|
2525
|
+
const targetTxIndex = randomInt(getTxsPerBlock(targetBlock));
|
|
2526
|
+
const numLogsInTx = targetBlock.body.txEffects[targetTxIndex].publicLogs.length;
|
|
2527
|
+
const targetLogIndex = numLogsInTx > 0 ? randomInt(numLogsInTx) : 0;
|
|
2528
|
+
const targetBlockHash = L2BlockHash.fromField(await targetBlock.header.hash());
|
|
2529
|
+
const afterLog = new LogId(BlockNumber(targetBlockIndex + INITIAL_L2_BLOCK_NUM), targetBlockHash, targetTxIndex, targetLogIndex);
|
|
1081
2530
|
const response = await store.getPublicLogs({
|
|
1082
2531
|
afterLog
|
|
1083
2532
|
});
|
|
@@ -1097,7 +2546,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1097
2546
|
it('"txHash" filter param is ignored when "afterLog" is set', async ()=>{
|
|
1098
2547
|
// Get random txHash
|
|
1099
2548
|
const txHash = TxHash.random();
|
|
1100
|
-
const afterLog = new LogId(1, 0, 0);
|
|
2549
|
+
const afterLog = new LogId(BlockNumber(1), L2BlockHash.random(), 0, 0);
|
|
1101
2550
|
const response = await store.getPublicLogs({
|
|
1102
2551
|
txHash,
|
|
1103
2552
|
afterLog
|
|
@@ -1113,7 +2562,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1113
2562
|
// "fromBlock" gets correctly trimmed to range and "toBlock" is exclusive
|
|
1114
2563
|
logs = (await store.getPublicLogs({
|
|
1115
2564
|
fromBlock: -10,
|
|
1116
|
-
toBlock: 5
|
|
2565
|
+
toBlock: BlockNumber(5)
|
|
1117
2566
|
})).logs;
|
|
1118
2567
|
let blockNumbers = new Set(logs.map((log)=>log.id.blockNumber));
|
|
1119
2568
|
expect(blockNumbers).toEqual(new Set([
|
|
@@ -1124,13 +2573,13 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1124
2573
|
]));
|
|
1125
2574
|
// "toBlock" should be exclusive
|
|
1126
2575
|
logs = (await store.getPublicLogs({
|
|
1127
|
-
fromBlock: 1,
|
|
1128
|
-
toBlock: 1
|
|
2576
|
+
fromBlock: BlockNumber(1),
|
|
2577
|
+
toBlock: BlockNumber(1)
|
|
1129
2578
|
})).logs;
|
|
1130
2579
|
expect(logs.length).toBe(0);
|
|
1131
2580
|
logs = (await store.getPublicLogs({
|
|
1132
|
-
fromBlock: 10,
|
|
1133
|
-
toBlock: 5
|
|
2581
|
+
fromBlock: BlockNumber(10),
|
|
2582
|
+
toBlock: BlockNumber(5)
|
|
1134
2583
|
})).logs;
|
|
1135
2584
|
expect(logs.length).toBe(0);
|
|
1136
2585
|
// both "fromBlock" and "toBlock" get correctly capped to range and logs from all blocks are returned
|
|
@@ -1139,35 +2588,38 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1139
2588
|
toBlock: +100
|
|
1140
2589
|
})).logs;
|
|
1141
2590
|
blockNumbers = new Set(logs.map((log)=>log.id.blockNumber));
|
|
1142
|
-
expect(blockNumbers.size).toBe(
|
|
2591
|
+
expect(blockNumbers.size).toBe(numBlocksForPublicLogs);
|
|
1143
2592
|
// intersecting with "afterLog" works
|
|
1144
2593
|
logs = (await store.getPublicLogs({
|
|
1145
|
-
fromBlock: 2,
|
|
1146
|
-
toBlock: 5,
|
|
1147
|
-
afterLog: new LogId(4, 0, 0)
|
|
2594
|
+
fromBlock: BlockNumber(2),
|
|
2595
|
+
toBlock: BlockNumber(5),
|
|
2596
|
+
afterLog: new LogId(BlockNumber(4), L2BlockHash.random(), 0, 0)
|
|
1148
2597
|
})).logs;
|
|
1149
2598
|
blockNumbers = new Set(logs.map((log)=>log.id.blockNumber));
|
|
1150
2599
|
expect(blockNumbers).toEqual(new Set([
|
|
1151
2600
|
4
|
|
1152
2601
|
]));
|
|
1153
2602
|
logs = (await store.getPublicLogs({
|
|
1154
|
-
toBlock: 5,
|
|
1155
|
-
afterLog: new LogId(5, 1, 0)
|
|
2603
|
+
toBlock: BlockNumber(5),
|
|
2604
|
+
afterLog: new LogId(BlockNumber(5), L2BlockHash.random(), 1, 0)
|
|
1156
2605
|
})).logs;
|
|
1157
2606
|
expect(logs.length).toBe(0);
|
|
1158
2607
|
logs = (await store.getPublicLogs({
|
|
1159
|
-
fromBlock: 2,
|
|
1160
|
-
toBlock: 5,
|
|
1161
|
-
afterLog: new LogId(100, 0, 0)
|
|
2608
|
+
fromBlock: BlockNumber(2),
|
|
2609
|
+
toBlock: BlockNumber(5),
|
|
2610
|
+
afterLog: new LogId(BlockNumber(100), L2BlockHash.random(), 0, 0)
|
|
1162
2611
|
})).logs;
|
|
1163
2612
|
expect(logs.length).toBe(0);
|
|
1164
2613
|
});
|
|
1165
2614
|
it('"txIndex" and "logIndex" are respected when "afterLog.blockNumber" is equal to "fromBlock"', async ()=>{
|
|
1166
2615
|
// Get a random log as reference
|
|
1167
|
-
const targetBlockIndex = randomInt(
|
|
1168
|
-
const
|
|
1169
|
-
const
|
|
1170
|
-
const
|
|
2616
|
+
const targetBlockIndex = randomInt(numBlocksForPublicLogs);
|
|
2617
|
+
const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0];
|
|
2618
|
+
const targetTxIndex = randomInt(getTxsPerBlock(targetBlock));
|
|
2619
|
+
const numLogsInTx = targetBlock.body.txEffects[targetTxIndex].publicLogs.length;
|
|
2620
|
+
const targetLogIndex = numLogsInTx > 0 ? randomInt(numLogsInTx) : 0;
|
|
2621
|
+
const targetBlockHash = L2BlockHash.fromField(await targetBlock.header.hash());
|
|
2622
|
+
const afterLog = new LogId(BlockNumber(targetBlockIndex + INITIAL_L2_BLOCK_NUM), targetBlockHash, targetTxIndex, targetLogIndex);
|
|
1171
2623
|
const response = await store.getPublicLogs({
|
|
1172
2624
|
afterLog,
|
|
1173
2625
|
fromBlock: afterLog.blockNumber
|
|
@@ -1186,6 +2638,36 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1186
2638
|
}
|
|
1187
2639
|
});
|
|
1188
2640
|
});
|
|
2641
|
+
describe('getContractClassLogs', ()=>{
|
|
2642
|
+
let targetBlock;
|
|
2643
|
+
let expectedContractClassLog;
|
|
2644
|
+
beforeEach(async ()=>{
|
|
2645
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
2646
|
+
targetBlock = publishedCheckpoints[0].checkpoint.blocks[0];
|
|
2647
|
+
expectedContractClassLog = await ContractClassLog.random();
|
|
2648
|
+
targetBlock.body.txEffects.forEach((txEffect, index)=>{
|
|
2649
|
+
txEffect.contractClassLogs = index === 0 ? [
|
|
2650
|
+
expectedContractClassLog
|
|
2651
|
+
] : [];
|
|
2652
|
+
});
|
|
2653
|
+
await store.addLogs([
|
|
2654
|
+
targetBlock
|
|
2655
|
+
]);
|
|
2656
|
+
});
|
|
2657
|
+
it('returns block hash on contract class log ids', async ()=>{
|
|
2658
|
+
const result = await store.getContractClassLogs({
|
|
2659
|
+
fromBlock: targetBlock.number,
|
|
2660
|
+
toBlock: targetBlock.number + 1
|
|
2661
|
+
});
|
|
2662
|
+
expect(result.maxLogsHit).toBeFalsy();
|
|
2663
|
+
expect(result.logs).toHaveLength(1);
|
|
2664
|
+
const [{ id, log }] = result.logs;
|
|
2665
|
+
const expectedBlockHash = L2BlockHash.fromField(await targetBlock.header.hash());
|
|
2666
|
+
expect(id.blockHash.equals(expectedBlockHash)).toBe(true);
|
|
2667
|
+
expect(id.blockNumber).toEqual(targetBlock.number);
|
|
2668
|
+
expect(log).toEqual(expectedContractClassLog);
|
|
2669
|
+
});
|
|
2670
|
+
});
|
|
1189
2671
|
describe('pendingChainValidationStatus', ()=>{
|
|
1190
2672
|
it('should return undefined when no status is set', async ()=>{
|
|
1191
2673
|
const status = await store.getPendingChainValidationStatus();
|
|
@@ -1207,7 +2689,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1207
2689
|
EthAddress.random(),
|
|
1208
2690
|
EthAddress.random()
|
|
1209
2691
|
],
|
|
1210
|
-
epoch:
|
|
2692
|
+
epoch: EpochNumber(123),
|
|
1211
2693
|
seed: 456n,
|
|
1212
2694
|
attestors: [
|
|
1213
2695
|
EthAddress.random()
|
|
@@ -1231,7 +2713,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1231
2713
|
attestors: [
|
|
1232
2714
|
EthAddress.random()
|
|
1233
2715
|
],
|
|
1234
|
-
epoch:
|
|
2716
|
+
epoch: EpochNumber(789),
|
|
1235
2717
|
seed: 101n,
|
|
1236
2718
|
attestations: [
|
|
1237
2719
|
CommitteeAttestation.random()
|
|
@@ -1253,7 +2735,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1253
2735
|
committee: [
|
|
1254
2736
|
EthAddress.random()
|
|
1255
2737
|
],
|
|
1256
|
-
epoch:
|
|
2738
|
+
epoch: EpochNumber(999),
|
|
1257
2739
|
seed: 888n,
|
|
1258
2740
|
attestors: [
|
|
1259
2741
|
EthAddress.random()
|
|
@@ -1273,7 +2755,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1273
2755
|
valid: false,
|
|
1274
2756
|
block: randomBlockInfo(4),
|
|
1275
2757
|
committee: [],
|
|
1276
|
-
epoch:
|
|
2758
|
+
epoch: EpochNumber(0),
|
|
1277
2759
|
seed: 0n,
|
|
1278
2760
|
attestors: [],
|
|
1279
2761
|
attestations: [],
|