@aztec/validator-client 1.0.0-nightly.20250708 → 1.0.0-staging.1
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 +43 -45
- 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 +45 -48
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;AAK9C,OAAO,EAAE,YAAY,EAAS,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
|
package/dest/validator.js
CHANGED
|
@@ -6,6 +6,7 @@ import { retryUntil } from '@aztec/foundation/retry';
|
|
|
6
6
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
7
7
|
import { sleep } from '@aztec/foundation/sleep';
|
|
8
8
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
9
|
+
import { TxCollector } from '@aztec/p2p';
|
|
9
10
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
10
11
|
import { computeInHashFromL1ToL2Messages } from '@aztec/prover-client/helpers';
|
|
11
12
|
import { Offense, WANT_TO_SLASH_EVENT } from '@aztec/slasher/config';
|
|
@@ -29,7 +30,6 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
29
30
|
p2pClient;
|
|
30
31
|
blockSource;
|
|
31
32
|
l1ToL2MessageSource;
|
|
32
|
-
txProvider;
|
|
33
33
|
config;
|
|
34
34
|
dateProvider;
|
|
35
35
|
log;
|
|
@@ -39,16 +39,18 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
39
39
|
// Used to check if we are sending the same proposal twice
|
|
40
40
|
previousProposal;
|
|
41
41
|
myAddresses;
|
|
42
|
-
|
|
42
|
+
lastEpoch;
|
|
43
43
|
epochCacheUpdateLoop;
|
|
44
44
|
blockProposalValidator;
|
|
45
|
+
txCollector;
|
|
45
46
|
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.
|
|
47
|
+
constructor(blockBuilder, keyStore, epochCache, p2pClient, blockSource, l1ToL2MessageSource, config, dateProvider = new DateProvider(), telemetry = getTelemetryClient(), log = createLogger('validator')){
|
|
48
|
+
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
49
|
this.tracer = telemetry.getTracer('Validator');
|
|
49
50
|
this.metrics = new ValidatorMetrics(telemetry);
|
|
50
51
|
this.validationService = new ValidationService(keyStore);
|
|
51
52
|
this.blockProposalValidator = new BlockProposalValidator(epochCache);
|
|
53
|
+
this.txCollector = new TxCollector(p2pClient, this.log);
|
|
52
54
|
// Refresh epoch cache every second to trigger alert if participation in committee changes
|
|
53
55
|
this.myAddresses = this.keyStore.getAddresses();
|
|
54
56
|
this.epochCacheUpdateLoop = new RunningPromise(this.handleEpochCommitteeUpdate.bind(this), log, 1000);
|
|
@@ -56,12 +58,12 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
56
58
|
}
|
|
57
59
|
async handleEpochCommitteeUpdate() {
|
|
58
60
|
try {
|
|
59
|
-
const { committee, epoch } = await this.epochCache.getCommittee('
|
|
61
|
+
const { committee, epoch } = await this.epochCache.getCommittee('now');
|
|
60
62
|
if (!committee) {
|
|
61
63
|
this.log.trace(`No committee found for slot`);
|
|
62
64
|
return;
|
|
63
65
|
}
|
|
64
|
-
if (epoch !== this.
|
|
66
|
+
if (epoch !== this.lastEpoch) {
|
|
65
67
|
const me = this.myAddresses;
|
|
66
68
|
const committeeSet = new Set(committee.map((v)=>v.toString()));
|
|
67
69
|
const inCommittee = me.filter((a)=>committeeSet.has(a.toString()));
|
|
@@ -70,19 +72,19 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
70
72
|
} else {
|
|
71
73
|
this.log.verbose(`Validators ${me.map((a)=>a.toString()).join(', ')} are not on the validator committee for epoch ${epoch}`);
|
|
72
74
|
}
|
|
73
|
-
this.
|
|
75
|
+
this.lastEpoch = epoch;
|
|
74
76
|
}
|
|
75
77
|
} catch (err) {
|
|
76
78
|
this.log.error(`Error updating epoch committee`, err);
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
|
-
static new(config, blockBuilder, epochCache, p2pClient, blockSource, l1ToL2MessageSource,
|
|
81
|
+
static new(config, blockBuilder, epochCache, p2pClient, blockSource, l1ToL2MessageSource, dateProvider = new DateProvider(), telemetry = getTelemetryClient()) {
|
|
80
82
|
if (!config.validatorPrivateKeys.getValue().length) {
|
|
81
83
|
throw new InvalidValidatorPrivateKeyError();
|
|
82
84
|
}
|
|
83
85
|
const privateKeys = config.validatorPrivateKeys.getValue().map(validatePrivateKey);
|
|
84
86
|
const localKeyStore = new LocalKeyStore(privateKeys);
|
|
85
|
-
const validator = new ValidatorClient(blockBuilder, localKeyStore, epochCache, p2pClient, blockSource, l1ToL2MessageSource,
|
|
87
|
+
const validator = new ValidatorClient(blockBuilder, localKeyStore, epochCache, p2pClient, blockSource, l1ToL2MessageSource, config, dateProvider, telemetry);
|
|
86
88
|
validator.registerBlockProposalHandler();
|
|
87
89
|
return validator;
|
|
88
90
|
}
|
|
@@ -98,7 +100,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
98
100
|
// Sync the committee from the smart contract
|
|
99
101
|
// https://github.com/AztecProtocol/aztec-packages/issues/7962
|
|
100
102
|
const myAddresses = this.keyStore.getAddresses();
|
|
101
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
103
|
+
const inCommittee = await this.epochCache.filterInCommittee(myAddresses);
|
|
102
104
|
if (inCommittee.length > 0) {
|
|
103
105
|
this.log.info(`Started validator with addresses in current validator committee: ${inCommittee.map((a)=>a.toString()).join(', ')}`);
|
|
104
106
|
} else {
|
|
@@ -111,40 +113,32 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
111
113
|
await this.epochCacheUpdateLoop.stop();
|
|
112
114
|
}
|
|
113
115
|
registerBlockProposalHandler() {
|
|
114
|
-
const handler = (block, proposalSender)=>
|
|
116
|
+
const handler = (block, proposalSender)=>{
|
|
117
|
+
return this.attestToProposal(block, proposalSender);
|
|
118
|
+
};
|
|
115
119
|
this.p2pClient.registerBlockProposalHandler(handler);
|
|
116
120
|
}
|
|
117
121
|
async attestToProposal(proposal, proposalSender) {
|
|
118
|
-
const slotNumber = proposal.slotNumber.
|
|
122
|
+
const slotNumber = proposal.slotNumber.toNumber();
|
|
119
123
|
const blockNumber = proposal.blockNumber;
|
|
120
124
|
const proposer = proposal.getSender();
|
|
121
125
|
// Check that I have any address in current committee before attesting
|
|
122
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
126
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
123
127
|
const partOfCommittee = inCommittee.length > 0;
|
|
124
128
|
const proposalInfo = {
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
slotNumber,
|
|
130
|
+
blockNumber,
|
|
131
|
+
proposer: proposer.toString(),
|
|
132
|
+
archive: proposal.payload.archive.toString(),
|
|
133
|
+
txCount: proposal.payload.txHashes.length,
|
|
134
|
+
txHashes: proposal.payload.txHashes.map((txHash)=>txHash.toString())
|
|
127
135
|
};
|
|
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
|
-
}
|
|
136
|
+
this.log.info(`Received request to attest for slot ${slotNumber}`, proposalInfo);
|
|
143
137
|
// Check that the proposal is from the current proposer, or the next proposer.
|
|
144
138
|
// Q: Should this be moved to the block proposal validator, so we disregard proposals from anyone?
|
|
145
139
|
const invalidProposal = await this.blockProposalValidator.validate(proposal);
|
|
146
140
|
if (invalidProposal) {
|
|
147
|
-
this.log.warn(`Proposal is not valid, skipping attestation
|
|
141
|
+
this.log.warn(`Proposal is not valid, skipping attestation`);
|
|
148
142
|
if (partOfCommittee) {
|
|
149
143
|
this.metrics.incFailedAttestations(1, 'invalid_proposal');
|
|
150
144
|
}
|
|
@@ -169,7 +163,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
169
163
|
return await this.blockSource.getBlock(blockNumber - 1);
|
|
170
164
|
}, 'Force Archiver Sync', timeoutDurationMs / 1000, 0.5);
|
|
171
165
|
if (parentBlock === undefined) {
|
|
172
|
-
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation
|
|
166
|
+
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation`);
|
|
173
167
|
if (partOfCommittee) {
|
|
174
168
|
this.metrics.incFailedAttestations(1, 'parent_block_not_found');
|
|
175
169
|
}
|
|
@@ -187,8 +181,20 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
187
181
|
return undefined;
|
|
188
182
|
}
|
|
189
183
|
}
|
|
184
|
+
// Collect txs from the proposal
|
|
185
|
+
const { missing, txs } = await this.txCollector.collectForBlockProposal(proposal, proposalSender);
|
|
186
|
+
// Check that all of the transactions in the proposal are available in the tx pool before attesting
|
|
187
|
+
if (missing && missing.length > 0) {
|
|
188
|
+
this.log.warn(`Missing ${missing.length}/${proposal.payload.txHashes.length} txs to attest to proposal`, {
|
|
189
|
+
...proposalInfo,
|
|
190
|
+
missing
|
|
191
|
+
});
|
|
192
|
+
if (partOfCommittee) {
|
|
193
|
+
this.metrics.incFailedAttestations(1, 'tx_not_available');
|
|
194
|
+
}
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
190
197
|
// 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
198
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
193
199
|
const computedInHash = await computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
194
200
|
const proposalInHash = proposal.payload.header.contentCommitment.inHash;
|
|
@@ -203,15 +209,8 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
203
209
|
}
|
|
204
210
|
return undefined;
|
|
205
211
|
}
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
}
|
|
212
|
+
if (!partOfCommittee) {
|
|
213
|
+
this.log.verbose(`No validator in the committee, skipping attestation`);
|
|
215
214
|
return undefined;
|
|
216
215
|
}
|
|
217
216
|
// Try re-executing the transactions in the proposal
|
|
@@ -245,8 +244,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
245
244
|
* Re-execute the transactions in the proposal and check that the state updates match the header state
|
|
246
245
|
* @param proposal - The proposal to re-execute
|
|
247
246
|
*/ async reExecuteTransactions(proposal, txs, l1ToL2Messages) {
|
|
248
|
-
const { header } = proposal.payload;
|
|
249
|
-
const { txHashes } = proposal;
|
|
247
|
+
const { header, txHashes } = proposal.payload;
|
|
250
248
|
// If we do not have all of the transactions, then we should fail
|
|
251
249
|
if (txs.length !== txHashes.length) {
|
|
252
250
|
const foundTxHashes = await Promise.all(txs.map(async (tx)=>await tx.getTxHash()));
|
|
@@ -336,7 +334,7 @@ const MAX_PROPOSERS_OF_INVALID_BLOCKS = 1000;
|
|
|
336
334
|
}
|
|
337
335
|
const proposalId = proposal.archive.toString();
|
|
338
336
|
// adds attestations for all of my addresses locally
|
|
339
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
337
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
340
338
|
await this.doAttestToProposal(proposal, inCommittee);
|
|
341
339
|
const myAddresses = this.keyStore.getAddresses();
|
|
342
340
|
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.1",
|
|
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.1",
|
|
68
|
+
"@aztec/epoch-cache": "1.0.0-staging.1",
|
|
69
|
+
"@aztec/ethereum": "1.0.0-staging.1",
|
|
70
|
+
"@aztec/foundation": "1.0.0-staging.1",
|
|
71
|
+
"@aztec/p2p": "1.0.0-staging.1",
|
|
72
|
+
"@aztec/prover-client": "1.0.0-staging.1",
|
|
73
|
+
"@aztec/slasher": "1.0.0-staging.1",
|
|
74
|
+
"@aztec/stdlib": "1.0.0-staging.1",
|
|
75
|
+
"@aztec/telemetry-client": "1.0.0-staging.1",
|
|
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
|
@@ -9,7 +9,7 @@ import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
|
9
9
|
import { sleep } from '@aztec/foundation/sleep';
|
|
10
10
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
11
11
|
import type { P2P, PeerId } from '@aztec/p2p';
|
|
12
|
-
import {
|
|
12
|
+
import { TxCollector } from '@aztec/p2p';
|
|
13
13
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
14
14
|
import { computeInHashFromL1ToL2Messages } from '@aztec/prover-client/helpers';
|
|
15
15
|
import {
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from '@aztec/slasher/config';
|
|
23
23
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
24
24
|
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
25
|
-
import type { IFullNodeBlockBuilder, SequencerConfig } from '@aztec/stdlib/interfaces/server';
|
|
25
|
+
import type { IFullNodeBlockBuilder, ITxCollector, SequencerConfig } from '@aztec/stdlib/interfaces/server';
|
|
26
26
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
27
27
|
import type { BlockAttestation, BlockProposal, BlockProposalOptions } from '@aztec/stdlib/p2p';
|
|
28
28
|
import { GlobalVariables, type ProposedBlockHeader, type StateReference, type Tx } from '@aztec/stdlib/tx';
|
|
@@ -80,21 +80,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
80
80
|
private previousProposal?: BlockProposal;
|
|
81
81
|
|
|
82
82
|
private myAddresses: EthAddress[];
|
|
83
|
-
private
|
|
83
|
+
private lastEpoch: bigint | undefined;
|
|
84
84
|
private epochCacheUpdateLoop: RunningPromise;
|
|
85
85
|
|
|
86
86
|
private blockProposalValidator: BlockProposalValidator;
|
|
87
|
-
|
|
87
|
+
private txCollector: ITxCollector;
|
|
88
88
|
private proposersOfInvalidBlocks: Set<EthAddress> = new Set();
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
constructor(
|
|
91
91
|
private blockBuilder: IFullNodeBlockBuilder,
|
|
92
92
|
private keyStore: ValidatorKeyStore,
|
|
93
93
|
private epochCache: EpochCache,
|
|
94
94
|
private p2pClient: P2P,
|
|
95
95
|
private blockSource: L2BlockSource,
|
|
96
96
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
97
|
-
private txProvider: TxProvider,
|
|
98
97
|
private config: ValidatorClientConfig &
|
|
99
98
|
Pick<SequencerConfig, 'txPublicSetupAllowList'> &
|
|
100
99
|
Pick<SlasherConfig, 'slashInvalidBlockEnabled' | 'slashInvalidBlockPenalty' | 'slashInvalidBlockMaxPenalty'>,
|
|
@@ -110,6 +109,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
110
109
|
|
|
111
110
|
this.blockProposalValidator = new BlockProposalValidator(epochCache);
|
|
112
111
|
|
|
112
|
+
this.txCollector = new TxCollector(p2pClient, this.log);
|
|
113
|
+
|
|
113
114
|
// Refresh epoch cache every second to trigger alert if participation in committee changes
|
|
114
115
|
this.myAddresses = this.keyStore.getAddresses();
|
|
115
116
|
this.epochCacheUpdateLoop = new RunningPromise(this.handleEpochCommitteeUpdate.bind(this), log, 1000);
|
|
@@ -119,12 +120,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
119
120
|
|
|
120
121
|
private async handleEpochCommitteeUpdate() {
|
|
121
122
|
try {
|
|
122
|
-
const { committee, epoch } = await this.epochCache.getCommittee('
|
|
123
|
+
const { committee, epoch } = await this.epochCache.getCommittee('now');
|
|
123
124
|
if (!committee) {
|
|
124
125
|
this.log.trace(`No committee found for slot`);
|
|
125
126
|
return;
|
|
126
127
|
}
|
|
127
|
-
if (epoch !== this.
|
|
128
|
+
if (epoch !== this.lastEpoch) {
|
|
128
129
|
const me = this.myAddresses;
|
|
129
130
|
const committeeSet = new Set(committee.map(v => v.toString()));
|
|
130
131
|
const inCommittee = me.filter(a => committeeSet.has(a.toString()));
|
|
@@ -137,7 +138,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
137
138
|
`Validators ${me.map(a => a.toString()).join(', ')} are not on the validator committee for epoch ${epoch}`,
|
|
138
139
|
);
|
|
139
140
|
}
|
|
140
|
-
this.
|
|
141
|
+
this.lastEpoch = epoch;
|
|
141
142
|
}
|
|
142
143
|
} catch (err) {
|
|
143
144
|
this.log.error(`Error updating epoch committee`, err);
|
|
@@ -152,7 +153,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
152
153
|
p2pClient: P2P,
|
|
153
154
|
blockSource: L2BlockSource,
|
|
154
155
|
l1ToL2MessageSource: L1ToL2MessageSource,
|
|
155
|
-
txProvider: TxProvider,
|
|
156
156
|
dateProvider: DateProvider = new DateProvider(),
|
|
157
157
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
158
158
|
) {
|
|
@@ -170,7 +170,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
170
170
|
p2pClient,
|
|
171
171
|
blockSource,
|
|
172
172
|
l1ToL2MessageSource,
|
|
173
|
-
txProvider,
|
|
174
173
|
config,
|
|
175
174
|
dateProvider,
|
|
176
175
|
telemetry,
|
|
@@ -200,7 +199,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
200
199
|
|
|
201
200
|
const myAddresses = this.keyStore.getAddresses();
|
|
202
201
|
|
|
203
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
202
|
+
const inCommittee = await this.epochCache.filterInCommittee(myAddresses);
|
|
204
203
|
if (inCommittee.length > 0) {
|
|
205
204
|
this.log.info(
|
|
206
205
|
`Started validator with addresses in current validator committee: ${inCommittee.map(a => a.toString()).join(', ')}`,
|
|
@@ -217,48 +216,36 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
217
216
|
}
|
|
218
217
|
|
|
219
218
|
public registerBlockProposalHandler() {
|
|
220
|
-
const handler = (block: BlockProposal, proposalSender: PeerId): Promise<BlockAttestation[] | undefined> =>
|
|
221
|
-
this.attestToProposal(block, proposalSender);
|
|
219
|
+
const handler = (block: BlockProposal, proposalSender: PeerId): Promise<BlockAttestation[] | undefined> => {
|
|
220
|
+
return this.attestToProposal(block, proposalSender);
|
|
221
|
+
};
|
|
222
222
|
this.p2pClient.registerBlockProposalHandler(handler);
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
async attestToProposal(proposal: BlockProposal, proposalSender: PeerId): Promise<BlockAttestation[] | undefined> {
|
|
226
|
-
const slotNumber = proposal.slotNumber.
|
|
226
|
+
const slotNumber = proposal.slotNumber.toNumber();
|
|
227
227
|
const blockNumber = proposal.blockNumber;
|
|
228
228
|
const proposer = proposal.getSender();
|
|
229
229
|
|
|
230
230
|
// Check that I have any address in current committee before attesting
|
|
231
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
231
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
232
232
|
const partOfCommittee = inCommittee.length > 0;
|
|
233
233
|
|
|
234
234
|
const proposalInfo = {
|
|
235
|
-
|
|
235
|
+
slotNumber,
|
|
236
|
+
blockNumber,
|
|
236
237
|
proposer: proposer.toString(),
|
|
238
|
+
archive: proposal.payload.archive.toString(),
|
|
239
|
+
txCount: proposal.payload.txHashes.length,
|
|
240
|
+
txHashes: proposal.payload.txHashes.map(txHash => txHash.toString()),
|
|
237
241
|
};
|
|
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
|
-
}
|
|
242
|
+
this.log.info(`Received request to attest for slot ${slotNumber}`, proposalInfo);
|
|
256
243
|
|
|
257
244
|
// Check that the proposal is from the current proposer, or the next proposer.
|
|
258
245
|
// Q: Should this be moved to the block proposal validator, so we disregard proposals from anyone?
|
|
259
246
|
const invalidProposal = await this.blockProposalValidator.validate(proposal);
|
|
260
247
|
if (invalidProposal) {
|
|
261
|
-
this.log.warn(`Proposal is not valid, skipping attestation
|
|
248
|
+
this.log.warn(`Proposal is not valid, skipping attestation`);
|
|
262
249
|
if (partOfCommittee) {
|
|
263
250
|
this.metrics.incFailedAttestations(1, 'invalid_proposal');
|
|
264
251
|
}
|
|
@@ -293,13 +280,14 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
293
280
|
);
|
|
294
281
|
|
|
295
282
|
if (parentBlock === undefined) {
|
|
296
|
-
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation
|
|
283
|
+
this.log.warn(`Parent block for ${blockNumber} not found, skipping attestation`);
|
|
284
|
+
|
|
297
285
|
if (partOfCommittee) {
|
|
298
286
|
this.metrics.incFailedAttestations(1, 'parent_block_not_found');
|
|
299
287
|
}
|
|
288
|
+
|
|
300
289
|
return undefined;
|
|
301
290
|
}
|
|
302
|
-
|
|
303
291
|
if (!proposal.payload.header.lastArchiveRoot.equals(parentBlock.archive.root)) {
|
|
304
292
|
this.log.warn(`Parent block archive root for proposal does not match, skipping attestation`, {
|
|
305
293
|
proposalLastArchiveRoot: proposal.payload.header.lastArchiveRoot.toString(),
|
|
@@ -313,8 +301,22 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
313
301
|
}
|
|
314
302
|
}
|
|
315
303
|
|
|
304
|
+
// Collect txs from the proposal
|
|
305
|
+
const { missing, txs } = await this.txCollector.collectForBlockProposal(proposal, proposalSender);
|
|
306
|
+
|
|
307
|
+
// Check that all of the transactions in the proposal are available in the tx pool before attesting
|
|
308
|
+
if (missing && missing.length > 0) {
|
|
309
|
+
this.log.warn(`Missing ${missing.length}/${proposal.payload.txHashes.length} txs to attest to proposal`, {
|
|
310
|
+
...proposalInfo,
|
|
311
|
+
missing,
|
|
312
|
+
});
|
|
313
|
+
if (partOfCommittee) {
|
|
314
|
+
this.metrics.incFailedAttestations(1, 'tx_not_available');
|
|
315
|
+
}
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
316
319
|
// 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
320
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
319
321
|
const computedInHash = await computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
320
322
|
const proposalInHash = proposal.payload.header.contentCommitment.inHash;
|
|
@@ -330,12 +332,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
330
332
|
return undefined;
|
|
331
333
|
}
|
|
332
334
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
this.log.warn(`Missing ${missingTxs.length} txs to attest to proposal`, { ...proposalInfo, missingTxs });
|
|
336
|
-
if (partOfCommittee) {
|
|
337
|
-
this.metrics.incFailedAttestations(1, 'TransactionsNotAvailableError');
|
|
338
|
-
}
|
|
335
|
+
if (!partOfCommittee) {
|
|
336
|
+
this.log.verbose(`No validator in the committee, skipping attestation`);
|
|
339
337
|
return undefined;
|
|
340
338
|
}
|
|
341
339
|
|
|
@@ -378,8 +376,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
378
376
|
* @param proposal - The proposal to re-execute
|
|
379
377
|
*/
|
|
380
378
|
async reExecuteTransactions(proposal: BlockProposal, txs: Tx[], l1ToL2Messages: Fr[]): Promise<void> {
|
|
381
|
-
const { header } = proposal.payload;
|
|
382
|
-
const { txHashes } = proposal;
|
|
379
|
+
const { header, txHashes } = proposal.payload;
|
|
383
380
|
|
|
384
381
|
// If we do not have all of the transactions, then we should fail
|
|
385
382
|
if (txs.length !== txHashes.length) {
|
|
@@ -514,7 +511,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
514
511
|
|
|
515
512
|
const proposalId = proposal.archive.toString();
|
|
516
513
|
// adds attestations for all of my addresses locally
|
|
517
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
514
|
+
const inCommittee = await this.epochCache.filterInCommittee(this.keyStore.getAddresses());
|
|
518
515
|
await this.doAttestToProposal(proposal, inCommittee);
|
|
519
516
|
|
|
520
517
|
const myAddresses = this.keyStore.getAddresses();
|