@aztec/archiver 5.0.0-private.20260319 → 5.0.0-rc.2
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 +19 -13
- package/dest/archiver.d.ts +71 -15
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +268 -74
- package/dest/config.d.ts +7 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +39 -16
- package/dest/errors.d.ts +44 -10
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +66 -12
- package/dest/factory.d.ts +12 -7
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +40 -25
- package/dest/index.d.ts +11 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +10 -2
- package/dest/l1/calldata_retriever.d.ts +2 -1
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +15 -5
- package/dest/l1/data_retrieval.d.ts +19 -10
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +25 -32
- package/dest/l1/trace_tx.d.ts +12 -66
- package/dest/l1/trace_tx.d.ts.map +1 -1
- package/dest/l1/validate_historical_logs.d.ts +23 -0
- package/dest/l1/validate_historical_logs.d.ts.map +1 -0
- package/dest/l1/validate_historical_logs.js +108 -0
- package/dest/modules/contract_data_source_adapter.d.ts +25 -0
- package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
- package/dest/modules/contract_data_source_adapter.js +32 -0
- package/dest/modules/data_source_base.d.ts +72 -48
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +266 -143
- package/dest/modules/data_store_updater.d.ts +30 -13
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +172 -117
- package/dest/modules/instrumentation.d.ts +7 -2
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +25 -7
- package/dest/modules/l1_synchronizer.d.ts +11 -6
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +437 -217
- package/dest/modules/outbox_trees_resolver.d.ts +62 -0
- package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
- package/dest/modules/outbox_trees_resolver.js +162 -0
- package/dest/modules/validation.d.ts +24 -5
- package/dest/modules/validation.d.ts.map +1 -1
- package/dest/modules/validation.js +40 -15
- package/dest/store/block_store.d.ts +164 -66
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +638 -233
- package/dest/store/contract_class_store.d.ts +17 -4
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +36 -68
- package/dest/store/contract_instance_store.d.ts +28 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +48 -2
- package/dest/store/data_stores.d.ts +68 -0
- package/dest/store/data_stores.d.ts.map +1 -0
- package/dest/store/data_stores.js +54 -0
- package/dest/store/function_names_cache.d.ts +17 -0
- package/dest/store/function_names_cache.d.ts.map +1 -0
- package/dest/store/function_names_cache.js +30 -0
- package/dest/store/l2_tips_cache.d.ts +13 -7
- package/dest/store/l2_tips_cache.d.ts.map +1 -1
- package/dest/store/l2_tips_cache.js +13 -76
- package/dest/store/log_store.d.ts +42 -40
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +257 -480
- package/dest/store/log_store_codec.d.ts +78 -0
- package/dest/store/log_store_codec.d.ts.map +1 -0
- package/dest/store/log_store_codec.js +110 -0
- package/dest/store/message_store.d.ts +9 -3
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +39 -10
- package/dest/test/fake_l1_state.d.ts +15 -3
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +80 -18
- package/dest/test/mock_l2_block_source.d.ts +48 -50
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +223 -179
- package/dest/test/mock_structs.d.ts +1 -1
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +2 -2
- package/dest/test/noop_l1_archiver.d.ts +14 -6
- package/dest/test/noop_l1_archiver.d.ts.map +1 -1
- package/dest/test/noop_l1_archiver.js +25 -9
- package/package.json +14 -14
- package/src/archiver.ts +353 -80
- package/src/config.ts +43 -20
- package/src/errors.ts +103 -18
- package/src/factory.ts +56 -24
- package/src/index.ts +18 -2
- package/src/l1/calldata_retriever.ts +16 -5
- package/src/l1/data_retrieval.ts +38 -46
- package/src/l1/validate_historical_logs.ts +140 -0
- package/src/modules/contract_data_source_adapter.ts +46 -0
- package/src/modules/data_source_base.ts +340 -189
- package/src/modules/data_store_updater.ts +211 -148
- package/src/modules/instrumentation.ts +28 -8
- package/src/modules/l1_synchronizer.ts +583 -255
- package/src/modules/outbox_trees_resolver.ts +199 -0
- package/src/modules/validation.ts +81 -21
- package/src/store/block_store.ts +792 -285
- package/src/store/contract_class_store.ts +43 -103
- package/src/store/contract_instance_store.ts +62 -5
- package/src/store/data_stores.ts +104 -0
- package/src/store/function_names_cache.ts +37 -0
- package/src/store/l2_tips_cache.ts +16 -70
- package/src/store/log_store.ts +296 -652
- package/src/store/log_store_codec.ts +143 -0
- package/src/store/message_store.ts +43 -12
- package/src/structs/inbox_message.ts +1 -1
- package/src/test/fake_l1_state.ts +99 -27
- package/src/test/mock_l2_block_source.ts +279 -233
- package/src/test/mock_structs.ts +5 -2
- package/src/test/noop_l1_archiver.ts +52 -9
- package/dest/store/kv_archiver_store.d.ts +0 -367
- package/dest/store/kv_archiver_store.d.ts.map +0 -1
- package/dest/store/kv_archiver_store.js +0 -481
- package/src/store/kv_archiver_store.ts +0 -697
|
@@ -2,14 +2,12 @@ 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 {
|
|
5
|
+
import { isProtocolContractClass } from '@aztec/protocol-contracts';
|
|
6
6
|
import type {
|
|
7
7
|
ContractClassPublic,
|
|
8
8
|
ContractClassPublicWithBlockNumber,
|
|
9
|
-
|
|
10
|
-
UtilityFunctionWithMembershipProof,
|
|
9
|
+
ContractClassPublicWithCommitment,
|
|
11
10
|
} from '@aztec/stdlib/contract';
|
|
12
|
-
import { Vector } from '@aztec/stdlib/types';
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* LMDB-based contract class storage for the archiver.
|
|
@@ -23,21 +21,58 @@ export class ContractClassStore {
|
|
|
23
21
|
this.#bytecodeCommitments = db.openMap('archiver_bytecode_commitments');
|
|
24
22
|
}
|
|
25
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Adds multiple contract classes to the store.
|
|
26
|
+
* @param data - Contract classes (with bytecode commitments) to add.
|
|
27
|
+
* @param blockNumber - L2 block number where the classes were registered.
|
|
28
|
+
* @returns True if every insert succeeded.
|
|
29
|
+
*/
|
|
30
|
+
async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: number): Promise<boolean> {
|
|
31
|
+
return (await Promise.all(data.map(c => this.addContractClass(c, c.publicBytecodeCommitment, blockNumber)))).every(
|
|
32
|
+
Boolean,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Removes multiple contract classes from the store, but only if they were registered at or after the given block.
|
|
38
|
+
* @param data - Contract classes to delete.
|
|
39
|
+
* @param blockNumber - Lower bound on the block number at which the classes were registered.
|
|
40
|
+
* @returns True if every delete succeeded.
|
|
41
|
+
*/
|
|
42
|
+
async deleteContractClasses(data: ContractClassPublic[], blockNumber: number): Promise<boolean> {
|
|
43
|
+
return (await Promise.all(data.map(c => this.deleteContractClass(c, blockNumber)))).every(Boolean);
|
|
44
|
+
}
|
|
45
|
+
|
|
26
46
|
async addContractClass(
|
|
27
47
|
contractClass: ContractClassPublic,
|
|
28
48
|
bytecodeCommitment: Fr,
|
|
29
49
|
blockNumber: number,
|
|
30
50
|
): Promise<void> {
|
|
31
51
|
await this.db.transactionAsync(async () => {
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
const key = contractClass.id.toString();
|
|
53
|
+
if (await this.#contractClasses.hasAsync(key)) {
|
|
54
|
+
// Protocol contracts are preloaded at block 0, so a later on-chain (re-)publish of a bundled
|
|
55
|
+
// protocol class id is valid and must be a no-op. Keep the existing block-0 entry untouched
|
|
56
|
+
// (do not bump its block number) so it survives reorgs of the publishing block.
|
|
57
|
+
if (isProtocolContractClass(contractClass.id)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
|
|
61
|
+
}
|
|
62
|
+
await this.#contractClasses.set(
|
|
63
|
+
key,
|
|
34
64
|
serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
|
|
35
65
|
);
|
|
36
|
-
await this.#bytecodeCommitments.
|
|
66
|
+
await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer());
|
|
37
67
|
});
|
|
38
68
|
}
|
|
39
69
|
|
|
40
|
-
async
|
|
70
|
+
async deleteContractClass(contractClass: ContractClassPublic, blockNumber: number): Promise<void> {
|
|
71
|
+
// Protocol contracts are preloaded at block 0 and must never be deleted, even when the block that
|
|
72
|
+
// (re-)published them on-chain is unwound by a reorg.
|
|
73
|
+
if (isProtocolContractClass(contractClass.id)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
41
76
|
const restoredContractClass = await this.#contractClasses.getAsync(contractClass.id.toString());
|
|
42
77
|
if (restoredContractClass && deserializeContractClassPublic(restoredContractClass).l2BlockNumber >= blockNumber) {
|
|
43
78
|
await this.db.transactionAsync(async () => {
|
|
@@ -60,37 +95,6 @@ export class ContractClassStore {
|
|
|
60
95
|
async getContractClassIds(): Promise<Fr[]> {
|
|
61
96
|
return (await toArray(this.#contractClasses.keysAsync())).map(key => Fr.fromHexString(key));
|
|
62
97
|
}
|
|
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
98
|
}
|
|
95
99
|
|
|
96
100
|
function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWithBlockNumber, 'id'>): Buffer {
|
|
@@ -98,83 +102,19 @@ function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWit
|
|
|
98
102
|
contractClass.l2BlockNumber,
|
|
99
103
|
numToUInt8(contractClass.version),
|
|
100
104
|
contractClass.artifactHash,
|
|
101
|
-
contractClass.privateFunctions.length,
|
|
102
|
-
contractClass.privateFunctions.map(serializePrivateFunction),
|
|
103
|
-
contractClass.utilityFunctions.length,
|
|
104
|
-
contractClass.utilityFunctions.map(serializeUtilityFunction),
|
|
105
105
|
contractClass.packedBytecode.length,
|
|
106
106
|
contractClass.packedBytecode,
|
|
107
107
|
contractClass.privateFunctionsRoot,
|
|
108
108
|
);
|
|
109
109
|
}
|
|
110
110
|
|
|
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
111
|
function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPublicWithBlockNumber, 'id'> {
|
|
141
112
|
const reader = BufferReader.asReader(buffer);
|
|
142
113
|
return {
|
|
143
114
|
l2BlockNumber: reader.readNumber(),
|
|
144
115
|
version: reader.readUInt8() as 1,
|
|
145
116
|
artifactHash: reader.readObject(Fr),
|
|
146
|
-
privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }),
|
|
147
|
-
utilityFunctions: reader.readVector({ fromBuffer: deserializeUtilityFunction }),
|
|
148
117
|
packedBytecode: reader.readBuffer(),
|
|
149
118
|
privateFunctionsRoot: reader.readObject(Fr),
|
|
150
119
|
};
|
|
151
120
|
}
|
|
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
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
2
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
3
|
+
import { isProtocolContract } from '@aztec/protocol-contracts';
|
|
3
4
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
5
|
import {
|
|
5
6
|
type ContractInstanceUpdateWithAddress,
|
|
@@ -25,17 +26,73 @@ export class ContractInstanceStore {
|
|
|
25
26
|
this.#contractInstanceUpdates = db.openMap('archiver_contract_instance_updates');
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Adds multiple contract instances to the store.
|
|
31
|
+
* @param data - Contract instances to add.
|
|
32
|
+
* @param blockNumber - L2 block number where the instances were deployed.
|
|
33
|
+
* @returns True if every insert succeeded.
|
|
34
|
+
*/
|
|
35
|
+
async addContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise<boolean> {
|
|
36
|
+
return (await Promise.all(data.map(c => this.addContractInstance(c, blockNumber)))).every(Boolean);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Removes multiple contract instances from the store.
|
|
41
|
+
* @param data - Contract instances to delete.
|
|
42
|
+
* @returns True if every delete succeeded.
|
|
43
|
+
*/
|
|
44
|
+
async deleteContractInstances(data: ContractInstanceWithAddress[]): Promise<boolean> {
|
|
45
|
+
return (await Promise.all(data.map(c => this.deleteContractInstance(c)))).every(Boolean);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Adds multiple contract instance updates to the store.
|
|
50
|
+
* @param data - Contract instance updates to add.
|
|
51
|
+
* @param timestamp - Timestamp at which the updates were scheduled.
|
|
52
|
+
* @returns True if every insert succeeded.
|
|
53
|
+
*/
|
|
54
|
+
async addContractInstanceUpdates(data: ContractInstanceUpdateWithAddress[], timestamp: UInt64): Promise<boolean> {
|
|
55
|
+
return (
|
|
56
|
+
await Promise.all(data.map((update, logIndex) => this.addContractInstanceUpdate(update, timestamp, logIndex)))
|
|
57
|
+
).every(Boolean);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Removes multiple contract instance updates from the store.
|
|
62
|
+
* @param data - Contract instance updates to delete.
|
|
63
|
+
* @param timestamp - Timestamp at which the updates were scheduled.
|
|
64
|
+
* @returns True if every delete succeeded.
|
|
65
|
+
*/
|
|
66
|
+
async deleteContractInstanceUpdates(data: ContractInstanceUpdateWithAddress[], timestamp: UInt64): Promise<boolean> {
|
|
67
|
+
return (
|
|
68
|
+
await Promise.all(data.map((update, logIndex) => this.deleteContractInstanceUpdate(update, timestamp, logIndex)))
|
|
69
|
+
).every(Boolean);
|
|
70
|
+
}
|
|
71
|
+
|
|
28
72
|
addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise<void> {
|
|
29
73
|
return this.db.transactionAsync(async () => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
74
|
+
const key = contractInstance.address.toString();
|
|
75
|
+
if (await this.#contractInstances.hasAsync(key)) {
|
|
76
|
+
// Protocol contracts are preloaded at block 0, so a later on-chain (re-)publish of a bundled
|
|
77
|
+
// protocol instance is valid and must be a no-op. Keep the existing block-0 entry untouched.
|
|
78
|
+
if (isProtocolContract(contractInstance.address)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
|
|
86
|
+
await this.#contractInstancePublishedAt.set(key, blockNumber);
|
|
35
87
|
});
|
|
36
88
|
}
|
|
37
89
|
|
|
38
90
|
deleteContractInstance(contractInstance: ContractInstanceWithAddress): Promise<void> {
|
|
91
|
+
// Protocol contracts are preloaded at block 0 and must never be deleted, even when the block that
|
|
92
|
+
// (re-)published them on-chain is unwound by a reorg.
|
|
93
|
+
if (isProtocolContract(contractInstance.address)) {
|
|
94
|
+
return Promise.resolve();
|
|
95
|
+
}
|
|
39
96
|
return this.db.transactionAsync(async () => {
|
|
40
97
|
await this.#contractInstances.delete(contractInstance.address.toString());
|
|
41
98
|
await this.#contractInstancePublishedAt.delete(contractInstance.address.toString());
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { L1BlockId } from '@aztec/ethereum/l1-types';
|
|
2
|
+
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
3
|
+
import type { BlockHash } from '@aztec/stdlib/block';
|
|
4
|
+
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
5
|
+
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
|
|
8
|
+
import { ArchiverContractDataSourceAdapter } from '../modules/contract_data_source_adapter.js';
|
|
9
|
+
import { BlockStore } from './block_store.js';
|
|
10
|
+
import { ContractClassStore } from './contract_class_store.js';
|
|
11
|
+
import { ContractInstanceStore } from './contract_instance_store.js';
|
|
12
|
+
import { FunctionNamesCache } from './function_names_cache.js';
|
|
13
|
+
import { LogStore } from './log_store.js';
|
|
14
|
+
import { MessageStore } from './message_store.js';
|
|
15
|
+
|
|
16
|
+
export const ARCHIVER_DB_VERSION = 7;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Represents the latest L1 block processed by the archiver for various objects in L2.
|
|
20
|
+
*/
|
|
21
|
+
export type ArchiverL1SynchPoint = {
|
|
22
|
+
/** Number of the last L1 block that added a new L2 checkpoint metadata. */
|
|
23
|
+
blocksSynchedTo?: bigint;
|
|
24
|
+
/** Last L1 block checked for L1 to L2 messages. */
|
|
25
|
+
messagesSynchedTo?: L1BlockId;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Bundle of archiver-owned LMDB substores plus the in-memory caches that span them.
|
|
30
|
+
*
|
|
31
|
+
* Replaces the former `KVArchiverDataStore` pass-through wrapper. Callers reach into
|
|
32
|
+
* the relevant substore directly (e.g. `stores.blocks.getBlock`) and use
|
|
33
|
+
* {@link createArchiverDataStores} to wire them up against a shared KV store.
|
|
34
|
+
*/
|
|
35
|
+
export type ArchiverDataStores = {
|
|
36
|
+
/** The underlying key-value store. Use {@link AztecAsyncKVStore.transactionAsync} to compose updates atomically. */
|
|
37
|
+
db: AztecAsyncKVStore;
|
|
38
|
+
/** Blocks, checkpoints, tx effects, proven/finalized state. */
|
|
39
|
+
blocks: BlockStore;
|
|
40
|
+
/** Public, private and contract class logs. */
|
|
41
|
+
logs: LogStore;
|
|
42
|
+
/** L1 to L2 messages and message sync state. */
|
|
43
|
+
messages: MessageStore;
|
|
44
|
+
/** Contract classes (with bytecode commitments). */
|
|
45
|
+
contractClasses: ContractClassStore;
|
|
46
|
+
/** Contract instances and contract instance updates. */
|
|
47
|
+
contractInstances: ContractInstanceStore;
|
|
48
|
+
/** In-memory cache of public function selectors -> names. */
|
|
49
|
+
functionNames: FunctionNamesCache;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Wires up the archiver substores against a shared KV store and returns the
|
|
54
|
+
* {@link ArchiverDataStores} bundle.
|
|
55
|
+
*
|
|
56
|
+
* @param genesisBlockHash - Hash of the synthetic genesis block, forwarded to the {@link LogStore} so it
|
|
57
|
+
* can resolve a genesis `referenceBlock` (used by the PXE during early sync) instead of treating it as a
|
|
58
|
+
* reorg.
|
|
59
|
+
*/
|
|
60
|
+
export function createArchiverDataStores(db: AztecAsyncKVStore, genesisBlockHash: BlockHash): ArchiverDataStores {
|
|
61
|
+
const blocks = new BlockStore(db);
|
|
62
|
+
return {
|
|
63
|
+
db,
|
|
64
|
+
blocks,
|
|
65
|
+
logs: new LogStore(db, blocks, genesisBlockHash),
|
|
66
|
+
messages: new MessageStore(db),
|
|
67
|
+
contractClasses: new ContractClassStore(db),
|
|
68
|
+
contractInstances: new ContractInstanceStore(db),
|
|
69
|
+
functionNames: new FunctionNamesCache(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Returns the L1 sync point of the archiver, combining the block sync point from {@link BlockStore}
|
|
75
|
+
* and the message sync point from {@link MessageStore}.
|
|
76
|
+
*/
|
|
77
|
+
export async function getArchiverSynchPoint(stores: ArchiverDataStores): Promise<ArchiverL1SynchPoint> {
|
|
78
|
+
const [blocksSynchedTo, messagesSynchedTo] = await Promise.all([
|
|
79
|
+
stores.blocks.getSynchedL1BlockNumber(),
|
|
80
|
+
stores.messages.getSynchedL1Block(),
|
|
81
|
+
]);
|
|
82
|
+
return { blocksSynchedTo, messagesSynchedTo };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Backs up the underlying KV store to the given folder. Returns the path to the resulting db file.
|
|
87
|
+
*/
|
|
88
|
+
export async function backupArchiverDataStores(
|
|
89
|
+
stores: ArchiverDataStores,
|
|
90
|
+
path: string,
|
|
91
|
+
compress = true,
|
|
92
|
+
): Promise<string> {
|
|
93
|
+
await stores.db.backupTo(path, compress);
|
|
94
|
+
return join(path, 'data.mdb');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Returns a {@link ContractDataSource} adapter over {@link ArchiverDataStores}.
|
|
99
|
+
* Used by contexts (e.g. offline epoch re-prover tools) that need a ContractDataSource
|
|
100
|
+
* but do not need a full archiver instance.
|
|
101
|
+
*/
|
|
102
|
+
export function createContractDataSource(stores: ArchiverDataStores): ContractDataSource {
|
|
103
|
+
return new ArchiverContractDataSourceAdapter(stores);
|
|
104
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { FunctionSelector } from '@aztec/stdlib/abi';
|
|
3
|
+
|
|
4
|
+
const MAX_FUNCTION_SIGNATURES = 1000;
|
|
5
|
+
const MAX_FUNCTION_NAME_LEN = 256;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* In-memory cache mapping public function selectors to function names.
|
|
9
|
+
*
|
|
10
|
+
* Populated opportunistically (e.g. by PXE registering signatures from artifacts) so the
|
|
11
|
+
* archiver can attach human-readable names to logs and traces. Bounded by
|
|
12
|
+
* {@link MAX_FUNCTION_SIGNATURES} to avoid unbounded growth from untrusted callers.
|
|
13
|
+
*/
|
|
14
|
+
export class FunctionNamesCache {
|
|
15
|
+
private readonly log = createLogger('archiver:data-stores');
|
|
16
|
+
private readonly names: Map<string, string> = new Map();
|
|
17
|
+
|
|
18
|
+
/** Adds the given public function signatures to the cache. */
|
|
19
|
+
public async register(signatures: string[]): Promise<void> {
|
|
20
|
+
for (const sig of signatures) {
|
|
21
|
+
if (this.names.size > MAX_FUNCTION_SIGNATURES) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const selector = await FunctionSelector.fromSignature(sig);
|
|
26
|
+
this.names.set(selector.toString(), sig.slice(0, sig.indexOf('(')).slice(0, MAX_FUNCTION_NAME_LEN));
|
|
27
|
+
} catch {
|
|
28
|
+
this.log.warn(`Failed to parse signature: ${sig}. Ignoring`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Looks up a function name for the given selector, or returns undefined if not registered. */
|
|
34
|
+
public get(selector: FunctionSelector): string | undefined {
|
|
35
|
+
return this.names.get(selector.toString());
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,89 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { type BlockData, type CheckpointId, GENESIS_CHECKPOINT_HEADER_HASH, type L2Tips } from '@aztec/stdlib/block';
|
|
1
|
+
import type { BlockHash, L2Tips } from '@aztec/stdlib/block';
|
|
4
2
|
|
|
5
3
|
import type { BlockStore } from './block_store.js';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* In-memory cache for L2 chain tips (proposed, checkpointed, proven, finalized).
|
|
9
7
|
* Populated from the BlockStore on first access, then kept up-to-date by the ArchiverDataStoreUpdater.
|
|
10
|
-
* Refresh calls should happen
|
|
8
|
+
* Refresh calls should happen *after* the store transaction that mutates block data has committed,
|
|
9
|
+
* so the cache loads from committed state and is never replaced if the writer aborts.
|
|
11
10
|
*/
|
|
12
11
|
export class L2TipsCache {
|
|
13
12
|
#tipsPromise: Promise<L2Tips> | undefined;
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The genesis block hash is dynamic — derived from the injected initial header, which depends on
|
|
16
|
+
* `genesisTimestamp` and any prefilled state — so it is supplied here rather than read from store.
|
|
17
|
+
* The genesis checkpoint hash, by contrast, is the static protocol constant and is resolved
|
|
18
|
+
* inside the block store.
|
|
19
|
+
*/
|
|
20
|
+
constructor(
|
|
21
|
+
private blockStore: BlockStore,
|
|
22
|
+
private readonly initialBlockHash: BlockHash,
|
|
23
|
+
) {}
|
|
16
24
|
|
|
17
25
|
/** Returns the cached L2 tips. Loads from the block store on first call. */
|
|
18
26
|
public getL2Tips(): Promise<L2Tips> {
|
|
19
|
-
return (this.#tipsPromise ??= this.
|
|
27
|
+
return (this.#tipsPromise ??= this.blockStore.getL2TipsData(this.initialBlockHash));
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
/** Reloads the L2 tips from the block store. Should be called
|
|
30
|
+
/** Reloads the L2 tips from the block store. Should be called after the writer transaction has committed. */
|
|
23
31
|
public async refresh(): Promise<void> {
|
|
24
|
-
this.#tipsPromise = this.
|
|
32
|
+
this.#tipsPromise = this.blockStore.getL2TipsData(this.initialBlockHash);
|
|
25
33
|
await this.#tipsPromise;
|
|
26
34
|
}
|
|
27
|
-
|
|
28
|
-
private async loadFromStore(): Promise<L2Tips> {
|
|
29
|
-
const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
|
|
30
|
-
this.blockStore.getLatestBlockNumber(),
|
|
31
|
-
this.blockStore.getProvenBlockNumber(),
|
|
32
|
-
this.blockStore.getCheckpointedL2BlockNumber(),
|
|
33
|
-
this.blockStore.getFinalizedL2BlockNumber(),
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
const genesisBlockHeader = {
|
|
37
|
-
blockHash: GENESIS_BLOCK_HEADER_HASH,
|
|
38
|
-
checkpointNumber: CheckpointNumber.ZERO,
|
|
39
|
-
} as const;
|
|
40
|
-
const beforeInitialBlockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
41
|
-
|
|
42
|
-
const getBlockData = (blockNumber: BlockNumber) =>
|
|
43
|
-
blockNumber > beforeInitialBlockNumber ? this.blockStore.getBlockData(blockNumber) : genesisBlockHeader;
|
|
44
|
-
|
|
45
|
-
const [latestBlockData, provenBlockData, checkpointedBlockData, finalizedBlockData] = await Promise.all(
|
|
46
|
-
[latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber].map(getBlockData),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
if (!latestBlockData || !provenBlockData || !finalizedBlockData || !checkpointedBlockData) {
|
|
50
|
-
throw new Error('Failed to load block data for L2 tips');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const [provenCheckpointId, finalizedCheckpointId, checkpointedCheckpointId] = await Promise.all([
|
|
54
|
-
this.getCheckpointIdForBlock(provenBlockData),
|
|
55
|
-
this.getCheckpointIdForBlock(finalizedBlockData),
|
|
56
|
-
this.getCheckpointIdForBlock(checkpointedBlockData),
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
proposed: { number: latestBlockNumber, hash: latestBlockData.blockHash.toString() },
|
|
61
|
-
proven: {
|
|
62
|
-
block: { number: provenBlockNumber, hash: provenBlockData.blockHash.toString() },
|
|
63
|
-
checkpoint: provenCheckpointId,
|
|
64
|
-
},
|
|
65
|
-
finalized: {
|
|
66
|
-
block: { number: finalizedBlockNumber, hash: finalizedBlockData.blockHash.toString() },
|
|
67
|
-
checkpoint: finalizedCheckpointId,
|
|
68
|
-
},
|
|
69
|
-
checkpointed: {
|
|
70
|
-
block: { number: checkpointedBlockNumber, hash: checkpointedBlockData.blockHash.toString() },
|
|
71
|
-
checkpoint: checkpointedCheckpointId,
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private async getCheckpointIdForBlock(blockData: Pick<BlockData, 'checkpointNumber'>): Promise<CheckpointId> {
|
|
77
|
-
const checkpointData = await this.blockStore.getCheckpointData(blockData.checkpointNumber);
|
|
78
|
-
if (!checkpointData) {
|
|
79
|
-
return {
|
|
80
|
-
number: CheckpointNumber.ZERO,
|
|
81
|
-
hash: GENESIS_CHECKPOINT_HEADER_HASH.toString(),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
number: checkpointData.checkpointNumber,
|
|
86
|
-
hash: checkpointData.header.hash().toString(),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
35
|
}
|