@aztec/aztec-node 0.0.1-commit.b655e406 → 0.0.1-commit.b8a057fa
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/aztec-node/block_response_helpers.d.ts +25 -0
- package/dest/aztec-node/block_response_helpers.d.ts.map +1 -0
- package/dest/aztec-node/block_response_helpers.js +112 -0
- package/dest/aztec-node/config.d.ts +26 -8
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +32 -8
- package/dest/aztec-node/node_metrics.d.ts +1 -1
- package/dest/aztec-node/node_metrics.d.ts.map +1 -1
- package/dest/aztec-node/node_metrics.js +9 -16
- package/dest/aztec-node/node_public_calls_simulator.d.ts +97 -0
- package/dest/aztec-node/node_public_calls_simulator.d.ts.map +1 -0
- package/dest/aztec-node/node_public_calls_simulator.js +351 -0
- package/dest/aztec-node/public_data_overrides.d.ts +13 -0
- package/dest/aztec-node/public_data_overrides.d.ts.map +1 -0
- package/dest/aztec-node/public_data_overrides.js +21 -0
- package/dest/aztec-node/register_node_rpc_handlers.d.ts +10 -0
- package/dest/aztec-node/register_node_rpc_handlers.d.ts.map +1 -0
- package/dest/aztec-node/register_node_rpc_handlers.js +31 -0
- package/dest/aztec-node/server.d.ts +163 -225
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +897 -703
- package/dest/bin/index.d.ts +1 -1
- package/dest/bin/index.js +15 -10
- package/dest/factory.d.ts +33 -0
- package/dest/factory.d.ts.map +1 -0
- package/dest/factory.js +523 -0
- package/dest/index.d.ts +3 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -0
- package/dest/modules/block_parameter.d.ts +25 -0
- package/dest/modules/block_parameter.d.ts.map +1 -0
- package/dest/modules/block_parameter.js +100 -0
- package/dest/modules/node_block_provider.d.ts +19 -0
- package/dest/modules/node_block_provider.d.ts.map +1 -0
- package/dest/modules/node_block_provider.js +112 -0
- package/dest/modules/node_tx_receipt.d.ts +24 -0
- package/dest/modules/node_tx_receipt.d.ts.map +1 -0
- package/dest/modules/node_tx_receipt.js +70 -0
- package/dest/modules/node_world_state_queries.d.ts +65 -0
- package/dest/modules/node_world_state_queries.d.ts.map +1 -0
- package/dest/modules/node_world_state_queries.js +272 -0
- package/dest/sentinel/config.d.ts +3 -2
- package/dest/sentinel/config.d.ts.map +1 -1
- package/dest/sentinel/config.js +15 -5
- package/dest/sentinel/factory.d.ts +6 -4
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/factory.js +13 -6
- package/dest/sentinel/index.d.ts +1 -1
- package/dest/sentinel/sentinel.d.ts +160 -34
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +285 -125
- package/dest/sentinel/store.d.ts +11 -10
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +36 -23
- package/dest/test/index.d.ts +3 -3
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +32 -28
- package/src/aztec-node/block_response_helpers.ts +161 -0
- package/src/aztec-node/config.ts +64 -21
- package/src/aztec-node/node_metrics.ts +6 -17
- package/src/aztec-node/node_public_calls_simulator.ts +394 -0
- package/src/aztec-node/public_data_overrides.ts +35 -0
- package/src/aztec-node/register_node_rpc_handlers.ts +29 -0
- package/src/aztec-node/server.ts +689 -940
- package/src/bin/index.ts +19 -12
- package/src/factory.ts +688 -0
- package/src/index.ts +2 -0
- package/src/modules/block_parameter.ts +93 -0
- package/src/modules/node_block_provider.ts +149 -0
- package/src/modules/node_tx_receipt.ts +115 -0
- package/src/modules/node_world_state_queries.ts +374 -0
- package/src/sentinel/README.md +103 -0
- package/src/sentinel/config.ts +18 -6
- package/src/sentinel/factory.ts +23 -13
- package/src/sentinel/sentinel.ts +358 -157
- package/src/sentinel/store.ts +43 -34
- package/src/test/index.ts +2 -2
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { chunkBy } from '@aztec/foundation/collection';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
6
|
+
import { MembershipWitness } from '@aztec/foundation/trees';
|
|
7
|
+
import { inspectBlockParameter } from '@aztec/stdlib/block';
|
|
8
|
+
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
9
|
+
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
10
|
+
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
11
|
+
import { WorldStateSynchronizerError } from '@aztec/world-state';
|
|
12
|
+
import { normalizeBlockParameter } from './block_parameter.js';
|
|
13
|
+
/** Attempts at resolving a query and syncing world state to it before giving up (see {@link NodeWorldStateQueries.getWorldState}). */ const WORLD_STATE_SYNC_ATTEMPTS = 3;
|
|
14
|
+
/** Delay between world-state sync attempts. */ const WORLD_STATE_SYNC_RETRY_DELAY_MS = 100;
|
|
15
|
+
/**
|
|
16
|
+
* Serves the node's Merkle-tree and membership-witness queries against committed world-state at a
|
|
17
|
+
* requested block. Extracted from `AztecNodeService` so the block-resolution and reorg-aware sync logic
|
|
18
|
+
* can be unit-tested without standing up the whole node, and to keep `server.ts` smaller.
|
|
19
|
+
*/ export class NodeWorldStateQueries {
|
|
20
|
+
worldStateSynchronizer;
|
|
21
|
+
blockSource;
|
|
22
|
+
l1ToL2MessageSource;
|
|
23
|
+
log;
|
|
24
|
+
constructor(deps){
|
|
25
|
+
this.worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
26
|
+
this.blockSource = deps.blockSource;
|
|
27
|
+
this.l1ToL2MessageSource = deps.l1ToL2MessageSource;
|
|
28
|
+
this.log = deps.log ?? createLogger('node:world-state-queries');
|
|
29
|
+
}
|
|
30
|
+
async findLeavesIndexes(referenceBlock, treeId, leafValues) {
|
|
31
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
32
|
+
const maybeIndices = await committedDb.findLeafIndices(treeId, leafValues.map((x)=>x.toBuffer()));
|
|
33
|
+
// Filter out undefined values to query block numbers only for found leaves
|
|
34
|
+
const definedIndices = maybeIndices.filter((x)=>x !== undefined);
|
|
35
|
+
// Now we find the block numbers for the defined indices
|
|
36
|
+
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, definedIndices);
|
|
37
|
+
// Build a map from leaf index to block number
|
|
38
|
+
const indexToBlockNumber = new Map();
|
|
39
|
+
for(let i = 0; i < definedIndices.length; i++){
|
|
40
|
+
const blockNumber = blockNumbers[i];
|
|
41
|
+
if (blockNumber === undefined) {
|
|
42
|
+
throw new Error(`Block number is undefined for leaf index ${definedIndices[i]} in tree ${MerkleTreeId[treeId]}`);
|
|
43
|
+
}
|
|
44
|
+
indexToBlockNumber.set(definedIndices[i], blockNumber);
|
|
45
|
+
}
|
|
46
|
+
// Get unique block numbers in order to optimize num calls to getLeafValue function.
|
|
47
|
+
const uniqueBlockNumbers = [
|
|
48
|
+
...new Set(indexToBlockNumber.values())
|
|
49
|
+
];
|
|
50
|
+
// Now we obtain the block hashes from the archive tree (block number = leaf index in archive tree).
|
|
51
|
+
const blockHashes = await Promise.all(uniqueBlockNumbers.map((blockNumber)=>{
|
|
52
|
+
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
53
|
+
}));
|
|
54
|
+
// Build a map from block number to block hash
|
|
55
|
+
const blockNumberToHash = new Map();
|
|
56
|
+
for(let i = 0; i < uniqueBlockNumbers.length; i++){
|
|
57
|
+
const blockHash = blockHashes[i];
|
|
58
|
+
if (blockHash === undefined) {
|
|
59
|
+
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
60
|
+
}
|
|
61
|
+
blockNumberToHash.set(uniqueBlockNumbers[i], blockHash);
|
|
62
|
+
}
|
|
63
|
+
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
64
|
+
return maybeIndices.map((index)=>{
|
|
65
|
+
if (index === undefined) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const blockNumber = indexToBlockNumber.get(index);
|
|
69
|
+
if (blockNumber === undefined) {
|
|
70
|
+
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
71
|
+
}
|
|
72
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
73
|
+
if (l2BlockHash === undefined) {
|
|
74
|
+
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
l2BlockNumber: blockNumber,
|
|
78
|
+
l2BlockHash,
|
|
79
|
+
data: index
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async getBlockHashMembershipWitness(referenceBlock, blockHash) {
|
|
84
|
+
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
85
|
+
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
86
|
+
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
87
|
+
const referenceBlockNumber = await this.#resolveBlockNumber(referenceBlock);
|
|
88
|
+
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
89
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
93
|
+
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.ARCHIVE, [
|
|
94
|
+
blockHash
|
|
95
|
+
]);
|
|
96
|
+
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
97
|
+
}
|
|
98
|
+
async getNoteHashMembershipWitness(referenceBlock, noteHash) {
|
|
99
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
100
|
+
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.NOTE_HASH_TREE, [
|
|
101
|
+
noteHash
|
|
102
|
+
]);
|
|
103
|
+
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
104
|
+
}
|
|
105
|
+
async getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message) {
|
|
106
|
+
const db = await this.getWorldState(referenceBlock);
|
|
107
|
+
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [
|
|
108
|
+
l1ToL2Message
|
|
109
|
+
]);
|
|
110
|
+
if (!witness) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
// REFACTOR: Return a MembershipWitness object
|
|
114
|
+
return [
|
|
115
|
+
witness.index,
|
|
116
|
+
witness.path
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
async getL1ToL2MessageCheckpoint(l1ToL2Message) {
|
|
120
|
+
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
121
|
+
return messageIndex !== undefined ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns all the L2 to L1 messages in an epoch (empty array if the epoch is not found). The public
|
|
125
|
+
* `AztecNodeService.getL2ToL1Messages` that delegates here is deprecated in favor of
|
|
126
|
+
* {@link getL2ToL1MembershipWitness}.
|
|
127
|
+
* @param epoch - The epoch at which to get the data.
|
|
128
|
+
*/ async getL2ToL1Messages(epoch) {
|
|
129
|
+
const blocks = await this.blockSource.getBlocks({
|
|
130
|
+
epoch,
|
|
131
|
+
onlyCheckpointed: true
|
|
132
|
+
});
|
|
133
|
+
const blocksInCheckpoints = chunkBy(blocks, (block)=>block.header.globalVariables.slotNumber);
|
|
134
|
+
return blocksInCheckpoints.map((slotBlocks)=>slotBlocks.map((block)=>block.body.txEffects.map((txEffect)=>txEffect.l2ToL1Msgs)));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Returns the L2-to-L1 membership witness for a message in `txHash`. Passthrough to the
|
|
138
|
+
* archiver's locally-cached resolver — see {@link Archiver.getL2ToL1MembershipWitness}.
|
|
139
|
+
*/ getL2ToL1MembershipWitness(txHash, message, messageIndexInTx) {
|
|
140
|
+
return this.blockSource.getL2ToL1MembershipWitness(txHash, message, messageIndexInTx);
|
|
141
|
+
}
|
|
142
|
+
async getNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
143
|
+
const db = await this.getWorldState(referenceBlock);
|
|
144
|
+
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [
|
|
145
|
+
nullifier.toBuffer()
|
|
146
|
+
]);
|
|
147
|
+
if (!witness) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const { index, path } = witness;
|
|
151
|
+
const leafPreimage = await db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
|
|
152
|
+
if (!leafPreimage) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
return new NullifierMembershipWitness(index, leafPreimage, path);
|
|
156
|
+
}
|
|
157
|
+
async getLowNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
158
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
159
|
+
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
160
|
+
if (!findResult) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
const { index, alreadyPresent } = findResult;
|
|
164
|
+
if (alreadyPresent) {
|
|
165
|
+
throw new Error(`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`);
|
|
166
|
+
}
|
|
167
|
+
const preimageData = await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
|
|
168
|
+
const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
|
|
169
|
+
return new NullifierMembershipWitness(BigInt(index), preimageData, siblingPath);
|
|
170
|
+
}
|
|
171
|
+
async getPublicDataWitness(referenceBlock, leafSlot) {
|
|
172
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
173
|
+
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
174
|
+
if (!lowLeafResult) {
|
|
175
|
+
return undefined;
|
|
176
|
+
} else {
|
|
177
|
+
const preimage = await committedDb.getLeafPreimage(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
|
|
178
|
+
const path = await committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
|
|
179
|
+
return new PublicDataWitness(lowLeafResult.index, preimage, path);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async getPublicStorageAt(referenceBlock, contract, slot) {
|
|
183
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
184
|
+
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
185
|
+
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
186
|
+
if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
|
|
187
|
+
return Fr.ZERO;
|
|
188
|
+
}
|
|
189
|
+
const preimage = await committedDb.getLeafPreimage(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
|
|
190
|
+
return preimage.leaf.value;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Returns an instance of MerkleTreeOperations having first ensured the world state is synced to the requested
|
|
194
|
+
* block on the correct fork. Every query variant is resolved to a concrete (block number, block hash), which is
|
|
195
|
+
* threaded through both the sync and the snapshot read so a reorg that replaced the block at that height is
|
|
196
|
+
* detected rather than served silently. Transient failures — a prune landing between resolution and sync, or a
|
|
197
|
+
* fork flip caught at either the sync or the snapshot stage — are retried a few times, re-resolving the query
|
|
198
|
+
* against the updated chain each time; terminal failures — an unknown block hash at resolution, or a block whose
|
|
199
|
+
* history world state has pruned away — are thrown immediately.
|
|
200
|
+
* @param block - The block parameter (block number, block hash, or tag) at which to get the data.
|
|
201
|
+
* @returns An instance of a committed MerkleTreeOperations
|
|
202
|
+
*/ async getWorldState(block) {
|
|
203
|
+
const query = normalizeBlockParameter(block);
|
|
204
|
+
for(let attempt = 1;; attempt++){
|
|
205
|
+
try {
|
|
206
|
+
return await this.#resolveWorldState(query);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
if (attempt >= WORLD_STATE_SYNC_ATTEMPTS || !(err instanceof WorldStateSynchronizerError)) {
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
this.log.verbose(`Retrying world state query after sync failure: ${err.message}`, {
|
|
212
|
+
attempt,
|
|
213
|
+
block: inspectBlockParameter(block)
|
|
214
|
+
});
|
|
215
|
+
await sleep(WORLD_STATE_SYNC_RETRY_DELAY_MS);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Resolves `query` to a concrete (block number, block hash), syncs world state to that exact fork, and returns
|
|
221
|
+
* the committed db (for `proposed` queries) or the fork-verified snapshot at the resolved block.
|
|
222
|
+
*/ async #resolveWorldState(query) {
|
|
223
|
+
// User requests 'latest on the current fork', so the committed db is returned unverified
|
|
224
|
+
if ('tag' in query && query.tag === 'proposed') {
|
|
225
|
+
this.log.debug(`Using committed db for latest block`);
|
|
226
|
+
await this.worldStateSynchronizer.syncImmediate();
|
|
227
|
+
return this.worldStateSynchronizer.getCommitted();
|
|
228
|
+
}
|
|
229
|
+
// Resolve the query against the block source BEFORE syncing to a concrete (number, hash), and drive the sync to
|
|
230
|
+
// that exact fork. Resolving after the sync races the block source: the resolved tip can advance past what world
|
|
231
|
+
// state synced while the sync is in flight. Passing the hash makes the sync reorg-aware — it barriers until the
|
|
232
|
+
// archive-tree commit for that block has landed and verifies it matches the requested fork, throwing otherwise.
|
|
233
|
+
const { blockNumber, blockHash } = await this.#resolveBlockNumberAndHash(query);
|
|
234
|
+
const blockSyncedTo = await this.worldStateSynchronizer.syncImmediate(blockNumber, blockHash);
|
|
235
|
+
// The fork could flip between it returning and the snapshot being read, so getVerifiedSnapshot pins the
|
|
236
|
+
// returned view to the resolved fork (re-resolved by the retry loop on mismatch).
|
|
237
|
+
this.log.debug(`Using verified snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
238
|
+
return await this.worldStateSynchronizer.getVerifiedSnapshot(blockNumber, blockHash);
|
|
239
|
+
}
|
|
240
|
+
/** Resolves any {@link BlockParameter} variant to its concrete `(blockNumber, blockHash)` via the block source. */ async #resolveBlockNumberAndHash(query) {
|
|
241
|
+
const blockData = await this.blockSource.getBlockData(query);
|
|
242
|
+
if (blockData === undefined) {
|
|
243
|
+
this.#throwOnUndefinedBlockData(query);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
blockNumber: blockData.header.getBlockNumber(),
|
|
247
|
+
blockHash: blockData.blockHash
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/** Resolves any {@link BlockParameter} variant to a concrete block number. */ async #resolveBlockNumber(block) {
|
|
251
|
+
const blockQuery = normalizeBlockParameter(block);
|
|
252
|
+
const blockNumber = await this.blockSource.getBlockNumber(blockQuery);
|
|
253
|
+
if (blockNumber === undefined) {
|
|
254
|
+
this.#throwOnUndefinedBlockData(blockQuery);
|
|
255
|
+
}
|
|
256
|
+
return blockNumber;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Hash and archive misses are terminal (an unknown block, likely a reorg); tag and number misses are transient —
|
|
260
|
+
* the block may have been pruned between the tag->number resolution and this data read, or may simply not have
|
|
261
|
+
* arrived yet — and surface as {@link WorldStateSynchronizerError} so the retry loop re-resolves against the
|
|
262
|
+
* current chain.
|
|
263
|
+
*/ #throwOnUndefinedBlockData(query) {
|
|
264
|
+
if ('hash' in query) {
|
|
265
|
+
throw new Error(`Block hash ${query.hash.toString()} not found when resolving query. If the node API has been queried with anchor block hash possibly a reorg has occurred.`);
|
|
266
|
+
}
|
|
267
|
+
if ('archive' in query) {
|
|
268
|
+
throw new Error(`Block with archive ${query.archive.toString()} not found when resolving query.`);
|
|
269
|
+
}
|
|
270
|
+
throw new WorldStateSynchronizerError(`Block not found for ${inspectBlockParameter(query)} when resolving query.`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type ConfigMappingsType } from '@aztec/foundation/config';
|
|
2
2
|
export type SentinelConfig = {
|
|
3
3
|
sentinelHistoryLengthInEpochs: number;
|
|
4
|
-
|
|
4
|
+
sentinelHistoricEpochPerformanceLengthInEpochs: number;
|
|
5
5
|
sentinelEnabled: boolean;
|
|
6
|
+
sentinelEpochEndBufferSlots: number;
|
|
6
7
|
};
|
|
7
8
|
export declare const sentinelConfigMappings: ConfigMappingsType<SentinelConfig>;
|
|
8
|
-
//# sourceMappingURL=
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VudGluZWwvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLGtCQUFrQixFQUEyQyxNQUFNLDBCQUEwQixDQUFDO0FBRTVHLE1BQU0sTUFBTSxjQUFjLEdBQUc7SUFDM0IsNkJBQTZCLEVBQUUsTUFBTSxDQUFDO0lBQ3RDLDhDQUE4QyxFQUFFLE1BQU0sQ0FBQztJQUN2RCxlQUFlLEVBQUUsT0FBTyxDQUFDO0lBQ3pCLDJCQUEyQixFQUFFLE1BQU0sQ0FBQztDQUNyQyxDQUFDO0FBRUYsZUFBTyxNQUFNLHNCQUFzQixFQUFFLGtCQUFrQixDQUFDLGNBQWMsQ0F1Q3JFLENBQUMifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/sentinel/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAA2C,MAAM,0BAA0B,CAAC;AAE5G,MAAM,MAAM,cAAc,GAAG;IAC3B,6BAA6B,EAAE,MAAM,CAAC;IACtC
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/sentinel/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAA2C,MAAM,0BAA0B,CAAC;AAE5G,MAAM,MAAM,cAAc,GAAG;IAC3B,6BAA6B,EAAE,MAAM,CAAC;IACtC,8CAA8C,EAAE,MAAM,CAAC;IACvD,eAAe,EAAE,OAAO,CAAC;IACzB,2BAA2B,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,CAAC,cAAc,CAuCrE,CAAC"}
|
package/dest/sentinel/config.js
CHANGED
|
@@ -6,8 +6,9 @@ export const sentinelConfigMappings = {
|
|
|
6
6
|
...numberConfigHelper(24)
|
|
7
7
|
},
|
|
8
8
|
/**
|
|
9
|
-
* The number of L2 epochs kept of
|
|
10
|
-
*
|
|
9
|
+
* The number of L2 epochs kept of per-epoch performance history for each validator. End-of-epoch
|
|
10
|
+
* activity is recorded here and used to decide consecutive-epoch inactivity slashing.
|
|
11
|
+
* This value must be large enough so that we have epoch performance for every validator
|
|
11
12
|
* for at least slashInactivityConsecutiveEpochThreshold. Assuming this value is 3,
|
|
12
13
|
* and the committee size is 48, and we have 10k validators, then we pick 48 out of 10k each draw.
|
|
13
14
|
* For any fixed element, per-draw prob = 48/10000 = 0.0048.
|
|
@@ -16,14 +17,23 @@ export const sentinelConfigMappings = {
|
|
|
16
17
|
* - 90% chance: n = 1108
|
|
17
18
|
* - 95% chance: n = 1310
|
|
18
19
|
* - 99% chance: n = 1749
|
|
19
|
-
*/
|
|
20
|
-
description: 'The number of L2 epochs kept of
|
|
21
|
-
env: '
|
|
20
|
+
*/ sentinelHistoricEpochPerformanceLengthInEpochs: {
|
|
21
|
+
description: 'The number of L2 epochs kept of per-epoch performance history for each validator.',
|
|
22
|
+
env: 'SENTINEL_HISTORIC_EPOCH_PERFORMANCE_LENGTH_IN_EPOCHS',
|
|
22
23
|
...numberConfigHelper(2000)
|
|
23
24
|
},
|
|
24
25
|
sentinelEnabled: {
|
|
25
26
|
description: 'Whether the sentinel is enabled or not.',
|
|
26
27
|
env: 'SENTINEL_ENABLED',
|
|
27
28
|
...booleanConfigHelper(false)
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Number of L2 slots to wait after the end of an epoch before computing the epoch's performance.
|
|
32
|
+
* The buffer allows P2P attestations and the local archiver to settle. Higher values reduce the
|
|
33
|
+
* risk of misjudging late-arriving activity at the cost of delayed slashing.
|
|
34
|
+
*/ sentinelEpochEndBufferSlots: {
|
|
35
|
+
description: 'Number of L2 slots after the end of an epoch before the sentinel evaluates it.',
|
|
36
|
+
env: 'SENTINEL_EPOCH_END_BUFFER_SLOTS',
|
|
37
|
+
...numberConfigHelper(2)
|
|
28
38
|
}
|
|
29
39
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
-
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
3
2
|
import type { P2PClient } from '@aztec/p2p';
|
|
4
3
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
5
|
-
import type {
|
|
4
|
+
import type { CheckpointReexecutionTracker } from '@aztec/stdlib/checkpoint';
|
|
5
|
+
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
6
|
+
import type { SlasherConfig, ValidatorClientConfig } from '@aztec/stdlib/interfaces/server';
|
|
7
|
+
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
6
8
|
import type { SentinelConfig } from './config.js';
|
|
7
9
|
import { Sentinel } from './sentinel.js';
|
|
8
|
-
export declare function createSentinel(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, config: SentinelConfig & DataStoreConfig & SlasherConfig, logger?: import("@aztec/foundation/log").Logger): Promise<Sentinel | undefined>;
|
|
9
|
-
//# sourceMappingURL=
|
|
10
|
+
export declare function createSentinel(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, reexecutionTracker: CheckpointReexecutionTracker, config: SentinelConfig & DataStoreConfig & SlasherConfig & Pick<ChainConfig, 'l1ChainId' | 'rollupAddress'> & Pick<ValidatorClientConfig, 'disableValidator'>, logger?: import("@aztec/foundation/log").Logger): Promise<Sentinel | undefined>;
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlbnRpbmVsL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFHckQsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzVDLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDN0UsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDeEQsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDNUYsT0FBTyxLQUFLLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFOUQsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFHekMsd0JBQXNCLGNBQWMsQ0FDbEMsVUFBVSxFQUFFLFVBQVUsRUFDdEIsUUFBUSxFQUFFLGFBQWEsRUFDdkIsR0FBRyxFQUFFLFNBQVMsRUFDZCxrQkFBa0IsRUFBRSw0QkFBNEIsRUFDaEQsTUFBTSxFQUFFLGNBQWMsR0FDcEIsZUFBZSxHQUNmLGFBQWEsR0FDYixJQUFJLENBQUMsV0FBVyxFQUFFLFdBQVcsR0FBRyxlQUFlLENBQUMsR0FDaEQsSUFBSSxDQUFDLHFCQUFxQixFQUFFLGtCQUFrQixDQUFDLEVBQ2pELE1BQU0seUNBQWdDLEdBQ3JDLE9BQU8sQ0FBQyxRQUFRLEdBQUcsU0FBUyxDQUFDLENBb0IvQiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,SAAS,EACd,kBAAkB,EAAE,4BAA4B,EAChD,MAAM,EAAE,cAAc,GACpB,eAAe,GACf,aAAa,GACb,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,eAAe,CAAC,GAChD,IAAI,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,EACjD,MAAM,yCAAgC,GACrC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAoB/B"}
|
package/dest/sentinel/factory.js
CHANGED
|
@@ -2,16 +2,23 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
2
2
|
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
3
3
|
import { Sentinel } from './sentinel.js';
|
|
4
4
|
import { SentinelStore } from './store.js';
|
|
5
|
-
export async function createSentinel(epochCache, archiver, p2p, config, logger = createLogger('node:sentinel')) {
|
|
6
|
-
|
|
5
|
+
export async function createSentinel(epochCache, archiver, p2p, reexecutionTracker, config, logger = createLogger('node:sentinel')) {
|
|
6
|
+
const runsValidator = !config.disableValidator;
|
|
7
|
+
if (!runsValidator && !config.sentinelEnabled) {
|
|
8
|
+
logger.verbose('Sentinel is disabled');
|
|
7
9
|
return undefined;
|
|
8
10
|
}
|
|
9
|
-
|
|
11
|
+
if (runsValidator) {
|
|
12
|
+
logger.info('Enabling sentinel since this node runs a validator');
|
|
13
|
+
} else {
|
|
14
|
+
logger.info('Enabling sentinel from SENTINEL_ENABLED configuration');
|
|
15
|
+
}
|
|
16
|
+
const kvStore = await createStore('sentinel', SentinelStore.SCHEMA_VERSION, config, logger.getBindings());
|
|
10
17
|
const storeHistoryLength = config.sentinelHistoryLengthInEpochs * epochCache.getL1Constants().epochDuration;
|
|
11
|
-
const
|
|
18
|
+
const storeHistoricEpochPerformanceLength = config.sentinelHistoricEpochPerformanceLengthInEpochs;
|
|
12
19
|
const sentinelStore = new SentinelStore(kvStore, {
|
|
13
20
|
historyLength: storeHistoryLength,
|
|
14
|
-
|
|
21
|
+
historicEpochPerformanceLength: storeHistoricEpochPerformanceLength
|
|
15
22
|
});
|
|
16
|
-
return new Sentinel(epochCache, archiver, p2p, sentinelStore, config, logger);
|
|
23
|
+
return new Sentinel(epochCache, archiver, p2p, sentinelStore, reexecutionTracker, config, logger);
|
|
17
24
|
}
|
package/dest/sentinel/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Sentinel } from './sentinel.js';
|
|
2
2
|
export type { ValidatorsStats, ValidatorStats, ValidatorStatusHistory, ValidatorStatusInSlot, } from '@aztec/stdlib/validators';
|
|
3
|
-
//# sourceMappingURL=
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZW50aW5lbC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXpDLFlBQVksRUFDVixlQUFlLEVBQ2YsY0FBYyxFQUNkLHNCQUFzQixFQUN0QixxQkFBcUIsR0FDdEIsTUFBTSwwQkFBMEIsQ0FBQyJ9
|