@aztec/archiver 0.0.1-commit.fce3e4f → 0.0.1-commit.ff7989d6c
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 +156 -22
- package/dest/archiver.d.ts +139 -0
- package/dest/archiver.d.ts.map +1 -0
- package/dest/archiver.js +699 -0
- package/dest/config.d.ts +30 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/{archiver/config.js → config.js} +21 -5
- package/dest/errors.d.ts +41 -0
- package/dest/errors.d.ts.map +1 -0
- package/dest/errors.js +62 -0
- package/dest/factory.d.ts +9 -7
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +97 -13
- package/dest/index.d.ts +11 -4
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +9 -3
- package/dest/interfaces.d.ts +9 -0
- package/dest/interfaces.d.ts.map +1 -0
- package/dest/interfaces.js +3 -0
- package/dest/l1/bin/retrieve-calldata.d.ts +3 -0
- package/dest/l1/bin/retrieve-calldata.d.ts.map +1 -0
- package/dest/l1/bin/retrieve-calldata.js +148 -0
- package/dest/l1/calldata_retriever.d.ts +118 -0
- package/dest/l1/calldata_retriever.d.ts.map +1 -0
- package/dest/l1/calldata_retriever.js +484 -0
- package/dest/l1/data_retrieval.d.ts +89 -0
- package/dest/l1/data_retrieval.d.ts.map +1 -0
- package/dest/{archiver → l1}/data_retrieval.js +80 -153
- package/dest/l1/debug_tx.d.ts +19 -0
- package/dest/l1/debug_tx.d.ts.map +1 -0
- package/dest/l1/debug_tx.js +73 -0
- package/dest/l1/spire_proposer.d.ts +70 -0
- package/dest/l1/spire_proposer.d.ts.map +1 -0
- package/dest/l1/spire_proposer.js +157 -0
- package/dest/l1/trace_tx.d.ts +97 -0
- package/dest/l1/trace_tx.d.ts.map +1 -0
- package/dest/l1/trace_tx.js +91 -0
- package/dest/l1/types.d.ts +12 -0
- package/dest/l1/types.d.ts.map +1 -0
- package/dest/l1/types.js +3 -0
- package/dest/l1/validate_trace.d.ts +32 -0
- package/dest/l1/validate_trace.d.ts.map +1 -0
- package/dest/l1/validate_trace.js +154 -0
- package/dest/modules/data_source_base.d.ts +89 -0
- package/dest/modules/data_source_base.d.ts.map +1 -0
- package/dest/modules/data_source_base.js +216 -0
- package/dest/modules/data_store_updater.d.ts +80 -0
- package/dest/modules/data_store_updater.d.ts.map +1 -0
- package/dest/modules/data_store_updater.js +323 -0
- package/dest/modules/instrumentation.d.ts +39 -0
- package/dest/modules/instrumentation.d.ts.map +1 -0
- package/dest/{archiver → modules}/instrumentation.js +39 -62
- package/dest/modules/l1_synchronizer.d.ts +76 -0
- package/dest/modules/l1_synchronizer.d.ts.map +1 -0
- package/dest/modules/l1_synchronizer.js +1112 -0
- package/dest/modules/validation.d.ts +17 -0
- package/dest/modules/validation.d.ts.map +1 -0
- package/dest/{archiver → modules}/validation.js +7 -1
- package/dest/store/block_store.d.ts +196 -0
- package/dest/store/block_store.d.ts.map +1 -0
- package/dest/store/block_store.js +773 -0
- package/dest/store/contract_class_store.d.ts +18 -0
- package/dest/store/contract_class_store.d.ts.map +1 -0
- package/dest/{archiver/kv_archiver_store → store}/contract_class_store.js +13 -9
- package/dest/store/contract_instance_store.d.ts +24 -0
- package/dest/store/contract_instance_store.d.ts.map +1 -0
- package/dest/{archiver/kv_archiver_store → store}/contract_instance_store.js +1 -1
- package/dest/store/kv_archiver_store.d.ts +354 -0
- package/dest/store/kv_archiver_store.d.ts.map +1 -0
- package/dest/store/kv_archiver_store.js +464 -0
- package/dest/store/l2_tips_cache.d.ts +19 -0
- package/dest/store/l2_tips_cache.d.ts.map +1 -0
- package/dest/store/l2_tips_cache.js +89 -0
- package/dest/store/log_store.d.ts +54 -0
- package/dest/store/log_store.d.ts.map +1 -0
- package/dest/store/log_store.js +456 -0
- package/dest/store/message_store.d.ts +40 -0
- package/dest/store/message_store.d.ts.map +1 -0
- package/dest/{archiver/kv_archiver_store → store}/message_store.js +15 -14
- package/dest/{archiver/structs → structs}/data_retrieval.d.ts +1 -1
- package/dest/structs/data_retrieval.d.ts.map +1 -0
- package/dest/structs/inbox_message.d.ts +15 -0
- package/dest/structs/inbox_message.d.ts.map +1 -0
- package/dest/{archiver/structs → structs}/inbox_message.js +6 -5
- package/dest/structs/published.d.ts +2 -0
- package/dest/structs/published.d.ts.map +1 -0
- package/dest/test/fake_l1_state.d.ts +193 -0
- package/dest/test/fake_l1_state.d.ts.map +1 -0
- package/dest/test/fake_l1_state.js +389 -0
- package/dest/test/index.d.ts +2 -1
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +4 -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 +21 -11
- package/dest/test/mock_l2_block_source.d.ts +51 -18
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +245 -82
- package/dest/test/mock_structs.d.ts +80 -4
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +145 -11
- package/dest/test/noop_l1_archiver.d.ts +23 -0
- package/dest/test/noop_l1_archiver.d.ts.map +1 -0
- package/dest/test/noop_l1_archiver.js +68 -0
- package/package.json +20 -20
- package/src/archiver.ts +443 -0
- package/src/{archiver/config.ts → config.ts} +28 -12
- package/src/errors.ts +102 -0
- package/src/factory.ts +143 -13
- package/src/index.ts +11 -3
- package/src/interfaces.ts +9 -0
- package/src/l1/README.md +98 -0
- package/src/l1/bin/retrieve-calldata.ts +181 -0
- package/src/l1/calldata_retriever.ts +663 -0
- package/src/{archiver → l1}/data_retrieval.ts +146 -224
- package/src/l1/debug_tx.ts +99 -0
- package/src/l1/spire_proposer.ts +160 -0
- package/src/l1/trace_tx.ts +128 -0
- package/src/l1/types.ts +13 -0
- package/src/l1/validate_trace.ts +229 -0
- package/src/modules/data_source_base.ts +328 -0
- package/src/modules/data_store_updater.ts +448 -0
- package/src/{archiver → modules}/instrumentation.ts +43 -66
- package/src/modules/l1_synchronizer.ts +932 -0
- package/src/{archiver → modules}/validation.ts +11 -6
- package/src/store/block_store.ts +1015 -0
- package/src/{archiver/kv_archiver_store → store}/contract_class_store.ts +13 -9
- package/src/{archiver/kv_archiver_store → store}/contract_instance_store.ts +2 -2
- package/src/store/kv_archiver_store.ts +671 -0
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +637 -0
- package/src/{archiver/kv_archiver_store → store}/message_store.ts +21 -18
- package/src/{archiver/structs → structs}/inbox_message.ts +8 -8
- package/src/{archiver/structs → structs}/published.ts +0 -1
- package/src/test/fake_l1_state.ts +607 -0
- 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/index.ts +4 -0
- package/src/test/mock_archiver.ts +23 -16
- package/src/test/mock_l1_to_l2_message_source.ts +18 -11
- package/src/test/mock_l2_block_source.ts +296 -90
- package/src/test/mock_structs.ts +275 -13
- package/src/test/noop_l1_archiver.ts +109 -0
- package/dest/archiver/archiver.d.ts +0 -287
- package/dest/archiver/archiver.d.ts.map +0 -1
- package/dest/archiver/archiver.js +0 -1408
- package/dest/archiver/archiver_store.d.ts +0 -255
- package/dest/archiver/archiver_store.d.ts.map +0 -1
- package/dest/archiver/archiver_store.js +0 -4
- package/dest/archiver/archiver_store_test_suite.d.ts +0 -8
- package/dest/archiver/archiver_store_test_suite.d.ts.map +0 -1
- package/dest/archiver/archiver_store_test_suite.js +0 -1289
- package/dest/archiver/config.d.ts +0 -21
- package/dest/archiver/config.d.ts.map +0 -1
- package/dest/archiver/data_retrieval.d.ts +0 -79
- package/dest/archiver/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/errors.d.ts +0 -12
- package/dest/archiver/errors.d.ts.map +0 -1
- package/dest/archiver/errors.js +0 -17
- package/dest/archiver/index.d.ts +0 -7
- package/dest/archiver/index.d.ts.map +0 -1
- package/dest/archiver/index.js +0 -4
- package/dest/archiver/instrumentation.d.ts +0 -35
- package/dest/archiver/instrumentation.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts +0 -124
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/block_store.js +0 -370
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +0 -18
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +0 -24
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +0 -168
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +0 -296
- package/dest/archiver/kv_archiver_store/log_store.d.ts +0 -49
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/log_store.js +0 -336
- package/dest/archiver/kv_archiver_store/message_store.d.ts +0 -39
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +0 -1
- package/dest/archiver/structs/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/structs/inbox_message.d.ts +0 -15
- package/dest/archiver/structs/inbox_message.d.ts.map +0 -1
- package/dest/archiver/structs/published.d.ts +0 -3
- package/dest/archiver/structs/published.d.ts.map +0 -1
- package/dest/archiver/validation.d.ts +0 -17
- package/dest/archiver/validation.d.ts.map +0 -1
- package/dest/rpc/index.d.ts +0 -9
- package/dest/rpc/index.d.ts.map +0 -1
- package/dest/rpc/index.js +0 -15
- package/src/archiver/archiver.ts +0 -1858
- package/src/archiver/archiver_store.ts +0 -305
- package/src/archiver/archiver_store_test_suite.ts +0 -1264
- package/src/archiver/errors.ts +0 -26
- package/src/archiver/index.ts +0 -6
- package/src/archiver/kv_archiver_store/block_store.ts +0 -481
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +0 -422
- package/src/archiver/kv_archiver_store/log_store.ts +0 -406
- package/src/rpc/index.ts +0 -16
- /package/dest/{archiver/structs → structs}/data_retrieval.js +0 -0
- /package/dest/{archiver/structs → structs}/published.js +0 -0
- /package/src/{archiver/structs → structs}/data_retrieval.ts +0 -0
|
@@ -1,1264 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
INITIAL_L2_BLOCK_NUM,
|
|
3
|
-
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
4
|
-
PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
5
|
-
} from '@aztec/constants';
|
|
6
|
-
import { makeTuple } from '@aztec/foundation/array';
|
|
7
|
-
import { EpochNumber } from '@aztec/foundation/branded-types';
|
|
8
|
-
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
9
|
-
import { times, timesParallel } from '@aztec/foundation/collection';
|
|
10
|
-
import { randomInt } from '@aztec/foundation/crypto';
|
|
11
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
12
|
-
import { toArray } from '@aztec/foundation/iterable';
|
|
13
|
-
import { sleep } from '@aztec/foundation/sleep';
|
|
14
|
-
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
15
|
-
import {
|
|
16
|
-
CommitteeAttestation,
|
|
17
|
-
EthAddress,
|
|
18
|
-
L2Block,
|
|
19
|
-
L2BlockHash,
|
|
20
|
-
PublishedL2Block,
|
|
21
|
-
type ValidateBlockResult,
|
|
22
|
-
randomBlockInfo,
|
|
23
|
-
wrapInBlock,
|
|
24
|
-
} from '@aztec/stdlib/block';
|
|
25
|
-
import {
|
|
26
|
-
type ContractClassPublic,
|
|
27
|
-
type ContractInstanceWithAddress,
|
|
28
|
-
SerializableContractInstance,
|
|
29
|
-
computePublicBytecodeCommitment,
|
|
30
|
-
} from '@aztec/stdlib/contract';
|
|
31
|
-
import { LogId, PrivateLog, PublicLog } from '@aztec/stdlib/logs';
|
|
32
|
-
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
33
|
-
import {
|
|
34
|
-
makeContractClassPublic,
|
|
35
|
-
makeExecutablePrivateFunctionWithMembershipProof,
|
|
36
|
-
makeUtilityFunctionWithMembershipProof,
|
|
37
|
-
} from '@aztec/stdlib/testing';
|
|
38
|
-
import '@aztec/stdlib/testing/jest';
|
|
39
|
-
import { type IndexedTxEffect, TxEffect, TxHash } from '@aztec/stdlib/tx';
|
|
40
|
-
|
|
41
|
-
import { makeInboxMessage, makeInboxMessages } from '../test/mock_structs.js';
|
|
42
|
-
import type { ArchiverDataStore, ArchiverL1SynchPoint } from './archiver_store.js';
|
|
43
|
-
import { BlockNumberNotSequentialError, InitialBlockNumberNotSequentialError } from './errors.js';
|
|
44
|
-
import { MessageStoreError } from './kv_archiver_store/message_store.js';
|
|
45
|
-
import type { InboxMessage } from './structs/inbox_message.js';
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @param testName - The name of the test suite.
|
|
49
|
-
* @param getStore - Returns an instance of a store that's already been initialized.
|
|
50
|
-
*/
|
|
51
|
-
export function describeArchiverDataStore(
|
|
52
|
-
testName: string,
|
|
53
|
-
getStore: () => ArchiverDataStore | Promise<ArchiverDataStore>,
|
|
54
|
-
) {
|
|
55
|
-
describe(testName, () => {
|
|
56
|
-
let store: ArchiverDataStore;
|
|
57
|
-
let blocks: PublishedL2Block[];
|
|
58
|
-
|
|
59
|
-
const blockTests: [number, number, () => PublishedL2Block[]][] = [
|
|
60
|
-
[1, 1, () => blocks.slice(0, 1)],
|
|
61
|
-
[10, 1, () => blocks.slice(9, 10)],
|
|
62
|
-
[1, 10, () => blocks.slice(0, 10)],
|
|
63
|
-
[2, 5, () => blocks.slice(1, 6)],
|
|
64
|
-
[5, 2, () => blocks.slice(4, 6)],
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
const makeBlockHash = (blockNumber: number) => `0x${blockNumber.toString(16).padStart(64, '0')}`;
|
|
68
|
-
|
|
69
|
-
const makePublished = (block: L2Block, l1BlockNumber: number): PublishedL2Block =>
|
|
70
|
-
PublishedL2Block.fromFields({
|
|
71
|
-
block: block,
|
|
72
|
-
l1: {
|
|
73
|
-
blockNumber: BigInt(l1BlockNumber),
|
|
74
|
-
blockHash: makeBlockHash(l1BlockNumber),
|
|
75
|
-
timestamp: BigInt(l1BlockNumber * 1000),
|
|
76
|
-
},
|
|
77
|
-
attestations: times(3, CommitteeAttestation.random),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const expectBlocksEqual = (actual: PublishedL2Block[], expected: PublishedL2Block[]) => {
|
|
81
|
-
expect(actual.length).toEqual(expected.length);
|
|
82
|
-
for (let i = 0; i < expected.length; i++) {
|
|
83
|
-
const expectedBlock = expected[i];
|
|
84
|
-
const actualBlock = actual[i];
|
|
85
|
-
expect(actualBlock.l1).toEqual(expectedBlock.l1);
|
|
86
|
-
expect(actualBlock.block.equals(expectedBlock.block)).toBe(true);
|
|
87
|
-
expect(actualBlock.attestations.every((a, i) => a.equals(expectedBlock.attestations[i]))).toBe(true);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
beforeEach(async () => {
|
|
92
|
-
store = await getStore();
|
|
93
|
-
blocks = await timesParallel(10, async i => makePublished(await L2Block.random(i + 1), i + 10));
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe('addBlocks', () => {
|
|
97
|
-
it('returns success when adding blocks', async () => {
|
|
98
|
-
await expect(store.addBlocks(blocks)).resolves.toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('allows duplicate blocks', async () => {
|
|
102
|
-
await store.addBlocks(blocks);
|
|
103
|
-
await expect(store.addBlocks(blocks)).resolves.toBe(true);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('throws an error if the previous block does not exist in the store', async () => {
|
|
107
|
-
const block = makePublished(await L2Block.random(2), 2);
|
|
108
|
-
await expect(store.addBlocks([block])).rejects.toThrow(InitialBlockNumberNotSequentialError);
|
|
109
|
-
await expect(store.getPublishedBlocks(1, 10)).resolves.toEqual([]);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('throws an error if there is a gap in the blocks being added', async () => {
|
|
113
|
-
const blocks = [makePublished(await L2Block.random(1), 1), makePublished(await L2Block.random(3), 3)];
|
|
114
|
-
await expect(store.addBlocks(blocks)).rejects.toThrow(BlockNumberNotSequentialError);
|
|
115
|
-
await expect(store.getPublishedBlocks(1, 10)).resolves.toEqual([]);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('unwindBlocks', () => {
|
|
120
|
-
it('unwinding blocks will remove blocks from the chain', async () => {
|
|
121
|
-
await store.addBlocks(blocks);
|
|
122
|
-
const blockNumber = await store.getSynchedL2BlockNumber();
|
|
123
|
-
|
|
124
|
-
expectBlocksEqual(await store.getPublishedBlocks(blockNumber, 1), [blocks[blocks.length - 1]]);
|
|
125
|
-
|
|
126
|
-
await store.unwindBlocks(blockNumber, 1);
|
|
127
|
-
|
|
128
|
-
expect(await store.getSynchedL2BlockNumber()).toBe(blockNumber - 1);
|
|
129
|
-
expect(await store.getPublishedBlocks(blockNumber, 1)).toEqual([]);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('can unwind multiple empty blocks', async () => {
|
|
133
|
-
const emptyBlocks = await timesParallel(10, async i => makePublished(await L2Block.random(i + 1, 0), i + 10));
|
|
134
|
-
await store.addBlocks(emptyBlocks);
|
|
135
|
-
expect(await store.getSynchedL2BlockNumber()).toBe(10);
|
|
136
|
-
|
|
137
|
-
await store.unwindBlocks(10, 3);
|
|
138
|
-
expect(await store.getSynchedL2BlockNumber()).toBe(7);
|
|
139
|
-
expect((await store.getPublishedBlocks(1, 10)).map(b => b.block.number)).toEqual([1, 2, 3, 4, 5, 6, 7]);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('refuses to unwind blocks if the tip is not the last block', async () => {
|
|
143
|
-
await store.addBlocks(blocks);
|
|
144
|
-
await expect(store.unwindBlocks(5, 1)).rejects.toThrow(/can only unwind blocks from the tip/i);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('unwound blocks and headers cannot be retrieved by hash or archive', async () => {
|
|
148
|
-
await store.addBlocks(blocks);
|
|
149
|
-
const lastBlock = blocks[blocks.length - 1];
|
|
150
|
-
const blockHash = await lastBlock.block.hash();
|
|
151
|
-
const archive = lastBlock.block.archive.root;
|
|
152
|
-
|
|
153
|
-
// Verify block and header exist before unwinding
|
|
154
|
-
expect(await store.getPublishedBlockByHash(blockHash)).toBeDefined();
|
|
155
|
-
expect(await store.getPublishedBlockByArchive(archive)).toBeDefined();
|
|
156
|
-
expect(await store.getBlockHeaderByHash(blockHash)).toBeDefined();
|
|
157
|
-
expect(await store.getBlockHeaderByArchive(archive)).toBeDefined();
|
|
158
|
-
|
|
159
|
-
// Unwind the block
|
|
160
|
-
await store.unwindBlocks(lastBlock.block.number, 1);
|
|
161
|
-
|
|
162
|
-
// Verify neither block nor header can be retrieved after unwinding
|
|
163
|
-
expect(await store.getPublishedBlockByHash(blockHash)).toBeUndefined();
|
|
164
|
-
expect(await store.getPublishedBlockByArchive(archive)).toBeUndefined();
|
|
165
|
-
expect(await store.getBlockHeaderByHash(blockHash)).toBeUndefined();
|
|
166
|
-
expect(await store.getBlockHeaderByArchive(archive)).toBeUndefined();
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe('getBlocks', () => {
|
|
171
|
-
beforeEach(async () => {
|
|
172
|
-
await store.addBlocks(blocks);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it.each(blockTests)('retrieves previously stored blocks', async (start, limit, getExpectedBlocks) => {
|
|
176
|
-
expectBlocksEqual(await store.getPublishedBlocks(start, limit), getExpectedBlocks());
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('returns an empty array if no blocks are found', async () => {
|
|
180
|
-
await expect(store.getPublishedBlocks(12, 1)).resolves.toEqual([]);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('throws an error if limit is invalid', async () => {
|
|
184
|
-
await expect(store.getPublishedBlocks(1, 0)).rejects.toThrow('Invalid limit: 0');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('throws an error if `from` it is out of range', async () => {
|
|
188
|
-
await expect(store.getPublishedBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).rejects.toThrow('Invalid start: -99');
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('throws an error if unexpected initial block number is found', async () => {
|
|
192
|
-
await store.addBlocks([makePublished(await L2Block.random(21), 31)], { force: true });
|
|
193
|
-
await expect(store.getPublishedBlocks(20, 1)).rejects.toThrow(`mismatch`);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('throws an error if a gap is found', async () => {
|
|
197
|
-
await store.addBlocks(
|
|
198
|
-
[makePublished(await L2Block.random(20), 30), makePublished(await L2Block.random(22), 32)],
|
|
199
|
-
{ force: true },
|
|
200
|
-
);
|
|
201
|
-
await expect(store.getPublishedBlocks(20, 2)).rejects.toThrow(`mismatch`);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
describe('getPublishedBlockByHash', () => {
|
|
206
|
-
beforeEach(async () => {
|
|
207
|
-
await store.addBlocks(blocks);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('retrieves a block by its hash', async () => {
|
|
211
|
-
const expectedBlock = blocks[5];
|
|
212
|
-
const blockHash = await expectedBlock.block.hash();
|
|
213
|
-
const retrievedBlock = await store.getPublishedBlockByHash(blockHash);
|
|
214
|
-
|
|
215
|
-
expect(retrievedBlock).toBeDefined();
|
|
216
|
-
expectBlocksEqual([retrievedBlock!], [expectedBlock]);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('returns undefined for non-existent block hash', async () => {
|
|
220
|
-
const nonExistentHash = Fr.random();
|
|
221
|
-
await expect(store.getPublishedBlockByHash(nonExistentHash)).resolves.toBeUndefined();
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
describe('getPublishedBlockByArchive', () => {
|
|
226
|
-
beforeEach(async () => {
|
|
227
|
-
await store.addBlocks(blocks);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('retrieves a block by its archive root', async () => {
|
|
231
|
-
const expectedBlock = blocks[3];
|
|
232
|
-
const archive = expectedBlock.block.archive.root;
|
|
233
|
-
const retrievedBlock = await store.getPublishedBlockByArchive(archive);
|
|
234
|
-
|
|
235
|
-
expect(retrievedBlock).toBeDefined();
|
|
236
|
-
expectBlocksEqual([retrievedBlock!], [expectedBlock]);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it('returns undefined for non-existent archive root', async () => {
|
|
240
|
-
const nonExistentArchive = Fr.random();
|
|
241
|
-
await expect(store.getPublishedBlockByArchive(nonExistentArchive)).resolves.toBeUndefined();
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
describe('getBlockHeaderByHash', () => {
|
|
246
|
-
beforeEach(async () => {
|
|
247
|
-
await store.addBlocks(blocks);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('retrieves a block header by its hash', async () => {
|
|
251
|
-
const expectedBlock = blocks[7];
|
|
252
|
-
const blockHash = await expectedBlock.block.hash();
|
|
253
|
-
const retrievedHeader = await store.getBlockHeaderByHash(blockHash);
|
|
254
|
-
|
|
255
|
-
expect(retrievedHeader).toBeDefined();
|
|
256
|
-
expect(retrievedHeader!.equals(expectedBlock.block.getBlockHeader())).toBe(true);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('returns undefined for non-existent block hash', async () => {
|
|
260
|
-
const nonExistentHash = Fr.random();
|
|
261
|
-
await expect(store.getBlockHeaderByHash(nonExistentHash)).resolves.toBeUndefined();
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
describe('getBlockHeaderByArchive', () => {
|
|
266
|
-
beforeEach(async () => {
|
|
267
|
-
await store.addBlocks(blocks);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('retrieves a block header by its archive root', async () => {
|
|
271
|
-
const expectedBlock = blocks[2];
|
|
272
|
-
const archive = expectedBlock.block.archive.root;
|
|
273
|
-
const retrievedHeader = await store.getBlockHeaderByArchive(archive);
|
|
274
|
-
|
|
275
|
-
expect(retrievedHeader).toBeDefined();
|
|
276
|
-
expect(retrievedHeader!.equals(expectedBlock.block.getBlockHeader())).toBe(true);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it('returns undefined for non-existent archive root', async () => {
|
|
280
|
-
const nonExistentArchive = Fr.random();
|
|
281
|
-
await expect(store.getBlockHeaderByArchive(nonExistentArchive)).resolves.toBeUndefined();
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
describe('getSyncedL2BlockNumber', () => {
|
|
286
|
-
it('returns the block number before INITIAL_L2_BLOCK_NUM if no blocks have been added', async () => {
|
|
287
|
-
await expect(store.getSynchedL2BlockNumber()).resolves.toEqual(INITIAL_L2_BLOCK_NUM - 1);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it("returns the most recently added block's number", async () => {
|
|
291
|
-
await store.addBlocks(blocks);
|
|
292
|
-
await expect(store.getSynchedL2BlockNumber()).resolves.toEqual(blocks.at(-1)!.block.number);
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
describe('getSynchPoint', () => {
|
|
297
|
-
it('returns undefined if no blocks have been added', async () => {
|
|
298
|
-
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
299
|
-
blocksSynchedTo: undefined,
|
|
300
|
-
messagesSynchedTo: undefined,
|
|
301
|
-
} satisfies ArchiverL1SynchPoint);
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
it('returns the L1 block number in which the most recent L2 block was published', async () => {
|
|
305
|
-
await store.addBlocks(blocks);
|
|
306
|
-
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
307
|
-
blocksSynchedTo: 19n,
|
|
308
|
-
messagesSynchedTo: undefined,
|
|
309
|
-
} satisfies ArchiverL1SynchPoint);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('returns the L1 block number that most recently added messages from inbox', async () => {
|
|
313
|
-
const l1BlockHash = Buffer32.random();
|
|
314
|
-
const l1BlockNumber = 10n;
|
|
315
|
-
await store.setMessageSynchedL1Block({ l1BlockNumber: 5n, l1BlockHash: Buffer32.random() });
|
|
316
|
-
await store.addL1ToL2Messages([makeInboxMessage(Buffer16.ZERO, { l1BlockNumber, l1BlockHash })]);
|
|
317
|
-
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
318
|
-
blocksSynchedTo: undefined,
|
|
319
|
-
messagesSynchedTo: { l1BlockHash, l1BlockNumber },
|
|
320
|
-
} satisfies ArchiverL1SynchPoint);
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('returns the latest syncpoint if latest message is behind', async () => {
|
|
324
|
-
const l1BlockHash = Buffer32.random();
|
|
325
|
-
const l1BlockNumber = 10n;
|
|
326
|
-
await store.setMessageSynchedL1Block({ l1BlockNumber, l1BlockHash });
|
|
327
|
-
const msg = makeInboxMessage(Buffer16.ZERO, { l1BlockNumber: 5n, l1BlockHash: Buffer32.random() });
|
|
328
|
-
await store.addL1ToL2Messages([msg]);
|
|
329
|
-
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
330
|
-
blocksSynchedTo: undefined,
|
|
331
|
-
messagesSynchedTo: { l1BlockHash, l1BlockNumber },
|
|
332
|
-
} satisfies ArchiverL1SynchPoint);
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
describe('addLogs', () => {
|
|
337
|
-
it('adds private & public logs', async () => {
|
|
338
|
-
const block = blocks[0].block;
|
|
339
|
-
await expect(store.addLogs([block])).resolves.toEqual(true);
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe('deleteLogs', () => {
|
|
344
|
-
it('deletes private & public logs', async () => {
|
|
345
|
-
const block = blocks[0].block;
|
|
346
|
-
await store.addBlocks([blocks[0]]);
|
|
347
|
-
await expect(store.addLogs([block])).resolves.toEqual(true);
|
|
348
|
-
|
|
349
|
-
expect((await store.getPrivateLogs(1, 1)).length).toEqual(
|
|
350
|
-
block.body.txEffects.map(txEffect => txEffect.privateLogs).flat().length,
|
|
351
|
-
);
|
|
352
|
-
expect((await store.getPublicLogs({ fromBlock: 1 })).logs.length).toEqual(
|
|
353
|
-
block.body.txEffects.map(txEffect => txEffect.publicLogs).flat().length,
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
// This one is a pain for memory as we would never want to just delete memory in the middle.
|
|
357
|
-
await store.deleteLogs([block]);
|
|
358
|
-
|
|
359
|
-
expect((await store.getPrivateLogs(1, 1)).length).toEqual(0);
|
|
360
|
-
expect((await store.getPublicLogs({ fromBlock: 1 })).logs.length).toEqual(0);
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
describe('getPrivateLogs', () => {
|
|
365
|
-
it('gets added private logs', async () => {
|
|
366
|
-
const block = blocks[0].block;
|
|
367
|
-
await store.addBlocks([blocks[0]]);
|
|
368
|
-
await store.addLogs([block]);
|
|
369
|
-
|
|
370
|
-
const privateLogs = await store.getPrivateLogs(1, 1);
|
|
371
|
-
expect(privateLogs).toEqual(block.body.txEffects.map(txEffect => txEffect.privateLogs).flat());
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
describe('getTxEffect', () => {
|
|
376
|
-
beforeEach(async () => {
|
|
377
|
-
await store.addLogs(blocks.map(b => b.block));
|
|
378
|
-
await store.addBlocks(blocks);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
it.each([
|
|
382
|
-
() => ({ data: blocks[0].block.body.txEffects[0], block: blocks[0].block, txIndexInBlock: 0 }),
|
|
383
|
-
() => ({ data: blocks[9].block.body.txEffects[3], block: blocks[9].block, txIndexInBlock: 3 }),
|
|
384
|
-
() => ({ data: blocks[3].block.body.txEffects[1], block: blocks[3].block, txIndexInBlock: 1 }),
|
|
385
|
-
() => ({ data: blocks[5].block.body.txEffects[2], block: blocks[5].block, txIndexInBlock: 2 }),
|
|
386
|
-
() => ({ data: blocks[1].block.body.txEffects[0], block: blocks[1].block, txIndexInBlock: 0 }),
|
|
387
|
-
])('retrieves a previously stored transaction', async getExpectedTx => {
|
|
388
|
-
const { data, block, txIndexInBlock } = getExpectedTx();
|
|
389
|
-
const expectedTx: IndexedTxEffect = {
|
|
390
|
-
data,
|
|
391
|
-
l2BlockNumber: block.number,
|
|
392
|
-
l2BlockHash: L2BlockHash.fromField(await block.hash()),
|
|
393
|
-
txIndexInBlock,
|
|
394
|
-
};
|
|
395
|
-
const actualTx = await store.getTxEffect(data.txHash);
|
|
396
|
-
expect(actualTx).toEqual(expectedTx);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
it('returns undefined if tx is not found', async () => {
|
|
400
|
-
await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
it.each([
|
|
404
|
-
() => wrapInBlock(blocks[0].block.body.txEffects[0], blocks[0].block),
|
|
405
|
-
() => wrapInBlock(blocks[9].block.body.txEffects[3], blocks[9].block),
|
|
406
|
-
() => wrapInBlock(blocks[3].block.body.txEffects[1], blocks[3].block),
|
|
407
|
-
() => wrapInBlock(blocks[5].block.body.txEffects[2], blocks[5].block),
|
|
408
|
-
() => wrapInBlock(blocks[1].block.body.txEffects[0], blocks[1].block),
|
|
409
|
-
])('tries to retrieves a previously stored transaction after deleted', async getExpectedTx => {
|
|
410
|
-
await store.unwindBlocks(blocks.length, blocks.length);
|
|
411
|
-
|
|
412
|
-
const expectedTx = await getExpectedTx();
|
|
413
|
-
const actualTx = await store.getTxEffect(expectedTx.data.txHash);
|
|
414
|
-
expect(actualTx).toEqual(undefined);
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it('returns undefined if tx is not found', async () => {
|
|
418
|
-
await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
it('does not fail if the block is unwound while requesting a tx', async () => {
|
|
422
|
-
const expectedTx = await wrapInBlock(blocks[1].block.body.txEffects[0], blocks[1].block);
|
|
423
|
-
let done = false;
|
|
424
|
-
void (async () => {
|
|
425
|
-
while (!done) {
|
|
426
|
-
void store.getTxEffect(expectedTx.data.txHash);
|
|
427
|
-
await sleep(1);
|
|
428
|
-
}
|
|
429
|
-
})();
|
|
430
|
-
await store.unwindBlocks(blocks.length, blocks.length);
|
|
431
|
-
done = true;
|
|
432
|
-
expect(await store.getTxEffect(expectedTx.data.txHash)).toEqual(undefined);
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
describe('L1 to L2 Messages', () => {
|
|
437
|
-
const initialL2BlockNumber = 13;
|
|
438
|
-
|
|
439
|
-
const checkMessages = async (msgs: InboxMessage[]) => {
|
|
440
|
-
expect(await store.getLastL1ToL2Message()).toEqual(msgs.at(-1));
|
|
441
|
-
expect(await toArray(store.iterateL1ToL2Messages())).toEqual(msgs);
|
|
442
|
-
expect(await store.getTotalL1ToL2MessageCount()).toEqual(BigInt(msgs.length));
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
const makeInboxMessagesWithFullBlocks = (blockCount: number, opts: { initialL2BlockNumber?: number } = {}) =>
|
|
446
|
-
makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, {
|
|
447
|
-
overrideFn: (msg, i) => {
|
|
448
|
-
const l2BlockNumber =
|
|
449
|
-
(opts.initialL2BlockNumber ?? initialL2BlockNumber) + Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
450
|
-
const index =
|
|
451
|
-
InboxLeaf.smallestIndexFromL2Block(l2BlockNumber) + BigInt(i % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
452
|
-
return { ...msg, l2BlockNumber, index };
|
|
453
|
-
},
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
it('stores first message ever', async () => {
|
|
457
|
-
const msg = makeInboxMessage(Buffer16.ZERO, { index: 0n, l2BlockNumber: 1 });
|
|
458
|
-
await store.addL1ToL2Messages([msg]);
|
|
459
|
-
|
|
460
|
-
await checkMessages([msg]);
|
|
461
|
-
expect(await store.getL1ToL2Messages(1)).toEqual([msg.leaf]);
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
it('stores single message', async () => {
|
|
465
|
-
const msg = makeInboxMessage(Buffer16.ZERO, { l2BlockNumber: 2 });
|
|
466
|
-
await store.addL1ToL2Messages([msg]);
|
|
467
|
-
|
|
468
|
-
await checkMessages([msg]);
|
|
469
|
-
expect(await store.getL1ToL2Messages(2)).toEqual([msg.leaf]);
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
it('stores and returns messages across different blocks', async () => {
|
|
473
|
-
const msgs = makeInboxMessages(5, { initialL2BlockNumber });
|
|
474
|
-
await store.addL1ToL2Messages(msgs);
|
|
475
|
-
|
|
476
|
-
await checkMessages(msgs);
|
|
477
|
-
expect(await store.getL1ToL2Messages(initialL2BlockNumber + 2)).toEqual([msgs[2]].map(m => m.leaf));
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
it('stores the same messages again', async () => {
|
|
481
|
-
const msgs = makeInboxMessages(5, { initialL2BlockNumber });
|
|
482
|
-
await store.addL1ToL2Messages(msgs);
|
|
483
|
-
await store.addL1ToL2Messages(msgs.slice(2));
|
|
484
|
-
|
|
485
|
-
await checkMessages(msgs);
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
it('stores and returns messages across different blocks with gaps', async () => {
|
|
489
|
-
const msgs1 = makeInboxMessages(3, { initialL2BlockNumber: 1 });
|
|
490
|
-
const msgs2 = makeInboxMessages(3, { initialL2BlockNumber: 20, initialHash: msgs1.at(-1)!.rollingHash });
|
|
491
|
-
|
|
492
|
-
await store.addL1ToL2Messages(msgs1);
|
|
493
|
-
await store.addL1ToL2Messages(msgs2);
|
|
494
|
-
|
|
495
|
-
await checkMessages([...msgs1, ...msgs2]);
|
|
496
|
-
|
|
497
|
-
expect(await store.getL1ToL2Messages(1)).toEqual([msgs1[0].leaf]);
|
|
498
|
-
expect(await store.getL1ToL2Messages(4)).toEqual([]);
|
|
499
|
-
expect(await store.getL1ToL2Messages(20)).toEqual([msgs2[0].leaf]);
|
|
500
|
-
expect(await store.getL1ToL2Messages(24)).toEqual([]);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it('stores and returns messages with block numbers larger than a byte', async () => {
|
|
504
|
-
const msgs = makeInboxMessages(5, { initialL2BlockNumber: 1000 });
|
|
505
|
-
await store.addL1ToL2Messages(msgs);
|
|
506
|
-
|
|
507
|
-
await checkMessages(msgs);
|
|
508
|
-
expect(await store.getL1ToL2Messages(1002)).toEqual([msgs[2]].map(m => m.leaf));
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
it('stores and returns multiple messages per block', async () => {
|
|
512
|
-
const msgs = makeInboxMessagesWithFullBlocks(4);
|
|
513
|
-
await store.addL1ToL2Messages(msgs);
|
|
514
|
-
|
|
515
|
-
await checkMessages(msgs);
|
|
516
|
-
const blockMessages = await store.getL1ToL2Messages(initialL2BlockNumber + 1);
|
|
517
|
-
expect(blockMessages).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
518
|
-
expect(blockMessages).toEqual(
|
|
519
|
-
msgs.slice(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2).map(m => m.leaf),
|
|
520
|
-
);
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
it('stores messages in multiple operations', async () => {
|
|
524
|
-
const msgs = makeInboxMessages(20, { initialL2BlockNumber });
|
|
525
|
-
await store.addL1ToL2Messages(msgs.slice(0, 10));
|
|
526
|
-
await store.addL1ToL2Messages(msgs.slice(10, 20));
|
|
527
|
-
|
|
528
|
-
expect(await store.getL1ToL2Messages(initialL2BlockNumber + 2)).toEqual([msgs[2]].map(m => m.leaf));
|
|
529
|
-
expect(await store.getL1ToL2Messages(initialL2BlockNumber + 12)).toEqual([msgs[12]].map(m => m.leaf));
|
|
530
|
-
await checkMessages(msgs);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
it('iterates over messages from start index', async () => {
|
|
534
|
-
const msgs = makeInboxMessages(10, { initialL2BlockNumber });
|
|
535
|
-
await store.addL1ToL2Messages(msgs);
|
|
536
|
-
|
|
537
|
-
const iterated = await toArray(store.iterateL1ToL2Messages({ start: msgs[3].index }));
|
|
538
|
-
expect(iterated).toEqual(msgs.slice(3));
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
it('iterates over messages in reverse', async () => {
|
|
542
|
-
const msgs = makeInboxMessages(10, { initialL2BlockNumber });
|
|
543
|
-
await store.addL1ToL2Messages(msgs);
|
|
544
|
-
|
|
545
|
-
const iterated = await toArray(store.iterateL1ToL2Messages({ reverse: true, end: msgs[3].index }));
|
|
546
|
-
expect(iterated).toEqual(msgs.slice(0, 4).reverse());
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
it('throws if messages are added out of order', async () => {
|
|
550
|
-
const msgs = makeInboxMessages(5, { overrideFn: (msg, i) => ({ ...msg, index: BigInt(10 - i) }) });
|
|
551
|
-
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
it('throws if block number for the first message is out of order', async () => {
|
|
555
|
-
const msgs = makeInboxMessages(4, { initialL2BlockNumber });
|
|
556
|
-
msgs[2].l2BlockNumber = initialL2BlockNumber - 1;
|
|
557
|
-
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
558
|
-
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
it('throws if rolling hash is not correct', async () => {
|
|
562
|
-
const msgs = makeInboxMessages(5);
|
|
563
|
-
msgs[1].rollingHash = Buffer16.random();
|
|
564
|
-
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
it('throws if rolling hash for first message is not correct', async () => {
|
|
568
|
-
const msgs = makeInboxMessages(4);
|
|
569
|
-
msgs[2].rollingHash = Buffer16.random();
|
|
570
|
-
await store.addL1ToL2Messages(msgs.slice(0, 2));
|
|
571
|
-
await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
it('throws if index is not in the correct range', async () => {
|
|
575
|
-
const msgs = makeInboxMessages(5, { initialL2BlockNumber });
|
|
576
|
-
msgs.at(-1)!.index += 100n;
|
|
577
|
-
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
it('throws if first index in block has gaps', async () => {
|
|
581
|
-
const msgs = makeInboxMessages(4, { initialL2BlockNumber });
|
|
582
|
-
msgs[2].index++;
|
|
583
|
-
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
it('throws if index does not follow previous one', async () => {
|
|
587
|
-
const msgs = makeInboxMessages(2, {
|
|
588
|
-
initialL2BlockNumber,
|
|
589
|
-
overrideFn: (msg, i) => ({
|
|
590
|
-
...msg,
|
|
591
|
-
l2BlockNumber: 2,
|
|
592
|
-
index: BigInt(i + NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2),
|
|
593
|
-
}),
|
|
594
|
-
});
|
|
595
|
-
msgs[1].index++;
|
|
596
|
-
await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
it('removes messages up to the given block number', async () => {
|
|
600
|
-
const msgs = makeInboxMessagesWithFullBlocks(4, { initialL2BlockNumber: 1 });
|
|
601
|
-
|
|
602
|
-
await store.addL1ToL2Messages(msgs);
|
|
603
|
-
await checkMessages(msgs);
|
|
604
|
-
|
|
605
|
-
expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
606
|
-
expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
607
|
-
expect(await store.getL1ToL2Messages(3)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
608
|
-
expect(await store.getL1ToL2Messages(4)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
609
|
-
|
|
610
|
-
await store.rollbackL1ToL2MessagesToL2Block(2);
|
|
611
|
-
|
|
612
|
-
expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
613
|
-
expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
614
|
-
expect(await store.getL1ToL2Messages(3)).toHaveLength(0);
|
|
615
|
-
expect(await store.getL1ToL2Messages(4)).toHaveLength(0);
|
|
616
|
-
|
|
617
|
-
await checkMessages(msgs.slice(0, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2));
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
it('removes messages starting with the given index', async () => {
|
|
621
|
-
const msgs = makeInboxMessagesWithFullBlocks(4, { initialL2BlockNumber: 1 });
|
|
622
|
-
await store.addL1ToL2Messages(msgs);
|
|
623
|
-
|
|
624
|
-
await store.removeL1ToL2Messages(msgs[13].index);
|
|
625
|
-
await checkMessages(msgs.slice(0, 13));
|
|
626
|
-
});
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
describe('contractInstances', () => {
|
|
630
|
-
let contractInstance: ContractInstanceWithAddress;
|
|
631
|
-
const blockNum = 10;
|
|
632
|
-
const timestamp = 3600n;
|
|
633
|
-
|
|
634
|
-
beforeEach(async () => {
|
|
635
|
-
const classId = Fr.random();
|
|
636
|
-
const randomInstance = await SerializableContractInstance.random({
|
|
637
|
-
currentContractClassId: classId,
|
|
638
|
-
originalContractClassId: classId,
|
|
639
|
-
});
|
|
640
|
-
contractInstance = { ...randomInstance, address: await AztecAddress.random() };
|
|
641
|
-
await store.addContractInstances([contractInstance], blockNum);
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
it('returns previously stored contract instances', async () => {
|
|
645
|
-
await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toMatchObject(
|
|
646
|
-
contractInstance,
|
|
647
|
-
);
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
it('returns undefined if contract instance is not found', async () => {
|
|
651
|
-
await expect(store.getContractInstance(await AztecAddress.random(), timestamp)).resolves.toBeUndefined();
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
it('returns undefined if previously stored contract instances was deleted', async () => {
|
|
655
|
-
await store.deleteContractInstances([contractInstance], blockNum);
|
|
656
|
-
await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toBeUndefined();
|
|
657
|
-
});
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
describe('contractInstanceUpdates', () => {
|
|
661
|
-
let contractInstance: ContractInstanceWithAddress;
|
|
662
|
-
let classId: Fr;
|
|
663
|
-
let nextClassId: Fr;
|
|
664
|
-
const timestampOfChange = 3600n;
|
|
665
|
-
|
|
666
|
-
beforeEach(async () => {
|
|
667
|
-
classId = Fr.random();
|
|
668
|
-
nextClassId = Fr.random();
|
|
669
|
-
const randomInstance = await SerializableContractInstance.random({
|
|
670
|
-
currentContractClassId: classId,
|
|
671
|
-
originalContractClassId: classId,
|
|
672
|
-
});
|
|
673
|
-
contractInstance = { ...randomInstance, address: await AztecAddress.random() };
|
|
674
|
-
await store.addContractInstances([contractInstance], 1);
|
|
675
|
-
await store.addContractInstanceUpdates(
|
|
676
|
-
[
|
|
677
|
-
{
|
|
678
|
-
prevContractClassId: classId,
|
|
679
|
-
newContractClassId: nextClassId,
|
|
680
|
-
timestampOfChange,
|
|
681
|
-
address: contractInstance.address,
|
|
682
|
-
},
|
|
683
|
-
],
|
|
684
|
-
timestampOfChange - 1n,
|
|
685
|
-
);
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
it('gets the correct current class id for a contract not updated yet', async () => {
|
|
689
|
-
const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange - 1n);
|
|
690
|
-
expect(fetchedInstance?.originalContractClassId).toEqual(classId);
|
|
691
|
-
expect(fetchedInstance?.currentContractClassId).toEqual(classId);
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
it('gets the correct current class id for a contract that has just been updated', async () => {
|
|
695
|
-
const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange);
|
|
696
|
-
expect(fetchedInstance?.originalContractClassId).toEqual(classId);
|
|
697
|
-
expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
it('gets the correct current class id for a contract that was updated in the past', async () => {
|
|
701
|
-
const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange + 1n);
|
|
702
|
-
expect(fetchedInstance?.originalContractClassId).toEqual(classId);
|
|
703
|
-
expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
it('ignores updates for the wrong contract', async () => {
|
|
707
|
-
const otherClassId = Fr.random();
|
|
708
|
-
const randomInstance = await SerializableContractInstance.random({
|
|
709
|
-
currentContractClassId: otherClassId,
|
|
710
|
-
originalContractClassId: otherClassId,
|
|
711
|
-
});
|
|
712
|
-
const otherContractInstance = {
|
|
713
|
-
...randomInstance,
|
|
714
|
-
address: await AztecAddress.random(),
|
|
715
|
-
};
|
|
716
|
-
await store.addContractInstances([otherContractInstance], 1);
|
|
717
|
-
|
|
718
|
-
const fetchedInstance = await store.getContractInstance(otherContractInstance.address, timestampOfChange + 1n);
|
|
719
|
-
expect(fetchedInstance?.originalContractClassId).toEqual(otherClassId);
|
|
720
|
-
expect(fetchedInstance?.currentContractClassId).toEqual(otherClassId);
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
it('bounds its search to the right contract if more than than one update exists', async () => {
|
|
724
|
-
const otherClassId = Fr.random();
|
|
725
|
-
const otherNextClassId = Fr.random();
|
|
726
|
-
const randomInstance = await SerializableContractInstance.random({
|
|
727
|
-
currentContractClassId: otherClassId,
|
|
728
|
-
originalContractClassId: otherNextClassId,
|
|
729
|
-
});
|
|
730
|
-
const otherContractInstance = {
|
|
731
|
-
...randomInstance,
|
|
732
|
-
address: await AztecAddress.random(),
|
|
733
|
-
};
|
|
734
|
-
await store.addContractInstances([otherContractInstance], 1);
|
|
735
|
-
await store.addContractInstanceUpdates(
|
|
736
|
-
[
|
|
737
|
-
{
|
|
738
|
-
prevContractClassId: otherClassId,
|
|
739
|
-
newContractClassId: otherNextClassId,
|
|
740
|
-
timestampOfChange,
|
|
741
|
-
address: otherContractInstance.address,
|
|
742
|
-
},
|
|
743
|
-
],
|
|
744
|
-
timestampOfChange - 1n,
|
|
745
|
-
);
|
|
746
|
-
|
|
747
|
-
const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange + 1n);
|
|
748
|
-
expect(fetchedInstance?.originalContractClassId).toEqual(classId);
|
|
749
|
-
expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
|
|
750
|
-
});
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
describe('contractClasses', () => {
|
|
754
|
-
let contractClass: ContractClassPublic;
|
|
755
|
-
const blockNum = 10;
|
|
756
|
-
|
|
757
|
-
beforeEach(async () => {
|
|
758
|
-
contractClass = await makeContractClassPublic();
|
|
759
|
-
await store.addContractClasses(
|
|
760
|
-
[contractClass],
|
|
761
|
-
[await computePublicBytecodeCommitment(contractClass.packedBytecode)],
|
|
762
|
-
blockNum,
|
|
763
|
-
);
|
|
764
|
-
});
|
|
765
|
-
|
|
766
|
-
it('returns previously stored contract class', async () => {
|
|
767
|
-
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
it('returns undefined if the initial deployed contract class was deleted', async () => {
|
|
771
|
-
await store.deleteContractClasses([contractClass], blockNum);
|
|
772
|
-
await expect(store.getContractClass(contractClass.id)).resolves.toBeUndefined();
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
it('returns contract class if later "deployment" class was deleted', async () => {
|
|
776
|
-
await store.addContractClasses(
|
|
777
|
-
[contractClass],
|
|
778
|
-
[await computePublicBytecodeCommitment(contractClass.packedBytecode)],
|
|
779
|
-
blockNum + 1,
|
|
780
|
-
);
|
|
781
|
-
await store.deleteContractClasses([contractClass], blockNum + 1);
|
|
782
|
-
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
it('returns undefined if contract class is not found', async () => {
|
|
786
|
-
await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined();
|
|
787
|
-
});
|
|
788
|
-
|
|
789
|
-
it('adds new private functions', async () => {
|
|
790
|
-
const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof);
|
|
791
|
-
await store.addFunctions(contractClass.id, fns, []);
|
|
792
|
-
const stored = await store.getContractClass(contractClass.id);
|
|
793
|
-
expect(stored?.privateFunctions).toEqual(fns);
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
it('does not duplicate private functions', async () => {
|
|
797
|
-
const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof);
|
|
798
|
-
await store.addFunctions(contractClass.id, fns.slice(0, 1), []);
|
|
799
|
-
await store.addFunctions(contractClass.id, fns, []);
|
|
800
|
-
const stored = await store.getContractClass(contractClass.id);
|
|
801
|
-
expect(stored?.privateFunctions).toEqual(fns);
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
it('adds new utility functions', async () => {
|
|
805
|
-
const fns = times(3, makeUtilityFunctionWithMembershipProof);
|
|
806
|
-
await store.addFunctions(contractClass.id, [], fns);
|
|
807
|
-
const stored = await store.getContractClass(contractClass.id);
|
|
808
|
-
expect(stored?.utilityFunctions).toEqual(fns);
|
|
809
|
-
});
|
|
810
|
-
|
|
811
|
-
it('does not duplicate utility functions', async () => {
|
|
812
|
-
const fns = times(3, makeUtilityFunctionWithMembershipProof);
|
|
813
|
-
await store.addFunctions(contractClass.id, [], fns.slice(0, 1));
|
|
814
|
-
await store.addFunctions(contractClass.id, [], fns);
|
|
815
|
-
const stored = await store.getContractClass(contractClass.id);
|
|
816
|
-
expect(stored?.utilityFunctions).toEqual(fns);
|
|
817
|
-
});
|
|
818
|
-
});
|
|
819
|
-
|
|
820
|
-
describe('getLogsByTags', () => {
|
|
821
|
-
const numBlocks = 3;
|
|
822
|
-
const numTxsPerBlock = 4;
|
|
823
|
-
const numPrivateLogsPerTx = 3;
|
|
824
|
-
const numPublicLogsPerTx = 2;
|
|
825
|
-
|
|
826
|
-
let blocks: PublishedL2Block[];
|
|
827
|
-
|
|
828
|
-
const makeTag = (blockNumber: number, txIndex: number, logIndex: number, isPublic = false) =>
|
|
829
|
-
blockNumber === 1 && txIndex === 0 && logIndex === 0
|
|
830
|
-
? Fr.ZERO // Shared tag
|
|
831
|
-
: new Fr((blockNumber * 100 + txIndex * 10 + logIndex) * (isPublic ? 123 : 1));
|
|
832
|
-
|
|
833
|
-
const makePrivateLog = (tag: Fr) =>
|
|
834
|
-
PrivateLog.from({
|
|
835
|
-
fields: makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, i => (!i ? tag : new Fr(tag.toNumber() + i))),
|
|
836
|
-
emittedLength: PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
const makePublicLog = (tag: Fr) =>
|
|
840
|
-
PublicLog.from({
|
|
841
|
-
contractAddress: AztecAddress.fromNumber(1),
|
|
842
|
-
// Arbitrary length
|
|
843
|
-
fields: new Array(10).fill(null).map((_, i) => (!i ? tag : new Fr(tag.toNumber() + i))),
|
|
844
|
-
});
|
|
845
|
-
|
|
846
|
-
const mockPrivateLogs = (blockNumber: number, txIndex: number) => {
|
|
847
|
-
return times(numPrivateLogsPerTx, (logIndex: number) => {
|
|
848
|
-
const tag = makeTag(blockNumber, txIndex, logIndex);
|
|
849
|
-
return makePrivateLog(tag);
|
|
850
|
-
});
|
|
851
|
-
};
|
|
852
|
-
|
|
853
|
-
const mockPublicLogs = (blockNumber: number, txIndex: number) => {
|
|
854
|
-
return times(numPublicLogsPerTx, (logIndex: number) => {
|
|
855
|
-
const tag = makeTag(blockNumber, txIndex, logIndex, /* isPublic */ true);
|
|
856
|
-
return makePublicLog(tag);
|
|
857
|
-
});
|
|
858
|
-
};
|
|
859
|
-
|
|
860
|
-
const mockBlockWithLogs = async (blockNumber: number): Promise<PublishedL2Block> => {
|
|
861
|
-
const block = await L2Block.random(blockNumber);
|
|
862
|
-
block.header.globalVariables.blockNumber = blockNumber;
|
|
863
|
-
|
|
864
|
-
block.body.txEffects = await timesParallel(numTxsPerBlock, async (txIndex: number) => {
|
|
865
|
-
const txEffect = await TxEffect.random();
|
|
866
|
-
txEffect.privateLogs = mockPrivateLogs(blockNumber, txIndex);
|
|
867
|
-
txEffect.publicLogs = mockPublicLogs(blockNumber, txIndex);
|
|
868
|
-
return txEffect;
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
return PublishedL2Block.fromFields({
|
|
872
|
-
block: block,
|
|
873
|
-
attestations: times(3, CommitteeAttestation.random),
|
|
874
|
-
l1: {
|
|
875
|
-
blockNumber: BigInt(blockNumber),
|
|
876
|
-
blockHash: makeBlockHash(blockNumber),
|
|
877
|
-
timestamp: BigInt(blockNumber),
|
|
878
|
-
},
|
|
879
|
-
});
|
|
880
|
-
};
|
|
881
|
-
|
|
882
|
-
beforeEach(async () => {
|
|
883
|
-
blocks = await timesParallel(numBlocks, (index: number) => mockBlockWithLogs(index + 1));
|
|
884
|
-
|
|
885
|
-
await store.addBlocks(blocks);
|
|
886
|
-
await store.addLogs(blocks.map(b => b.block));
|
|
887
|
-
});
|
|
888
|
-
|
|
889
|
-
it('is possible to batch request private logs via tags', async () => {
|
|
890
|
-
const tags = [makeTag(2, 1, 2), makeTag(1, 2, 0)];
|
|
891
|
-
|
|
892
|
-
const logsByTags = await store.getLogsByTags(tags);
|
|
893
|
-
|
|
894
|
-
expect(logsByTags).toEqual([
|
|
895
|
-
[
|
|
896
|
-
expect.objectContaining({
|
|
897
|
-
blockNumber: 2,
|
|
898
|
-
log: makePrivateLog(tags[0]),
|
|
899
|
-
isFromPublic: false,
|
|
900
|
-
}),
|
|
901
|
-
],
|
|
902
|
-
[
|
|
903
|
-
expect.objectContaining({
|
|
904
|
-
blockNumber: 1,
|
|
905
|
-
log: makePrivateLog(tags[1]),
|
|
906
|
-
isFromPublic: false,
|
|
907
|
-
}),
|
|
908
|
-
],
|
|
909
|
-
]);
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
it('is possible to batch request all logs (private and public) via tags', async () => {
|
|
913
|
-
// Tag(1, 0, 0) is shared with the first private log and the first public log.
|
|
914
|
-
const tags = [makeTag(1, 0, 0)];
|
|
915
|
-
|
|
916
|
-
const logsByTags = await store.getLogsByTags(tags);
|
|
917
|
-
|
|
918
|
-
expect(logsByTags).toEqual([
|
|
919
|
-
[
|
|
920
|
-
expect.objectContaining({
|
|
921
|
-
blockNumber: 1,
|
|
922
|
-
log: makePrivateLog(tags[0]),
|
|
923
|
-
isFromPublic: false,
|
|
924
|
-
}),
|
|
925
|
-
expect.objectContaining({
|
|
926
|
-
blockNumber: 1,
|
|
927
|
-
log: makePublicLog(tags[0]),
|
|
928
|
-
isFromPublic: true,
|
|
929
|
-
}),
|
|
930
|
-
],
|
|
931
|
-
]);
|
|
932
|
-
});
|
|
933
|
-
|
|
934
|
-
it('is possible to batch request logs that have the same tag but different content', async () => {
|
|
935
|
-
const tags = [makeTag(1, 2, 1)];
|
|
936
|
-
|
|
937
|
-
// Create a block containing logs that have the same tag as the blocks before.
|
|
938
|
-
const newBlockNumber = numBlocks;
|
|
939
|
-
const newBlock = await mockBlockWithLogs(newBlockNumber);
|
|
940
|
-
const newLog = newBlock.block.body.txEffects[1].privateLogs[1];
|
|
941
|
-
newLog.fields[0] = tags[0];
|
|
942
|
-
newBlock.block.body.txEffects[1].privateLogs[1] = newLog;
|
|
943
|
-
await store.addBlocks([newBlock]);
|
|
944
|
-
await store.addLogs([newBlock.block]);
|
|
945
|
-
|
|
946
|
-
const logsByTags = await store.getLogsByTags(tags);
|
|
947
|
-
|
|
948
|
-
expect(logsByTags).toEqual([
|
|
949
|
-
[
|
|
950
|
-
expect.objectContaining({
|
|
951
|
-
blockNumber: 1,
|
|
952
|
-
log: makePrivateLog(tags[0]),
|
|
953
|
-
isFromPublic: false,
|
|
954
|
-
}),
|
|
955
|
-
expect.objectContaining({
|
|
956
|
-
blockNumber: newBlockNumber,
|
|
957
|
-
log: newLog,
|
|
958
|
-
isFromPublic: false,
|
|
959
|
-
}),
|
|
960
|
-
],
|
|
961
|
-
]);
|
|
962
|
-
});
|
|
963
|
-
|
|
964
|
-
it('is possible to request logs for non-existing tags and determine their position', async () => {
|
|
965
|
-
const tags = [makeTag(99, 88, 77), makeTag(1, 1, 1)];
|
|
966
|
-
|
|
967
|
-
const logsByTags = await store.getLogsByTags(tags);
|
|
968
|
-
|
|
969
|
-
expect(logsByTags).toEqual([
|
|
970
|
-
[
|
|
971
|
-
// No logs for the first tag.
|
|
972
|
-
],
|
|
973
|
-
[
|
|
974
|
-
expect.objectContaining({
|
|
975
|
-
blockNumber: 1,
|
|
976
|
-
log: makePrivateLog(tags[1]),
|
|
977
|
-
isFromPublic: false,
|
|
978
|
-
}),
|
|
979
|
-
],
|
|
980
|
-
]);
|
|
981
|
-
});
|
|
982
|
-
});
|
|
983
|
-
|
|
984
|
-
describe('getPublicLogs', () => {
|
|
985
|
-
const txsPerBlock = 4;
|
|
986
|
-
const numPublicFunctionCalls = 3;
|
|
987
|
-
const numPublicLogs = 2;
|
|
988
|
-
const numBlocks = 10;
|
|
989
|
-
let blocks: PublishedL2Block[];
|
|
990
|
-
|
|
991
|
-
beforeEach(async () => {
|
|
992
|
-
blocks = await timesParallel(numBlocks, async (index: number) =>
|
|
993
|
-
PublishedL2Block.fromFields({
|
|
994
|
-
block: await L2Block.random(index + 1, txsPerBlock, numPublicFunctionCalls, numPublicLogs),
|
|
995
|
-
l1: { blockNumber: BigInt(index), blockHash: makeBlockHash(index), timestamp: BigInt(index) },
|
|
996
|
-
attestations: times(3, CommitteeAttestation.random),
|
|
997
|
-
}),
|
|
998
|
-
);
|
|
999
|
-
|
|
1000
|
-
await store.addBlocks(blocks);
|
|
1001
|
-
await store.addLogs(blocks.map(b => b.block));
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
it('no logs returned if deleted ("txHash" filter param is respected variant)', async () => {
|
|
1005
|
-
// get random tx
|
|
1006
|
-
const targetBlockIndex = randomInt(numBlocks);
|
|
1007
|
-
const targetTxIndex = randomInt(txsPerBlock);
|
|
1008
|
-
const targetTxHash = blocks[targetBlockIndex].block.body.txEffects[targetTxIndex].txHash;
|
|
1009
|
-
|
|
1010
|
-
await Promise.all([
|
|
1011
|
-
store.unwindBlocks(blocks.length, blocks.length),
|
|
1012
|
-
store.deleteLogs(blocks.map(b => b.block)),
|
|
1013
|
-
]);
|
|
1014
|
-
|
|
1015
|
-
const response = await store.getPublicLogs({ txHash: targetTxHash });
|
|
1016
|
-
const logs = response.logs;
|
|
1017
|
-
|
|
1018
|
-
expect(response.maxLogsHit).toBeFalsy();
|
|
1019
|
-
expect(logs.length).toEqual(0);
|
|
1020
|
-
});
|
|
1021
|
-
|
|
1022
|
-
it('"txHash" filter param is respected', async () => {
|
|
1023
|
-
// get random tx
|
|
1024
|
-
const targetBlockIndex = randomInt(numBlocks);
|
|
1025
|
-
const targetTxIndex = randomInt(txsPerBlock);
|
|
1026
|
-
const targetTxHash = blocks[targetBlockIndex].block.body.txEffects[targetTxIndex].txHash;
|
|
1027
|
-
|
|
1028
|
-
const response = await store.getPublicLogs({ txHash: targetTxHash });
|
|
1029
|
-
const logs = response.logs;
|
|
1030
|
-
|
|
1031
|
-
expect(response.maxLogsHit).toBeFalsy();
|
|
1032
|
-
|
|
1033
|
-
const expectedNumLogs = numPublicFunctionCalls * numPublicLogs;
|
|
1034
|
-
expect(logs.length).toEqual(expectedNumLogs);
|
|
1035
|
-
|
|
1036
|
-
const targeBlockNumber = targetBlockIndex + INITIAL_L2_BLOCK_NUM;
|
|
1037
|
-
for (const log of logs) {
|
|
1038
|
-
expect(log.id.blockNumber).toEqual(targeBlockNumber);
|
|
1039
|
-
expect(log.id.txIndex).toEqual(targetTxIndex);
|
|
1040
|
-
}
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
it('"fromBlock" and "toBlock" filter params are respected', async () => {
|
|
1044
|
-
// Set "fromBlock" and "toBlock"
|
|
1045
|
-
const fromBlock = 3;
|
|
1046
|
-
const toBlock = 7;
|
|
1047
|
-
|
|
1048
|
-
const response = await store.getPublicLogs({ fromBlock, toBlock });
|
|
1049
|
-
const logs = response.logs;
|
|
1050
|
-
|
|
1051
|
-
expect(response.maxLogsHit).toBeFalsy();
|
|
1052
|
-
|
|
1053
|
-
const expectedNumLogs = txsPerBlock * numPublicFunctionCalls * numPublicLogs * (toBlock - fromBlock);
|
|
1054
|
-
expect(logs.length).toEqual(expectedNumLogs);
|
|
1055
|
-
|
|
1056
|
-
for (const log of logs) {
|
|
1057
|
-
const blockNumber = log.id.blockNumber;
|
|
1058
|
-
expect(blockNumber).toBeGreaterThanOrEqual(fromBlock);
|
|
1059
|
-
expect(blockNumber).toBeLessThan(toBlock);
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1062
|
-
|
|
1063
|
-
it('"contractAddress" filter param is respected', async () => {
|
|
1064
|
-
// Get a random contract address from the logs
|
|
1065
|
-
const targetBlockIndex = randomInt(numBlocks);
|
|
1066
|
-
const targetTxIndex = randomInt(txsPerBlock);
|
|
1067
|
-
const targetLogIndex = randomInt(numPublicLogs * numPublicFunctionCalls);
|
|
1068
|
-
const targetContractAddress =
|
|
1069
|
-
blocks[targetBlockIndex].block.body.txEffects[targetTxIndex].publicLogs[targetLogIndex].contractAddress;
|
|
1070
|
-
|
|
1071
|
-
const response = await store.getPublicLogs({ contractAddress: targetContractAddress });
|
|
1072
|
-
|
|
1073
|
-
expect(response.maxLogsHit).toBeFalsy();
|
|
1074
|
-
|
|
1075
|
-
for (const extendedLog of response.logs) {
|
|
1076
|
-
expect(extendedLog.log.contractAddress.equals(targetContractAddress)).toBeTruthy();
|
|
1077
|
-
}
|
|
1078
|
-
});
|
|
1079
|
-
|
|
1080
|
-
it('"afterLog" filter param is respected', async () => {
|
|
1081
|
-
// Get a random log as reference
|
|
1082
|
-
const targetBlockIndex = randomInt(numBlocks);
|
|
1083
|
-
const targetTxIndex = randomInt(txsPerBlock);
|
|
1084
|
-
const targetLogIndex = randomInt(numPublicLogs);
|
|
1085
|
-
|
|
1086
|
-
const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex);
|
|
1087
|
-
|
|
1088
|
-
const response = await store.getPublicLogs({ afterLog });
|
|
1089
|
-
const logs = response.logs;
|
|
1090
|
-
|
|
1091
|
-
expect(response.maxLogsHit).toBeFalsy();
|
|
1092
|
-
|
|
1093
|
-
for (const log of logs) {
|
|
1094
|
-
const logId = log.id;
|
|
1095
|
-
expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber);
|
|
1096
|
-
if (logId.blockNumber === afterLog.blockNumber) {
|
|
1097
|
-
expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex);
|
|
1098
|
-
if (logId.txIndex === afterLog.txIndex) {
|
|
1099
|
-
expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
});
|
|
1104
|
-
|
|
1105
|
-
it('"txHash" filter param is ignored when "afterLog" is set', async () => {
|
|
1106
|
-
// Get random txHash
|
|
1107
|
-
const txHash = TxHash.random();
|
|
1108
|
-
const afterLog = new LogId(1, 0, 0);
|
|
1109
|
-
|
|
1110
|
-
const response = await store.getPublicLogs({ txHash, afterLog });
|
|
1111
|
-
expect(response.logs.length).toBeGreaterThan(1);
|
|
1112
|
-
});
|
|
1113
|
-
|
|
1114
|
-
it('intersecting works', async () => {
|
|
1115
|
-
let logs = (await store.getPublicLogs({ fromBlock: -10, toBlock: -5 })).logs;
|
|
1116
|
-
expect(logs.length).toBe(0);
|
|
1117
|
-
|
|
1118
|
-
// "fromBlock" gets correctly trimmed to range and "toBlock" is exclusive
|
|
1119
|
-
logs = (await store.getPublicLogs({ fromBlock: -10, toBlock: 5 })).logs;
|
|
1120
|
-
let blockNumbers = new Set(logs.map(log => log.id.blockNumber));
|
|
1121
|
-
expect(blockNumbers).toEqual(new Set([1, 2, 3, 4]));
|
|
1122
|
-
|
|
1123
|
-
// "toBlock" should be exclusive
|
|
1124
|
-
logs = (await store.getPublicLogs({ fromBlock: 1, toBlock: 1 })).logs;
|
|
1125
|
-
expect(logs.length).toBe(0);
|
|
1126
|
-
|
|
1127
|
-
logs = (await store.getPublicLogs({ fromBlock: 10, toBlock: 5 })).logs;
|
|
1128
|
-
expect(logs.length).toBe(0);
|
|
1129
|
-
|
|
1130
|
-
// both "fromBlock" and "toBlock" get correctly capped to range and logs from all blocks are returned
|
|
1131
|
-
logs = (await store.getPublicLogs({ fromBlock: -100, toBlock: +100 })).logs;
|
|
1132
|
-
blockNumbers = new Set(logs.map(log => log.id.blockNumber));
|
|
1133
|
-
expect(blockNumbers.size).toBe(numBlocks);
|
|
1134
|
-
|
|
1135
|
-
// intersecting with "afterLog" works
|
|
1136
|
-
logs = (await store.getPublicLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(4, 0, 0) })).logs;
|
|
1137
|
-
blockNumbers = new Set(logs.map(log => log.id.blockNumber));
|
|
1138
|
-
expect(blockNumbers).toEqual(new Set([4]));
|
|
1139
|
-
|
|
1140
|
-
logs = (await store.getPublicLogs({ toBlock: 5, afterLog: new LogId(5, 1, 0) })).logs;
|
|
1141
|
-
expect(logs.length).toBe(0);
|
|
1142
|
-
|
|
1143
|
-
logs = (await store.getPublicLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(100, 0, 0) })).logs;
|
|
1144
|
-
expect(logs.length).toBe(0);
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
it('"txIndex" and "logIndex" are respected when "afterLog.blockNumber" is equal to "fromBlock"', async () => {
|
|
1148
|
-
// Get a random log as reference
|
|
1149
|
-
const targetBlockIndex = randomInt(numBlocks);
|
|
1150
|
-
const targetTxIndex = randomInt(txsPerBlock);
|
|
1151
|
-
const targetLogIndex = randomInt(numPublicLogs);
|
|
1152
|
-
|
|
1153
|
-
const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex);
|
|
1154
|
-
|
|
1155
|
-
const response = await store.getPublicLogs({ afterLog, fromBlock: afterLog.blockNumber });
|
|
1156
|
-
const logs = response.logs;
|
|
1157
|
-
|
|
1158
|
-
expect(response.maxLogsHit).toBeFalsy();
|
|
1159
|
-
|
|
1160
|
-
for (const log of logs) {
|
|
1161
|
-
const logId = log.id;
|
|
1162
|
-
expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber);
|
|
1163
|
-
if (logId.blockNumber === afterLog.blockNumber) {
|
|
1164
|
-
expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex);
|
|
1165
|
-
if (logId.txIndex === afterLog.txIndex) {
|
|
1166
|
-
expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
});
|
|
1171
|
-
});
|
|
1172
|
-
|
|
1173
|
-
describe('pendingChainValidationStatus', () => {
|
|
1174
|
-
it('should return undefined when no status is set', async () => {
|
|
1175
|
-
const status = await store.getPendingChainValidationStatus();
|
|
1176
|
-
expect(status).toBeUndefined();
|
|
1177
|
-
});
|
|
1178
|
-
|
|
1179
|
-
it('should store and retrieve a valid validation status', async () => {
|
|
1180
|
-
const validStatus: ValidateBlockResult = { valid: true };
|
|
1181
|
-
|
|
1182
|
-
await store.setPendingChainValidationStatus(validStatus);
|
|
1183
|
-
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
1184
|
-
|
|
1185
|
-
expect(retrievedStatus).toEqual(validStatus);
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
|
-
it('should store and retrieve an invalid validation status with insufficient attestations', async () => {
|
|
1189
|
-
const invalidStatus: ValidateBlockResult = {
|
|
1190
|
-
valid: false,
|
|
1191
|
-
block: randomBlockInfo(1),
|
|
1192
|
-
committee: [EthAddress.random(), EthAddress.random()],
|
|
1193
|
-
epoch: EpochNumber(123),
|
|
1194
|
-
seed: 456n,
|
|
1195
|
-
attestors: [EthAddress.random()],
|
|
1196
|
-
attestations: [CommitteeAttestation.random()],
|
|
1197
|
-
reason: 'insufficient-attestations',
|
|
1198
|
-
};
|
|
1199
|
-
|
|
1200
|
-
await store.setPendingChainValidationStatus(invalidStatus);
|
|
1201
|
-
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
1202
|
-
|
|
1203
|
-
expect(retrievedStatus).toEqual(invalidStatus);
|
|
1204
|
-
});
|
|
1205
|
-
|
|
1206
|
-
it('should store and retrieve an invalid validation status with invalid attestation', async () => {
|
|
1207
|
-
const invalidStatus: ValidateBlockResult = {
|
|
1208
|
-
valid: false,
|
|
1209
|
-
block: randomBlockInfo(2),
|
|
1210
|
-
committee: [EthAddress.random()],
|
|
1211
|
-
attestors: [EthAddress.random()],
|
|
1212
|
-
epoch: EpochNumber(789),
|
|
1213
|
-
seed: 101n,
|
|
1214
|
-
attestations: [CommitteeAttestation.random()],
|
|
1215
|
-
reason: 'invalid-attestation',
|
|
1216
|
-
invalidIndex: 5,
|
|
1217
|
-
};
|
|
1218
|
-
|
|
1219
|
-
await store.setPendingChainValidationStatus(invalidStatus);
|
|
1220
|
-
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
1221
|
-
|
|
1222
|
-
expect(retrievedStatus).toEqual(invalidStatus);
|
|
1223
|
-
});
|
|
1224
|
-
|
|
1225
|
-
it('should overwrite existing status when setting a new one', async () => {
|
|
1226
|
-
const firstStatus: ValidateBlockResult = { valid: true };
|
|
1227
|
-
const secondStatus: ValidateBlockResult = {
|
|
1228
|
-
valid: false,
|
|
1229
|
-
block: randomBlockInfo(3),
|
|
1230
|
-
committee: [EthAddress.random()],
|
|
1231
|
-
epoch: EpochNumber(999),
|
|
1232
|
-
seed: 888n,
|
|
1233
|
-
attestors: [EthAddress.random()],
|
|
1234
|
-
attestations: [CommitteeAttestation.random()],
|
|
1235
|
-
reason: 'insufficient-attestations',
|
|
1236
|
-
};
|
|
1237
|
-
|
|
1238
|
-
await store.setPendingChainValidationStatus(firstStatus);
|
|
1239
|
-
await store.setPendingChainValidationStatus(secondStatus);
|
|
1240
|
-
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
1241
|
-
|
|
1242
|
-
expect(retrievedStatus).toEqual(secondStatus);
|
|
1243
|
-
});
|
|
1244
|
-
|
|
1245
|
-
it('should handle empty committee and attestations arrays', async () => {
|
|
1246
|
-
const statusWithEmptyArrays: ValidateBlockResult = {
|
|
1247
|
-
valid: false,
|
|
1248
|
-
block: randomBlockInfo(4),
|
|
1249
|
-
committee: [],
|
|
1250
|
-
epoch: EpochNumber(0),
|
|
1251
|
-
seed: 0n,
|
|
1252
|
-
attestors: [],
|
|
1253
|
-
attestations: [],
|
|
1254
|
-
reason: 'insufficient-attestations',
|
|
1255
|
-
};
|
|
1256
|
-
|
|
1257
|
-
await store.setPendingChainValidationStatus(statusWithEmptyArrays);
|
|
1258
|
-
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
1259
|
-
|
|
1260
|
-
expect(retrievedStatus).toEqual(statusWithEmptyArrays);
|
|
1261
|
-
});
|
|
1262
|
-
});
|
|
1263
|
-
});
|
|
1264
|
-
}
|