@aztec/validator-client 1.0.0-nightly.20250708 → 1.0.0-staging.0
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/config.d.ts.map +1 -1
- package/dest/config.js +1 -4
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +1 -1
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +1 -2
- package/dest/validator.d.ts +4 -5
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +49 -74
- package/package.json +10 -10
- package/src/config.ts +0 -1
- package/src/duties/validation_service.ts +1 -2
- package/src/factory.ts +2 -4
- package/src/validator.ts +50 -88
package/dest/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAKjB,MAAM,0BAA0B,CAAC;AAElC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,oBAAoB,EAAE,WAAW,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC,CAAC;IAEnD,+BAA+B;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAE1B,+DAA+D;IAC/D,4BAA4B,EAAE,MAAM,CAAC;IAErC,+CAA+C;IAC/C,kBAAkB,EAAE,OAAO,CAAC;IAE5B,wEAAwE;IACxE,4BAA4B,EAAE,MAAM,CAAC;CACtC;AAED,eAAO,MAAM,6BAA6B,EAAE,kBAAkB,CAAC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAKjB,MAAM,0BAA0B,CAAC;AAElC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,oBAAoB,EAAE,WAAW,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC,CAAC;IAEnD,+BAA+B;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAE1B,+DAA+D;IAC/D,4BAA4B,EAAE,MAAM,CAAC;IAErC,+CAA+C;IAC/C,kBAAkB,EAAE,OAAO,CAAC;IAE5B,wEAAwE;IACxE,4BAA4B,EAAE,MAAM,CAAC;CACtC;AAED,eAAO,MAAM,6BAA6B,EAAE,kBAAkB,CAAC,qBAAqB,CA4BnF,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,qBAAqB,CAExD"}
|
package/dest/config.js
CHANGED
|
@@ -3,10 +3,7 @@ export const validatorClientConfigMappings = {
|
|
|
3
3
|
validatorPrivateKeys: {
|
|
4
4
|
env: 'VALIDATOR_PRIVATE_KEYS',
|
|
5
5
|
description: 'List of private keys of the validators participating in attestation duties',
|
|
6
|
-
...secretValueConfigHelper((val)=>val ? val.split(',').map((key)=>`0x${key.replace('0x', '')}`) : [])
|
|
7
|
-
fallback: [
|
|
8
|
-
'VALIDATOR_PRIVATE_KEY'
|
|
9
|
-
]
|
|
6
|
+
...secretValueConfigHelper((val)=>val ? val.split(',').map((key)=>`0x${key.replace('0x', '')}`) : [])
|
|
10
7
|
},
|
|
11
8
|
disableValidator: {
|
|
12
9
|
env: 'VALIDATOR_DISABLED',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation_service.d.ts","sourceRoot":"","sources":["../../src/duties/validation_service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,oBAAoB,EAG1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,iBAAiB;IAE/C;;;;;;;;;OASG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,EAAE,EACX,cAAc,EAAE,cAAc,EAC9B,GAAG,EAAE,EAAE,EAAE,EACT,uBAAuB,EAAE,UAAU,GAAG,SAAS,EAC/C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"validation_service.d.ts","sourceRoot":"","sources":["../../src/duties/validation_service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,oBAAoB,EAG1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,iBAAiB;IAE/C;;;;;;;;;OASG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,EAAE,EACX,cAAc,EAAE,cAAc,EAC9B,GAAG,EAAE,EAAE,EAAE,EACT,uBAAuB,EAAE,UAAU,GAAG,SAAS,EAC/C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC;IAoBzB;;;;;;;;;OASG;IACG,gBAAgB,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAUtG"}
|
|
@@ -26,7 +26,7 @@ export class ValidationService {
|
|
|
26
26
|
}
|
|
27
27
|
// TODO: check if this is calculated earlier / can not be recomputed
|
|
28
28
|
const txHashes = await Promise.all(txs.map((tx)=>tx.getTxHash()));
|
|
29
|
-
return BlockProposal.createProposalFromSigner(blockNumber, new ConsensusPayload(header, archive, stateReference
|
|
29
|
+
return BlockProposal.createProposalFromSigner(blockNumber, new ConsensusPayload(header, archive, stateReference, txHashes), options.publishFullTxs ? txs : undefined, payloadSigner);
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Attest with selection of validators to the given block proposal, constructed by the current sequencer
|
package/dest/factory.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
2
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
3
|
-
import type {
|
|
3
|
+
import type { P2P } from '@aztec/p2p';
|
|
4
4
|
import type { SlasherConfig } from '@aztec/slasher/config';
|
|
5
5
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
6
6
|
import type { IFullNodeBlockBuilder } from '@aztec/stdlib/interfaces/server';
|
|
@@ -10,7 +10,7 @@ import type { ValidatorClientConfig } from './config.js';
|
|
|
10
10
|
import { ValidatorClient } from './validator.js';
|
|
11
11
|
export declare function createValidatorClient(config: ValidatorClientConfig & Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>, deps: {
|
|
12
12
|
blockBuilder: IFullNodeBlockBuilder;
|
|
13
|
-
p2pClient:
|
|
13
|
+
p2pClient: P2P;
|
|
14
14
|
blockSource: L2BlockSource;
|
|
15
15
|
l1ToL2MessageSource: L1ToL2MessageSource;
|
|
16
16
|
telemetry: TelemetryClient;
|
package/dest/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,GAC3B,IAAI,CAAC,aAAa,EAAE,0BAA0B,GAAG,0BAA0B,GAAG,6BAA6B,CAAC,EAC9G,IAAI,EAAE;IACJ,YAAY,EAAE,qBAAqB,CAAC;IACpC,SAAS,EAAE,GAAG,CAAC;IACf,WAAW,EAAE,aAAa,CAAC;IAC3B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;CACxB,+BAmBF"}
|
package/dest/factory.js
CHANGED
|
@@ -10,6 +10,5 @@ export function createValidatorClient(config, deps) {
|
|
|
10
10
|
generatePrivateKey()
|
|
11
11
|
]);
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
return ValidatorClient.new(config, deps.blockBuilder, deps.epochCache, deps.p2pClient, deps.blockSource, deps.l1ToL2MessageSource, txProvider, deps.dateProvider, deps.telemetry);
|
|
13
|
+
return ValidatorClient.new(config, deps.blockBuilder, deps.epochCache, deps.p2pClient, deps.blockSource, deps.l1ToL2MessageSource, deps.dateProvider, deps.telemetry);
|
|
15
14
|
}
|
package/dest/validator.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
3
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
4
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
5
5
|
import type { P2P, PeerId } from '@aztec/p2p';
|
|
6
|
-
import { TxProvider } from '@aztec/p2p';
|
|
7
6
|
import { type SlasherConfig, type WantToSlashArgs, type Watcher, type WatcherEmitter } from '@aztec/slasher/config';
|
|
8
7
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
9
8
|
import type { IFullNodeBlockBuilder, SequencerConfig } from '@aztec/stdlib/interfaces/server';
|
|
@@ -32,7 +31,6 @@ export declare class ValidatorClient extends ValidatorClient_base implements Val
|
|
|
32
31
|
private p2pClient;
|
|
33
32
|
private blockSource;
|
|
34
33
|
private l1ToL2MessageSource;
|
|
35
|
-
private txProvider;
|
|
36
34
|
private config;
|
|
37
35
|
private dateProvider;
|
|
38
36
|
private log;
|
|
@@ -41,13 +39,14 @@ export declare class ValidatorClient extends ValidatorClient_base implements Val
|
|
|
41
39
|
private metrics;
|
|
42
40
|
private previousProposal?;
|
|
43
41
|
private myAddresses;
|
|
44
|
-
private
|
|
42
|
+
private lastEpoch;
|
|
45
43
|
private epochCacheUpdateLoop;
|
|
46
44
|
private blockProposalValidator;
|
|
45
|
+
private txCollector;
|
|
47
46
|
private proposersOfInvalidBlocks;
|
|
48
|
-
|
|
47
|
+
constructor(blockBuilder: IFullNodeBlockBuilder, keyStore: ValidatorKeyStore, epochCache: EpochCache, p2pClient: P2P, blockSource: L2BlockSource, l1ToL2MessageSource: L1ToL2MessageSource, config: ValidatorClientConfig & Pick<SequencerConfig, 'txPublicSetupAllowList'> & Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>, dateProvider?: DateProvider, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
|
|
49
48
|
private handleEpochCommitteeUpdate;
|
|
50
|
-
static new(config: ValidatorClientConfig & Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>, blockBuilder: IFullNodeBlockBuilder, epochCache: EpochCache, p2pClient: P2P, blockSource: L2BlockSource, l1ToL2MessageSource: L1ToL2MessageSource,
|
|
49
|
+
static new(config: ValidatorClientConfig & Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>, blockBuilder: IFullNodeBlockBuilder, epochCache: EpochCache, p2pClient: P2P, blockSource: L2BlockSource, l1ToL2MessageSource: L1ToL2MessageSource, dateProvider?: DateProvider, telemetry?: TelemetryClient): ValidatorClient;
|
|
51
50
|
getValidatorAddresses(): EthAddress[];
|
|
52
51
|
configureSlashing(config: Partial<Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>>): void;
|
|
53
52
|
start(): Promise<void>;
|
package/dest/validator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
|
package/dest/validator.js
CHANGED
|
@@ -2,10 +2,10 @@ import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
|
2
2
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
3
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
-
import { retryUntil } from '@aztec/foundation/retry';
|
|
6
5
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
7
6
|
import { sleep } from '@aztec/foundation/sleep';
|
|
8
7
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
8
|
+
import { TxCollector } from '@aztec/p2p';
|
|
9
9
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
10
10
|
import { computeInHashFromL1ToL2Messages } from '@aztec/prover-client/helpers';
|
|
11
11
|
import { Offense, WANT_TO_SLASH_EVENT } from '@aztec/slasher/config';
|
|
@@ -29,7 +29,6 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
29
29
|
p2pClient;
|
|
30
30
|
blockSource;
|
|
31
31
|
l1ToL2MessageSource;
|
|
32
|
-
txProvider;
|
|
33
32
|
config;
|
|
34
33
|
dateProvider;
|
|
35
34
|
log;
|
|
@@ -39,16 +38,18 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
39
38
|
// Used to check if we are sending the same proposal twice
|
|
40
39
|
previousProposal;
|
|
41
40
|
myAddresses;
|
|
42
|
-
|
|
41
|
+
lastEpoch;
|
|
43
42
|
epochCacheUpdateLoop;
|
|
44
43
|
blockProposalValidator;
|
|
44
|
+
txCollector;
|
|
45
45
|
proposersOfInvalidBlocks;
|
|
46
|
-
constructor(blockBuilder, keyStore, epochCache, p2pClient, blockSource, l1ToL2MessageSource,
|
|
47
|
-
super(), this.blockBuilder = blockBuilder, this.keyStore = keyStore, this.epochCache = epochCache, this.p2pClient = p2pClient, this.blockSource = blockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.
|
|
46
|
+
constructor(blockBuilder, keyStore, epochCache, p2pClient, blockSource, l1ToL2MessageSource, config, dateProvider = new DateProvider(), telemetry = getTelemetryClient(), log = createLogger('validator')){
|
|
47
|
+
super(), this.blockBuilder = blockBuilder, this.keyStore = keyStore, this.epochCache = epochCache, this.p2pClient = p2pClient, this.blockSource = blockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.config = config, this.dateProvider = dateProvider, this.log = log, this.proposersOfInvalidBlocks = new Set();
|
|
48
48
|
this.tracer = telemetry.getTracer('Validator');
|
|
49
49
|
this.metrics = new ValidatorMetrics(telemetry);
|
|
50
50
|
this.validationService = new ValidationService(keyStore);
|
|
51
51
|
this.blockProposalValidator = new BlockProposalValidator(epochCache);
|
|
52
|
+
this.txCollector = new TxCollector(p2pClient, this.log);
|
|
52
53
|
// Refresh epoch cache every second to trigger alert if participation in committee changes
|
|
53
54
|
this.myAddresses = this.keyStore.getAddresses();
|
|
54
55
|
this.epochCacheUpdateLoop = new RunningPromise(this.handleEpochCommitteeUpdate.bind(this), log, 1000);
|
|
@@ -56,12 +57,12 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
56
57
|
}
|
|
57
58
|
async handleEpochCommitteeUpdate() {
|
|
58
59
|
try {
|
|
59
|
-
const { committee, epoch } = await this.epochCache.getCommittee('
|
|
60
|
+
const { committee, epoch } = await this.epochCache.getCommittee('now');
|
|
60
61
|
if (!committee) {
|
|
61
62
|
this.log.trace(`No committee found for slot`);
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
|
-
if (epoch !== this.
|
|
65
|
+
if (epoch !== this.lastEpoch) {
|
|
65
66
|
const me = this.myAddresses;
|
|
66
67
|
const committeeSet = new Set(committee.map((v)=>v.toString()));
|
|
67
68
|
const inCommittee = me.filter((a)=>committeeSet.has(a.toString()));
|
|
@@ -70,19 +71,19 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
70
71
|
} else {
|
|
71
72
|
this.log.verbose(`Validators ${me.map((a)=>a.toString()).join(', ')} are not on the validator committee for epoch ${epoch}`);
|
|
72
73
|
}
|
|
73
|
-
this.
|
|
74
|
+
this.lastEpoch = epoch;
|
|
74
75
|
}
|
|
75
76
|
} catch (err) {
|
|
76
77
|
this.log.error(`Error updating epoch committee`, err);
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
|
-
static new(config, blockBuilder, epochCache, p2pClient, blockSource, l1ToL2MessageSource,
|
|
80
|
+
static new(config, blockBuilder, epochCache, p2pClient, blockSource, l1ToL2MessageSource, dateProvider = new DateProvider(), telemetry = getTelemetryClient()) {
|
|
80
81
|
if (!config.validatorPrivateKeys.getValue().length) {
|
|
81
82
|
throw new InvalidValidatorPrivateKeyError();
|
|
82
83
|
}
|
|
83
84
|
const privateKeys = config.validatorPrivateKeys.getValue().map(validatePrivateKey);
|
|
84
85
|
const localKeyStore = new LocalKeyStore(privateKeys);
|
|
85
|
-
const validator = new ValidatorClient(blockBuilder, localKeyStore, epochCache, p2pClient, blockSource, l1ToL2MessageSource,
|
|
86
|
+
const validator = new ValidatorClient(blockBuilder, localKeyStore, epochCache, p2pClient, blockSource, l1ToL2MessageSource, config, dateProvider, telemetry);
|
|
86
87
|
validator.registerBlockProposalHandler();
|
|
87
88
|
return validator;
|
|
88
89
|
}
|
|
@@ -98,7 +99,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
98
99
|
// Sync the committee from the smart contract
|
|
99
100
|
// https://github.com/AztecProtocol/aztec-packages/issues/7962
|
|
100
101
|
const myAddresses = this.keyStore.getAddresses();
|
|
101
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
102
|
+
const inCommittee = await this.epochCache.filterInCommittee(myAddresses);
|
|
102
103
|
if (inCommittee.length > 0) {
|
|
103
104
|
this.log.info(`Started validator with addresses in current validator committee: ${inCommittee.map((a)=>a.toString()).join(', ')}`);
|
|
104
105
|
} else {
|
|
@@ -111,43 +112,30 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
111
112
|
await this.epochCacheUpdateLoop.stop();
|
|
112
113
|
}
|
|
113
114
|
registerBlockProposalHandler() {
|
|
114
|
-
const handler = (block, proposalSender)=>
|
|
115
|
+
const handler = (block, proposalSender)=>{
|
|
116
|
+
return this.attestToProposal(block, proposalSender);
|
|
117
|
+
};
|
|
115
118
|
this.p2pClient.registerBlockProposalHandler(handler);
|
|
116
119
|
}
|
|
117
120
|
async attestToProposal(proposal, proposalSender) {
|
|
118
|
-
const slotNumber = proposal.slotNumber.
|
|
121
|
+
const slotNumber = proposal.slotNumber.toNumber();
|
|
119
122
|
const blockNumber = proposal.blockNumber;
|
|
120
123
|
const proposer = proposal.getSender();
|
|
121
|
-
// Check that I have any address in current committee before attesting
|
|
122
|
-
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.keyStore.getAddresses());
|
|
123
|
-
const partOfCommittee = inCommittee.length > 0;
|
|
124
124
|
const proposalInfo = {
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
slotNumber,
|
|
126
|
+
blockNumber,
|
|
127
|
+
proposer: proposer.toString(),
|
|
128
|
+
archive: proposal.payload.archive.toString(),
|
|
129
|
+
txCount: proposal.payload.txHashes.length,
|
|
130
|
+
txHashes: proposal.payload.txHashes.map((txHash)=>txHash.toString())
|
|
127
131
|
};
|
|
128
|
-
this.log.info(`Received
|
|
129
|
-
...proposalInfo,
|
|
130
|
-
txHashes: proposal.txHashes.map((txHash)=>txHash.toString())
|
|
131
|
-
});
|
|
132
|
-
// Collect txs from the proposal. Note that we do this before checking if we have an address in the
|
|
133
|
-
// current committee, since we want to collect txs anyway to facilitate propagation.
|
|
134
|
-
const { txs, missingTxs } = await this.txProvider.getTxsForBlockProposal(proposal, {
|
|
135
|
-
pinnedPeer: proposalSender,
|
|
136
|
-
deadline: this.getReexecutionDeadline(proposal, this.blockBuilder.getConfig())
|
|
137
|
-
});
|
|
138
|
-
// Check that I have any address in current committee before attesting
|
|
139
|
-
if (!partOfCommittee) {
|
|
140
|
-
this.log.verbose(`No validator in the current committee, skipping attestation`, proposalInfo);
|
|
141
|
-
return undefined;
|
|
142
|
-
}
|
|
132
|
+
this.log.info(`Received request to attest for slot ${slotNumber}`, proposalInfo);
|
|
143
133
|
// Check that the proposal is from the current proposer, or the next proposer.
|
|
144
134
|
// Q: Should this be moved to the block proposal validator, so we disregard proposals from anyone?
|
|
145
135
|
const invalidProposal = await this.blockProposalValidator.validate(proposal);
|
|
146
136
|
if (invalidProposal) {
|
|
147
|
-
this.log.warn(`Proposal is not valid, skipping attestation
|
|
148
|
-
|
|
149
|
-
this.metrics.incFailedAttestations(1, 'invalid_proposal');
|
|
150
|
-
}
|
|
137
|
+
this.log.warn(`Proposal is not valid, skipping attestation`);
|
|
138
|
+
this.metrics.incFailedAttestations(1, 'invalid_proposal');
|
|
151
139
|
return undefined;
|
|
152
140
|
}
|
|
153
141
|
// Check that the parent proposal is a block we know, otherwise reexecution would fail.
|
|
@@ -156,23 +144,10 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
156
144
|
// would not be rebroadcasted. But it also means that nodes that have not fully synced would
|
|
157
145
|
// not rebroadcast the proposal.
|
|
158
146
|
if (blockNumber > INITIAL_L2_BLOCK_NUM) {
|
|
159
|
-
const
|
|
160
|
-
const deadline = this.getReexecutionDeadline(proposal, config);
|
|
161
|
-
const currentTime = this.dateProvider.now();
|
|
162
|
-
const timeoutDurationMs = deadline.getTime() - currentTime;
|
|
163
|
-
const parentBlock = timeoutDurationMs <= 0 ? undefined : await retryUntil(async ()=>{
|
|
164
|
-
const block = await this.blockSource.getBlock(blockNumber - 1);
|
|
165
|
-
if (block) {
|
|
166
|
-
return block;
|
|
167
|
-
}
|
|
168
|
-
await this.blockSource.syncImmediate();
|
|
169
|
-
return await this.blockSource.getBlock(blockNumber - 1);
|
|
170
|
-
}, 'Force Archiver Sync', timeoutDurationMs / 1000, 0.5);
|
|
147
|
+
const parentBlock = await this.blockSource.getBlock(blockNumber - 1);
|
|
171
148
|
if (parentBlock === undefined) {
|
|
172
|
-
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation
|
|
173
|
-
|
|
174
|
-
this.metrics.incFailedAttestations(1, 'parent_block_not_found');
|
|
175
|
-
}
|
|
149
|
+
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation`);
|
|
150
|
+
this.metrics.incFailedAttestations(1, 'parent_block_not_found');
|
|
176
151
|
return undefined;
|
|
177
152
|
}
|
|
178
153
|
if (!proposal.payload.header.lastArchiveRoot.equals(parentBlock.archive.root)) {
|
|
@@ -181,14 +156,28 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
181
156
|
parentBlockArchiveRoot: parentBlock.archive.root.toString(),
|
|
182
157
|
...proposalInfo
|
|
183
158
|
});
|
|
184
|
-
|
|
185
|
-
this.metrics.incFailedAttestations(1, 'parent_block_does_not_match');
|
|
186
|
-
}
|
|
159
|
+
this.metrics.incFailedAttestations(1, 'parent_block_does_not_match');
|
|
187
160
|
return undefined;
|
|
188
161
|
}
|
|
189
162
|
}
|
|
163
|
+
// Collect txs from the proposal
|
|
164
|
+
const { missing, txs } = await this.txCollector.collectForBlockProposal(proposal, proposalSender);
|
|
165
|
+
// Check that I have any address in current committee before attesting
|
|
166
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
167
|
+
if (inCommittee.length === 0) {
|
|
168
|
+
this.log.verbose(`No validator in the committee, skipping attestation`);
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
// Check that all of the transactions in the proposal are available in the tx pool before attesting
|
|
172
|
+
if (missing && missing.length > 0) {
|
|
173
|
+
this.log.warn(`Missing ${missing.length}/${proposal.payload.txHashes.length} txs to attest to proposal`, {
|
|
174
|
+
...proposalInfo,
|
|
175
|
+
missing
|
|
176
|
+
});
|
|
177
|
+
this.metrics.incFailedAttestations(1, 'TransactionsNotAvailableError');
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
190
180
|
// Check that I have the same set of l1ToL2Messages as the proposal
|
|
191
|
-
// Q: Same as above, should this be part of p2p validation?
|
|
192
181
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
193
182
|
const computedInHash = await computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
194
183
|
const proposalInHash = proposal.payload.header.contentCommitment.inHash;
|
|
@@ -198,20 +187,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
198
187
|
computedInHash: computedInHash.toString(),
|
|
199
188
|
...proposalInfo
|
|
200
189
|
});
|
|
201
|
-
|
|
202
|
-
this.metrics.incFailedAttestations(1, 'in_hash_mismatch');
|
|
203
|
-
}
|
|
204
|
-
return undefined;
|
|
205
|
-
}
|
|
206
|
-
// Check that all of the transactions in the proposal are available in the tx pool before attesting
|
|
207
|
-
if (missingTxs.length > 0) {
|
|
208
|
-
this.log.warn(`Missing ${missingTxs.length} txs to attest to proposal`, {
|
|
209
|
-
...proposalInfo,
|
|
210
|
-
missingTxs
|
|
211
|
-
});
|
|
212
|
-
if (partOfCommittee) {
|
|
213
|
-
this.metrics.incFailedAttestations(1, 'TransactionsNotAvailableError');
|
|
214
|
-
}
|
|
190
|
+
this.metrics.incFailedAttestations(1, 'in_hash_mismatch');
|
|
215
191
|
return undefined;
|
|
216
192
|
}
|
|
217
193
|
// Try re-executing the transactions in the proposal
|
|
@@ -245,8 +221,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
245
221
|
* Re-execute the transactions in the proposal and check that the state updates match the header state
|
|
246
222
|
* @param proposal - The proposal to re-execute
|
|
247
223
|
*/ async reExecuteTransactions(proposal, txs, l1ToL2Messages) {
|
|
248
|
-
const { header } = proposal.payload;
|
|
249
|
-
const { txHashes } = proposal;
|
|
224
|
+
const { header, txHashes } = proposal.payload;
|
|
250
225
|
// If we do not have all of the transactions, then we should fail
|
|
251
226
|
if (txs.length !== txHashes.length) {
|
|
252
227
|
const foundTxHashes = await Promise.all(txs.map(async (tx)=>await tx.getTxHash()));
|
|
@@ -336,7 +311,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
336
311
|
}
|
|
337
312
|
const proposalId = proposal.archive.toString();
|
|
338
313
|
// adds attestations for all of my addresses locally
|
|
339
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
314
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
340
315
|
await this.doAttestToProposal(proposal, inCommittee);
|
|
341
316
|
const myAddresses = this.keyStore.getAddresses();
|
|
342
317
|
let attestations = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/validator-client",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-staging.0",
|
|
4
4
|
"main": "dest/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -64,15 +64,15 @@
|
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@aztec/constants": "1.0.0-
|
|
68
|
-
"@aztec/epoch-cache": "1.0.0-
|
|
69
|
-
"@aztec/ethereum": "1.0.0-
|
|
70
|
-
"@aztec/foundation": "1.0.0-
|
|
71
|
-
"@aztec/p2p": "1.0.0-
|
|
72
|
-
"@aztec/prover-client": "1.0.0-
|
|
73
|
-
"@aztec/slasher": "1.0.0-
|
|
74
|
-
"@aztec/stdlib": "1.0.0-
|
|
75
|
-
"@aztec/telemetry-client": "1.0.0-
|
|
67
|
+
"@aztec/constants": "1.0.0-staging.0",
|
|
68
|
+
"@aztec/epoch-cache": "1.0.0-staging.0",
|
|
69
|
+
"@aztec/ethereum": "1.0.0-staging.0",
|
|
70
|
+
"@aztec/foundation": "1.0.0-staging.0",
|
|
71
|
+
"@aztec/p2p": "1.0.0-staging.0",
|
|
72
|
+
"@aztec/prover-client": "1.0.0-staging.0",
|
|
73
|
+
"@aztec/slasher": "1.0.0-staging.0",
|
|
74
|
+
"@aztec/stdlib": "1.0.0-staging.0",
|
|
75
|
+
"@aztec/telemetry-client": "1.0.0-staging.0",
|
|
76
76
|
"koa": "^2.16.1",
|
|
77
77
|
"koa-router": "^12.0.0",
|
|
78
78
|
"tslib": "^2.4.0",
|
package/src/config.ts
CHANGED
|
@@ -34,7 +34,6 @@ export const validatorClientConfigMappings: ConfigMappingsType<ValidatorClientCo
|
|
|
34
34
|
...secretValueConfigHelper<`0x${string}`[]>(val =>
|
|
35
35
|
val ? val.split(',').map<`0x${string}`>(key => `0x${key.replace('0x', '')}`) : [],
|
|
36
36
|
),
|
|
37
|
-
fallback: ['VALIDATOR_PRIVATE_KEY'],
|
|
38
37
|
},
|
|
39
38
|
disableValidator: {
|
|
40
39
|
env: 'VALIDATOR_DISABLED',
|
|
@@ -49,8 +49,7 @@ export class ValidationService {
|
|
|
49
49
|
|
|
50
50
|
return BlockProposal.createProposalFromSigner(
|
|
51
51
|
blockNumber,
|
|
52
|
-
new ConsensusPayload(header, archive, stateReference),
|
|
53
|
-
txHashes,
|
|
52
|
+
new ConsensusPayload(header, archive, stateReference, txHashes),
|
|
54
53
|
options.publishFullTxs ? txs : undefined,
|
|
55
54
|
payloadSigner,
|
|
56
55
|
);
|
package/src/factory.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
2
|
import { SecretValue } from '@aztec/foundation/config';
|
|
3
3
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
4
|
-
import type {
|
|
4
|
+
import type { P2P } from '@aztec/p2p';
|
|
5
5
|
import type { SlasherConfig } from '@aztec/slasher/config';
|
|
6
6
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
7
7
|
import type { IFullNodeBlockBuilder } from '@aztec/stdlib/interfaces/server';
|
|
@@ -18,7 +18,7 @@ export function createValidatorClient(
|
|
|
18
18
|
Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>,
|
|
19
19
|
deps: {
|
|
20
20
|
blockBuilder: IFullNodeBlockBuilder;
|
|
21
|
-
p2pClient:
|
|
21
|
+
p2pClient: P2P;
|
|
22
22
|
blockSource: L2BlockSource;
|
|
23
23
|
l1ToL2MessageSource: L1ToL2MessageSource;
|
|
24
24
|
telemetry: TelemetryClient;
|
|
@@ -33,7 +33,6 @@ export function createValidatorClient(
|
|
|
33
33
|
config.validatorPrivateKeys = new SecretValue([generatePrivateKey()]);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const txProvider = deps.p2pClient.getTxProvider();
|
|
37
36
|
return ValidatorClient.new(
|
|
38
37
|
config,
|
|
39
38
|
deps.blockBuilder,
|
|
@@ -41,7 +40,6 @@ export function createValidatorClient(
|
|
|
41
40
|
deps.p2pClient,
|
|
42
41
|
deps.blockSource,
|
|
43
42
|
deps.l1ToL2MessageSource,
|
|
44
|
-
txProvider,
|
|
45
43
|
deps.dateProvider,
|
|
46
44
|
deps.telemetry,
|
|
47
45
|
);
|
package/src/validator.ts
CHANGED
|
@@ -4,12 +4,11 @@ import { Buffer32 } from '@aztec/foundation/buffer';
|
|
|
4
4
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
5
|
import { Fr } from '@aztec/foundation/fields';
|
|
6
6
|
import { createLogger } from '@aztec/foundation/log';
|
|
7
|
-
import { retryUntil } from '@aztec/foundation/retry';
|
|
8
7
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
9
8
|
import { sleep } from '@aztec/foundation/sleep';
|
|
10
9
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
11
10
|
import type { P2P, PeerId } from '@aztec/p2p';
|
|
12
|
-
import {
|
|
11
|
+
import { TxCollector } from '@aztec/p2p';
|
|
13
12
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
14
13
|
import { computeInHashFromL1ToL2Messages } from '@aztec/prover-client/helpers';
|
|
15
14
|
import {
|
|
@@ -22,7 +21,7 @@ import {
|
|
|
22
21
|
} from '@aztec/slasher/config';
|
|
23
22
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
24
23
|
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
25
|
-
import type { IFullNodeBlockBuilder, SequencerConfig } from '@aztec/stdlib/interfaces/server';
|
|
24
|
+
import type { IFullNodeBlockBuilder, ITxCollector, SequencerConfig } from '@aztec/stdlib/interfaces/server';
|
|
26
25
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
27
26
|
import type { BlockAttestation, BlockProposal, BlockProposalOptions } from '@aztec/stdlib/p2p';
|
|
28
27
|
import { GlobalVariables, type ProposedBlockHeader, type StateReference, type Tx } from '@aztec/stdlib/tx';
|
|
@@ -80,21 +79,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
80
79
|
private previousProposal?: BlockProposal;
|
|
81
80
|
|
|
82
81
|
private myAddresses: EthAddress[];
|
|
83
|
-
private
|
|
82
|
+
private lastEpoch: bigint | undefined;
|
|
84
83
|
private epochCacheUpdateLoop: RunningPromise;
|
|
85
84
|
|
|
86
85
|
private blockProposalValidator: BlockProposalValidator;
|
|
87
|
-
|
|
86
|
+
private txCollector: ITxCollector;
|
|
88
87
|
private proposersOfInvalidBlocks: Set<EthAddress> = new Set();
|
|
89
88
|
|
|
90
|
-
|
|
89
|
+
constructor(
|
|
91
90
|
private blockBuilder: IFullNodeBlockBuilder,
|
|
92
91
|
private keyStore: ValidatorKeyStore,
|
|
93
92
|
private epochCache: EpochCache,
|
|
94
93
|
private p2pClient: P2P,
|
|
95
94
|
private blockSource: L2BlockSource,
|
|
96
95
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
97
|
-
private txProvider: TxProvider,
|
|
98
96
|
private config: ValidatorClientConfig &
|
|
99
97
|
Pick<SequencerConfig, 'txPublicSetupAllowList'> &
|
|
100
98
|
Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>,
|
|
@@ -110,6 +108,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
110
108
|
|
|
111
109
|
this.blockProposalValidator = new BlockProposalValidator(epochCache);
|
|
112
110
|
|
|
111
|
+
this.txCollector = new TxCollector(p2pClient, this.log);
|
|
112
|
+
|
|
113
113
|
// Refresh epoch cache every second to trigger alert if participation in committee changes
|
|
114
114
|
this.myAddresses = this.keyStore.getAddresses();
|
|
115
115
|
this.epochCacheUpdateLoop = new RunningPromise(this.handleEpochCommitteeUpdate.bind(this), log, 1000);
|
|
@@ -119,12 +119,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
119
119
|
|
|
120
120
|
private async handleEpochCommitteeUpdate() {
|
|
121
121
|
try {
|
|
122
|
-
const { committee, epoch } = await this.epochCache.getCommittee('
|
|
122
|
+
const { committee, epoch } = await this.epochCache.getCommittee('now');
|
|
123
123
|
if (!committee) {
|
|
124
124
|
this.log.trace(`No committee found for slot`);
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
|
-
if (epoch !== this.
|
|
127
|
+
if (epoch !== this.lastEpoch) {
|
|
128
128
|
const me = this.myAddresses;
|
|
129
129
|
const committeeSet = new Set(committee.map(v => v.toString()));
|
|
130
130
|
const inCommittee = me.filter(a => committeeSet.has(a.toString()));
|
|
@@ -137,7 +137,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
137
137
|
`Validators ${me.map(a => a.toString()).join(', ')} are not on the validator committee for epoch ${epoch}`,
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
|
-
this.
|
|
140
|
+
this.lastEpoch = epoch;
|
|
141
141
|
}
|
|
142
142
|
} catch (err) {
|
|
143
143
|
this.log.error(`Error updating epoch committee`, err);
|
|
@@ -152,7 +152,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
152
152
|
p2pClient: P2P,
|
|
153
153
|
blockSource: L2BlockSource,
|
|
154
154
|
l1ToL2MessageSource: L1ToL2MessageSource,
|
|
155
|
-
txProvider: TxProvider,
|
|
156
155
|
dateProvider: DateProvider = new DateProvider(),
|
|
157
156
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
158
157
|
) {
|
|
@@ -170,7 +169,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
170
169
|
p2pClient,
|
|
171
170
|
blockSource,
|
|
172
171
|
l1ToL2MessageSource,
|
|
173
|
-
txProvider,
|
|
174
172
|
config,
|
|
175
173
|
dateProvider,
|
|
176
174
|
telemetry,
|
|
@@ -200,7 +198,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
200
198
|
|
|
201
199
|
const myAddresses = this.keyStore.getAddresses();
|
|
202
200
|
|
|
203
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
201
|
+
const inCommittee = await this.epochCache.filterInCommittee(myAddresses);
|
|
204
202
|
if (inCommittee.length > 0) {
|
|
205
203
|
this.log.info(
|
|
206
204
|
`Started validator with addresses in current validator committee: ${inCommittee.map(a => a.toString()).join(', ')}`,
|
|
@@ -217,51 +215,33 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
217
215
|
}
|
|
218
216
|
|
|
219
217
|
public registerBlockProposalHandler() {
|
|
220
|
-
const handler = (block: BlockProposal, proposalSender: PeerId): Promise<BlockAttestation[] | undefined> =>
|
|
221
|
-
this.attestToProposal(block, proposalSender);
|
|
218
|
+
const handler = (block: BlockProposal, proposalSender: PeerId): Promise<BlockAttestation[] | undefined> => {
|
|
219
|
+
return this.attestToProposal(block, proposalSender);
|
|
220
|
+
};
|
|
222
221
|
this.p2pClient.registerBlockProposalHandler(handler);
|
|
223
222
|
}
|
|
224
223
|
|
|
225
224
|
async attestToProposal(proposal: BlockProposal, proposalSender: PeerId): Promise<BlockAttestation[] | undefined> {
|
|
226
|
-
const slotNumber = proposal.slotNumber.
|
|
225
|
+
const slotNumber = proposal.slotNumber.toNumber();
|
|
227
226
|
const blockNumber = proposal.blockNumber;
|
|
228
227
|
const proposer = proposal.getSender();
|
|
229
228
|
|
|
230
|
-
// Check that I have any address in current committee before attesting
|
|
231
|
-
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.keyStore.getAddresses());
|
|
232
|
-
const partOfCommittee = inCommittee.length > 0;
|
|
233
|
-
|
|
234
229
|
const proposalInfo = {
|
|
235
|
-
|
|
230
|
+
slotNumber,
|
|
231
|
+
blockNumber,
|
|
236
232
|
proposer: proposer.toString(),
|
|
233
|
+
archive: proposal.payload.archive.toString(),
|
|
234
|
+
txCount: proposal.payload.txHashes.length,
|
|
235
|
+
txHashes: proposal.payload.txHashes.map(txHash => txHash.toString()),
|
|
237
236
|
};
|
|
238
|
-
|
|
239
|
-
this.log.info(`Received proposal for slot ${slotNumber}`, {
|
|
240
|
-
...proposalInfo,
|
|
241
|
-
txHashes: proposal.txHashes.map(txHash => txHash.toString()),
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Collect txs from the proposal. Note that we do this before checking if we have an address in the
|
|
245
|
-
// current committee, since we want to collect txs anyway to facilitate propagation.
|
|
246
|
-
const { txs, missingTxs } = await this.txProvider.getTxsForBlockProposal(proposal, {
|
|
247
|
-
pinnedPeer: proposalSender,
|
|
248
|
-
deadline: this.getReexecutionDeadline(proposal, this.blockBuilder.getConfig()),
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// Check that I have any address in current committee before attesting
|
|
252
|
-
if (!partOfCommittee) {
|
|
253
|
-
this.log.verbose(`No validator in the current committee, skipping attestation`, proposalInfo);
|
|
254
|
-
return undefined;
|
|
255
|
-
}
|
|
237
|
+
this.log.info(`Received request to attest for slot ${slotNumber}`, proposalInfo);
|
|
256
238
|
|
|
257
239
|
// Check that the proposal is from the current proposer, or the next proposer.
|
|
258
240
|
// Q: Should this be moved to the block proposal validator, so we disregard proposals from anyone?
|
|
259
241
|
const invalidProposal = await this.blockProposalValidator.validate(proposal);
|
|
260
242
|
if (invalidProposal) {
|
|
261
|
-
this.log.warn(`Proposal is not valid, skipping attestation
|
|
262
|
-
|
|
263
|
-
this.metrics.incFailedAttestations(1, 'invalid_proposal');
|
|
264
|
-
}
|
|
243
|
+
this.log.warn(`Proposal is not valid, skipping attestation`);
|
|
244
|
+
this.metrics.incFailedAttestations(1, 'invalid_proposal');
|
|
265
245
|
return undefined;
|
|
266
246
|
}
|
|
267
247
|
|
|
@@ -271,50 +251,44 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
271
251
|
// would not be rebroadcasted. But it also means that nodes that have not fully synced would
|
|
272
252
|
// not rebroadcast the proposal.
|
|
273
253
|
if (blockNumber > INITIAL_L2_BLOCK_NUM) {
|
|
274
|
-
const
|
|
275
|
-
const deadline = this.getReexecutionDeadline(proposal, config);
|
|
276
|
-
const currentTime = this.dateProvider.now();
|
|
277
|
-
const timeoutDurationMs = deadline.getTime() - currentTime;
|
|
278
|
-
const parentBlock =
|
|
279
|
-
timeoutDurationMs <= 0
|
|
280
|
-
? undefined
|
|
281
|
-
: await retryUntil(
|
|
282
|
-
async () => {
|
|
283
|
-
const block = await this.blockSource.getBlock(blockNumber - 1);
|
|
284
|
-
if (block) {
|
|
285
|
-
return block;
|
|
286
|
-
}
|
|
287
|
-
await this.blockSource.syncImmediate();
|
|
288
|
-
return await this.blockSource.getBlock(blockNumber - 1);
|
|
289
|
-
},
|
|
290
|
-
'Force Archiver Sync',
|
|
291
|
-
timeoutDurationMs / 1000, // Continue retrying until the deadline
|
|
292
|
-
0.5, // Retry every 500ms
|
|
293
|
-
);
|
|
294
|
-
|
|
254
|
+
const parentBlock = await this.blockSource.getBlock(blockNumber - 1);
|
|
295
255
|
if (parentBlock === undefined) {
|
|
296
|
-
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation
|
|
297
|
-
|
|
298
|
-
this.metrics.incFailedAttestations(1, 'parent_block_not_found');
|
|
299
|
-
}
|
|
256
|
+
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation`);
|
|
257
|
+
this.metrics.incFailedAttestations(1, 'parent_block_not_found');
|
|
300
258
|
return undefined;
|
|
301
259
|
}
|
|
302
|
-
|
|
303
260
|
if (!proposal.payload.header.lastArchiveRoot.equals(parentBlock.archive.root)) {
|
|
304
261
|
this.log.warn(`Parent block archive root for proposal does not match, skipping attestation`, {
|
|
305
262
|
proposalLastArchiveRoot: proposal.payload.header.lastArchiveRoot.toString(),
|
|
306
263
|
parentBlockArchiveRoot: parentBlock.archive.root.toString(),
|
|
307
264
|
...proposalInfo,
|
|
308
265
|
});
|
|
309
|
-
|
|
310
|
-
this.metrics.incFailedAttestations(1, 'parent_block_does_not_match');
|
|
311
|
-
}
|
|
266
|
+
this.metrics.incFailedAttestations(1, 'parent_block_does_not_match');
|
|
312
267
|
return undefined;
|
|
313
268
|
}
|
|
314
269
|
}
|
|
315
270
|
|
|
271
|
+
// Collect txs from the proposal
|
|
272
|
+
const { missing, txs } = await this.txCollector.collectForBlockProposal(proposal, proposalSender);
|
|
273
|
+
|
|
274
|
+
// Check that I have any address in current committee before attesting
|
|
275
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
276
|
+
if (inCommittee.length === 0) {
|
|
277
|
+
this.log.verbose(`No validator in the committee, skipping attestation`);
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check that all of the transactions in the proposal are available in the tx pool before attesting
|
|
282
|
+
if (missing && missing.length > 0) {
|
|
283
|
+
this.log.warn(`Missing ${missing.length}/${proposal.payload.txHashes.length} txs to attest to proposal`, {
|
|
284
|
+
...proposalInfo,
|
|
285
|
+
missing,
|
|
286
|
+
});
|
|
287
|
+
this.metrics.incFailedAttestations(1, 'TransactionsNotAvailableError');
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
316
291
|
// Check that I have the same set of l1ToL2Messages as the proposal
|
|
317
|
-
// Q: Same as above, should this be part of p2p validation?
|
|
318
292
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
319
293
|
const computedInHash = await computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
320
294
|
const proposalInHash = proposal.payload.header.contentCommitment.inHash;
|
|
@@ -324,18 +298,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
324
298
|
computedInHash: computedInHash.toString(),
|
|
325
299
|
...proposalInfo,
|
|
326
300
|
});
|
|
327
|
-
|
|
328
|
-
this.metrics.incFailedAttestations(1, 'in_hash_mismatch');
|
|
329
|
-
}
|
|
330
|
-
return undefined;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Check that all of the transactions in the proposal are available in the tx pool before attesting
|
|
334
|
-
if (missingTxs.length > 0) {
|
|
335
|
-
this.log.warn(`Missing ${missingTxs.length} txs to attest to proposal`, { ...proposalInfo, missingTxs });
|
|
336
|
-
if (partOfCommittee) {
|
|
337
|
-
this.metrics.incFailedAttestations(1, 'TransactionsNotAvailableError');
|
|
338
|
-
}
|
|
301
|
+
this.metrics.incFailedAttestations(1, 'in_hash_mismatch');
|
|
339
302
|
return undefined;
|
|
340
303
|
}
|
|
341
304
|
|
|
@@ -378,8 +341,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
378
341
|
* @param proposal - The proposal to re-execute
|
|
379
342
|
*/
|
|
380
343
|
async reExecuteTransactions(proposal: BlockProposal, txs: Tx[], l1ToL2Messages: Fr[]): Promise<void> {
|
|
381
|
-
const { header } = proposal.payload;
|
|
382
|
-
const { txHashes } = proposal;
|
|
344
|
+
const { header, txHashes } = proposal.payload;
|
|
383
345
|
|
|
384
346
|
// If we do not have all of the transactions, then we should fail
|
|
385
347
|
if (txs.length !== txHashes.length) {
|
|
@@ -514,7 +476,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
514
476
|
|
|
515
477
|
const proposalId = proposal.archive.toString();
|
|
516
478
|
// adds attestations for all of my addresses locally
|
|
517
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
479
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
518
480
|
await this.doAttestToProposal(proposal, inCommittee);
|
|
519
481
|
|
|
520
482
|
const myAddresses = this.keyStore.getAddresses();
|