@aztec/aztec-node 0.0.1-commit.b655e406 → 0.0.1-commit.d3ec352c
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 +1 -1
- 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/server.d.ts +28 -27
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +32 -20
- package/dest/bin/index.d.ts +1 -1
- package/dest/index.d.ts +1 -1
- package/dest/sentinel/config.d.ts +1 -1
- package/dest/sentinel/factory.d.ts +1 -1
- package/dest/sentinel/index.d.ts +1 -1
- package/dest/sentinel/sentinel.d.ts +20 -19
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +23 -16
- package/dest/sentinel/store.d.ts +6 -5
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +3 -2
- package/dest/test/index.d.ts +1 -1
- package/package.json +28 -27
- package/src/aztec-node/server.ts +69 -54
- package/src/sentinel/sentinel.ts +47 -35
- package/src/sentinel/store.ts +11 -10
package/src/aztec-node/server.ts
CHANGED
|
@@ -17,12 +17,12 @@ import {
|
|
|
17
17
|
createEthereumChain,
|
|
18
18
|
getPublicClient,
|
|
19
19
|
} from '@aztec/ethereum';
|
|
20
|
+
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
20
21
|
import { compactArray, pick } from '@aztec/foundation/collection';
|
|
21
22
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
22
23
|
import { Fr } from '@aztec/foundation/fields';
|
|
23
24
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
24
25
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
25
|
-
import { SerialQueue } from '@aztec/foundation/queue';
|
|
26
26
|
import { count } from '@aztec/foundation/string';
|
|
27
27
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
28
28
|
import { MembershipWitness, SiblingPath } from '@aztec/foundation/trees';
|
|
@@ -46,12 +46,13 @@ import {
|
|
|
46
46
|
type Watcher,
|
|
47
47
|
createSlasher,
|
|
48
48
|
} from '@aztec/slasher';
|
|
49
|
+
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
49
50
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
50
51
|
import {
|
|
51
|
-
type
|
|
52
|
+
type BlockParameter,
|
|
53
|
+
type DataInBlock,
|
|
52
54
|
type L2Block,
|
|
53
55
|
L2BlockHash,
|
|
54
|
-
type L2BlockNumber,
|
|
55
56
|
type L2BlockSource,
|
|
56
57
|
type PublishedL2Block,
|
|
57
58
|
} from '@aztec/stdlib/block';
|
|
@@ -62,7 +63,7 @@ import type {
|
|
|
62
63
|
NodeInfo,
|
|
63
64
|
ProtocolContractAddresses,
|
|
64
65
|
} from '@aztec/stdlib/contract';
|
|
65
|
-
import
|
|
66
|
+
import { GasFees } from '@aztec/stdlib/gas';
|
|
66
67
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
67
68
|
import {
|
|
68
69
|
type AztecNode,
|
|
@@ -132,9 +133,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
132
133
|
// Prevent two snapshot operations to happen simultaneously
|
|
133
134
|
private isUploadingSnapshot = false;
|
|
134
135
|
|
|
135
|
-
// Serial queue to ensure that we only send one tx at a time
|
|
136
|
-
private txQueue: SerialQueue = new SerialQueue();
|
|
137
|
-
|
|
138
136
|
public readonly tracer: Tracer;
|
|
139
137
|
|
|
140
138
|
constructor(
|
|
@@ -160,7 +158,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
160
158
|
) {
|
|
161
159
|
this.metrics = new NodeMetrics(telemetry, 'AztecNodeService');
|
|
162
160
|
this.tracer = telemetry.getTracer('AztecNodeService');
|
|
163
|
-
this.txQueue.start();
|
|
164
161
|
|
|
165
162
|
this.log.info(`Aztec Node version: ${this.packageVersion}`);
|
|
166
163
|
this.log.info(`Aztec Node started on chain 0x${l1ChainId.toString(16)}`, config.l1Contracts);
|
|
@@ -287,7 +284,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
287
284
|
options.prefilledPublicData,
|
|
288
285
|
telemetry,
|
|
289
286
|
);
|
|
290
|
-
const circuitVerifier = config.realProofs
|
|
287
|
+
const circuitVerifier = config.realProofs
|
|
288
|
+
? await BBCircuitVerifier.new(config)
|
|
289
|
+
: new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
291
290
|
if (!config.realProofs) {
|
|
292
291
|
log.warn(`Aztec node is accepting fake proofs`);
|
|
293
292
|
}
|
|
@@ -550,8 +549,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
550
549
|
* @param number - The block number being requested.
|
|
551
550
|
* @returns The requested block.
|
|
552
551
|
*/
|
|
553
|
-
public async getBlock(number:
|
|
554
|
-
const blockNumber = number === 'latest' ? await this.getBlockNumber() : number;
|
|
552
|
+
public async getBlock(number: BlockParameter): Promise<L2Block | undefined> {
|
|
553
|
+
const blockNumber = number === 'latest' ? await this.getBlockNumber() : (number as BlockNumber);
|
|
555
554
|
return await this.blockSource.getBlock(blockNumber);
|
|
556
555
|
}
|
|
557
556
|
|
|
@@ -581,11 +580,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
581
580
|
* @param limit - The maximum number of blocks to obtain.
|
|
582
581
|
* @returns The blocks requested.
|
|
583
582
|
*/
|
|
584
|
-
public async getBlocks(from:
|
|
583
|
+
public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
585
584
|
return (await this.blockSource.getBlocks(from, limit)) ?? [];
|
|
586
585
|
}
|
|
587
586
|
|
|
588
|
-
public async getPublishedBlocks(from:
|
|
587
|
+
public async getPublishedBlocks(from: BlockNumber, limit: number): Promise<PublishedL2Block[]> {
|
|
589
588
|
return (await this.blockSource.getPublishedBlocks(from, limit)) ?? [];
|
|
590
589
|
}
|
|
591
590
|
|
|
@@ -597,15 +596,23 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
597
596
|
return await this.globalVariableBuilder.getCurrentBaseFees();
|
|
598
597
|
}
|
|
599
598
|
|
|
599
|
+
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
600
|
+
for await (const tx of this.p2pClient.iteratePendingTxs()) {
|
|
601
|
+
return tx.getGasSettings().maxPriorityFeesPerGas;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return GasFees.from({ feePerDaGas: 0n, feePerL2Gas: 0n });
|
|
605
|
+
}
|
|
606
|
+
|
|
600
607
|
/**
|
|
601
608
|
* Method to fetch the latest block number synchronized by the node.
|
|
602
609
|
* @returns The block number.
|
|
603
610
|
*/
|
|
604
|
-
public async getBlockNumber(): Promise<
|
|
611
|
+
public async getBlockNumber(): Promise<BlockNumber> {
|
|
605
612
|
return await this.blockSource.getBlockNumber();
|
|
606
613
|
}
|
|
607
614
|
|
|
608
|
-
public async getProvenBlockNumber(): Promise<
|
|
615
|
+
public async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
609
616
|
return await this.blockSource.getProvenBlockNumber();
|
|
610
617
|
}
|
|
611
618
|
|
|
@@ -647,7 +654,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
647
654
|
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
648
655
|
* @returns An array of private logs from the specified range of blocks.
|
|
649
656
|
*/
|
|
650
|
-
public getPrivateLogs(from:
|
|
657
|
+
public getPrivateLogs(from: BlockNumber, limit: number): Promise<PrivateLog[]> {
|
|
651
658
|
return this.logsSource.getPrivateLogs(from, limit);
|
|
652
659
|
}
|
|
653
660
|
|
|
@@ -685,7 +692,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
685
692
|
* @param tx - The transaction to be submitted.
|
|
686
693
|
*/
|
|
687
694
|
public async sendTx(tx: Tx) {
|
|
688
|
-
await this
|
|
695
|
+
await this.#sendTx(tx);
|
|
689
696
|
}
|
|
690
697
|
|
|
691
698
|
async #sendTx(tx: Tx) {
|
|
@@ -732,7 +739,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
732
739
|
*/
|
|
733
740
|
public async stop() {
|
|
734
741
|
this.log.info(`Stopping Aztec Node`);
|
|
735
|
-
await this.txQueue.end();
|
|
736
742
|
await tryStop(this.validatorsSentinel);
|
|
737
743
|
await tryStop(this.epochPruneWatcher);
|
|
738
744
|
await tryStop(this.slasherClient);
|
|
@@ -786,10 +792,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
786
792
|
* @returns The indices of leaves and the block metadata of a block in which the leaves were inserted.
|
|
787
793
|
*/
|
|
788
794
|
public async findLeavesIndexes(
|
|
789
|
-
blockNumber:
|
|
795
|
+
blockNumber: BlockParameter,
|
|
790
796
|
treeId: MerkleTreeId,
|
|
791
797
|
leafValues: Fr[],
|
|
792
|
-
): Promise<(
|
|
798
|
+
): Promise<(DataInBlock<bigint> | undefined)[]> {
|
|
793
799
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
794
800
|
const maybeIndices = await committedDb.findLeafIndices(
|
|
795
801
|
treeId,
|
|
@@ -815,7 +821,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
815
821
|
// (note that block number corresponds to the leaf index in the archive tree).
|
|
816
822
|
const blockHashes = await Promise.all(
|
|
817
823
|
uniqueBlockNumbers.map(blockNumber => {
|
|
818
|
-
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, blockNumber
|
|
824
|
+
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
819
825
|
}),
|
|
820
826
|
);
|
|
821
827
|
|
|
@@ -826,7 +832,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
826
832
|
}
|
|
827
833
|
}
|
|
828
834
|
|
|
829
|
-
// Create
|
|
835
|
+
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
830
836
|
return maybeIndices.map((index, i) => {
|
|
831
837
|
if (index === undefined) {
|
|
832
838
|
return undefined;
|
|
@@ -841,7 +847,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
841
847
|
return undefined;
|
|
842
848
|
}
|
|
843
849
|
return {
|
|
844
|
-
l2BlockNumber: Number(blockNumber),
|
|
850
|
+
l2BlockNumber: BlockNumber(Number(blockNumber)),
|
|
845
851
|
l2BlockHash: L2BlockHash.fromField(blockHash),
|
|
846
852
|
data: index,
|
|
847
853
|
};
|
|
@@ -855,7 +861,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
855
861
|
* @returns The sibling path for the leaf index.
|
|
856
862
|
*/
|
|
857
863
|
public async getNullifierSiblingPath(
|
|
858
|
-
blockNumber:
|
|
864
|
+
blockNumber: BlockParameter,
|
|
859
865
|
leafIndex: bigint,
|
|
860
866
|
): Promise<SiblingPath<typeof NULLIFIER_TREE_HEIGHT>> {
|
|
861
867
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -869,7 +875,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
869
875
|
* @returns The sibling path for the leaf index.
|
|
870
876
|
*/
|
|
871
877
|
public async getNoteHashSiblingPath(
|
|
872
|
-
blockNumber:
|
|
878
|
+
blockNumber: BlockParameter,
|
|
873
879
|
leafIndex: bigint,
|
|
874
880
|
): Promise<SiblingPath<typeof NOTE_HASH_TREE_HEIGHT>> {
|
|
875
881
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -877,7 +883,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
877
883
|
}
|
|
878
884
|
|
|
879
885
|
public async getArchiveMembershipWitness(
|
|
880
|
-
blockNumber:
|
|
886
|
+
blockNumber: BlockParameter,
|
|
881
887
|
archive: Fr,
|
|
882
888
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
883
889
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -888,7 +894,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
888
894
|
}
|
|
889
895
|
|
|
890
896
|
public async getNoteHashMembershipWitness(
|
|
891
|
-
blockNumber:
|
|
897
|
+
blockNumber: BlockParameter,
|
|
892
898
|
noteHash: Fr,
|
|
893
899
|
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
894
900
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -908,7 +914,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
908
914
|
* @returns A tuple of the index and the sibling path of the L1ToL2Message (undefined if not found).
|
|
909
915
|
*/
|
|
910
916
|
public async getL1ToL2MessageMembershipWitness(
|
|
911
|
-
blockNumber:
|
|
917
|
+
blockNumber: BlockParameter,
|
|
912
918
|
l1ToL2Message: Fr,
|
|
913
919
|
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
|
|
914
920
|
const db = await this.#getWorldState(blockNumber);
|
|
@@ -921,9 +927,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
921
927
|
return [witness.index, witness.path];
|
|
922
928
|
}
|
|
923
929
|
|
|
924
|
-
public async getL1ToL2MessageBlock(l1ToL2Message: Fr): Promise<
|
|
930
|
+
public async getL1ToL2MessageBlock(l1ToL2Message: Fr): Promise<BlockNumber | undefined> {
|
|
925
931
|
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
926
|
-
return messageIndex ? InboxLeaf.l2BlockFromIndex(messageIndex) : undefined;
|
|
932
|
+
return messageIndex ? BlockNumber(InboxLeaf.l2BlockFromIndex(messageIndex)) : undefined;
|
|
927
933
|
}
|
|
928
934
|
|
|
929
935
|
/**
|
|
@@ -941,8 +947,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
941
947
|
* @param blockNumber - The block number at which to get the data.
|
|
942
948
|
* @returns The L2 to L1 messages (undefined if the block number is not found).
|
|
943
949
|
*/
|
|
944
|
-
public async getL2ToL1Messages(blockNumber:
|
|
945
|
-
const block = await this.blockSource.getBlock(
|
|
950
|
+
public async getL2ToL1Messages(blockNumber: BlockParameter): Promise<Fr[][] | undefined> {
|
|
951
|
+
const block = await this.blockSource.getBlock(
|
|
952
|
+
blockNumber === 'latest' ? await this.getBlockNumber() : (blockNumber as BlockNumber),
|
|
953
|
+
);
|
|
946
954
|
return block?.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
|
|
947
955
|
}
|
|
948
956
|
|
|
@@ -953,7 +961,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
953
961
|
* @returns The sibling path.
|
|
954
962
|
*/
|
|
955
963
|
public async getArchiveSiblingPath(
|
|
956
|
-
blockNumber:
|
|
964
|
+
blockNumber: BlockParameter,
|
|
957
965
|
leafIndex: bigint,
|
|
958
966
|
): Promise<SiblingPath<typeof ARCHIVE_HEIGHT>> {
|
|
959
967
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -967,7 +975,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
967
975
|
* @returns The sibling path.
|
|
968
976
|
*/
|
|
969
977
|
public async getPublicDataSiblingPath(
|
|
970
|
-
blockNumber:
|
|
978
|
+
blockNumber: BlockParameter,
|
|
971
979
|
leafIndex: bigint,
|
|
972
980
|
): Promise<SiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>> {
|
|
973
981
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -981,7 +989,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
981
989
|
* @returns The nullifier membership witness (if found).
|
|
982
990
|
*/
|
|
983
991
|
public async getNullifierMembershipWitness(
|
|
984
|
-
blockNumber:
|
|
992
|
+
blockNumber: BlockParameter,
|
|
985
993
|
nullifier: Fr,
|
|
986
994
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
987
995
|
const db = await this.#getWorldState(blockNumber);
|
|
@@ -1014,7 +1022,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1014
1022
|
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1015
1023
|
*/
|
|
1016
1024
|
public async getLowNullifierMembershipWitness(
|
|
1017
|
-
blockNumber:
|
|
1025
|
+
blockNumber: BlockParameter,
|
|
1018
1026
|
nullifier: Fr,
|
|
1019
1027
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1020
1028
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
@@ -1032,7 +1040,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1032
1040
|
return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
|
|
1033
1041
|
}
|
|
1034
1042
|
|
|
1035
|
-
async getPublicDataWitness(blockNumber:
|
|
1043
|
+
async getPublicDataWitness(blockNumber: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
1036
1044
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
1037
1045
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1038
1046
|
if (!lowLeafResult) {
|
|
@@ -1058,7 +1066,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1058
1066
|
* @param blockNumber - The block number at which to get the data or 'latest'.
|
|
1059
1067
|
* @returns Storage value at the given contract slot.
|
|
1060
1068
|
*/
|
|
1061
|
-
public async getPublicStorageAt(blockNumber:
|
|
1069
|
+
public async getPublicStorageAt(blockNumber: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
1062
1070
|
const committedDb = await this.#getWorldState(blockNumber);
|
|
1063
1071
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1064
1072
|
|
|
@@ -1077,10 +1085,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1077
1085
|
* Returns the currently committed block header, or the initial header if no blocks have been produced.
|
|
1078
1086
|
* @returns The current committed block header.
|
|
1079
1087
|
*/
|
|
1080
|
-
public async getBlockHeader(blockNumber:
|
|
1081
|
-
return blockNumber ===
|
|
1088
|
+
public async getBlockHeader(blockNumber: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
|
|
1089
|
+
return blockNumber === BlockNumber.ZERO ||
|
|
1090
|
+
(blockNumber === 'latest' && (await this.blockSource.getBlockNumber()) === BlockNumber.ZERO)
|
|
1082
1091
|
? this.worldStateSynchronizer.getCommitted().getInitialHeader()
|
|
1083
|
-
: this.blockSource.getBlockHeader(blockNumber);
|
|
1092
|
+
: this.blockSource.getBlockHeader(blockNumber === 'latest' ? blockNumber : (blockNumber as BlockNumber));
|
|
1084
1093
|
}
|
|
1085
1094
|
|
|
1086
1095
|
/**
|
|
@@ -1124,7 +1133,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1124
1133
|
}
|
|
1125
1134
|
|
|
1126
1135
|
const txHash = tx.getTxHash();
|
|
1127
|
-
const blockNumber = (await this.blockSource.getBlockNumber()) + 1;
|
|
1136
|
+
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
1128
1137
|
|
|
1129
1138
|
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
1130
1139
|
const coinbase = EthAddress.ZERO;
|
|
@@ -1149,11 +1158,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1149
1158
|
|
|
1150
1159
|
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
1151
1160
|
try {
|
|
1152
|
-
const
|
|
1161
|
+
const config = PublicSimulatorConfig.from({
|
|
1153
1162
|
skipFeeEnforcement,
|
|
1154
|
-
|
|
1155
|
-
|
|
1163
|
+
collectDebugLogs: true,
|
|
1164
|
+
collectHints: false,
|
|
1165
|
+
collectCallMetadata: true,
|
|
1166
|
+
collectStatistics: false,
|
|
1167
|
+
collectionLimits: CollectionLimitsConfig.from({
|
|
1168
|
+
maxDebugLogMemoryReads: this.config.rpcSimulatePublicMaxDebugLogMemoryReads,
|
|
1169
|
+
}),
|
|
1156
1170
|
});
|
|
1171
|
+
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1157
1172
|
|
|
1158
1173
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1159
1174
|
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([tx]);
|
|
@@ -1185,7 +1200,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1185
1200
|
|
|
1186
1201
|
// We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
|
|
1187
1202
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1188
|
-
const blockNumber = (await this.blockSource.getBlockNumber()) + 1;
|
|
1203
|
+
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
1189
1204
|
const validator = createValidatorForAcceptingTxs(db, this.contractDataSource, verifier, {
|
|
1190
1205
|
timestamp: nextSlotTimestamp,
|
|
1191
1206
|
blockNumber,
|
|
@@ -1242,8 +1257,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1242
1257
|
|
|
1243
1258
|
public getValidatorStats(
|
|
1244
1259
|
validatorAddress: EthAddress,
|
|
1245
|
-
fromSlot?:
|
|
1246
|
-
toSlot?:
|
|
1260
|
+
fromSlot?: SlotNumber,
|
|
1261
|
+
toSlot?: SlotNumber,
|
|
1247
1262
|
): Promise<SingleValidatorStats | undefined> {
|
|
1248
1263
|
return this.validatorsSentinel?.getValidatorStats(validatorAddress, fromSlot, toSlot) ?? Promise.resolve(undefined);
|
|
1249
1264
|
}
|
|
@@ -1292,7 +1307,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1292
1307
|
return Promise.resolve();
|
|
1293
1308
|
}
|
|
1294
1309
|
|
|
1295
|
-
public async rollbackTo(targetBlock:
|
|
1310
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1296
1311
|
const archiver = this.blockSource as Archiver;
|
|
1297
1312
|
if (!('rollbackTo' in archiver)) {
|
|
1298
1313
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1364,12 +1379,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1364
1379
|
* @param blockNumber - The block number at which to get the data.
|
|
1365
1380
|
* @returns An instance of a committed MerkleTreeOperations
|
|
1366
1381
|
*/
|
|
1367
|
-
async #getWorldState(blockNumber:
|
|
1382
|
+
async #getWorldState(blockNumber: BlockParameter) {
|
|
1368
1383
|
if (typeof blockNumber === 'number' && blockNumber < INITIAL_L2_BLOCK_NUM - 1) {
|
|
1369
1384
|
throw new Error('Invalid block number to get world state for: ' + blockNumber);
|
|
1370
1385
|
}
|
|
1371
1386
|
|
|
1372
|
-
let blockSyncedTo:
|
|
1387
|
+
let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
|
|
1373
1388
|
try {
|
|
1374
1389
|
// Attempt to sync the world state if necessary
|
|
1375
1390
|
blockSyncedTo = await this.#syncWorldState();
|
|
@@ -1383,7 +1398,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1383
1398
|
return this.worldStateSynchronizer.getCommitted();
|
|
1384
1399
|
} else if (blockNumber <= blockSyncedTo) {
|
|
1385
1400
|
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1386
|
-
return this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1401
|
+
return this.worldStateSynchronizer.getSnapshot(blockNumber as BlockNumber);
|
|
1387
1402
|
} else {
|
|
1388
1403
|
throw new Error(`Block ${blockNumber} not yet synced`);
|
|
1389
1404
|
}
|
|
@@ -1393,8 +1408,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1393
1408
|
* Ensure we fully sync the world state
|
|
1394
1409
|
* @returns A promise that fulfils once the world state is synced
|
|
1395
1410
|
*/
|
|
1396
|
-
async #syncWorldState(): Promise<
|
|
1411
|
+
async #syncWorldState(): Promise<BlockNumber> {
|
|
1397
1412
|
const blockSourceHeight = await this.blockSource.getBlockNumber();
|
|
1398
|
-
return this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
|
|
1413
|
+
return await this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
|
|
1399
1414
|
}
|
|
1400
1415
|
}
|
package/src/sentinel/sentinel.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
+
import { BlockNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
3
|
import { countWhile, filterAsync, fromEntries, getEntries, mapValues } from '@aztec/foundation/collection';
|
|
3
4
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -40,9 +41,10 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
40
41
|
protected blockStream!: L2BlockStream;
|
|
41
42
|
protected l2TipsStore: L2TipsStore;
|
|
42
43
|
|
|
43
|
-
protected initialSlot:
|
|
44
|
-
protected lastProcessedSlot:
|
|
45
|
-
|
|
44
|
+
protected initialSlot: SlotNumber | undefined;
|
|
45
|
+
protected lastProcessedSlot: SlotNumber | undefined;
|
|
46
|
+
// eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
|
|
47
|
+
protected slotNumberToBlock: Map<SlotNumber, { blockNumber: BlockNumber; archive: string; attestors: EthAddress[] }> =
|
|
46
48
|
new Map();
|
|
47
49
|
|
|
48
50
|
constructor(
|
|
@@ -74,7 +76,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
74
76
|
/** Loads initial slot and initializes blockstream. We will not process anything at or before the initial slot. */
|
|
75
77
|
protected async init() {
|
|
76
78
|
this.initialSlot = this.epochCache.getEpochAndSlotNow().slot;
|
|
77
|
-
const startingBlock = await this.archiver.getBlockNumber();
|
|
79
|
+
const startingBlock = BlockNumber(await this.archiver.getBlockNumber());
|
|
78
80
|
this.logger.info(`Starting validator sentinel with initial slot ${this.initialSlot} and block ${startingBlock}`);
|
|
79
81
|
this.blockStream = new L2BlockStream(this.archiver, this.l2TipsStore, this, this.logger, { startingBlock });
|
|
80
82
|
}
|
|
@@ -89,7 +91,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
89
91
|
// Store mapping from slot to archive, block number, and attestors
|
|
90
92
|
for (const block of event.blocks) {
|
|
91
93
|
this.slotNumberToBlock.set(block.block.header.getSlot(), {
|
|
92
|
-
blockNumber: block.block.number,
|
|
94
|
+
blockNumber: BlockNumber(block.block.number),
|
|
93
95
|
archive: block.block.archive.root.toString(),
|
|
94
96
|
attestors: getAttestationInfoFromPublishedL2Block(block)
|
|
95
97
|
.filter(a => a.status === 'recovered-from-signature')
|
|
@@ -116,7 +118,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
116
118
|
if (event.type !== 'chain-proven') {
|
|
117
119
|
return;
|
|
118
120
|
}
|
|
119
|
-
const blockNumber = event.block.number;
|
|
121
|
+
const blockNumber = BlockNumber(event.block.number);
|
|
120
122
|
const block = await this.archiver.getBlock(blockNumber);
|
|
121
123
|
if (!block) {
|
|
122
124
|
this.logger.error(`Failed to get block ${blockNumber}`, { block });
|
|
@@ -134,7 +136,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
134
136
|
await this.handleProvenPerformance(epoch, performance);
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
protected async computeProvenPerformance(epoch:
|
|
139
|
+
protected async computeProvenPerformance(epoch: EpochNumber): Promise<ValidatorsEpochPerformance> {
|
|
138
140
|
const [fromSlot, toSlot] = getSlotRangeForEpoch(epoch, this.epochCache.getL1Constants());
|
|
139
141
|
const { committee } = await this.epochCache.getCommittee(fromSlot);
|
|
140
142
|
if (!committee) {
|
|
@@ -142,7 +144,11 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
142
144
|
return {};
|
|
143
145
|
}
|
|
144
146
|
|
|
145
|
-
const stats = await this.computeStats({
|
|
147
|
+
const stats = await this.computeStats({
|
|
148
|
+
fromSlot,
|
|
149
|
+
toSlot,
|
|
150
|
+
validators: committee,
|
|
151
|
+
});
|
|
146
152
|
this.logger.debug(`Stats for epoch ${epoch}`, { ...stats, fromSlot, toSlot, epoch });
|
|
147
153
|
|
|
148
154
|
// Note that we are NOT using the total slots in the epoch as `total` here, since we only
|
|
@@ -165,7 +171,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
165
171
|
*/
|
|
166
172
|
protected async checkPastInactivity(
|
|
167
173
|
validator: EthAddress,
|
|
168
|
-
currentEpoch:
|
|
174
|
+
currentEpoch: EpochNumber,
|
|
169
175
|
requiredConsecutiveEpochs: number,
|
|
170
176
|
): Promise<boolean> {
|
|
171
177
|
if (requiredConsecutiveEpochs === 0) {
|
|
@@ -175,23 +181,24 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
175
181
|
// Get all historical performance for this validator
|
|
176
182
|
const allPerformance = await this.store.getProvenPerformance(validator);
|
|
177
183
|
|
|
184
|
+
// Sort by epoch descending to get most recent first, keep only epochs strictly before the current one, and get the first N
|
|
185
|
+
const pastEpochs = allPerformance.sort((a, b) => Number(b.epoch - a.epoch)).filter(p => p.epoch < currentEpoch);
|
|
186
|
+
|
|
178
187
|
// If we don't have enough historical data, don't slash
|
|
179
|
-
if (
|
|
188
|
+
if (pastEpochs.length < requiredConsecutiveEpochs) {
|
|
180
189
|
this.logger.debug(
|
|
181
190
|
`Not enough historical data for slashing ${validator} for inactivity (${allPerformance.length} epochs < ${requiredConsecutiveEpochs} required)`,
|
|
182
191
|
);
|
|
183
192
|
return false;
|
|
184
193
|
}
|
|
185
194
|
|
|
186
|
-
//
|
|
187
|
-
return
|
|
188
|
-
.sort((a, b) => Number(b.epoch - a.epoch))
|
|
189
|
-
.filter(p => p.epoch < currentEpoch)
|
|
195
|
+
// Check that we have at least requiredConsecutiveEpochs and that all of them are above the inactivity threshold
|
|
196
|
+
return pastEpochs
|
|
190
197
|
.slice(0, requiredConsecutiveEpochs)
|
|
191
198
|
.every(p => p.missed / p.total >= this.config.slashInactivityTargetPercentage);
|
|
192
199
|
}
|
|
193
200
|
|
|
194
|
-
protected async handleProvenPerformance(epoch:
|
|
201
|
+
protected async handleProvenPerformance(epoch: EpochNumber, performance: ValidatorsEpochPerformance) {
|
|
195
202
|
if (this.config.slashInactivityPenalty === 0n) {
|
|
196
203
|
return;
|
|
197
204
|
}
|
|
@@ -215,7 +222,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
215
222
|
validator: EthAddress.fromString(address),
|
|
216
223
|
amount: this.config.slashInactivityPenalty,
|
|
217
224
|
offenseType: OffenseType.INACTIVITY,
|
|
218
|
-
epochOrSlot: epoch,
|
|
225
|
+
epochOrSlot: BigInt(epoch),
|
|
219
226
|
}));
|
|
220
227
|
|
|
221
228
|
if (criminals.length > 0) {
|
|
@@ -256,8 +263,13 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
256
263
|
* We also don't move past the archiver last synced L2 slot, as we don't want to process data that is not yet available.
|
|
257
264
|
* Last, we check the p2p is synced with the archiver, so it has pulled all attestations from it.
|
|
258
265
|
*/
|
|
259
|
-
protected async isReadyToProcess(currentSlot:
|
|
260
|
-
|
|
266
|
+
protected async isReadyToProcess(currentSlot: SlotNumber): Promise<SlotNumber | false> {
|
|
267
|
+
if (currentSlot < 2) {
|
|
268
|
+
this.logger.trace(`Current slot ${currentSlot} too early.`);
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const targetSlot = SlotNumber(currentSlot - 2);
|
|
261
273
|
if (this.lastProcessedSlot && this.lastProcessedSlot >= targetSlot) {
|
|
262
274
|
this.logger.trace(`Already processed slot ${targetSlot}`, { lastProcessedSlot: this.lastProcessedSlot });
|
|
263
275
|
return false;
|
|
@@ -294,7 +306,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
294
306
|
* Gathers committee and proposer data for a given slot, computes slot stats,
|
|
295
307
|
* and updates overall stats.
|
|
296
308
|
*/
|
|
297
|
-
protected async processSlot(slot:
|
|
309
|
+
protected async processSlot(slot: SlotNumber) {
|
|
298
310
|
const { epoch, seed, committee } = await this.epochCache.getCommittee(slot);
|
|
299
311
|
if (!committee || committee.length === 0) {
|
|
300
312
|
this.logger.trace(`No committee found for slot ${slot} at epoch ${epoch}`);
|
|
@@ -310,7 +322,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
310
322
|
}
|
|
311
323
|
|
|
312
324
|
/** Computes activity for a given slot. */
|
|
313
|
-
protected async getSlotActivity(slot:
|
|
325
|
+
protected async getSlotActivity(slot: SlotNumber, epoch: EpochNumber, proposer: EthAddress, committee: EthAddress[]) {
|
|
314
326
|
this.logger.debug(`Computing stats for slot ${slot} at epoch ${epoch}`, { slot, epoch, proposer, committee });
|
|
315
327
|
|
|
316
328
|
// Check if there is an L2 block in L1 for this L2 slot
|
|
@@ -372,7 +384,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
372
384
|
}
|
|
373
385
|
|
|
374
386
|
/** Push the status for each slot for each validator. */
|
|
375
|
-
protected updateValidators(slot:
|
|
387
|
+
protected updateValidators(slot: SlotNumber, stats: Record<`0x${string}`, ValidatorStatusInSlot | undefined>) {
|
|
376
388
|
return this.store.updateValidators(slot, stats);
|
|
377
389
|
}
|
|
378
390
|
|
|
@@ -381,13 +393,13 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
381
393
|
fromSlot,
|
|
382
394
|
toSlot,
|
|
383
395
|
validators,
|
|
384
|
-
}: { fromSlot?:
|
|
396
|
+
}: { fromSlot?: SlotNumber; toSlot?: SlotNumber; validators?: EthAddress[] } = {}): Promise<ValidatorsStats> {
|
|
385
397
|
const histories = validators
|
|
386
398
|
? fromEntries(await Promise.all(validators.map(async v => [v.toString(), await this.store.getHistory(v)])))
|
|
387
399
|
: await this.store.getHistories();
|
|
388
400
|
|
|
389
401
|
const slotNow = this.epochCache.getEpochAndSlotNow().slot;
|
|
390
|
-
fromSlot ??= (this.lastProcessedSlot ?? slotNow) -
|
|
402
|
+
fromSlot ??= SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
|
|
391
403
|
toSlot ??= this.lastProcessedSlot ?? slotNow;
|
|
392
404
|
|
|
393
405
|
const stats = mapValues(histories, (history, address) =>
|
|
@@ -405,8 +417,8 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
405
417
|
/** Computes stats for a single validator. */
|
|
406
418
|
public async getValidatorStats(
|
|
407
419
|
validatorAddress: EthAddress,
|
|
408
|
-
fromSlot?:
|
|
409
|
-
toSlot?:
|
|
420
|
+
fromSlot?: SlotNumber,
|
|
421
|
+
toSlot?: SlotNumber,
|
|
410
422
|
): Promise<SingleValidatorStats | undefined> {
|
|
411
423
|
const history = await this.store.getHistory(validatorAddress);
|
|
412
424
|
|
|
@@ -415,13 +427,14 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
415
427
|
}
|
|
416
428
|
|
|
417
429
|
const slotNow = this.epochCache.getEpochAndSlotNow().slot;
|
|
418
|
-
const effectiveFromSlot =
|
|
430
|
+
const effectiveFromSlot =
|
|
431
|
+
fromSlot ?? SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
|
|
419
432
|
const effectiveToSlot = toSlot ?? this.lastProcessedSlot ?? slotNow;
|
|
420
433
|
|
|
421
434
|
const historyLength = BigInt(this.store.getHistoryLength());
|
|
422
|
-
if (effectiveToSlot - effectiveFromSlot > historyLength) {
|
|
435
|
+
if (BigInt(effectiveToSlot) - BigInt(effectiveFromSlot) > historyLength) {
|
|
423
436
|
throw new Error(
|
|
424
|
-
`Slot range (${effectiveToSlot - effectiveFromSlot}) exceeds history length (${historyLength}). ` +
|
|
437
|
+
`Slot range (${BigInt(effectiveToSlot) - BigInt(effectiveFromSlot)}) exceeds history length (${historyLength}). ` +
|
|
425
438
|
`Requested range: ${effectiveFromSlot} to ${effectiveToSlot}.`,
|
|
426
439
|
);
|
|
427
440
|
}
|
|
@@ -432,11 +445,10 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
432
445
|
effectiveFromSlot,
|
|
433
446
|
effectiveToSlot,
|
|
434
447
|
);
|
|
435
|
-
const allTimeProvenPerformance = await this.store.getProvenPerformance(validatorAddress);
|
|
436
448
|
|
|
437
449
|
return {
|
|
438
450
|
validator,
|
|
439
|
-
allTimeProvenPerformance,
|
|
451
|
+
allTimeProvenPerformance: await this.store.getProvenPerformance(validatorAddress),
|
|
440
452
|
lastProcessedSlot: this.lastProcessedSlot,
|
|
441
453
|
initialSlot: this.initialSlot,
|
|
442
454
|
slotWindow: this.store.getHistoryLength(),
|
|
@@ -446,11 +458,11 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
446
458
|
protected computeStatsForValidator(
|
|
447
459
|
address: `0x${string}`,
|
|
448
460
|
allHistory: ValidatorStatusHistory,
|
|
449
|
-
fromSlot?:
|
|
450
|
-
toSlot?:
|
|
461
|
+
fromSlot?: SlotNumber,
|
|
462
|
+
toSlot?: SlotNumber,
|
|
451
463
|
): ValidatorStats {
|
|
452
|
-
let history = fromSlot ? allHistory.filter(h => h.slot >= fromSlot) : allHistory;
|
|
453
|
-
history = toSlot ? history.filter(h => h.slot <= toSlot) : history;
|
|
464
|
+
let history = fromSlot ? allHistory.filter(h => BigInt(h.slot) >= fromSlot) : allHistory;
|
|
465
|
+
history = toSlot ? history.filter(h => BigInt(h.slot) <= toSlot) : history;
|
|
454
466
|
const lastProposal = history.filter(h => h.status === 'block-proposed' || h.status === 'block-mined').at(-1);
|
|
455
467
|
const lastAttestation = history.filter(h => h.status === 'attestation-sent').at(-1);
|
|
456
468
|
return {
|
|
@@ -479,7 +491,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
479
491
|
};
|
|
480
492
|
}
|
|
481
493
|
|
|
482
|
-
protected computeFromSlot(slot:
|
|
494
|
+
protected computeFromSlot(slot: SlotNumber | undefined) {
|
|
483
495
|
if (slot === undefined) {
|
|
484
496
|
return undefined;
|
|
485
497
|
}
|