@aztec/archiver 0.0.1-commit.343b43af6 → 0.0.1-commit.35158ae7e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/archiver.d.ts +1 -2
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +21 -11
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -1
- package/dest/errors.d.ts +15 -1
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +18 -0
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +16 -13
- package/dest/modules/data_source_base.d.ts +3 -3
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +5 -5
- package/dest/modules/data_store_updater.d.ts +3 -6
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +47 -65
- package/dest/modules/l1_synchronizer.d.ts +1 -1
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +9 -5
- package/dest/store/block_store.d.ts +3 -2
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +7 -4
- package/dest/store/contract_class_store.d.ts +2 -3
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +7 -67
- package/dest/store/contract_instance_store.d.ts +1 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +6 -2
- package/dest/store/kv_archiver_store.d.ts +14 -12
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +15 -14
- package/dest/store/log_store.d.ts +6 -3
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +93 -16
- package/dest/store/message_store.d.ts +5 -1
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +13 -0
- package/dest/test/fake_l1_state.d.ts +1 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +11 -3
- package/dest/test/mock_l2_block_source.d.ts +1 -1
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +2 -2
- package/package.json +13 -13
- package/src/archiver.ts +24 -11
- package/src/config.ts +8 -1
- package/src/errors.ts +30 -0
- package/src/factory.ts +17 -10
- package/src/modules/data_source_base.ts +9 -4
- package/src/modules/data_store_updater.ts +54 -94
- package/src/modules/l1_synchronizer.ts +15 -10
- package/src/store/block_store.ts +11 -2
- package/src/store/contract_class_store.ts +8 -106
- package/src/store/contract_instance_store.ts +8 -5
- package/src/store/kv_archiver_store.ts +22 -25
- package/src/store/log_store.ts +126 -27
- package/src/store/message_store.ts +19 -0
- package/src/test/fake_l1_state.ts +15 -5
- package/src/test/mock_l2_block_source.ts +7 -1
|
@@ -3,6 +3,7 @@ import { EpochCache } from '@aztec/epoch-cache';
|
|
|
3
3
|
import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
4
4
|
import type { L1BlockId } from '@aztec/ethereum/l1-types';
|
|
5
5
|
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
6
|
+
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
6
7
|
import { maxBigint } from '@aztec/foundation/bigint';
|
|
7
8
|
import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
8
9
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
@@ -333,17 +334,20 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
333
334
|
|
|
334
335
|
const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
|
|
335
336
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
checkpoints
|
|
343
|
-
.filter(isDefined)
|
|
344
|
-
.map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
|
|
337
|
+
// Fetch checkpoints and blocks in bounded batches to avoid unbounded concurrent
|
|
338
|
+
// promises when the gap between local pending and proven checkpoint numbers is large.
|
|
339
|
+
const BATCH_SIZE = 10;
|
|
340
|
+
const indices = Array.from({ length: checkpointsToUnwind }, (_, i) => CheckpointNumber(i + pruneFrom));
|
|
341
|
+
const checkpoints = (await asyncPool(BATCH_SIZE, indices, idx => this.store.getCheckpointData(idx))).filter(
|
|
342
|
+
isDefined,
|
|
345
343
|
);
|
|
346
|
-
const newBlocks =
|
|
344
|
+
const newBlocks = (
|
|
345
|
+
await asyncPool(BATCH_SIZE, checkpoints, cp =>
|
|
346
|
+
this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
.filter(isDefined)
|
|
350
|
+
.flat();
|
|
347
351
|
|
|
348
352
|
// Emit an event for listening services to react to the chain prune
|
|
349
353
|
this.events.emit(L2BlockSourceEvents.L2PruneUnproven, {
|
|
@@ -391,6 +395,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
391
395
|
const localMessagesInserted = await this.store.getTotalL1ToL2MessageCount();
|
|
392
396
|
const localLastMessage = await this.store.getLastL1ToL2Message();
|
|
393
397
|
const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
|
|
398
|
+
await this.store.setInboxTreeInProgress(remoteMessagesState.treeInProgress);
|
|
394
399
|
|
|
395
400
|
this.log.trace(`Retrieved remote inbox state at L1 block ${currentL1BlockNumber}.`, {
|
|
396
401
|
localMessagesInserted,
|
package/src/store/block_store.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
serializeValidateCheckpointResult,
|
|
21
21
|
} from '@aztec/stdlib/block';
|
|
22
22
|
import { type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
23
|
+
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
23
24
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
24
25
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
25
26
|
import {
|
|
@@ -851,7 +852,10 @@ export class BlockStore {
|
|
|
851
852
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
852
853
|
* @returns The requested tx receipt (or undefined if not found).
|
|
853
854
|
*/
|
|
854
|
-
async getSettledTxReceipt(
|
|
855
|
+
async getSettledTxReceipt(
|
|
856
|
+
txHash: TxHash,
|
|
857
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
858
|
+
): Promise<TxReceipt | undefined> {
|
|
855
859
|
const txEffect = await this.getTxEffect(txHash);
|
|
856
860
|
if (!txEffect) {
|
|
857
861
|
return undefined;
|
|
@@ -860,10 +864,11 @@ export class BlockStore {
|
|
|
860
864
|
const blockNumber = BlockNumber(txEffect.l2BlockNumber);
|
|
861
865
|
|
|
862
866
|
// Use existing archiver methods to determine finalization level
|
|
863
|
-
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
|
|
867
|
+
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber, blockData] = await Promise.all([
|
|
864
868
|
this.getProvenBlockNumber(),
|
|
865
869
|
this.getCheckpointedL2BlockNumber(),
|
|
866
870
|
this.getFinalizedL2BlockNumber(),
|
|
871
|
+
this.getBlockData(blockNumber),
|
|
867
872
|
]);
|
|
868
873
|
|
|
869
874
|
let status: TxStatus;
|
|
@@ -877,6 +882,9 @@ export class BlockStore {
|
|
|
877
882
|
status = TxStatus.PROPOSED;
|
|
878
883
|
}
|
|
879
884
|
|
|
885
|
+
const epochNumber =
|
|
886
|
+
blockData && l1Constants ? getEpochAtSlot(blockData.header.globalVariables.slotNumber, l1Constants) : undefined;
|
|
887
|
+
|
|
880
888
|
return new TxReceipt(
|
|
881
889
|
txHash,
|
|
882
890
|
status,
|
|
@@ -885,6 +893,7 @@ export class BlockStore {
|
|
|
885
893
|
txEffect.data.transactionFee.toBigInt(),
|
|
886
894
|
txEffect.l2BlockHash,
|
|
887
895
|
blockNumber,
|
|
896
|
+
epochNumber,
|
|
888
897
|
);
|
|
889
898
|
}
|
|
890
899
|
|
|
@@ -2,14 +2,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
2
2
|
import { toArray } from '@aztec/foundation/iterable';
|
|
3
3
|
import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
4
4
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
5
|
-
import {
|
|
6
|
-
import type {
|
|
7
|
-
ContractClassPublic,
|
|
8
|
-
ContractClassPublicWithBlockNumber,
|
|
9
|
-
ExecutablePrivateFunctionWithMembershipProof,
|
|
10
|
-
UtilityFunctionWithMembershipProof,
|
|
11
|
-
} from '@aztec/stdlib/contract';
|
|
12
|
-
import { Vector } from '@aztec/stdlib/types';
|
|
5
|
+
import type { ContractClassPublic, ContractClassPublicWithBlockNumber } from '@aztec/stdlib/contract';
|
|
13
6
|
|
|
14
7
|
/**
|
|
15
8
|
* LMDB-based contract class storage for the archiver.
|
|
@@ -29,11 +22,15 @@ export class ContractClassStore {
|
|
|
29
22
|
blockNumber: number,
|
|
30
23
|
): Promise<void> {
|
|
31
24
|
await this.db.transactionAsync(async () => {
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
const key = contractClass.id.toString();
|
|
26
|
+
if (await this.#contractClasses.hasAsync(key)) {
|
|
27
|
+
throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
|
|
28
|
+
}
|
|
29
|
+
await this.#contractClasses.set(
|
|
30
|
+
key,
|
|
34
31
|
serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
|
|
35
32
|
);
|
|
36
|
-
await this.#bytecodeCommitments.
|
|
33
|
+
await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer());
|
|
37
34
|
});
|
|
38
35
|
}
|
|
39
36
|
|
|
@@ -60,37 +57,6 @@ export class ContractClassStore {
|
|
|
60
57
|
async getContractClassIds(): Promise<Fr[]> {
|
|
61
58
|
return (await toArray(this.#contractClasses.keysAsync())).map(key => Fr.fromHexString(key));
|
|
62
59
|
}
|
|
63
|
-
|
|
64
|
-
async addFunctions(
|
|
65
|
-
contractClassId: Fr,
|
|
66
|
-
newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
|
|
67
|
-
newUtilityFunctions: UtilityFunctionWithMembershipProof[],
|
|
68
|
-
): Promise<boolean> {
|
|
69
|
-
await this.db.transactionAsync(async () => {
|
|
70
|
-
const existingClassBuffer = await this.#contractClasses.getAsync(contractClassId.toString());
|
|
71
|
-
if (!existingClassBuffer) {
|
|
72
|
-
throw new Error(`Unknown contract class ${contractClassId} when adding private functions to store`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const existingClass = deserializeContractClassPublic(existingClassBuffer);
|
|
76
|
-
const { privateFunctions: existingPrivateFns, utilityFunctions: existingUtilityFns } = existingClass;
|
|
77
|
-
|
|
78
|
-
const updatedClass: Omit<ContractClassPublicWithBlockNumber, 'id'> = {
|
|
79
|
-
...existingClass,
|
|
80
|
-
privateFunctions: [
|
|
81
|
-
...existingPrivateFns,
|
|
82
|
-
...newPrivateFunctions.filter(newFn => !existingPrivateFns.some(f => f.selector.equals(newFn.selector))),
|
|
83
|
-
],
|
|
84
|
-
utilityFunctions: [
|
|
85
|
-
...existingUtilityFns,
|
|
86
|
-
...newUtilityFunctions.filter(newFn => !existingUtilityFns.some(f => f.selector.equals(newFn.selector))),
|
|
87
|
-
],
|
|
88
|
-
};
|
|
89
|
-
await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
60
|
}
|
|
95
61
|
|
|
96
62
|
function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWithBlockNumber, 'id'>): Buffer {
|
|
@@ -98,83 +64,19 @@ function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWit
|
|
|
98
64
|
contractClass.l2BlockNumber,
|
|
99
65
|
numToUInt8(contractClass.version),
|
|
100
66
|
contractClass.artifactHash,
|
|
101
|
-
contractClass.privateFunctions.length,
|
|
102
|
-
contractClass.privateFunctions.map(serializePrivateFunction),
|
|
103
|
-
contractClass.utilityFunctions.length,
|
|
104
|
-
contractClass.utilityFunctions.map(serializeUtilityFunction),
|
|
105
67
|
contractClass.packedBytecode.length,
|
|
106
68
|
contractClass.packedBytecode,
|
|
107
69
|
contractClass.privateFunctionsRoot,
|
|
108
70
|
);
|
|
109
71
|
}
|
|
110
72
|
|
|
111
|
-
function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipProof): Buffer {
|
|
112
|
-
return serializeToBuffer(
|
|
113
|
-
fn.selector,
|
|
114
|
-
fn.vkHash,
|
|
115
|
-
fn.bytecode.length,
|
|
116
|
-
fn.bytecode,
|
|
117
|
-
fn.functionMetadataHash,
|
|
118
|
-
fn.artifactMetadataHash,
|
|
119
|
-
fn.utilityFunctionsTreeRoot,
|
|
120
|
-
new Vector(fn.privateFunctionTreeSiblingPath),
|
|
121
|
-
fn.privateFunctionTreeLeafIndex,
|
|
122
|
-
new Vector(fn.artifactTreeSiblingPath),
|
|
123
|
-
fn.artifactTreeLeafIndex,
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function serializeUtilityFunction(fn: UtilityFunctionWithMembershipProof): Buffer {
|
|
128
|
-
return serializeToBuffer(
|
|
129
|
-
fn.selector,
|
|
130
|
-
fn.bytecode.length,
|
|
131
|
-
fn.bytecode,
|
|
132
|
-
fn.functionMetadataHash,
|
|
133
|
-
fn.artifactMetadataHash,
|
|
134
|
-
fn.privateFunctionsArtifactTreeRoot,
|
|
135
|
-
new Vector(fn.artifactTreeSiblingPath),
|
|
136
|
-
fn.artifactTreeLeafIndex,
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
73
|
function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPublicWithBlockNumber, 'id'> {
|
|
141
74
|
const reader = BufferReader.asReader(buffer);
|
|
142
75
|
return {
|
|
143
76
|
l2BlockNumber: reader.readNumber(),
|
|
144
77
|
version: reader.readUInt8() as 1,
|
|
145
78
|
artifactHash: reader.readObject(Fr),
|
|
146
|
-
privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }),
|
|
147
|
-
utilityFunctions: reader.readVector({ fromBuffer: deserializeUtilityFunction }),
|
|
148
79
|
packedBytecode: reader.readBuffer(),
|
|
149
80
|
privateFunctionsRoot: reader.readObject(Fr),
|
|
150
81
|
};
|
|
151
82
|
}
|
|
152
|
-
|
|
153
|
-
function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePrivateFunctionWithMembershipProof {
|
|
154
|
-
const reader = BufferReader.asReader(buffer);
|
|
155
|
-
return {
|
|
156
|
-
selector: reader.readObject(FunctionSelector),
|
|
157
|
-
vkHash: reader.readObject(Fr),
|
|
158
|
-
bytecode: reader.readBuffer(),
|
|
159
|
-
functionMetadataHash: reader.readObject(Fr),
|
|
160
|
-
artifactMetadataHash: reader.readObject(Fr),
|
|
161
|
-
utilityFunctionsTreeRoot: reader.readObject(Fr),
|
|
162
|
-
privateFunctionTreeSiblingPath: reader.readVector(Fr),
|
|
163
|
-
privateFunctionTreeLeafIndex: reader.readNumber(),
|
|
164
|
-
artifactTreeSiblingPath: reader.readVector(Fr),
|
|
165
|
-
artifactTreeLeafIndex: reader.readNumber(),
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function deserializeUtilityFunction(buffer: Buffer | BufferReader): UtilityFunctionWithMembershipProof {
|
|
170
|
-
const reader = BufferReader.asReader(buffer);
|
|
171
|
-
return {
|
|
172
|
-
selector: reader.readObject(FunctionSelector),
|
|
173
|
-
bytecode: reader.readBuffer(),
|
|
174
|
-
functionMetadataHash: reader.readObject(Fr),
|
|
175
|
-
artifactMetadataHash: reader.readObject(Fr),
|
|
176
|
-
privateFunctionsArtifactTreeRoot: reader.readObject(Fr),
|
|
177
|
-
artifactTreeSiblingPath: reader.readVector(Fr),
|
|
178
|
-
artifactTreeLeafIndex: reader.readNumber(),
|
|
179
|
-
};
|
|
180
|
-
}
|
|
@@ -27,11 +27,14 @@ export class ContractInstanceStore {
|
|
|
27
27
|
|
|
28
28
|
addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise<void> {
|
|
29
29
|
return this.db.transactionAsync(async () => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
new
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const key = contractInstance.address.toString();
|
|
31
|
+
if (await this.#contractInstances.hasAsync(key)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
|
|
37
|
+
await this.#contractInstancePublishedAt.set(key, blockNumber);
|
|
35
38
|
});
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
import type { CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
17
17
|
import type {
|
|
18
18
|
ContractClassPublic,
|
|
19
|
+
ContractClassPublicWithCommitment,
|
|
19
20
|
ContractDataSource,
|
|
20
21
|
ContractInstanceUpdateWithAddress,
|
|
21
22
|
ContractInstanceWithAddress,
|
|
22
|
-
ExecutablePrivateFunctionWithMembershipProof,
|
|
23
|
-
UtilityFunctionWithMembershipProof,
|
|
24
23
|
} from '@aztec/stdlib/contract';
|
|
24
|
+
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
25
25
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
26
26
|
import type { LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
27
27
|
import type { BlockHeader, TxHash, TxReceipt } from '@aztec/stdlib/tx';
|
|
@@ -36,7 +36,7 @@ import { ContractInstanceStore } from './contract_instance_store.js';
|
|
|
36
36
|
import { LogStore } from './log_store.js';
|
|
37
37
|
import { MessageStore } from './message_store.js';
|
|
38
38
|
|
|
39
|
-
export const ARCHIVER_DB_VERSION =
|
|
39
|
+
export const ARCHIVER_DB_VERSION = 6;
|
|
40
40
|
export const MAX_FUNCTION_SIGNATURES = 1000;
|
|
41
41
|
export const MAX_FUNCTION_NAME_LEN = 256;
|
|
42
42
|
|
|
@@ -166,19 +166,14 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
166
166
|
|
|
167
167
|
/**
|
|
168
168
|
* Add new contract classes from an L2 block to the store's list.
|
|
169
|
-
* @param data - List of contract classes to be added.
|
|
170
|
-
* @param bytecodeCommitments - Bytecode commitments for the contract classes.
|
|
169
|
+
* @param data - List of contract classes (with bytecode commitments) to be added.
|
|
171
170
|
* @param blockNumber - Number of the L2 block the contracts were registered in.
|
|
172
171
|
* @returns True if the operation is successful.
|
|
173
172
|
*/
|
|
174
|
-
async addContractClasses(
|
|
175
|
-
data: ContractClassPublic[],
|
|
176
|
-
bytecodeCommitments: Fr[],
|
|
177
|
-
blockNumber: BlockNumber,
|
|
178
|
-
): Promise<boolean> {
|
|
173
|
+
async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: BlockNumber): Promise<boolean> {
|
|
179
174
|
return (
|
|
180
175
|
await Promise.all(
|
|
181
|
-
data.map(
|
|
176
|
+
data.map(c => this.#contractClassStore.addContractClass(c, c.publicBytecodeCommitment, blockNumber)),
|
|
182
177
|
)
|
|
183
178
|
).every(Boolean);
|
|
184
179
|
}
|
|
@@ -193,15 +188,6 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
193
188
|
return this.#contractClassStore.getBytecodeCommitment(contractClassId);
|
|
194
189
|
}
|
|
195
190
|
|
|
196
|
-
/** Adds private functions to a contract class. */
|
|
197
|
-
addFunctions(
|
|
198
|
-
contractClassId: Fr,
|
|
199
|
-
privateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
|
|
200
|
-
utilityFunctions: UtilityFunctionWithMembershipProof[],
|
|
201
|
-
): Promise<boolean> {
|
|
202
|
-
return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, utilityFunctions);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
191
|
/**
|
|
206
192
|
* Add new contract instances from an L2 block to the store's list.
|
|
207
193
|
* @param data - List of contract instances to be added.
|
|
@@ -408,8 +394,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
408
394
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
409
395
|
* @returns The requested tx receipt (or undefined if not found).
|
|
410
396
|
*/
|
|
411
|
-
getSettledTxReceipt(
|
|
412
|
-
|
|
397
|
+
getSettledTxReceipt(
|
|
398
|
+
txHash: TxHash,
|
|
399
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
400
|
+
): Promise<TxReceipt | undefined> {
|
|
401
|
+
return this.#blockStore.getSettledTxReceipt(txHash, l1Constants);
|
|
413
402
|
}
|
|
414
403
|
|
|
415
404
|
/**
|
|
@@ -470,10 +459,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
470
459
|
* array implies no logs match that tag.
|
|
471
460
|
* @param tags - The tags to search for.
|
|
472
461
|
* @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
|
|
462
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
473
463
|
*/
|
|
474
|
-
getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
|
|
464
|
+
getPrivateLogsByTags(tags: SiloedTag[], page?: number, upToBlockNumber?: BlockNumber): Promise<TxScopedL2Log[][]> {
|
|
475
465
|
try {
|
|
476
|
-
return this.#logStore.getPrivateLogsByTags(tags, page);
|
|
466
|
+
return this.#logStore.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
477
467
|
} catch (err) {
|
|
478
468
|
return Promise.reject(err);
|
|
479
469
|
}
|
|
@@ -485,14 +475,16 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
485
475
|
* @param contractAddress - The contract address to search logs for.
|
|
486
476
|
* @param tags - The tags to search for.
|
|
487
477
|
* @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
|
|
478
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
488
479
|
*/
|
|
489
480
|
getPublicLogsByTagsFromContract(
|
|
490
481
|
contractAddress: AztecAddress,
|
|
491
482
|
tags: Tag[],
|
|
492
483
|
page?: number,
|
|
484
|
+
upToBlockNumber?: BlockNumber,
|
|
493
485
|
): Promise<TxScopedL2Log[][]> {
|
|
494
486
|
try {
|
|
495
|
-
return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
487
|
+
return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
496
488
|
} catch (err) {
|
|
497
489
|
return Promise.reject(err);
|
|
498
490
|
}
|
|
@@ -599,6 +591,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
599
591
|
return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
600
592
|
}
|
|
601
593
|
|
|
594
|
+
/** Persists the inbox tree-in-progress checkpoint number from L1 state. */
|
|
595
|
+
public setInboxTreeInProgress(value: bigint): Promise<void> {
|
|
596
|
+
return this.#messageStore.setInboxTreeInProgress(value);
|
|
597
|
+
}
|
|
598
|
+
|
|
602
599
|
/** Returns an async iterator to all L1 to L2 messages on the range. */
|
|
603
600
|
public iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
|
|
604
601
|
return this.#messageStore.iterateL1ToL2Messages(range);
|
package/src/store/log_store.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
2
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { filterAsync } from '@aztec/foundation/collection';
|
|
3
|
+
import { compactArray, filterAsync } from '@aztec/foundation/collection';
|
|
4
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
6
|
import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from '@aztec/stdlib/logs';
|
|
23
23
|
import { TxHash } from '@aztec/stdlib/tx';
|
|
24
24
|
|
|
25
|
+
import { OutOfOrderLogInsertionError } from '../errors.js';
|
|
25
26
|
import type { BlockStore } from './block_store.js';
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -165,10 +166,21 @@ export class LogStore {
|
|
|
165
166
|
|
|
166
167
|
for (const taggedLogBuffer of currentPrivateTaggedLogs) {
|
|
167
168
|
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
168
|
-
privateTaggedLogs.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
const newLogs = privateTaggedLogs.get(taggedLogBuffer.tag)!;
|
|
170
|
+
if (newLogs.length === 0) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
|
|
174
|
+
const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
|
|
175
|
+
if (lastExisting.blockNumber > firstNew.blockNumber) {
|
|
176
|
+
throw new OutOfOrderLogInsertionError(
|
|
177
|
+
'private',
|
|
178
|
+
taggedLogBuffer.tag,
|
|
179
|
+
lastExisting.blockNumber,
|
|
180
|
+
firstNew.blockNumber,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
privateTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
|
|
172
184
|
}
|
|
173
185
|
}
|
|
174
186
|
|
|
@@ -200,10 +212,21 @@ export class LogStore {
|
|
|
200
212
|
|
|
201
213
|
for (const taggedLogBuffer of currentPublicTaggedLogs) {
|
|
202
214
|
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
203
|
-
publicTaggedLogs.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
215
|
+
const newLogs = publicTaggedLogs.get(taggedLogBuffer.tag)!;
|
|
216
|
+
if (newLogs.length === 0) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
|
|
220
|
+
const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
|
|
221
|
+
if (lastExisting.blockNumber > firstNew.blockNumber) {
|
|
222
|
+
throw new OutOfOrderLogInsertionError(
|
|
223
|
+
'public',
|
|
224
|
+
taggedLogBuffer.tag,
|
|
225
|
+
lastExisting.blockNumber,
|
|
226
|
+
firstNew.blockNumber,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
publicTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
|
|
207
230
|
}
|
|
208
231
|
}
|
|
209
232
|
|
|
@@ -290,18 +313,49 @@ export class LogStore {
|
|
|
290
313
|
|
|
291
314
|
deleteLogs(blocks: L2Block[]): Promise<boolean> {
|
|
292
315
|
return this.db.transactionAsync(async () => {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// Delete public logs
|
|
300
|
-
const publicKeys = (await this.#publicLogKeysByBlock.getAsync(block.number)) ?? [];
|
|
301
|
-
await Promise.all(publicKeys.map(key => this.#publicLogsByContractAndTag.delete(key)));
|
|
302
|
-
}),
|
|
316
|
+
const blockNumbers = new Set(blocks.map(block => block.number));
|
|
317
|
+
const firstBlockToDelete = Math.min(...blockNumbers);
|
|
318
|
+
|
|
319
|
+
// Collect all unique private tags across all blocks being deleted
|
|
320
|
+
const allPrivateTags = new Set(
|
|
321
|
+
compactArray(await Promise.all(blocks.map(block => this.#privateLogKeysByBlock.getAsync(block.number)))).flat(),
|
|
303
322
|
);
|
|
304
323
|
|
|
324
|
+
// Trim private logs: for each tag, delete all instances including and after the first block being deleted.
|
|
325
|
+
// This hinges on the invariant that logs for a given tag are always inserted in order of block number, which is enforced in #addPrivateLogs.
|
|
326
|
+
for (const tag of allPrivateTags) {
|
|
327
|
+
const existing = await this.#privateLogsByTag.getAsync(tag);
|
|
328
|
+
if (existing === undefined || existing.length === 0) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const lastIndexToKeep = existing.findLastIndex(
|
|
332
|
+
buf => TxScopedL2Log.getBlockNumberFromBuffer(buf) < firstBlockToDelete,
|
|
333
|
+
);
|
|
334
|
+
const remaining = existing.slice(0, lastIndexToKeep + 1);
|
|
335
|
+
await (remaining.length > 0 ? this.#privateLogsByTag.set(tag, remaining) : this.#privateLogsByTag.delete(tag));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Collect all unique public keys across all blocks being deleted
|
|
339
|
+
const allPublicKeys = new Set(
|
|
340
|
+
compactArray(await Promise.all(blocks.map(block => this.#publicLogKeysByBlock.getAsync(block.number)))).flat(),
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
// And do the same as we did with private logs
|
|
344
|
+
for (const key of allPublicKeys) {
|
|
345
|
+
const existing = await this.#publicLogsByContractAndTag.getAsync(key);
|
|
346
|
+
if (existing === undefined || existing.length === 0) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const lastIndexToKeep = existing.findLastIndex(
|
|
350
|
+
buf => TxScopedL2Log.getBlockNumberFromBuffer(buf) < firstBlockToDelete,
|
|
351
|
+
);
|
|
352
|
+
const remaining = existing.slice(0, lastIndexToKeep + 1);
|
|
353
|
+
await (remaining.length > 0
|
|
354
|
+
? this.#publicLogsByContractAndTag.set(key, remaining)
|
|
355
|
+
: this.#publicLogsByContractAndTag.delete(key));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// After trimming the tagged logs, we can delete the block-level keys that track which tags are in which blocks.
|
|
305
359
|
await Promise.all(
|
|
306
360
|
blocks.map(block =>
|
|
307
361
|
Promise.all([
|
|
@@ -322,17 +376,30 @@ export class LogStore {
|
|
|
322
376
|
* array implies no logs match that tag.
|
|
323
377
|
* @param tags - The tags to search for.
|
|
324
378
|
* @param page - The page number (0-indexed) for pagination.
|
|
379
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
325
380
|
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
326
381
|
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
327
382
|
*/
|
|
328
|
-
async getPrivateLogsByTags(
|
|
383
|
+
async getPrivateLogsByTags(
|
|
384
|
+
tags: SiloedTag[],
|
|
385
|
+
page: number = 0,
|
|
386
|
+
upToBlockNumber?: BlockNumber,
|
|
387
|
+
): Promise<TxScopedL2Log[][]> {
|
|
329
388
|
const logs = await Promise.all(tags.map(tag => this.#privateLogsByTag.getAsync(tag.toString())));
|
|
389
|
+
|
|
330
390
|
const start = page * MAX_LOGS_PER_TAG;
|
|
331
391
|
const end = start + MAX_LOGS_PER_TAG;
|
|
332
392
|
|
|
333
|
-
return logs.map(
|
|
334
|
-
|
|
335
|
-
|
|
393
|
+
return logs.map(logBuffers => {
|
|
394
|
+
const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
|
|
395
|
+
if (upToBlockNumber !== undefined) {
|
|
396
|
+
const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
|
|
397
|
+
if (cutoff !== -1) {
|
|
398
|
+
return deserialized.slice(0, cutoff);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return deserialized;
|
|
402
|
+
});
|
|
336
403
|
}
|
|
337
404
|
|
|
338
405
|
/**
|
|
@@ -341,6 +408,7 @@ export class LogStore {
|
|
|
341
408
|
* @param contractAddress - The contract address to search logs for.
|
|
342
409
|
* @param tags - The tags to search for.
|
|
343
410
|
* @param page - The page number (0-indexed) for pagination.
|
|
411
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
344
412
|
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
345
413
|
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
346
414
|
*/
|
|
@@ -348,6 +416,7 @@ export class LogStore {
|
|
|
348
416
|
contractAddress: AztecAddress,
|
|
349
417
|
tags: Tag[],
|
|
350
418
|
page: number = 0,
|
|
419
|
+
upToBlockNumber?: BlockNumber,
|
|
351
420
|
): Promise<TxScopedL2Log[][]> {
|
|
352
421
|
const logs = await Promise.all(
|
|
353
422
|
tags.map(tag => {
|
|
@@ -358,9 +427,16 @@ export class LogStore {
|
|
|
358
427
|
const start = page * MAX_LOGS_PER_TAG;
|
|
359
428
|
const end = start + MAX_LOGS_PER_TAG;
|
|
360
429
|
|
|
361
|
-
return logs.map(
|
|
362
|
-
|
|
363
|
-
|
|
430
|
+
return logs.map(logBuffers => {
|
|
431
|
+
const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
|
|
432
|
+
if (upToBlockNumber !== undefined) {
|
|
433
|
+
const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
|
|
434
|
+
if (cutoff !== -1) {
|
|
435
|
+
return deserialized.slice(0, cutoff);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return deserialized;
|
|
439
|
+
});
|
|
364
440
|
}
|
|
365
441
|
|
|
366
442
|
/**
|
|
@@ -588,11 +664,24 @@ export class LogStore {
|
|
|
588
664
|
txLogs: PublicLog[],
|
|
589
665
|
filter: LogFilter = {},
|
|
590
666
|
): boolean {
|
|
667
|
+
if (filter.fromBlock && blockNumber < filter.fromBlock) {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
if (filter.toBlock && blockNumber >= filter.toBlock) {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
if (filter.txHash && !txHash.equals(filter.txHash)) {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
|
|
591
677
|
let maxLogsHit = false;
|
|
592
678
|
let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
|
|
593
679
|
for (; logIndex < txLogs.length; logIndex++) {
|
|
594
680
|
const log = txLogs[logIndex];
|
|
595
|
-
if (
|
|
681
|
+
if (
|
|
682
|
+
(!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) &&
|
|
683
|
+
(!filter.tag || log.fields[0]?.equals(filter.tag))
|
|
684
|
+
) {
|
|
596
685
|
results.push(
|
|
597
686
|
new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log),
|
|
598
687
|
);
|
|
@@ -616,6 +705,16 @@ export class LogStore {
|
|
|
616
705
|
txLogs: ContractClassLog[],
|
|
617
706
|
filter: LogFilter = {},
|
|
618
707
|
): boolean {
|
|
708
|
+
if (filter.fromBlock && blockNumber < filter.fromBlock) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
if (filter.toBlock && blockNumber >= filter.toBlock) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
if (filter.txHash && !txHash.equals(filter.txHash)) {
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
|
|
619
718
|
let maxLogsHit = false;
|
|
620
719
|
let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
|
|
621
720
|
for (; logIndex < txLogs.length; logIndex++) {
|