@aztec/aztec-node 0.87.1 → 0.87.2-nightly.20250524
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/config.d.ts +2 -1
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +2 -0
- package/dest/aztec-node/server.d.ts +13 -7
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +93 -56
- package/dest/sentinel/factory.d.ts +2 -1
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/factory.js +1 -1
- package/dest/sentinel/sentinel.d.ts +18 -5
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +101 -14
- package/dest/sentinel/store.d.ts +13 -3
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +73 -6
- package/package.json +23 -22
- package/src/aztec-node/config.ts +4 -1
- package/src/aztec-node/server.ts +117 -65
- package/src/sentinel/factory.ts +3 -2
- package/src/sentinel/sentinel.ts +114 -6
- package/src/sentinel/store.ts +83 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sentinel/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sentinel/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,0BAA0B,CAAC;AAIlC,qBAAa,aAAa;IAWtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IAXhB,gBAAuB,cAAc,KAAK;IAG1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuC;IAIlE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;gBAGvD,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE;IAMpC,gBAAgB;IAIV,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B;IAW9E,oBAAoB,CAAC,GAAG,EAAE,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;YAKpG,sCAAsC;IA6BvC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,qBAAqB,GAAG,SAAS,CAAC;YAUhG,0BAA0B;IAU3B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC;YAQrE,UAAU;IAKxB,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,gBAAgB;CAgBzB"}
|
package/dest/sentinel/store.js
CHANGED
|
@@ -1,17 +1,65 @@
|
|
|
1
1
|
import { BufferReader, numToUInt8, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
2
|
+
import { isAddress } from 'viem';
|
|
2
3
|
export class SentinelStore {
|
|
3
4
|
store;
|
|
4
5
|
config;
|
|
5
|
-
static SCHEMA_VERSION =
|
|
6
|
-
map
|
|
6
|
+
static SCHEMA_VERSION = 2;
|
|
7
|
+
// a map from validator address to their ValidatorStatusHistory
|
|
8
|
+
historyMap;
|
|
9
|
+
// a map from validator address to their historical proven epoch performance
|
|
10
|
+
// e.g. { validator: [{ epoch: 1, missed: 1, total: 10 }, { epoch: 2, missed: 3, total: 7 }, ...] }
|
|
11
|
+
provenMap;
|
|
7
12
|
constructor(store, config){
|
|
8
13
|
this.store = store;
|
|
9
14
|
this.config = config;
|
|
10
|
-
this.
|
|
15
|
+
this.historyMap = store.openMap('sentinel-validator-status');
|
|
16
|
+
this.provenMap = store.openMap('sentinel-validator-proven');
|
|
11
17
|
}
|
|
12
18
|
getHistoryLength() {
|
|
13
19
|
return this.config.historyLength;
|
|
14
20
|
}
|
|
21
|
+
async updateProvenPerformance(epoch, performance) {
|
|
22
|
+
await this.store.transactionAsync(async ()=>{
|
|
23
|
+
for (const [who, { missed, total }] of Object.entries(performance)){
|
|
24
|
+
if (!isAddress(who)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
await this.pushValidatorProvenPerformanceForEpoch({
|
|
28
|
+
who,
|
|
29
|
+
missed,
|
|
30
|
+
total,
|
|
31
|
+
epoch
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async getProvenPerformance(who) {
|
|
37
|
+
const currentPerformanceBuffer = await this.provenMap.getAsync(who);
|
|
38
|
+
return currentPerformanceBuffer ? this.deserializePerformance(currentPerformanceBuffer) : [];
|
|
39
|
+
}
|
|
40
|
+
async pushValidatorProvenPerformanceForEpoch({ who, missed, total, epoch }) {
|
|
41
|
+
const currentPerformance = await this.getProvenPerformance(who);
|
|
42
|
+
const existingIndex = currentPerformance.findIndex((p)=>p.epoch === epoch);
|
|
43
|
+
if (existingIndex !== -1) {
|
|
44
|
+
currentPerformance[existingIndex] = {
|
|
45
|
+
missed,
|
|
46
|
+
total,
|
|
47
|
+
epoch
|
|
48
|
+
};
|
|
49
|
+
} else {
|
|
50
|
+
currentPerformance.push({
|
|
51
|
+
missed,
|
|
52
|
+
total,
|
|
53
|
+
epoch
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// This should be sorted by epoch, but just in case.
|
|
57
|
+
// Since we keep the size small, this is not a big deal.
|
|
58
|
+
currentPerformance.sort((a, b)=>Number(a.epoch - b.epoch));
|
|
59
|
+
// keep the most recent `historyLength` entries.
|
|
60
|
+
const performanceToKeep = currentPerformance.slice(-this.config.historyLength);
|
|
61
|
+
await this.provenMap.set(who, this.serializePerformance(performanceToKeep));
|
|
62
|
+
}
|
|
15
63
|
async updateValidators(slot, statuses) {
|
|
16
64
|
await this.store.transactionAsync(async ()=>{
|
|
17
65
|
for (const [who, status] of Object.entries(statuses)){
|
|
@@ -30,19 +78,38 @@ export class SentinelStore {
|
|
|
30
78
|
status
|
|
31
79
|
}
|
|
32
80
|
].slice(-this.config.historyLength);
|
|
33
|
-
await this.
|
|
81
|
+
await this.historyMap.set(who, this.serializeHistory(newHistory));
|
|
34
82
|
}
|
|
35
83
|
async getHistories() {
|
|
36
84
|
const histories = {};
|
|
37
|
-
for await (const [address, history] of this.
|
|
85
|
+
for await (const [address, history] of this.historyMap.entriesAsync()){
|
|
38
86
|
histories[address] = this.deserializeHistory(history);
|
|
39
87
|
}
|
|
40
88
|
return histories;
|
|
41
89
|
}
|
|
42
90
|
async getHistory(address) {
|
|
43
|
-
const data = await this.
|
|
91
|
+
const data = await this.historyMap.getAsync(address);
|
|
44
92
|
return data && this.deserializeHistory(data);
|
|
45
93
|
}
|
|
94
|
+
serializePerformance(performance) {
|
|
95
|
+
return serializeToBuffer(performance.map((p)=>[
|
|
96
|
+
numToUInt32BE(Number(p.epoch)),
|
|
97
|
+
numToUInt32BE(p.missed),
|
|
98
|
+
numToUInt32BE(p.total)
|
|
99
|
+
]));
|
|
100
|
+
}
|
|
101
|
+
deserializePerformance(buffer) {
|
|
102
|
+
const reader = new BufferReader(buffer);
|
|
103
|
+
const performance = [];
|
|
104
|
+
while(!reader.isEmpty()){
|
|
105
|
+
performance.push({
|
|
106
|
+
epoch: BigInt(reader.readNumber()),
|
|
107
|
+
missed: reader.readNumber(),
|
|
108
|
+
total: reader.readNumber()
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return performance;
|
|
112
|
+
}
|
|
46
113
|
serializeHistory(history) {
|
|
47
114
|
return serializeToBuffer(history.map((h)=>[
|
|
48
115
|
numToUInt32BE(Number(h.slot)),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/aztec-node",
|
|
3
|
-
"version": "0.87.
|
|
3
|
+
"version": "0.87.2-nightly.20250524",
|
|
4
4
|
"main": "dest/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -61,27 +61,28 @@
|
|
|
61
61
|
]
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@aztec/archiver": "0.87.
|
|
65
|
-
"@aztec/bb-prover": "0.87.
|
|
66
|
-
"@aztec/blob-sink": "0.87.
|
|
67
|
-
"@aztec/constants": "0.87.
|
|
68
|
-
"@aztec/epoch-cache": "0.87.
|
|
69
|
-
"@aztec/ethereum": "0.87.
|
|
70
|
-
"@aztec/foundation": "0.87.
|
|
71
|
-
"@aztec/kv-store": "0.87.
|
|
72
|
-
"@aztec/l1-artifacts": "0.87.
|
|
73
|
-
"@aztec/merkle-tree": "0.87.
|
|
74
|
-
"@aztec/node-lib": "0.87.
|
|
75
|
-
"@aztec/noir-protocol-circuits-types": "0.87.
|
|
76
|
-
"@aztec/p2p": "0.87.
|
|
77
|
-
"@aztec/protocol-contracts": "0.87.
|
|
78
|
-
"@aztec/prover-client": "0.87.
|
|
79
|
-
"@aztec/sequencer-client": "0.87.
|
|
80
|
-
"@aztec/simulator": "0.87.
|
|
81
|
-
"@aztec/
|
|
82
|
-
"@aztec/
|
|
83
|
-
"@aztec/
|
|
84
|
-
"@aztec/
|
|
64
|
+
"@aztec/archiver": "0.87.2-nightly.20250524",
|
|
65
|
+
"@aztec/bb-prover": "0.87.2-nightly.20250524",
|
|
66
|
+
"@aztec/blob-sink": "0.87.2-nightly.20250524",
|
|
67
|
+
"@aztec/constants": "0.87.2-nightly.20250524",
|
|
68
|
+
"@aztec/epoch-cache": "0.87.2-nightly.20250524",
|
|
69
|
+
"@aztec/ethereum": "0.87.2-nightly.20250524",
|
|
70
|
+
"@aztec/foundation": "0.87.2-nightly.20250524",
|
|
71
|
+
"@aztec/kv-store": "0.87.2-nightly.20250524",
|
|
72
|
+
"@aztec/l1-artifacts": "0.87.2-nightly.20250524",
|
|
73
|
+
"@aztec/merkle-tree": "0.87.2-nightly.20250524",
|
|
74
|
+
"@aztec/node-lib": "0.87.2-nightly.20250524",
|
|
75
|
+
"@aztec/noir-protocol-circuits-types": "0.87.2-nightly.20250524",
|
|
76
|
+
"@aztec/p2p": "0.87.2-nightly.20250524",
|
|
77
|
+
"@aztec/protocol-contracts": "0.87.2-nightly.20250524",
|
|
78
|
+
"@aztec/prover-client": "0.87.2-nightly.20250524",
|
|
79
|
+
"@aztec/sequencer-client": "0.87.2-nightly.20250524",
|
|
80
|
+
"@aztec/simulator": "0.87.2-nightly.20250524",
|
|
81
|
+
"@aztec/slasher": "0.87.2-nightly.20250524",
|
|
82
|
+
"@aztec/stdlib": "0.87.2-nightly.20250524",
|
|
83
|
+
"@aztec/telemetry-client": "0.87.2-nightly.20250524",
|
|
84
|
+
"@aztec/validator-client": "0.87.2-nightly.20250524",
|
|
85
|
+
"@aztec/world-state": "0.87.2-nightly.20250524",
|
|
85
86
|
"koa": "^2.16.1",
|
|
86
87
|
"koa-router": "^12.0.0",
|
|
87
88
|
"tslib": "^2.4.0",
|
package/src/aztec-node/config.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { type SharedNodeConfig, sharedNodeConfigMappings } from '@aztec/node-lib
|
|
|
11
11
|
import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config';
|
|
12
12
|
import { type ProverClientUserConfig, proverClientConfigMappings } from '@aztec/prover-client/config';
|
|
13
13
|
import { type SequencerClientConfig, sequencerClientConfigMappings } from '@aztec/sequencer-client/config';
|
|
14
|
+
import { type SlasherConfig, slasherConfigMappings } from '@aztec/slasher';
|
|
14
15
|
import { type NodeRPCConfig, nodeRpcConfigMappings } from '@aztec/stdlib/config';
|
|
15
16
|
import { type ValidatorClientConfig, validatorClientConfigMappings } from '@aztec/validator-client/config';
|
|
16
17
|
import { type WorldStateConfig, worldStateConfigMappings } from '@aztec/world-state/config';
|
|
@@ -33,7 +34,8 @@ export type AztecNodeConfig = ArchiverConfig &
|
|
|
33
34
|
SentinelConfig &
|
|
34
35
|
SharedNodeConfig &
|
|
35
36
|
GenesisStateConfig &
|
|
36
|
-
NodeRPCConfig &
|
|
37
|
+
NodeRPCConfig &
|
|
38
|
+
SlasherConfig & {
|
|
37
39
|
/** L1 contracts addresses */
|
|
38
40
|
l1Contracts: L1ContractAddresses;
|
|
39
41
|
/** Whether the validator is disabled for this node */
|
|
@@ -52,6 +54,7 @@ export const aztecNodeConfigMappings: ConfigMappingsType<AztecNodeConfig> = {
|
|
|
52
54
|
...sharedNodeConfigMappings,
|
|
53
55
|
...genesisStateConfigMappings,
|
|
54
56
|
...nodeRpcConfigMappings,
|
|
57
|
+
...slasherConfigMappings,
|
|
55
58
|
l1Contracts: {
|
|
56
59
|
description: 'The deployed L1 contract addresses',
|
|
57
60
|
nested: l1ContractAddressesMapping,
|
package/src/aztec-node/server.ts
CHANGED
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
type PUBLIC_DATA_TREE_HEIGHT,
|
|
11
11
|
} from '@aztec/constants';
|
|
12
12
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
type L1ContractAddresses,
|
|
15
|
+
RegistryContract,
|
|
16
|
+
createEthereumChain,
|
|
17
|
+
createExtendedL1Client,
|
|
18
|
+
} from '@aztec/ethereum';
|
|
19
|
+
import { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
14
20
|
import { compactArray } from '@aztec/foundation/collection';
|
|
15
21
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
16
22
|
import { Fr } from '@aztec/foundation/fields';
|
|
@@ -20,7 +26,6 @@ import { SerialQueue } from '@aztec/foundation/queue';
|
|
|
20
26
|
import { count } from '@aztec/foundation/string';
|
|
21
27
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
22
28
|
import { SiblingPath } from '@aztec/foundation/trees';
|
|
23
|
-
import type { AztecKVStore } from '@aztec/kv-store';
|
|
24
29
|
import { openTmpStore } from '@aztec/kv-store/lmdb';
|
|
25
30
|
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
26
31
|
import { SHA256Trunc, StandardTree, UnbalancedTree } from '@aztec/merkle-tree';
|
|
@@ -31,10 +36,10 @@ import {
|
|
|
31
36
|
GlobalVariableBuilder,
|
|
32
37
|
SequencerClient,
|
|
33
38
|
type SequencerPublisher,
|
|
34
|
-
createSlasherClient,
|
|
35
39
|
createValidatorForAcceptingTxs,
|
|
36
40
|
} from '@aztec/sequencer-client';
|
|
37
41
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
42
|
+
import { EpochPruneWatcher, SlasherClient, type Watcher } from '@aztec/slasher';
|
|
38
43
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
39
44
|
import type { InBlock, L2Block, L2BlockNumber, L2BlockSource, PublishedL2Block } from '@aztec/stdlib/block';
|
|
40
45
|
import type {
|
|
@@ -182,7 +187,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
182
187
|
|
|
183
188
|
const publicClient = createPublicClient({
|
|
184
189
|
chain: ethereumChain.chainInfo,
|
|
185
|
-
transport: fallback(config.l1RpcUrls.map(url => http(url))),
|
|
190
|
+
transport: fallback(config.l1RpcUrls.map((url: string) => http(url))),
|
|
186
191
|
pollingInterval: config.viemPollingIntervalMS,
|
|
187
192
|
});
|
|
188
193
|
|
|
@@ -195,6 +200,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
195
200
|
// Overwrite the passed in vars.
|
|
196
201
|
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
|
|
197
202
|
|
|
203
|
+
const l1Client = createExtendedL1Client(config.l1RpcUrls, config.publisherPrivateKey, ethereumChain.chainInfo);
|
|
204
|
+
const l1TxUtils = new L1TxUtilsWithBlobs(l1Client, log, config);
|
|
205
|
+
|
|
198
206
|
const rollup = getContract({
|
|
199
207
|
address: l1ContractsAddresses.rollupAddress.toString(),
|
|
200
208
|
abi: RollupAbi,
|
|
@@ -248,8 +256,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
248
256
|
// Start p2p. Note that it depends on world state to be running.
|
|
249
257
|
await p2pClient.start();
|
|
250
258
|
|
|
251
|
-
const
|
|
252
|
-
|
|
259
|
+
const watchers: Watcher[] = [];
|
|
260
|
+
|
|
261
|
+
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
262
|
+
if (validatorsSentinel) {
|
|
263
|
+
watchers.push(validatorsSentinel);
|
|
264
|
+
}
|
|
265
|
+
const epochPruneWatcher = new EpochPruneWatcher(archiver, epochCache, config.slashPrunePenalty);
|
|
266
|
+
watchers.push(epochPruneWatcher);
|
|
267
|
+
|
|
268
|
+
const slasherClient = await SlasherClient.new(config, config.l1Contracts, l1TxUtils, watchers, dateProvider);
|
|
269
|
+
await slasherClient.start();
|
|
253
270
|
|
|
254
271
|
const validatorClient = createValidatorClient(config, {
|
|
255
272
|
p2pClient,
|
|
@@ -259,16 +276,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
259
276
|
blockSource: archiver,
|
|
260
277
|
});
|
|
261
278
|
|
|
262
|
-
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
263
|
-
await validatorsSentinel?.start();
|
|
264
|
-
|
|
265
279
|
log.verbose(`All Aztec Node subsystems synced`);
|
|
266
280
|
|
|
267
281
|
// now create the sequencer
|
|
268
282
|
const sequencer = config.disableValidator
|
|
269
283
|
? undefined
|
|
270
284
|
: await SequencerClient.new(config, {
|
|
285
|
+
// if deps were provided, they should override the defaults,
|
|
286
|
+
// or things that we created in this function
|
|
271
287
|
...deps,
|
|
288
|
+
epochCache,
|
|
289
|
+
l1TxUtils,
|
|
272
290
|
validatorClient,
|
|
273
291
|
p2pClient,
|
|
274
292
|
worldStateSynchronizer,
|
|
@@ -529,7 +547,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
529
547
|
public async stop() {
|
|
530
548
|
this.log.info(`Stopping`);
|
|
531
549
|
await this.txQueue.end();
|
|
532
|
-
await this.validatorsSentinel?.stop();
|
|
550
|
+
// await this.validatorsSentinel?.stop(); <- The slasher client will stop this
|
|
533
551
|
await this.sequencer?.stop();
|
|
534
552
|
await this.p2pClient.stop();
|
|
535
553
|
await this.worldStateSynchronizer.stop();
|
|
@@ -699,14 +717,20 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
699
717
|
}
|
|
700
718
|
|
|
701
719
|
/**
|
|
702
|
-
* Returns the index
|
|
703
|
-
* @remarks
|
|
704
|
-
*
|
|
705
|
-
* The tree is
|
|
720
|
+
* Returns the index and sibling path for a L2->L1 message in a block's message tree.
|
|
721
|
+
* @remarks The message tree is constructed on-demand by taking all L2->L1 messages in a block
|
|
722
|
+
* and creating a variable depth append-only tree with the messages as leaves.
|
|
723
|
+
* The tree is constructed in two layers:
|
|
724
|
+
* 1. Subtree - For each transaction in the block, a subtree is created containing all L2->L1 messages from that
|
|
725
|
+
* transaction.
|
|
726
|
+
* 2. Top tree - A tree containing the roots of all the subtrees as leaves
|
|
727
|
+
* The final path is constructed by concatenating the path in the subtree with the path in the top tree.
|
|
728
|
+
* When there is only one transaction in the block, the subtree itself becomes the block's L2->L1 message tree,
|
|
729
|
+
* and no top tree is needed. The out hash is the root of the the block's L2->L1 message tree.
|
|
706
730
|
* TODO: Handle the case where two messages in the same tx have the same hash.
|
|
707
|
-
* @param blockNumber -
|
|
708
|
-
* @param l2ToL1Message -
|
|
709
|
-
* @returns
|
|
731
|
+
* @param blockNumber - Block number to get data from
|
|
732
|
+
* @param l2ToL1Message - Message to get index/path for
|
|
733
|
+
* @returns [index, siblingPath] for the message
|
|
710
734
|
*/
|
|
711
735
|
public async getL2ToL1MessageMembershipWitness(
|
|
712
736
|
blockNumber: L2BlockNumber,
|
|
@@ -715,70 +739,98 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
715
739
|
const block = await this.blockSource.getBlock(blockNumber === 'latest' ? await this.getBlockNumber() : blockNumber);
|
|
716
740
|
|
|
717
741
|
if (block === undefined) {
|
|
718
|
-
throw new Error('Block
|
|
742
|
+
throw new Error('Block not found in getL2ToL1MessageMembershipWitness');
|
|
719
743
|
}
|
|
720
744
|
|
|
721
|
-
const
|
|
745
|
+
const messagesPerTx = block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
|
|
722
746
|
|
|
723
|
-
// Find index of message
|
|
724
|
-
let
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
747
|
+
// Find index of message in subtree and index of tx in a block
|
|
748
|
+
let messageIndexInTx = -1,
|
|
749
|
+
txIndex = -1;
|
|
750
|
+
{
|
|
751
|
+
txIndex = messagesPerTx.findIndex(messages => {
|
|
752
|
+
const idx = messages.findIndex(msg => msg.equals(l2ToL1Message));
|
|
753
|
+
messageIndexInTx = Math.max(messageIndexInTx, idx);
|
|
754
|
+
return idx !== -1;
|
|
755
|
+
});
|
|
756
|
+
}
|
|
730
757
|
|
|
731
|
-
if (
|
|
758
|
+
if (txIndex === -1) {
|
|
732
759
|
throw new Error('The L2ToL1Message you are trying to prove inclusion of does not exist');
|
|
733
760
|
}
|
|
734
761
|
|
|
735
|
-
|
|
762
|
+
// Get the message path in subtree and message subtree height
|
|
763
|
+
let messagePathInSubtree: SiblingPath<number>;
|
|
764
|
+
let messageSubtreeHeight: number;
|
|
765
|
+
{
|
|
766
|
+
const subtreeStore = openTmpStore(true);
|
|
767
|
+
const txMessages = messagesPerTx[txIndex];
|
|
768
|
+
messageSubtreeHeight = txMessages.length <= 1 ? 1 : Math.ceil(Math.log2(txMessages.length));
|
|
769
|
+
const subtree = new StandardTree(
|
|
770
|
+
subtreeStore,
|
|
771
|
+
new SHA256Trunc(),
|
|
772
|
+
`subtree_${txIndex}`,
|
|
773
|
+
messageSubtreeHeight,
|
|
774
|
+
0n,
|
|
775
|
+
Fr,
|
|
776
|
+
);
|
|
777
|
+
subtree.appendLeaves(txMessages);
|
|
778
|
+
messagePathInSubtree = await subtree.getSiblingPath(BigInt(messageIndexInTx), true);
|
|
779
|
+
await subtreeStore.delete();
|
|
780
|
+
}
|
|
736
781
|
|
|
737
|
-
//
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
tree.appendLeaves(msgs);
|
|
744
|
-
return tree;
|
|
745
|
-
});
|
|
782
|
+
// If the number of txs is 1 we are dealing with a special case where the tx subtree itself is the whole block's
|
|
783
|
+
// l2 to l1 message tree.
|
|
784
|
+
const numTransactions = block.body.txEffects.length;
|
|
785
|
+
if (numTransactions === 1) {
|
|
786
|
+
return [BigInt(messageIndexInTx), messagePathInSubtree];
|
|
787
|
+
}
|
|
746
788
|
|
|
747
|
-
//
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
|
|
789
|
+
// Calculate roots for all tx subtrees
|
|
790
|
+
const txSubtreeRoots = await Promise.all(
|
|
791
|
+
messagesPerTx.map(async (messages, txIdx) => {
|
|
792
|
+
// For a tx with no messages, we have to set an out hash of 0 to match what the circuit does.
|
|
793
|
+
if (messages.length === 0) {
|
|
794
|
+
return Fr.ZERO;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const txStore = openTmpStore(true);
|
|
798
|
+
const txTreeHeight = messages.length <= 1 ? 1 : Math.ceil(Math.log2(messages.length));
|
|
799
|
+
const txTree = new StandardTree(
|
|
800
|
+
txStore,
|
|
801
|
+
new SHA256Trunc(),
|
|
802
|
+
`tx_messages_subtree_${txIdx}`,
|
|
803
|
+
txTreeHeight,
|
|
804
|
+
0n,
|
|
805
|
+
Fr,
|
|
806
|
+
);
|
|
807
|
+
txTree.appendLeaves(messages);
|
|
808
|
+
const root = Fr.fromBuffer(txTree.getRoot(true));
|
|
809
|
+
await txStore.delete();
|
|
810
|
+
return root;
|
|
811
|
+
}),
|
|
751
812
|
);
|
|
752
813
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
814
|
+
// Construct the top tree and compute the combined path
|
|
815
|
+
let combinedPath: Buffer[];
|
|
816
|
+
{
|
|
817
|
+
const topTreeHeight = Math.ceil(Math.log2(txSubtreeRoots.length));
|
|
818
|
+
// The root of this tree is the out_hash calculated in Noir => we truncate to match Noir's SHA
|
|
819
|
+
const topTree = new UnbalancedTree(new SHA256Trunc(), 'top_tree', topTreeHeight, Fr);
|
|
820
|
+
await topTree.appendLeaves(txSubtreeRoots);
|
|
821
|
+
|
|
822
|
+
const txPathInTopTree = await topTree.getSiblingPath(txSubtreeRoots[txIndex].toBigInt());
|
|
823
|
+
// Append subtree path to top tree path
|
|
824
|
+
combinedPath = messagePathInSubtree.toBufferArray().concat(txPathInTopTree.toBufferArray());
|
|
756
825
|
}
|
|
757
826
|
|
|
758
|
-
//
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
);
|
|
762
|
-
const maxTreeHeight = Math.ceil(Math.log2(l2toL1SubtreeRoots.length));
|
|
763
|
-
// The root of this tree is the out_hash calculated in Noir => we truncate to match Noir's SHA
|
|
764
|
-
const outHashTree = new UnbalancedTree(new SHA256Trunc(), 'temp_outhash_sibling_path', maxTreeHeight, Fr);
|
|
765
|
-
await outHashTree.appendLeaves(l2toL1SubtreeRoots);
|
|
766
|
-
|
|
767
|
-
const pathOfTxInOutHashTree = await outHashTree.getSiblingPath(l2toL1SubtreeRoots[indexOfMsgTx].toBigInt());
|
|
768
|
-
// Append subtree path to out hash tree path
|
|
769
|
-
const mergedPath = subtreePathOfL2ToL1Message.toBufferArray().concat(pathOfTxInOutHashTree.toBufferArray());
|
|
770
|
-
// Append binary index of subtree path to binary index of out hash tree path
|
|
771
|
-
const mergedIndex = parseInt(
|
|
772
|
-
indexOfMsgTx
|
|
773
|
-
.toString(2)
|
|
774
|
-
.concat(indexOfMsgInSubtree.toString(2).padStart(l2toL1Subtrees[indexOfMsgTx].getDepth(), '0')),
|
|
827
|
+
// Append binary index of subtree path to binary index of top tree path
|
|
828
|
+
const combinedIndex = parseInt(
|
|
829
|
+
txIndex.toString(2).concat(messageIndexInTx.toString(2).padStart(messageSubtreeHeight, '0')),
|
|
775
830
|
2,
|
|
776
831
|
);
|
|
777
832
|
|
|
778
|
-
|
|
779
|
-
await Promise.all(tempStores.map(store => store.delete()));
|
|
780
|
-
|
|
781
|
-
return [BigInt(mergedIndex), new SiblingPath(mergedPath.length, mergedPath)];
|
|
833
|
+
return [BigInt(combinedIndex), new SiblingPath(combinedPath.length, combinedPath)];
|
|
782
834
|
}
|
|
783
835
|
|
|
784
836
|
/**
|
package/src/sentinel/factory.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
3
3
|
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
4
4
|
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
5
5
|
import type { P2PClient } from '@aztec/p2p';
|
|
6
|
+
import type { SlasherConfig } from '@aztec/slasher/config';
|
|
6
7
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
7
8
|
|
|
8
9
|
import type { SentinelConfig } from './config.js';
|
|
@@ -13,7 +14,7 @@ export async function createSentinel(
|
|
|
13
14
|
epochCache: EpochCache,
|
|
14
15
|
archiver: L2BlockSource,
|
|
15
16
|
p2p: P2PClient,
|
|
16
|
-
config: SentinelConfig & DataStoreConfig,
|
|
17
|
+
config: SentinelConfig & DataStoreConfig & SlasherConfig,
|
|
17
18
|
logger = createLogger('node:sentinel'),
|
|
18
19
|
): Promise<Sentinel | undefined> {
|
|
19
20
|
if (!config.sentinelEnabled) {
|
|
@@ -27,5 +28,5 @@ export async function createSentinel(
|
|
|
27
28
|
);
|
|
28
29
|
const storeHistoryLength = config.sentinelHistoryLengthInEpochs * epochCache.getL1Constants().epochDuration;
|
|
29
30
|
const sentinelStore = new SentinelStore(kvStore, { historyLength: storeHistoryLength });
|
|
30
|
-
return new Sentinel(epochCache, archiver, p2p, sentinelStore, logger);
|
|
31
|
+
return new Sentinel(epochCache, archiver, p2p, sentinelStore, config, logger);
|
|
31
32
|
}
|