@aztec/archiver 4.0.0-nightly.20250907 → 4.0.0-nightly.20260107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -6
- package/dest/archiver/archiver.d.ts +127 -84
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +1128 -380
- package/dest/archiver/archiver_store.d.ts +122 -45
- 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 +2013 -343
- package/dest/archiver/config.d.ts +7 -20
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +21 -5
- 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 +14 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +83 -15
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +396 -73
- 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 +51 -55
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +82 -46
- 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 +66 -44
- package/dest/factory.d.ts +4 -6
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +5 -4
- 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 +31 -20
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +85 -18
- 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 +971 -475
- package/src/archiver/archiver_store.ts +141 -44
- package/src/archiver/archiver_store_test_suite.ts +2114 -331
- package/src/archiver/config.ts +30 -35
- package/src/archiver/errors.ts +64 -0
- package/src/archiver/index.ts +1 -1
- package/src/archiver/instrumentation.ts +19 -2
- package/src/archiver/kv_archiver_store/block_store.ts +541 -83
- 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 +107 -67
- 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 +86 -32
- package/src/factory.ts +6 -7
- 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 +114 -27
- package/src/test/mock_structs.ts +10 -9
- package/dest/archiver/data_retrieval.d.ts +0 -78
- package/dest/archiver/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/data_retrieval.js +0 -354
- package/src/archiver/data_retrieval.ts +0 -535
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import { INITIAL_L2_BLOCK_NUM, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PRIVATE_LOG_SIZE_IN_FIELDS
|
|
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,
|
|
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,52 +286,1337 @@ 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);
|
|
292
|
+
});
|
|
293
|
+
it('unwound blocks and headers cannot be retrieved by hash or archive', async ()=>{
|
|
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;
|
|
299
|
+
// Verify block and header exist before unwinding
|
|
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);
|
|
314
|
+
// Verify neither block nor header can be retrieved after unwinding
|
|
315
|
+
expect(await store.getCheckpointedBlockByHash(blockHash)).toBeUndefined();
|
|
316
|
+
expect(await store.getCheckpointedBlockByArchive(archive)).toBeUndefined();
|
|
317
|
+
expect(await store.getBlockHeaderByHash(blockHash)).toBeUndefined();
|
|
318
|
+
expect(await store.getBlockHeaderByArchive(archive)).toBeUndefined();
|
|
132
319
|
});
|
|
133
320
|
});
|
|
134
|
-
describe('
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|
|
137
453
|
});
|
|
138
|
-
it
|
|
139
|
-
|
|
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
|
+
}
|
|
140
494
|
});
|
|
141
|
-
it('returns
|
|
142
|
-
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
|
+
}
|
|
143
513
|
});
|
|
144
|
-
it('
|
|
145
|
-
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
|
+
}
|
|
146
532
|
});
|
|
147
|
-
it('
|
|
148
|
-
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);
|
|
149
553
|
});
|
|
150
|
-
|
|
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
|
+
});
|
|
151
585
|
await store.addBlocks([
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
655
|
+
});
|
|
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
|
+
]);
|
|
899
|
+
});
|
|
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
|
|
155
991
|
});
|
|
156
|
-
await expect(store.
|
|
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);
|
|
157
1017
|
});
|
|
158
|
-
it('throws
|
|
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
|
+
});
|
|
159
1109
|
await store.addBlocks([
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
162
1146
|
], {
|
|
163
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
|
|
164
1170
|
});
|
|
165
|
-
await expect(store.
|
|
1171
|
+
await expect(store.addBlocks([
|
|
1172
|
+
block3,
|
|
1173
|
+
block4
|
|
1174
|
+
], {
|
|
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
|
|
1191
|
+
});
|
|
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);
|
|
166
1226
|
});
|
|
167
1227
|
});
|
|
168
|
-
describe('
|
|
169
|
-
it('returns
|
|
170
|
-
await
|
|
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
|
+
]);
|
|
171
1259
|
});
|
|
172
|
-
it(
|
|
173
|
-
|
|
174
|
-
|
|
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();
|
|
1323
|
+
});
|
|
1324
|
+
});
|
|
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', ()=>{
|
|
1530
|
+
beforeEach(async ()=>{
|
|
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);
|
|
1550
|
+
});
|
|
1551
|
+
it('retrieves a block by its hash', async ()=>{
|
|
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);
|
|
1556
|
+
expect(retrievedBlock).toBeDefined();
|
|
1557
|
+
expectCheckpointedBlockEquals(retrievedBlock, expectedBlock, expectedCheckpoint);
|
|
1558
|
+
});
|
|
1559
|
+
it('returns undefined for non-existent block hash', async ()=>{
|
|
1560
|
+
const nonExistentHash = Fr.random();
|
|
1561
|
+
await expect(store.getCheckpointedBlockByHash(nonExistentHash)).resolves.toBeUndefined();
|
|
1562
|
+
});
|
|
1563
|
+
});
|
|
1564
|
+
describe('getCheckpointedBlockByArchive', ()=>{
|
|
1565
|
+
beforeEach(async ()=>{
|
|
1566
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
1567
|
+
});
|
|
1568
|
+
it('retrieves a block by its archive root', async ()=>{
|
|
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);
|
|
1573
|
+
expect(retrievedBlock).toBeDefined();
|
|
1574
|
+
expectCheckpointedBlockEquals(retrievedBlock, expectedBlock, expectedCheckpoint);
|
|
1575
|
+
});
|
|
1576
|
+
it('returns undefined for non-existent archive root', async ()=>{
|
|
1577
|
+
const nonExistentArchive = Fr.random();
|
|
1578
|
+
await expect(store.getCheckpointedBlockByArchive(nonExistentArchive)).resolves.toBeUndefined();
|
|
1579
|
+
});
|
|
1580
|
+
});
|
|
1581
|
+
describe('getBlockHeaderByHash', ()=>{
|
|
1582
|
+
beforeEach(async ()=>{
|
|
1583
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
1584
|
+
});
|
|
1585
|
+
it('retrieves a block header by its hash', async ()=>{
|
|
1586
|
+
const expectedBlock = publishedCheckpoints[7].checkpoint.blocks[0];
|
|
1587
|
+
const blockHash = await expectedBlock.header.hash();
|
|
1588
|
+
const retrievedHeader = await store.getBlockHeaderByHash(blockHash);
|
|
1589
|
+
expect(retrievedHeader).toBeDefined();
|
|
1590
|
+
expect(retrievedHeader.equals(expectedBlock.header)).toBe(true);
|
|
1591
|
+
});
|
|
1592
|
+
it('returns undefined for non-existent block hash', async ()=>{
|
|
1593
|
+
const nonExistentHash = Fr.random();
|
|
1594
|
+
await expect(store.getBlockHeaderByHash(nonExistentHash)).resolves.toBeUndefined();
|
|
1595
|
+
});
|
|
1596
|
+
});
|
|
1597
|
+
describe('getBlockHeaderByArchive', ()=>{
|
|
1598
|
+
beforeEach(async ()=>{
|
|
1599
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
1600
|
+
});
|
|
1601
|
+
it('retrieves a block header by its archive root', async ()=>{
|
|
1602
|
+
const expectedBlock = publishedCheckpoints[2].checkpoint.blocks[0];
|
|
1603
|
+
const archive = expectedBlock.archive.root;
|
|
1604
|
+
const retrievedHeader = await store.getBlockHeaderByArchive(archive);
|
|
1605
|
+
expect(retrievedHeader).toBeDefined();
|
|
1606
|
+
expect(retrievedHeader.equals(expectedBlock.header)).toBe(true);
|
|
1607
|
+
});
|
|
1608
|
+
it('returns undefined for non-existent archive root', async ()=>{
|
|
1609
|
+
const nonExistentArchive = Fr.random();
|
|
1610
|
+
await expect(store.getBlockHeaderByArchive(nonExistentArchive)).resolves.toBeUndefined();
|
|
1611
|
+
});
|
|
1612
|
+
});
|
|
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);
|
|
1616
|
+
});
|
|
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);
|
|
175
1620
|
});
|
|
176
1621
|
});
|
|
177
1622
|
describe('getSynchPoint', ()=>{
|
|
@@ -182,7 +1627,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
182
1627
|
});
|
|
183
1628
|
});
|
|
184
1629
|
it('returns the L1 block number in which the most recent L2 block was published', async ()=>{
|
|
185
|
-
await store.
|
|
1630
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
186
1631
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
187
1632
|
blocksSynchedTo: 19n,
|
|
188
1633
|
messagesSynchedTo: undefined
|
|
@@ -234,77 +1679,62 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
234
1679
|
});
|
|
235
1680
|
describe('addLogs', ()=>{
|
|
236
1681
|
it('adds private & public logs', async ()=>{
|
|
237
|
-
const
|
|
238
|
-
await
|
|
239
|
-
|
|
240
|
-
])).resolves.toEqual(true);
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
describe('deleteLogs', ()=>{
|
|
244
|
-
it('deletes private & public logs', async ()=>{
|
|
245
|
-
const block = blocks[0].block;
|
|
246
|
-
await store.addBlocks([
|
|
247
|
-
blocks[0]
|
|
1682
|
+
const checkpoint = publishedCheckpoints[0];
|
|
1683
|
+
await store.addCheckpoints([
|
|
1684
|
+
checkpoint
|
|
248
1685
|
]);
|
|
249
|
-
await expect(store.addLogs(
|
|
250
|
-
block
|
|
251
|
-
])).resolves.toEqual(true);
|
|
252
|
-
expect((await store.getPrivateLogs(1, 1)).length).toEqual(block.body.txEffects.map((txEffect)=>txEffect.privateLogs).flat().length);
|
|
253
|
-
expect((await store.getPublicLogs({
|
|
254
|
-
fromBlock: 1
|
|
255
|
-
})).logs.length).toEqual(block.body.txEffects.map((txEffect)=>txEffect.publicLogs).flat().length);
|
|
256
|
-
// This one is a pain for memory as we would never want to just delete memory in the middle.
|
|
257
|
-
await store.deleteLogs([
|
|
258
|
-
block
|
|
259
|
-
]);
|
|
260
|
-
expect((await store.getPrivateLogs(1, 1)).length).toEqual(0);
|
|
261
|
-
expect((await store.getPublicLogs({
|
|
262
|
-
fromBlock: 1
|
|
263
|
-
})).logs.length).toEqual(0);
|
|
1686
|
+
await expect(store.addLogs(checkpoint.checkpoint.blocks)).resolves.toEqual(true);
|
|
264
1687
|
});
|
|
265
1688
|
});
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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);
|
|
278
1707
|
});
|
|
279
1708
|
describe('getTxEffect', ()=>{
|
|
1709
|
+
const getBlock = (i)=>publishedCheckpoints[i].checkpoint.blocks[0];
|
|
280
1710
|
beforeEach(async ()=>{
|
|
281
|
-
await store.addLogs(
|
|
282
|
-
await store.
|
|
1711
|
+
await store.addLogs(publishedCheckpoints.flatMap((x)=>x.checkpoint.blocks));
|
|
1712
|
+
await store.addCheckpoints(publishedCheckpoints);
|
|
283
1713
|
});
|
|
284
1714
|
it.each([
|
|
285
1715
|
()=>({
|
|
286
|
-
data:
|
|
287
|
-
block:
|
|
1716
|
+
data: getBlock(0).body.txEffects[0],
|
|
1717
|
+
block: getBlock(0),
|
|
288
1718
|
txIndexInBlock: 0
|
|
289
1719
|
}),
|
|
290
1720
|
()=>({
|
|
291
|
-
data:
|
|
292
|
-
block:
|
|
1721
|
+
data: getBlock(9).body.txEffects[3],
|
|
1722
|
+
block: getBlock(9),
|
|
293
1723
|
txIndexInBlock: 3
|
|
294
1724
|
}),
|
|
295
1725
|
()=>({
|
|
296
|
-
data:
|
|
297
|
-
block:
|
|
1726
|
+
data: getBlock(3).body.txEffects[1],
|
|
1727
|
+
block: getBlock(3),
|
|
298
1728
|
txIndexInBlock: 1
|
|
299
1729
|
}),
|
|
300
1730
|
()=>({
|
|
301
|
-
data:
|
|
302
|
-
block:
|
|
1731
|
+
data: getBlock(5).body.txEffects[2],
|
|
1732
|
+
block: getBlock(5),
|
|
303
1733
|
txIndexInBlock: 2
|
|
304
1734
|
}),
|
|
305
1735
|
()=>({
|
|
306
|
-
data:
|
|
307
|
-
block:
|
|
1736
|
+
data: getBlock(1).body.txEffects[0],
|
|
1737
|
+
block: getBlock(1),
|
|
308
1738
|
txIndexInBlock: 0
|
|
309
1739
|
})
|
|
310
1740
|
])('retrieves a previously stored transaction', async (getExpectedTx)=>{
|
|
@@ -312,7 +1742,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
312
1742
|
const expectedTx = {
|
|
313
1743
|
data,
|
|
314
1744
|
l2BlockNumber: block.number,
|
|
315
|
-
l2BlockHash: L2BlockHash.fromField(await block.hash()),
|
|
1745
|
+
l2BlockHash: L2BlockHash.fromField(await block.header.hash()),
|
|
316
1746
|
txIndexInBlock
|
|
317
1747
|
};
|
|
318
1748
|
const actualTx = await store.getTxEffect(data.txHash);
|
|
@@ -322,36 +1752,36 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
322
1752
|
await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
|
|
323
1753
|
});
|
|
324
1754
|
it.each([
|
|
325
|
-
()=>
|
|
326
|
-
()=>
|
|
327
|
-
()=>
|
|
328
|
-
()=>
|
|
329
|
-
()=>
|
|
330
|
-
])('tries to retrieves a previously stored transaction after deleted', async (
|
|
331
|
-
await store.
|
|
332
|
-
const
|
|
333
|
-
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);
|
|
334
1764
|
expect(actualTx).toEqual(undefined);
|
|
335
1765
|
});
|
|
336
1766
|
it('returns undefined if tx is not found', async ()=>{
|
|
337
1767
|
await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
|
|
338
1768
|
});
|
|
339
1769
|
it('does not fail if the block is unwound while requesting a tx', async ()=>{
|
|
340
|
-
const
|
|
1770
|
+
const txEffect = getBlock(1).body.txEffects[0];
|
|
341
1771
|
let done = false;
|
|
342
1772
|
void (async ()=>{
|
|
343
1773
|
while(!done){
|
|
344
|
-
void store.getTxEffect(
|
|
1774
|
+
void store.getTxEffect(txEffect.txHash);
|
|
345
1775
|
await sleep(1);
|
|
346
1776
|
}
|
|
347
1777
|
})();
|
|
348
|
-
await store.
|
|
1778
|
+
await store.unwindCheckpoints(CheckpointNumber(publishedCheckpoints.length), publishedCheckpoints.length);
|
|
349
1779
|
done = true;
|
|
350
|
-
expect(await store.getTxEffect(
|
|
1780
|
+
expect(await store.getTxEffect(txEffect.txHash)).toEqual(undefined);
|
|
351
1781
|
});
|
|
352
1782
|
});
|
|
353
1783
|
describe('L1 to L2 Messages', ()=>{
|
|
354
|
-
const
|
|
1784
|
+
const initialCheckpointNumber = CheckpointNumber(13);
|
|
355
1785
|
const checkMessages = async (msgs)=>{
|
|
356
1786
|
expect(await store.getLastL1ToL2Message()).toEqual(msgs.at(-1));
|
|
357
1787
|
expect(await toArray(store.iterateL1ToL2Messages())).toEqual(msgs);
|
|
@@ -359,11 +1789,11 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
359
1789
|
};
|
|
360
1790
|
const makeInboxMessagesWithFullBlocks = (blockCount, opts = {})=>makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, {
|
|
361
1791
|
overrideFn: (msg, i)=>{
|
|
362
|
-
const
|
|
363
|
-
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);
|
|
364
1794
|
return {
|
|
365
1795
|
...msg,
|
|
366
|
-
|
|
1796
|
+
checkpointNumber,
|
|
367
1797
|
index
|
|
368
1798
|
};
|
|
369
1799
|
}
|
|
@@ -371,7 +1801,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
371
1801
|
it('stores first message ever', async ()=>{
|
|
372
1802
|
const msg = makeInboxMessage(Buffer16.ZERO, {
|
|
373
1803
|
index: 0n,
|
|
374
|
-
|
|
1804
|
+
checkpointNumber: CheckpointNumber(1)
|
|
375
1805
|
});
|
|
376
1806
|
await store.addL1ToL2Messages([
|
|
377
1807
|
msg
|
|
@@ -379,13 +1809,13 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
379
1809
|
await checkMessages([
|
|
380
1810
|
msg
|
|
381
1811
|
]);
|
|
382
|
-
expect(await store.getL1ToL2Messages(1)).toEqual([
|
|
1812
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toEqual([
|
|
383
1813
|
msg.leaf
|
|
384
1814
|
]);
|
|
385
1815
|
});
|
|
386
1816
|
it('stores single message', async ()=>{
|
|
387
1817
|
const msg = makeInboxMessage(Buffer16.ZERO, {
|
|
388
|
-
|
|
1818
|
+
checkpointNumber: CheckpointNumber(2)
|
|
389
1819
|
});
|
|
390
1820
|
await store.addL1ToL2Messages([
|
|
391
1821
|
msg
|
|
@@ -393,23 +1823,23 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
393
1823
|
await checkMessages([
|
|
394
1824
|
msg
|
|
395
1825
|
]);
|
|
396
|
-
expect(await store.getL1ToL2Messages(2)).toEqual([
|
|
1826
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(2))).toEqual([
|
|
397
1827
|
msg.leaf
|
|
398
1828
|
]);
|
|
399
1829
|
});
|
|
400
1830
|
it('stores and returns messages across different blocks', async ()=>{
|
|
401
1831
|
const msgs = makeInboxMessages(5, {
|
|
402
|
-
|
|
1832
|
+
initialCheckpointNumber
|
|
403
1833
|
});
|
|
404
1834
|
await store.addL1ToL2Messages(msgs);
|
|
405
1835
|
await checkMessages(msgs);
|
|
406
|
-
expect(await store.getL1ToL2Messages(
|
|
1836
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual([
|
|
407
1837
|
msgs[2]
|
|
408
1838
|
].map((m)=>m.leaf));
|
|
409
1839
|
});
|
|
410
1840
|
it('stores the same messages again', async ()=>{
|
|
411
1841
|
const msgs = makeInboxMessages(5, {
|
|
412
|
-
|
|
1842
|
+
initialCheckpointNumber
|
|
413
1843
|
});
|
|
414
1844
|
await store.addL1ToL2Messages(msgs);
|
|
415
1845
|
await store.addL1ToL2Messages(msgs.slice(2));
|
|
@@ -417,10 +1847,10 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
417
1847
|
});
|
|
418
1848
|
it('stores and returns messages across different blocks with gaps', async ()=>{
|
|
419
1849
|
const msgs1 = makeInboxMessages(3, {
|
|
420
|
-
|
|
1850
|
+
initialCheckpointNumber: CheckpointNumber(1)
|
|
421
1851
|
});
|
|
422
1852
|
const msgs2 = makeInboxMessages(3, {
|
|
423
|
-
|
|
1853
|
+
initialCheckpointNumber: CheckpointNumber(20),
|
|
424
1854
|
initialHash: msgs1.at(-1).rollingHash
|
|
425
1855
|
});
|
|
426
1856
|
await store.addL1ToL2Messages(msgs1);
|
|
@@ -429,22 +1859,22 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
429
1859
|
...msgs1,
|
|
430
1860
|
...msgs2
|
|
431
1861
|
]);
|
|
432
|
-
expect(await store.getL1ToL2Messages(1)).toEqual([
|
|
1862
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toEqual([
|
|
433
1863
|
msgs1[0].leaf
|
|
434
1864
|
]);
|
|
435
|
-
expect(await store.getL1ToL2Messages(4)).toEqual([]);
|
|
436
|
-
expect(await store.getL1ToL2Messages(20)).toEqual([
|
|
1865
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(4))).toEqual([]);
|
|
1866
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(20))).toEqual([
|
|
437
1867
|
msgs2[0].leaf
|
|
438
1868
|
]);
|
|
439
|
-
expect(await store.getL1ToL2Messages(24)).toEqual([]);
|
|
1869
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(24))).toEqual([]);
|
|
440
1870
|
});
|
|
441
1871
|
it('stores and returns messages with block numbers larger than a byte', async ()=>{
|
|
442
1872
|
const msgs = makeInboxMessages(5, {
|
|
443
|
-
|
|
1873
|
+
initialCheckpointNumber: CheckpointNumber(1000)
|
|
444
1874
|
});
|
|
445
1875
|
await store.addL1ToL2Messages(msgs);
|
|
446
1876
|
await checkMessages(msgs);
|
|
447
|
-
expect(await store.getL1ToL2Messages(1002)).toEqual([
|
|
1877
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(1002))).toEqual([
|
|
448
1878
|
msgs[2]
|
|
449
1879
|
].map((m)=>m.leaf));
|
|
450
1880
|
});
|
|
@@ -452,27 +1882,27 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
452
1882
|
const msgs = makeInboxMessagesWithFullBlocks(4);
|
|
453
1883
|
await store.addL1ToL2Messages(msgs);
|
|
454
1884
|
await checkMessages(msgs);
|
|
455
|
-
const blockMessages = await store.getL1ToL2Messages(
|
|
1885
|
+
const blockMessages = await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 1));
|
|
456
1886
|
expect(blockMessages).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
457
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));
|
|
458
1888
|
});
|
|
459
1889
|
it('stores messages in multiple operations', async ()=>{
|
|
460
1890
|
const msgs = makeInboxMessages(20, {
|
|
461
|
-
|
|
1891
|
+
initialCheckpointNumber
|
|
462
1892
|
});
|
|
463
1893
|
await store.addL1ToL2Messages(msgs.slice(0, 10));
|
|
464
1894
|
await store.addL1ToL2Messages(msgs.slice(10, 20));
|
|
465
|
-
expect(await store.getL1ToL2Messages(
|
|
1895
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual([
|
|
466
1896
|
msgs[2]
|
|
467
1897
|
].map((m)=>m.leaf));
|
|
468
|
-
expect(await store.getL1ToL2Messages(
|
|
1898
|
+
expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 12))).toEqual([
|
|
469
1899
|
msgs[12]
|
|
470
1900
|
].map((m)=>m.leaf));
|
|
471
1901
|
await checkMessages(msgs);
|
|
472
1902
|
});
|
|
473
1903
|
it('iterates over messages from start index', async ()=>{
|
|
474
1904
|
const msgs = makeInboxMessages(10, {
|
|
475
|
-
|
|
1905
|
+
initialCheckpointNumber
|
|
476
1906
|
});
|
|
477
1907
|
await store.addL1ToL2Messages(msgs);
|
|
478
1908
|
const iterated = await toArray(store.iterateL1ToL2Messages({
|
|
@@ -482,9 +1912,10 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
482
1912
|
});
|
|
483
1913
|
it('iterates over messages in reverse', async ()=>{
|
|
484
1914
|
const msgs = makeInboxMessages(10, {
|
|
485
|
-
|
|
1915
|
+
initialCheckpointNumber
|
|
486
1916
|
});
|
|
487
1917
|
await store.addL1ToL2Messages(msgs);
|
|
1918
|
+
initialCheckpointNumber;
|
|
488
1919
|
const iterated = await toArray(store.iterateL1ToL2Messages({
|
|
489
1920
|
reverse: true,
|
|
490
1921
|
end: msgs[3].index
|
|
@@ -502,9 +1933,9 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
502
1933
|
});
|
|
503
1934
|
it('throws if block number for the first message is out of order', async ()=>{
|
|
504
1935
|
const msgs = makeInboxMessages(4, {
|
|
505
|
-
|
|
1936
|
+
initialCheckpointNumber
|
|
506
1937
|
});
|
|
507
|
-
msgs[2].
|
|
1938
|
+
msgs[2].checkpointNumber = CheckpointNumber(initialCheckpointNumber - 1);
|
|
508
1939
|
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
509
1940
|
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
510
1941
|
});
|
|
@@ -516,29 +1947,29 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
516
1947
|
it('throws if rolling hash for first message is not correct', async ()=>{
|
|
517
1948
|
const msgs = makeInboxMessages(4);
|
|
518
1949
|
msgs[2].rollingHash = Buffer16.random();
|
|
519
|
-
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
1950
|
+
await store.addL1ToL2Messages(msgs.slice(0, CheckpointNumber(2)));
|
|
520
1951
|
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
521
1952
|
});
|
|
522
1953
|
it('throws if index is not in the correct range', async ()=>{
|
|
523
1954
|
const msgs = makeInboxMessages(5, {
|
|
524
|
-
|
|
1955
|
+
initialCheckpointNumber
|
|
525
1956
|
});
|
|
526
1957
|
msgs.at(-1).index += 100n;
|
|
527
1958
|
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
528
1959
|
});
|
|
529
1960
|
it('throws if first index in block has gaps', async ()=>{
|
|
530
1961
|
const msgs = makeInboxMessages(4, {
|
|
531
|
-
|
|
1962
|
+
initialCheckpointNumber
|
|
532
1963
|
});
|
|
533
1964
|
msgs[2].index++;
|
|
534
1965
|
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
535
1966
|
});
|
|
536
1967
|
it('throws if index does not follow previous one', async ()=>{
|
|
537
1968
|
const msgs = makeInboxMessages(2, {
|
|
538
|
-
|
|
1969
|
+
initialCheckpointNumber,
|
|
539
1970
|
overrideFn: (msg, i)=>({
|
|
540
1971
|
...msg,
|
|
541
|
-
|
|
1972
|
+
checkpointNumber: CheckpointNumber(2),
|
|
542
1973
|
index: BigInt(i + NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2)
|
|
543
1974
|
})
|
|
544
1975
|
});
|
|
@@ -547,24 +1978,24 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
547
1978
|
});
|
|
548
1979
|
it('removes messages up to the given block number', async ()=>{
|
|
549
1980
|
const msgs = makeInboxMessagesWithFullBlocks(4, {
|
|
550
|
-
|
|
1981
|
+
initialCheckpointNumber: CheckpointNumber(1)
|
|
551
1982
|
});
|
|
552
1983
|
await store.addL1ToL2Messages(msgs);
|
|
553
1984
|
await checkMessages(msgs);
|
|
554
|
-
expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
555
|
-
expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
556
|
-
expect(await store.getL1ToL2Messages(3)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
557
|
-
expect(await store.getL1ToL2Messages(4)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
558
|
-
await store.
|
|
559
|
-
expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
560
|
-
expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
561
|
-
expect(await store.getL1ToL2Messages(3)).toHaveLength(0);
|
|
562
|
-
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);
|
|
563
1994
|
await checkMessages(msgs.slice(0, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2));
|
|
564
1995
|
});
|
|
565
1996
|
it('removes messages starting with the given index', async ()=>{
|
|
566
1997
|
const msgs = makeInboxMessagesWithFullBlocks(4, {
|
|
567
|
-
|
|
1998
|
+
initialCheckpointNumber: CheckpointNumber(1)
|
|
568
1999
|
});
|
|
569
2000
|
await store.addL1ToL2Messages(msgs);
|
|
570
2001
|
await store.removeL1ToL2Messages(msgs[13].index);
|
|
@@ -587,7 +2018,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
587
2018
|
};
|
|
588
2019
|
await store.addContractInstances([
|
|
589
2020
|
contractInstance
|
|
590
|
-
], blockNum);
|
|
2021
|
+
], BlockNumber(blockNum));
|
|
591
2022
|
});
|
|
592
2023
|
it('returns previously stored contract instances', async ()=>{
|
|
593
2024
|
await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toMatchObject(contractInstance);
|
|
@@ -598,7 +2029,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
598
2029
|
it('returns undefined if previously stored contract instances was deleted', async ()=>{
|
|
599
2030
|
await store.deleteContractInstances([
|
|
600
2031
|
contractInstance
|
|
601
|
-
], blockNum);
|
|
2032
|
+
], BlockNumber(blockNum));
|
|
602
2033
|
await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toBeUndefined();
|
|
603
2034
|
});
|
|
604
2035
|
});
|
|
@@ -620,7 +2051,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
620
2051
|
};
|
|
621
2052
|
await store.addContractInstances([
|
|
622
2053
|
contractInstance
|
|
623
|
-
], 1);
|
|
2054
|
+
], BlockNumber(1));
|
|
624
2055
|
await store.addContractInstanceUpdates([
|
|
625
2056
|
{
|
|
626
2057
|
prevContractClassId: classId,
|
|
@@ -657,7 +2088,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
657
2088
|
};
|
|
658
2089
|
await store.addContractInstances([
|
|
659
2090
|
otherContractInstance
|
|
660
|
-
], 1);
|
|
2091
|
+
], BlockNumber(1));
|
|
661
2092
|
const fetchedInstance = await store.getContractInstance(otherContractInstance.address, timestampOfChange + 1n);
|
|
662
2093
|
expect(fetchedInstance?.originalContractClassId).toEqual(otherClassId);
|
|
663
2094
|
expect(fetchedInstance?.currentContractClassId).toEqual(otherClassId);
|
|
@@ -675,7 +2106,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
675
2106
|
};
|
|
676
2107
|
await store.addContractInstances([
|
|
677
2108
|
otherContractInstance
|
|
678
|
-
], 1);
|
|
2109
|
+
], BlockNumber(1));
|
|
679
2110
|
await store.addContractInstanceUpdates([
|
|
680
2111
|
{
|
|
681
2112
|
prevContractClassId: otherClassId,
|
|
@@ -698,7 +2129,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
698
2129
|
contractClass
|
|
699
2130
|
], [
|
|
700
2131
|
await computePublicBytecodeCommitment(contractClass.packedBytecode)
|
|
701
|
-
], blockNum);
|
|
2132
|
+
], BlockNumber(blockNum));
|
|
702
2133
|
});
|
|
703
2134
|
it('returns previously stored contract class', async ()=>{
|
|
704
2135
|
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
@@ -706,7 +2137,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
706
2137
|
it('returns undefined if the initial deployed contract class was deleted', async ()=>{
|
|
707
2138
|
await store.deleteContractClasses([
|
|
708
2139
|
contractClass
|
|
709
|
-
], blockNum);
|
|
2140
|
+
], BlockNumber(blockNum));
|
|
710
2141
|
await expect(store.getContractClass(contractClass.id)).resolves.toBeUndefined();
|
|
711
2142
|
});
|
|
712
2143
|
it('returns contract class if later "deployment" class was deleted', async ()=>{
|
|
@@ -714,10 +2145,10 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
714
2145
|
contractClass
|
|
715
2146
|
], [
|
|
716
2147
|
await computePublicBytecodeCommitment(contractClass.packedBytecode)
|
|
717
|
-
], blockNum + 1);
|
|
2148
|
+
], BlockNumber(blockNum + 1));
|
|
718
2149
|
await store.deleteContractClasses([
|
|
719
2150
|
contractClass
|
|
720
|
-
], blockNum + 1);
|
|
2151
|
+
], BlockNumber(blockNum + 1));
|
|
721
2152
|
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
722
2153
|
});
|
|
723
2154
|
it('returns undefined if contract class is not found', async ()=>{
|
|
@@ -750,180 +2181,268 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
750
2181
|
expect(stored?.utilityFunctions).toEqual(fns);
|
|
751
2182
|
});
|
|
752
2183
|
});
|
|
753
|
-
describe('
|
|
754
|
-
const
|
|
2184
|
+
describe('getPrivateLogsByTags', ()=>{
|
|
2185
|
+
const numBlocksForLogs = 3;
|
|
755
2186
|
const numTxsPerBlock = 4;
|
|
756
2187
|
const numPrivateLogsPerTx = 3;
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
: 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));
|
|
761
2191
|
const makePrivateLog = (tag)=>PrivateLog.from({
|
|
762
|
-
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))),
|
|
763
2193
|
emittedLength: PRIVATE_LOG_SIZE_IN_FIELDS
|
|
764
2194
|
});
|
|
765
|
-
const makePublicLog = (tag)=>PublicLog.from({
|
|
766
|
-
contractAddress: AztecAddress.fromNumber(1),
|
|
767
|
-
fields: makeTuple(PUBLIC_LOG_SIZE_IN_FIELDS, (i)=>!i ? tag : new Fr(tag.toNumber() + i)),
|
|
768
|
-
emittedLength: PUBLIC_LOG_SIZE_IN_FIELDS
|
|
769
|
-
});
|
|
770
2195
|
const mockPrivateLogs = (blockNumber, txIndex)=>{
|
|
771
2196
|
return times(numPrivateLogsPerTx, (logIndex)=>{
|
|
772
|
-
const tag =
|
|
2197
|
+
const tag = makePrivateLogTag(blockNumber, txIndex, logIndex);
|
|
773
2198
|
return makePrivateLog(tag);
|
|
774
2199
|
});
|
|
775
2200
|
};
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
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
|
+
} : {}
|
|
780
2209
|
});
|
|
781
|
-
|
|
782
|
-
const mockBlockWithLogs = async (blockNumber)=>{
|
|
783
|
-
const block = await L2Block.random(blockNumber);
|
|
784
|
-
block.header.globalVariables.blockNumber = blockNumber;
|
|
2210
|
+
block.header.globalVariables.blockNumber = BlockNumber(blockNumber);
|
|
785
2211
|
block.body.txEffects = await timesParallel(numTxsPerBlock, async (txIndex)=>{
|
|
786
2212
|
const txEffect = await TxEffect.random();
|
|
787
2213
|
txEffect.privateLogs = mockPrivateLogs(blockNumber, txIndex);
|
|
788
|
-
txEffect.publicLogs =
|
|
2214
|
+
txEffect.publicLogs = []; // No public logs needed for private log tests
|
|
789
2215
|
return txEffect;
|
|
790
2216
|
});
|
|
791
|
-
|
|
792
|
-
block
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
blockNumber: BigInt(blockNumber),
|
|
796
|
-
blockHash: makeBlockHash(blockNumber),
|
|
797
|
-
timestamp: BigInt(blockNumber)
|
|
798
|
-
}
|
|
799
|
-
};
|
|
2217
|
+
const checkpoint = new Checkpoint(AppendOnlyTreeSnapshot.random(), CheckpointHeader.random(), [
|
|
2218
|
+
block
|
|
2219
|
+
], CheckpointNumber(blockNumber));
|
|
2220
|
+
return makePublishedCheckpoint(checkpoint, blockNumber);
|
|
800
2221
|
};
|
|
801
2222
|
beforeEach(async ()=>{
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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));
|
|
805
2231
|
});
|
|
806
2232
|
it('is possible to batch request private logs via tags', async ()=>{
|
|
807
2233
|
const tags = [
|
|
808
|
-
|
|
809
|
-
|
|
2234
|
+
makePrivateLogTag(2, 1, 2),
|
|
2235
|
+
makePrivateLogTag(1, 2, 0)
|
|
810
2236
|
];
|
|
811
|
-
const logsByTags = await store.
|
|
2237
|
+
const logsByTags = await store.getPrivateLogsByTags(tags);
|
|
812
2238
|
expect(logsByTags).toEqual([
|
|
813
2239
|
[
|
|
814
2240
|
expect.objectContaining({
|
|
815
2241
|
blockNumber: 2,
|
|
816
|
-
|
|
817
|
-
isFromPublic: false
|
|
2242
|
+
logData: makePrivateLog(tags[0]).getEmittedFields()
|
|
818
2243
|
})
|
|
819
2244
|
],
|
|
820
2245
|
[
|
|
821
2246
|
expect.objectContaining({
|
|
822
2247
|
blockNumber: 1,
|
|
823
|
-
|
|
824
|
-
isFromPublic: false
|
|
2248
|
+
logData: makePrivateLog(tags[1]).getEmittedFields()
|
|
825
2249
|
})
|
|
826
2250
|
]
|
|
827
2251
|
]);
|
|
828
2252
|
});
|
|
829
|
-
it('is possible to batch request
|
|
830
|
-
// 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 ()=>{
|
|
831
2254
|
const tags = [
|
|
832
|
-
|
|
2255
|
+
makePrivateLogTag(1, 2, 1)
|
|
833
2256
|
];
|
|
834
|
-
|
|
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);
|
|
835
2272
|
expect(logsByTags).toEqual([
|
|
836
2273
|
[
|
|
837
2274
|
expect.objectContaining({
|
|
838
2275
|
blockNumber: 1,
|
|
839
|
-
|
|
840
|
-
isFromPublic: false
|
|
2276
|
+
logData: makePrivateLog(tags[0]).getEmittedFields()
|
|
841
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
|
+
[
|
|
2294
|
+
expect.objectContaining({
|
|
2295
|
+
blockNumber: 1,
|
|
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
|
+
[
|
|
842
2366
|
expect.objectContaining({
|
|
843
2367
|
blockNumber: 1,
|
|
844
|
-
|
|
845
|
-
isFromPublic: true
|
|
2368
|
+
logData: makePublicLog(tags[1]).getEmittedFields()
|
|
846
2369
|
})
|
|
847
2370
|
]
|
|
848
2371
|
]);
|
|
849
2372
|
});
|
|
850
2373
|
it('is possible to batch request logs that have the same tag but different content', async ()=>{
|
|
851
2374
|
const tags = [
|
|
852
|
-
|
|
2375
|
+
makePublicLogTag(1, 2, 1)
|
|
853
2376
|
];
|
|
854
|
-
// Create a
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
|
862
2387
|
]);
|
|
863
2388
|
await store.addLogs([
|
|
864
|
-
|
|
2389
|
+
newCheckpoint.checkpoint.blocks[0]
|
|
865
2390
|
]);
|
|
866
|
-
const logsByTags = await store.
|
|
2391
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
867
2392
|
expect(logsByTags).toEqual([
|
|
868
2393
|
[
|
|
869
2394
|
expect.objectContaining({
|
|
870
2395
|
blockNumber: 1,
|
|
871
|
-
|
|
872
|
-
isFromPublic: false
|
|
2396
|
+
logData: makePublicLog(tags[0]).getEmittedFields()
|
|
873
2397
|
}),
|
|
874
2398
|
expect.objectContaining({
|
|
875
2399
|
blockNumber: newBlockNumber,
|
|
876
|
-
|
|
877
|
-
isFromPublic: false
|
|
2400
|
+
logData: newLog.getEmittedFields()
|
|
878
2401
|
})
|
|
879
2402
|
]
|
|
880
2403
|
]);
|
|
881
2404
|
});
|
|
882
2405
|
it('is possible to request logs for non-existing tags and determine their position', async ()=>{
|
|
883
2406
|
const tags = [
|
|
884
|
-
|
|
885
|
-
|
|
2407
|
+
makePublicLogTag(99, 88, 77),
|
|
2408
|
+
makePublicLogTag(1, 1, 0)
|
|
886
2409
|
];
|
|
887
|
-
const logsByTags = await store.
|
|
2410
|
+
const logsByTags = await store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
888
2411
|
expect(logsByTags).toEqual([
|
|
889
2412
|
[],
|
|
890
2413
|
[
|
|
891
2414
|
expect.objectContaining({
|
|
892
2415
|
blockNumber: 1,
|
|
893
|
-
|
|
894
|
-
isFromPublic: false
|
|
2416
|
+
logData: makePublicLog(tags[1]).getEmittedFields()
|
|
895
2417
|
})
|
|
896
2418
|
]
|
|
897
2419
|
]);
|
|
898
2420
|
});
|
|
899
2421
|
});
|
|
900
2422
|
describe('getPublicLogs', ()=>{
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
const
|
|
904
|
-
|
|
905
|
-
|
|
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;
|
|
906
2428
|
beforeEach(async ()=>{
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
attestations: times(3, CommitteeAttestation.random)
|
|
915
|
-
}));
|
|
916
|
-
await store.addBlocks(blocks);
|
|
917
|
-
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
|
+
}
|
|
918
2436
|
});
|
|
919
2437
|
it('no logs returned if deleted ("txHash" filter param is respected variant)', async ()=>{
|
|
920
2438
|
// get random tx
|
|
921
|
-
const targetBlockIndex = randomInt(
|
|
922
|
-
const
|
|
923
|
-
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;
|
|
924
2443
|
await Promise.all([
|
|
925
|
-
store.
|
|
926
|
-
store.deleteLogs(
|
|
2444
|
+
store.unwindCheckpoints(CheckpointNumber(numBlocksForPublicLogs), numBlocksForPublicLogs),
|
|
2445
|
+
store.deleteLogs(publishedCheckpoints.slice(0, numBlocksForPublicLogs).flatMap((b)=>b.checkpoint.blocks))
|
|
927
2446
|
]);
|
|
928
2447
|
const response = await store.getPublicLogs({
|
|
929
2448
|
txHash: targetTxHash
|
|
@@ -934,15 +2453,16 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
934
2453
|
});
|
|
935
2454
|
it('"txHash" filter param is respected', async ()=>{
|
|
936
2455
|
// get random tx
|
|
937
|
-
const targetBlockIndex = randomInt(
|
|
938
|
-
const
|
|
939
|
-
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;
|
|
940
2460
|
const response = await store.getPublicLogs({
|
|
941
2461
|
txHash: targetTxHash
|
|
942
2462
|
});
|
|
943
2463
|
const logs = response.logs;
|
|
944
2464
|
expect(response.maxLogsHit).toBeFalsy();
|
|
945
|
-
const expectedNumLogs =
|
|
2465
|
+
const expectedNumLogs = getPublicLogsPerTx(targetBlock, targetTxIndex);
|
|
946
2466
|
expect(logs.length).toEqual(expectedNumLogs);
|
|
947
2467
|
const targeBlockNumber = targetBlockIndex + INITIAL_L2_BLOCK_NUM;
|
|
948
2468
|
for (const log of logs){
|
|
@@ -950,6 +2470,16 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
950
2470
|
expect(log.id.txIndex).toEqual(targetTxIndex);
|
|
951
2471
|
}
|
|
952
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
|
+
});
|
|
953
2483
|
it('"fromBlock" and "toBlock" filter params are respected', async ()=>{
|
|
954
2484
|
// Set "fromBlock" and "toBlock"
|
|
955
2485
|
const fromBlock = 3;
|
|
@@ -960,7 +2490,12 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
960
2490
|
});
|
|
961
2491
|
const logs = response.logs;
|
|
962
2492
|
expect(response.maxLogsHit).toBeFalsy();
|
|
963
|
-
|
|
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
|
+
}
|
|
964
2499
|
expect(logs.length).toEqual(expectedNumLogs);
|
|
965
2500
|
for (const log of logs){
|
|
966
2501
|
const blockNumber = log.id.blockNumber;
|
|
@@ -970,10 +2505,11 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
970
2505
|
});
|
|
971
2506
|
it('"contractAddress" filter param is respected', async ()=>{
|
|
972
2507
|
// Get a random contract address from the logs
|
|
973
|
-
const targetBlockIndex = randomInt(
|
|
974
|
-
const
|
|
975
|
-
const
|
|
976
|
-
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;
|
|
977
2513
|
const response = await store.getPublicLogs({
|
|
978
2514
|
contractAddress: targetContractAddress
|
|
979
2515
|
});
|
|
@@ -984,10 +2520,13 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
984
2520
|
});
|
|
985
2521
|
it('"afterLog" filter param is respected', async ()=>{
|
|
986
2522
|
// Get a random log as reference
|
|
987
|
-
const targetBlockIndex = randomInt(
|
|
988
|
-
const
|
|
989
|
-
const
|
|
990
|
-
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);
|
|
991
2530
|
const response = await store.getPublicLogs({
|
|
992
2531
|
afterLog
|
|
993
2532
|
});
|
|
@@ -1007,7 +2546,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1007
2546
|
it('"txHash" filter param is ignored when "afterLog" is set', async ()=>{
|
|
1008
2547
|
// Get random txHash
|
|
1009
2548
|
const txHash = TxHash.random();
|
|
1010
|
-
const afterLog = new LogId(1, 0, 0);
|
|
2549
|
+
const afterLog = new LogId(BlockNumber(1), L2BlockHash.random(), 0, 0);
|
|
1011
2550
|
const response = await store.getPublicLogs({
|
|
1012
2551
|
txHash,
|
|
1013
2552
|
afterLog
|
|
@@ -1023,7 +2562,7 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1023
2562
|
// "fromBlock" gets correctly trimmed to range and "toBlock" is exclusive
|
|
1024
2563
|
logs = (await store.getPublicLogs({
|
|
1025
2564
|
fromBlock: -10,
|
|
1026
|
-
toBlock: 5
|
|
2565
|
+
toBlock: BlockNumber(5)
|
|
1027
2566
|
})).logs;
|
|
1028
2567
|
let blockNumbers = new Set(logs.map((log)=>log.id.blockNumber));
|
|
1029
2568
|
expect(blockNumbers).toEqual(new Set([
|
|
@@ -1034,13 +2573,13 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1034
2573
|
]));
|
|
1035
2574
|
// "toBlock" should be exclusive
|
|
1036
2575
|
logs = (await store.getPublicLogs({
|
|
1037
|
-
fromBlock: 1,
|
|
1038
|
-
toBlock: 1
|
|
2576
|
+
fromBlock: BlockNumber(1),
|
|
2577
|
+
toBlock: BlockNumber(1)
|
|
1039
2578
|
})).logs;
|
|
1040
2579
|
expect(logs.length).toBe(0);
|
|
1041
2580
|
logs = (await store.getPublicLogs({
|
|
1042
|
-
fromBlock: 10,
|
|
1043
|
-
toBlock: 5
|
|
2581
|
+
fromBlock: BlockNumber(10),
|
|
2582
|
+
toBlock: BlockNumber(5)
|
|
1044
2583
|
})).logs;
|
|
1045
2584
|
expect(logs.length).toBe(0);
|
|
1046
2585
|
// both "fromBlock" and "toBlock" get correctly capped to range and logs from all blocks are returned
|
|
@@ -1049,35 +2588,38 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1049
2588
|
toBlock: +100
|
|
1050
2589
|
})).logs;
|
|
1051
2590
|
blockNumbers = new Set(logs.map((log)=>log.id.blockNumber));
|
|
1052
|
-
expect(blockNumbers.size).toBe(
|
|
2591
|
+
expect(blockNumbers.size).toBe(numBlocksForPublicLogs);
|
|
1053
2592
|
// intersecting with "afterLog" works
|
|
1054
2593
|
logs = (await store.getPublicLogs({
|
|
1055
|
-
fromBlock: 2,
|
|
1056
|
-
toBlock: 5,
|
|
1057
|
-
afterLog: new LogId(4, 0, 0)
|
|
2594
|
+
fromBlock: BlockNumber(2),
|
|
2595
|
+
toBlock: BlockNumber(5),
|
|
2596
|
+
afterLog: new LogId(BlockNumber(4), L2BlockHash.random(), 0, 0)
|
|
1058
2597
|
})).logs;
|
|
1059
2598
|
blockNumbers = new Set(logs.map((log)=>log.id.blockNumber));
|
|
1060
2599
|
expect(blockNumbers).toEqual(new Set([
|
|
1061
2600
|
4
|
|
1062
2601
|
]));
|
|
1063
2602
|
logs = (await store.getPublicLogs({
|
|
1064
|
-
toBlock: 5,
|
|
1065
|
-
afterLog: new LogId(5, 1, 0)
|
|
2603
|
+
toBlock: BlockNumber(5),
|
|
2604
|
+
afterLog: new LogId(BlockNumber(5), L2BlockHash.random(), 1, 0)
|
|
1066
2605
|
})).logs;
|
|
1067
2606
|
expect(logs.length).toBe(0);
|
|
1068
2607
|
logs = (await store.getPublicLogs({
|
|
1069
|
-
fromBlock: 2,
|
|
1070
|
-
toBlock: 5,
|
|
1071
|
-
afterLog: new LogId(100, 0, 0)
|
|
2608
|
+
fromBlock: BlockNumber(2),
|
|
2609
|
+
toBlock: BlockNumber(5),
|
|
2610
|
+
afterLog: new LogId(BlockNumber(100), L2BlockHash.random(), 0, 0)
|
|
1072
2611
|
})).logs;
|
|
1073
2612
|
expect(logs.length).toBe(0);
|
|
1074
2613
|
});
|
|
1075
2614
|
it('"txIndex" and "logIndex" are respected when "afterLog.blockNumber" is equal to "fromBlock"', async ()=>{
|
|
1076
2615
|
// Get a random log as reference
|
|
1077
|
-
const targetBlockIndex = randomInt(
|
|
1078
|
-
const
|
|
1079
|
-
const
|
|
1080
|
-
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);
|
|
1081
2623
|
const response = await store.getPublicLogs({
|
|
1082
2624
|
afterLog,
|
|
1083
2625
|
fromBlock: afterLog.blockNumber
|
|
@@ -1096,5 +2638,133 @@ import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
|
1096
2638
|
}
|
|
1097
2639
|
});
|
|
1098
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
|
+
});
|
|
2671
|
+
describe('pendingChainValidationStatus', ()=>{
|
|
2672
|
+
it('should return undefined when no status is set', async ()=>{
|
|
2673
|
+
const status = await store.getPendingChainValidationStatus();
|
|
2674
|
+
expect(status).toBeUndefined();
|
|
2675
|
+
});
|
|
2676
|
+
it('should store and retrieve a valid validation status', async ()=>{
|
|
2677
|
+
const validStatus = {
|
|
2678
|
+
valid: true
|
|
2679
|
+
};
|
|
2680
|
+
await store.setPendingChainValidationStatus(validStatus);
|
|
2681
|
+
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
2682
|
+
expect(retrievedStatus).toEqual(validStatus);
|
|
2683
|
+
});
|
|
2684
|
+
it('should store and retrieve an invalid validation status with insufficient attestations', async ()=>{
|
|
2685
|
+
const invalidStatus = {
|
|
2686
|
+
valid: false,
|
|
2687
|
+
block: randomBlockInfo(1),
|
|
2688
|
+
committee: [
|
|
2689
|
+
EthAddress.random(),
|
|
2690
|
+
EthAddress.random()
|
|
2691
|
+
],
|
|
2692
|
+
epoch: EpochNumber(123),
|
|
2693
|
+
seed: 456n,
|
|
2694
|
+
attestors: [
|
|
2695
|
+
EthAddress.random()
|
|
2696
|
+
],
|
|
2697
|
+
attestations: [
|
|
2698
|
+
CommitteeAttestation.random()
|
|
2699
|
+
],
|
|
2700
|
+
reason: 'insufficient-attestations'
|
|
2701
|
+
};
|
|
2702
|
+
await store.setPendingChainValidationStatus(invalidStatus);
|
|
2703
|
+
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
2704
|
+
expect(retrievedStatus).toEqual(invalidStatus);
|
|
2705
|
+
});
|
|
2706
|
+
it('should store and retrieve an invalid validation status with invalid attestation', async ()=>{
|
|
2707
|
+
const invalidStatus = {
|
|
2708
|
+
valid: false,
|
|
2709
|
+
block: randomBlockInfo(2),
|
|
2710
|
+
committee: [
|
|
2711
|
+
EthAddress.random()
|
|
2712
|
+
],
|
|
2713
|
+
attestors: [
|
|
2714
|
+
EthAddress.random()
|
|
2715
|
+
],
|
|
2716
|
+
epoch: EpochNumber(789),
|
|
2717
|
+
seed: 101n,
|
|
2718
|
+
attestations: [
|
|
2719
|
+
CommitteeAttestation.random()
|
|
2720
|
+
],
|
|
2721
|
+
reason: 'invalid-attestation',
|
|
2722
|
+
invalidIndex: 5
|
|
2723
|
+
};
|
|
2724
|
+
await store.setPendingChainValidationStatus(invalidStatus);
|
|
2725
|
+
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
2726
|
+
expect(retrievedStatus).toEqual(invalidStatus);
|
|
2727
|
+
});
|
|
2728
|
+
it('should overwrite existing status when setting a new one', async ()=>{
|
|
2729
|
+
const firstStatus = {
|
|
2730
|
+
valid: true
|
|
2731
|
+
};
|
|
2732
|
+
const secondStatus = {
|
|
2733
|
+
valid: false,
|
|
2734
|
+
block: randomBlockInfo(3),
|
|
2735
|
+
committee: [
|
|
2736
|
+
EthAddress.random()
|
|
2737
|
+
],
|
|
2738
|
+
epoch: EpochNumber(999),
|
|
2739
|
+
seed: 888n,
|
|
2740
|
+
attestors: [
|
|
2741
|
+
EthAddress.random()
|
|
2742
|
+
],
|
|
2743
|
+
attestations: [
|
|
2744
|
+
CommitteeAttestation.random()
|
|
2745
|
+
],
|
|
2746
|
+
reason: 'insufficient-attestations'
|
|
2747
|
+
};
|
|
2748
|
+
await store.setPendingChainValidationStatus(firstStatus);
|
|
2749
|
+
await store.setPendingChainValidationStatus(secondStatus);
|
|
2750
|
+
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
2751
|
+
expect(retrievedStatus).toEqual(secondStatus);
|
|
2752
|
+
});
|
|
2753
|
+
it('should handle empty committee and attestations arrays', async ()=>{
|
|
2754
|
+
const statusWithEmptyArrays = {
|
|
2755
|
+
valid: false,
|
|
2756
|
+
block: randomBlockInfo(4),
|
|
2757
|
+
committee: [],
|
|
2758
|
+
epoch: EpochNumber(0),
|
|
2759
|
+
seed: 0n,
|
|
2760
|
+
attestors: [],
|
|
2761
|
+
attestations: [],
|
|
2762
|
+
reason: 'insufficient-attestations'
|
|
2763
|
+
};
|
|
2764
|
+
await store.setPendingChainValidationStatus(statusWithEmptyArrays);
|
|
2765
|
+
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
2766
|
+
expect(retrievedStatus).toEqual(statusWithEmptyArrays);
|
|
2767
|
+
});
|
|
2768
|
+
});
|
|
1099
2769
|
});
|
|
1100
2770
|
}
|