@aztec/validator-client 3.0.0-nightly.20251023 → 3.0.0-nightly.20251025
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/block_proposal_handler.d.ts +23 -18
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +81 -56
- package/dest/duties/validation_service.d.ts +1 -2
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +4 -5
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +3 -1
- package/dest/validator.d.ts +3 -4
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +9 -7
- package/package.json +11 -11
- package/src/block_proposal_handler.ts +110 -84
- package/src/duties/validation_service.ts +2 -5
- package/src/factory.ts +3 -1
- package/src/validator.ts +15 -16
|
@@ -3,24 +3,32 @@ import { DateProvider } from '@aztec/foundation/timer';
|
|
|
3
3
|
import type { P2P, PeerId } from '@aztec/p2p';
|
|
4
4
|
import { TxProvider } from '@aztec/p2p';
|
|
5
5
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
6
|
-
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
6
|
+
import type { L2Block, L2BlockSource } from '@aztec/stdlib/block';
|
|
7
7
|
import type { IFullNodeBlockBuilder, ValidatorClientFullConfig } from '@aztec/stdlib/interfaces/server';
|
|
8
8
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
9
9
|
import { type BlockProposal } from '@aztec/stdlib/p2p';
|
|
10
10
|
import { type FailedTx, type Tx } from '@aztec/stdlib/tx';
|
|
11
11
|
import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
|
|
12
12
|
import type { ValidatorMetrics } from './metrics.js';
|
|
13
|
-
export type BlockProposalValidationFailureReason = 'invalid_proposal' | 'parent_block_not_found' | '
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
export type BlockProposalValidationFailureReason = 'invalid_proposal' | 'parent_block_not_found' | 'parent_block_wrong_slot' | 'in_hash_mismatch' | 'block_number_already_exists' | 'txs_not_available' | 'state_mismatch' | 'failed_txs' | 'timeout' | 'unknown_error';
|
|
14
|
+
type ReexecuteTransactionsResult = {
|
|
15
|
+
block: L2Block;
|
|
16
|
+
failedTxs: FailedTx[];
|
|
17
|
+
reexecutionTimeMs: number;
|
|
18
|
+
totalManaUsed: number;
|
|
19
|
+
};
|
|
20
|
+
export type BlockProposalValidationSuccessResult = {
|
|
21
|
+
isValid: true;
|
|
22
|
+
blockNumber: number;
|
|
23
|
+
reexecutionResult?: ReexecuteTransactionsResult;
|
|
24
|
+
};
|
|
25
|
+
export type BlockProposalValidationFailureResult = {
|
|
26
|
+
isValid: false;
|
|
27
|
+
reason: BlockProposalValidationFailureReason;
|
|
28
|
+
blockNumber?: number;
|
|
29
|
+
reexecutionResult?: ReexecuteTransactionsResult;
|
|
30
|
+
};
|
|
31
|
+
export type BlockProposalValidationResult = BlockProposalValidationSuccessResult | BlockProposalValidationFailureResult;
|
|
24
32
|
export declare class BlockProposalHandler {
|
|
25
33
|
private blockBuilder;
|
|
26
34
|
private blockSource;
|
|
@@ -35,13 +43,10 @@ export declare class BlockProposalHandler {
|
|
|
35
43
|
constructor(blockBuilder: IFullNodeBlockBuilder, blockSource: L2BlockSource, l1ToL2MessageSource: L1ToL2MessageSource, txProvider: TxProvider, blockProposalValidator: BlockProposalValidator, config: ValidatorClientFullConfig, metrics?: ValidatorMetrics | undefined, dateProvider?: DateProvider, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
|
|
36
44
|
registerForReexecution(p2pClient: P2P): BlockProposalHandler;
|
|
37
45
|
handleBlockProposal(proposal: BlockProposal, proposalSender: PeerId, shouldReexecute: boolean): Promise<BlockProposalValidationResult>;
|
|
46
|
+
private getParentBlock;
|
|
38
47
|
private getReexecutionDeadline;
|
|
39
48
|
private getReexecuteFailureReason;
|
|
40
|
-
reexecuteTransactions(proposal: BlockProposal, txs: Tx[], l1ToL2Messages: Fr[]): Promise<
|
|
41
|
-
block: any;
|
|
42
|
-
failedTxs: FailedTx[];
|
|
43
|
-
reexecutionTimeMs: number;
|
|
44
|
-
totalManaUsed: number;
|
|
45
|
-
}>;
|
|
49
|
+
reexecuteTransactions(proposal: BlockProposal, blockNumber: number, txs: Tx[], l1ToL2Messages: Fr[]): Promise<ReexecuteTransactionsResult>;
|
|
46
50
|
}
|
|
51
|
+
export {};
|
|
47
52
|
//# sourceMappingURL=block_proposal_handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
+
import { TimeoutError } from '@aztec/foundation/error';
|
|
2
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
@@ -36,16 +37,16 @@ export class BlockProposalHandler {
|
|
|
36
37
|
const handler = async (proposal, proposalSender)=>{
|
|
37
38
|
try {
|
|
38
39
|
const result = await this.handleBlockProposal(proposal, proposalSender, true);
|
|
39
|
-
if (result.isValid
|
|
40
|
+
if (result.isValid) {
|
|
40
41
|
this.log.info(`Non-validator reexecution completed for slot ${proposal.slotNumber.toBigInt()}`, {
|
|
41
|
-
blockNumber:
|
|
42
|
-
reexecutionTimeMs: result.reexecutionResult
|
|
43
|
-
totalManaUsed: result.reexecutionResult
|
|
44
|
-
numTxs: result.reexecutionResult
|
|
42
|
+
blockNumber: result.blockNumber,
|
|
43
|
+
reexecutionTimeMs: result.reexecutionResult?.reexecutionTimeMs,
|
|
44
|
+
totalManaUsed: result.reexecutionResult?.totalManaUsed,
|
|
45
|
+
numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0
|
|
45
46
|
});
|
|
46
47
|
} else {
|
|
47
48
|
this.log.warn(`Non-validator reexecution failed for slot ${proposal.slotNumber.toBigInt()}`, {
|
|
48
|
-
blockNumber:
|
|
49
|
+
blockNumber: result.blockNumber,
|
|
49
50
|
reason: result.reason
|
|
50
51
|
});
|
|
51
52
|
}
|
|
@@ -59,8 +60,8 @@ export class BlockProposalHandler {
|
|
|
59
60
|
}
|
|
60
61
|
async handleBlockProposal(proposal, proposalSender, shouldReexecute) {
|
|
61
62
|
const slotNumber = proposal.slotNumber.toBigInt();
|
|
62
|
-
const blockNumber = proposal.blockNumber;
|
|
63
63
|
const proposer = proposal.getSender();
|
|
64
|
+
const config = this.blockBuilder.getConfig();
|
|
64
65
|
// Reject proposals with invalid signatures
|
|
65
66
|
if (!proposer) {
|
|
66
67
|
this.log.warn(`Received proposal with invalid signature for slot ${slotNumber}`);
|
|
@@ -87,42 +88,45 @@ export class BlockProposalHandler {
|
|
|
87
88
|
reason: 'invalid_proposal'
|
|
88
89
|
};
|
|
89
90
|
}
|
|
91
|
+
// Check that the parent proposal is a block we know, otherwise reexecution would fail
|
|
92
|
+
const parentBlockHeader = await this.getParentBlock(proposal);
|
|
93
|
+
if (parentBlockHeader === undefined) {
|
|
94
|
+
this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
|
|
95
|
+
return {
|
|
96
|
+
isValid: false,
|
|
97
|
+
reason: 'parent_block_not_found'
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Check that the parent block's slot is less than the proposal's slot (should not happen, but we check anyway)
|
|
101
|
+
if (parentBlockHeader !== 'genesis' && parentBlockHeader.getSlot() >= slotNumber) {
|
|
102
|
+
this.log.warn(`Parent block slot is greater than or equal to proposal slot, skipping processing`, {
|
|
103
|
+
parentBlockSlot: parentBlockHeader.getSlot().toString(),
|
|
104
|
+
proposalSlot: slotNumber.toString(),
|
|
105
|
+
...proposalInfo
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
isValid: false,
|
|
109
|
+
reason: 'parent_block_wrong_slot'
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Compute the block number based on the parent block
|
|
113
|
+
const blockNumber = parentBlockHeader === 'genesis' ? INITIAL_L2_BLOCK_NUM : parentBlockHeader.getBlockNumber() + 1;
|
|
114
|
+
// Check that this block number does not exist already
|
|
115
|
+
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
116
|
+
if (existingBlock) {
|
|
117
|
+
this.log.warn(`Block number ${blockNumber} already exists, skipping processing`, proposalInfo);
|
|
118
|
+
return {
|
|
119
|
+
isValid: false,
|
|
120
|
+
blockNumber,
|
|
121
|
+
reason: 'block_number_already_exists'
|
|
122
|
+
};
|
|
123
|
+
}
|
|
90
124
|
// Collect txs from the proposal. We start doing this as early as possible,
|
|
91
|
-
// and we do it even if we don't plan to re-execute the txs, so that we have them
|
|
92
|
-
|
|
93
|
-
const config = this.blockBuilder.getConfig();
|
|
94
|
-
const { txs, missingTxs } = await this.txProvider.getTxsForBlockProposal(proposal, {
|
|
125
|
+
// and we do it even if we don't plan to re-execute the txs, so that we have them if another node needs them.
|
|
126
|
+
const { txs, missingTxs } = await this.txProvider.getTxsForBlockProposal(proposal, blockNumber, {
|
|
95
127
|
pinnedPeer: proposalSender,
|
|
96
|
-
deadline: this.getReexecutionDeadline(
|
|
128
|
+
deadline: this.getReexecutionDeadline(slotNumber, config)
|
|
97
129
|
});
|
|
98
|
-
// Check that the parent proposal is a block we know, otherwise reexecution would fail
|
|
99
|
-
if (blockNumber > INITIAL_L2_BLOCK_NUM) {
|
|
100
|
-
const deadline = this.getReexecutionDeadline(proposal, config);
|
|
101
|
-
const currentTime = this.dateProvider.now();
|
|
102
|
-
const timeoutDurationMs = deadline.getTime() - currentTime;
|
|
103
|
-
const parentBlock = await this.blockSource.getBlock(blockNumber - 1) ?? (timeoutDurationMs <= 0 ? undefined : await retryUntil(async ()=>{
|
|
104
|
-
await this.blockSource.syncImmediate();
|
|
105
|
-
return await this.blockSource.getBlock(blockNumber - 1);
|
|
106
|
-
}, 'Force Archiver Sync', timeoutDurationMs / 1000, 0.5));
|
|
107
|
-
if (parentBlock === undefined) {
|
|
108
|
-
this.log.warn(`Parent block for ${blockNumber} not found, skipping processing`, proposalInfo);
|
|
109
|
-
return {
|
|
110
|
-
isValid: false,
|
|
111
|
-
reason: 'parent_block_not_found'
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
if (!proposal.payload.header.lastArchiveRoot.equals(parentBlock.archive.root)) {
|
|
115
|
-
this.log.warn(`Parent block archive root for proposal does not match, skipping processing`, {
|
|
116
|
-
proposalLastArchiveRoot: proposal.payload.header.lastArchiveRoot.toString(),
|
|
117
|
-
parentBlockArchiveRoot: parentBlock.archive.root.toString(),
|
|
118
|
-
...proposalInfo
|
|
119
|
-
});
|
|
120
|
-
return {
|
|
121
|
-
isValid: false,
|
|
122
|
-
reason: 'parent_block_does_not_match'
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
130
|
// Check that I have the same set of l1ToL2Messages as the proposal
|
|
127
131
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
128
132
|
const computedInHash = await computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
@@ -135,18 +139,10 @@ export class BlockProposalHandler {
|
|
|
135
139
|
});
|
|
136
140
|
return {
|
|
137
141
|
isValid: false,
|
|
142
|
+
blockNumber,
|
|
138
143
|
reason: 'in_hash_mismatch'
|
|
139
144
|
};
|
|
140
145
|
}
|
|
141
|
-
// Check that this block number does not exist already
|
|
142
|
-
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
143
|
-
if (existingBlock) {
|
|
144
|
-
this.log.warn(`Block number ${blockNumber} already exists, skipping processing`, proposalInfo);
|
|
145
|
-
return {
|
|
146
|
-
isValid: false,
|
|
147
|
-
reason: 'block_number_already_exists'
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
146
|
// Check that all of the transactions in the proposal are available
|
|
151
147
|
if (missingTxs.length > 0) {
|
|
152
148
|
this.log.warn(`Missing ${missingTxs.length} txs to process proposal`, {
|
|
@@ -155,6 +151,7 @@ export class BlockProposalHandler {
|
|
|
155
151
|
});
|
|
156
152
|
return {
|
|
157
153
|
isValid: false,
|
|
154
|
+
blockNumber,
|
|
158
155
|
reason: 'txs_not_available'
|
|
159
156
|
};
|
|
160
157
|
}
|
|
@@ -163,12 +160,13 @@ export class BlockProposalHandler {
|
|
|
163
160
|
if (shouldReexecute) {
|
|
164
161
|
try {
|
|
165
162
|
this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
|
|
166
|
-
reexecutionResult = await this.reexecuteTransactions(proposal, txs, l1ToL2Messages);
|
|
163
|
+
reexecutionResult = await this.reexecuteTransactions(proposal, blockNumber, txs, l1ToL2Messages);
|
|
167
164
|
} catch (error) {
|
|
168
165
|
this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
|
|
169
166
|
const reason = this.getReexecuteFailureReason(error);
|
|
170
167
|
return {
|
|
171
168
|
isValid: false,
|
|
169
|
+
blockNumber,
|
|
172
170
|
reason,
|
|
173
171
|
reexecutionResult
|
|
174
172
|
};
|
|
@@ -177,11 +175,38 @@ export class BlockProposalHandler {
|
|
|
177
175
|
this.log.info(`Successfully processed proposal for slot ${slotNumber}`, proposalInfo);
|
|
178
176
|
return {
|
|
179
177
|
isValid: true,
|
|
178
|
+
blockNumber,
|
|
180
179
|
reexecutionResult
|
|
181
180
|
};
|
|
182
181
|
}
|
|
183
|
-
|
|
184
|
-
const
|
|
182
|
+
async getParentBlock(proposal) {
|
|
183
|
+
const parentArchive = proposal.payload.header.lastArchiveRoot;
|
|
184
|
+
const slot = proposal.slotNumber.toBigInt();
|
|
185
|
+
const config = this.blockBuilder.getConfig();
|
|
186
|
+
const { genesisArchiveRoot } = await this.blockSource.getGenesisValues();
|
|
187
|
+
if (parentArchive.equals(genesisArchiveRoot)) {
|
|
188
|
+
return 'genesis';
|
|
189
|
+
}
|
|
190
|
+
const deadline = this.getReexecutionDeadline(slot, config);
|
|
191
|
+
const currentTime = this.dateProvider.now();
|
|
192
|
+
const timeoutDurationMs = deadline.getTime() - currentTime;
|
|
193
|
+
try {
|
|
194
|
+
return await this.blockSource.getBlockHeaderByArchive(parentArchive) ?? (timeoutDurationMs <= 0 ? undefined : await retryUntil(()=>this.blockSource.syncImmediate().then(()=>this.blockSource.getBlockHeaderByArchive(parentArchive)), 'force archiver sync', timeoutDurationMs / 1000, 0.5));
|
|
195
|
+
} catch (err) {
|
|
196
|
+
if (err instanceof TimeoutError) {
|
|
197
|
+
this.log.debug(`Timed out getting parent block by archive root`, {
|
|
198
|
+
parentArchive
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
this.log.error('Error getting parent block by archive root', err, {
|
|
202
|
+
parentArchive
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
getReexecutionDeadline(slot, config) {
|
|
209
|
+
const nextSlotTimestampSeconds = Number(getTimestampForSlot(slot + 1n, config));
|
|
185
210
|
const msNeededForPropagationAndPublishing = this.config.validatorReexecuteDeadlineMs;
|
|
186
211
|
return new Date(nextSlotTimestampSeconds * 1000 - msNeededForPropagationAndPublishing);
|
|
187
212
|
}
|
|
@@ -192,11 +217,11 @@ export class BlockProposalHandler {
|
|
|
192
217
|
return 'failed_txs';
|
|
193
218
|
} else if (err instanceof ReExTimeoutError) {
|
|
194
219
|
return 'timeout';
|
|
195
|
-
} else
|
|
220
|
+
} else {
|
|
196
221
|
return 'unknown_error';
|
|
197
222
|
}
|
|
198
223
|
}
|
|
199
|
-
async reexecuteTransactions(proposal, txs, l1ToL2Messages) {
|
|
224
|
+
async reexecuteTransactions(proposal, blockNumber, txs, l1ToL2Messages) {
|
|
200
225
|
const { header } = proposal.payload;
|
|
201
226
|
const { txHashes } = proposal;
|
|
202
227
|
// If we do not have all of the transactions, then we should fail
|
|
@@ -214,13 +239,13 @@ export class BlockProposalHandler {
|
|
|
214
239
|
coinbase: proposal.payload.header.coinbase,
|
|
215
240
|
feeRecipient: proposal.payload.header.feeRecipient,
|
|
216
241
|
gasFees: proposal.payload.header.gasFees,
|
|
217
|
-
blockNumber
|
|
242
|
+
blockNumber,
|
|
218
243
|
timestamp: header.timestamp,
|
|
219
244
|
chainId: new Fr(config.l1ChainId),
|
|
220
245
|
version: new Fr(config.rollupVersion)
|
|
221
246
|
});
|
|
222
247
|
const { block, failedTxs } = await this.blockBuilder.buildBlock(txs, l1ToL2Messages, globalVariables, {
|
|
223
|
-
deadline: this.getReexecutionDeadline(proposal, config)
|
|
248
|
+
deadline: this.getReexecutionDeadline(proposal.payload.header.slotNumber.toBigInt(), config)
|
|
224
249
|
});
|
|
225
250
|
const numFailedTxs = failedTxs.length;
|
|
226
251
|
const slot = proposal.slotNumber.toBigInt();
|
|
@@ -13,7 +13,6 @@ export declare class ValidationService {
|
|
|
13
13
|
/**
|
|
14
14
|
* Create a block proposal with the given header, archive, and transactions
|
|
15
15
|
*
|
|
16
|
-
* @param blockNumber - The block number this proposal is for
|
|
17
16
|
* @param header - The block header
|
|
18
17
|
* @param archive - The archive of the current block
|
|
19
18
|
* @param txs - TxHash[] ordered list of transactions
|
|
@@ -21,7 +20,7 @@ export declare class ValidationService {
|
|
|
21
20
|
*
|
|
22
21
|
* @returns A block proposal signing the above information (not the current implementation!!!)
|
|
23
22
|
*/
|
|
24
|
-
createBlockProposal(
|
|
23
|
+
createBlockProposal(header: CheckpointHeader, archive: Fr, stateReference: StateReference, txs: Tx[], proposerAttesterAddress: EthAddress | undefined, options: BlockProposalOptions): Promise<BlockProposal>;
|
|
25
24
|
/**
|
|
26
25
|
* Attest with selection of validators to the given block proposal, constructed by the current sequencer
|
|
27
26
|
*
|
|
@@ -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;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAG9C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,oBAAoB,EAG1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,GAAG;gBADH,QAAQ,EAAE,iBAAiB,EAC3B,GAAG,yCAA+C;IAG5D
|
|
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;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAG9C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,oBAAoB,EAG1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,GAAG;gBADH,QAAQ,EAAE,iBAAiB,EAC3B,GAAG,yCAA+C;IAG5D;;;;;;;;;OASG;IACG,mBAAmB,CACvB,MAAM,EAAE,gBAAgB,EACxB,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;IA0BzB;;;;;;;;;OASG;IACG,gBAAgB,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAU/F,0BAA0B,CAC9B,sBAAsB,EAAE,+BAA+B,EACvD,QAAQ,EAAE,UAAU,GACnB,OAAO,CAAC,SAAS,CAAC;CAMtB"}
|
|
@@ -14,14 +14,13 @@ export class ValidationService {
|
|
|
14
14
|
/**
|
|
15
15
|
* Create a block proposal with the given header, archive, and transactions
|
|
16
16
|
*
|
|
17
|
-
* @param blockNumber - The block number this proposal is for
|
|
18
17
|
* @param header - The block header
|
|
19
18
|
* @param archive - The archive of the current block
|
|
20
19
|
* @param txs - TxHash[] ordered list of transactions
|
|
21
20
|
* @param options - Block proposal options (including broadcastInvalidBlockProposal for testing)
|
|
22
21
|
*
|
|
23
22
|
* @returns A block proposal signing the above information (not the current implementation!!!)
|
|
24
|
-
*/ async createBlockProposal(
|
|
23
|
+
*/ async createBlockProposal(header, archive, stateReference, txs, proposerAttesterAddress, options) {
|
|
25
24
|
let payloadSigner;
|
|
26
25
|
if (proposerAttesterAddress !== undefined) {
|
|
27
26
|
payloadSigner = (payload)=>this.keyStore.signMessageWithAddress(proposerAttesterAddress, payload);
|
|
@@ -35,9 +34,9 @@ export class ValidationService {
|
|
|
35
34
|
// For testing: corrupt the state reference to trigger state_mismatch validation failure
|
|
36
35
|
if (options.broadcastInvalidBlockProposal) {
|
|
37
36
|
unfreeze(stateReference.partial).noteHashTree = AppendOnlyTreeSnapshot.random();
|
|
38
|
-
this.log.warn(`Creating INVALID block proposal for
|
|
37
|
+
this.log.warn(`Creating INVALID block proposal for slot ${header.slotNumber.toBigInt()}`);
|
|
39
38
|
}
|
|
40
|
-
return BlockProposal.createProposalFromSigner(
|
|
39
|
+
return BlockProposal.createProposalFromSigner(new ConsensusPayload(header, archive, stateReference), txHashes, options.publishFullTxs ? txs : undefined, payloadSigner);
|
|
41
40
|
}
|
|
42
41
|
/**
|
|
43
42
|
* Attest with selection of validators to the given block proposal, constructed by the current sequencer
|
|
@@ -51,7 +50,7 @@ export class ValidationService {
|
|
|
51
50
|
*/ async attestToProposal(proposal, attestors) {
|
|
52
51
|
const buf = Buffer32.fromBuffer(keccak256(proposal.payload.getPayloadToSign(SignatureDomainSeparator.blockAttestation)));
|
|
53
52
|
const signatures = await Promise.all(attestors.map((attestor)=>this.keyStore.signMessageWithAddress(attestor, buf)));
|
|
54
|
-
return signatures.map((sig)=>new BlockAttestation(proposal.
|
|
53
|
+
return signatures.map((sig)=>new BlockAttestation(proposal.payload, sig, proposal.signature));
|
|
55
54
|
}
|
|
56
55
|
async signAttestationsAndSigners(attestationsAndSigners, proposer) {
|
|
57
56
|
const buf = Buffer32.fromBuffer(keccak256(attestationsAndSigners.getPayloadToSign(SignatureDomainSeparator.attestationsAndSigners)));
|
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;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AACxG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,yBAAyB,EACjC,IAAI,EAAE;IACJ,YAAY,EAAE,qBAAqB,CAAC;IACpC,WAAW,EAAE,aAAa,CAAC;IAC3B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,eAAe,CAAC;CAC5B,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AACxG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,yBAAyB,EACjC,IAAI,EAAE;IACJ,YAAY,EAAE,qBAAqB,CAAC;IACpC,WAAW,EAAE,aAAa,CAAC;IAC3B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,eAAe,CAAC;CAC5B,wBAiBF;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,yBAAyB,EACjC,IAAI,EAAE;IACJ,YAAY,EAAE,qBAAqB,CAAC;IACpC,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,aAAa,CAAC;IAC3B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;CAC9C,+BAmBF"}
|
package/dest/factory.js
CHANGED
|
@@ -4,7 +4,9 @@ import { ValidatorMetrics } from './metrics.js';
|
|
|
4
4
|
import { ValidatorClient } from './validator.js';
|
|
5
5
|
export function createBlockProposalHandler(config, deps) {
|
|
6
6
|
const metrics = new ValidatorMetrics(deps.telemetry);
|
|
7
|
-
const blockProposalValidator = new BlockProposalValidator(deps.epochCache
|
|
7
|
+
const blockProposalValidator = new BlockProposalValidator(deps.epochCache, {
|
|
8
|
+
txsPermitted: !config.disableTransactions
|
|
9
|
+
});
|
|
8
10
|
return new BlockProposalHandler(deps.blockBuilder, deps.blockSource, deps.l1ToL2MessageSource, deps.p2pClient.getTxProvider(), blockProposalValidator, config, metrics, deps.dateProvider, deps.telemetry);
|
|
9
11
|
}
|
|
10
12
|
export function createValidatorClient(config, deps) {
|
package/dest/validator.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type Logger } from '@aztec/foundation/log';
|
|
|
6
6
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
7
7
|
import type { KeystoreManager } from '@aztec/node-keystore';
|
|
8
8
|
import type { P2P, PeerId, TxProvider } from '@aztec/p2p';
|
|
9
|
-
import { type
|
|
9
|
+
import { type Watcher, type WatcherEmitter } from '@aztec/slasher';
|
|
10
10
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
11
11
|
import type { CommitteeAttestationsAndSigners, L2BlockSource } from '@aztec/stdlib/block';
|
|
12
12
|
import type { IFullNodeBlockBuilder, Validator, ValidatorClientFullConfig } from '@aztec/stdlib/interfaces/server';
|
|
@@ -17,7 +17,6 @@ import type { StateReference, Tx } from '@aztec/stdlib/tx';
|
|
|
17
17
|
import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
|
|
18
18
|
import type { TypedDataDefinition } from 'viem';
|
|
19
19
|
import { BlockProposalHandler } from './block_proposal_handler.js';
|
|
20
|
-
import type { ValidatorClientConfig } from './config.js';
|
|
21
20
|
import { NodeKeystoreAdapter } from './key_store/node_keystore_adapter.js';
|
|
22
21
|
declare const ValidatorClient_base: new () => WatcherEmitter;
|
|
23
22
|
/**
|
|
@@ -42,10 +41,10 @@ export declare class ValidatorClient extends ValidatorClient_base implements Val
|
|
|
42
41
|
protected constructor(keyStore: NodeKeystoreAdapter, epochCache: EpochCache, p2pClient: P2P, blockProposalHandler: BlockProposalHandler, config: ValidatorClientFullConfig, dateProvider?: DateProvider, telemetry?: TelemetryClient, log?: Logger);
|
|
43
42
|
static validateKeyStoreConfiguration(keyStoreManager: KeystoreManager, logger?: Logger): void;
|
|
44
43
|
private handleEpochCommitteeUpdate;
|
|
45
|
-
static new(config:
|
|
44
|
+
static new(config: ValidatorClientFullConfig, blockBuilder: IFullNodeBlockBuilder, epochCache: EpochCache, p2pClient: P2P, blockSource: L2BlockSource, l1ToL2MessageSource: L1ToL2MessageSource, txProvider: TxProvider, keyStoreManager: KeystoreManager, dateProvider?: DateProvider, telemetry?: TelemetryClient): ValidatorClient;
|
|
46
45
|
getValidatorAddresses(): EthAddress[];
|
|
47
46
|
getBlockProposalHandler(): BlockProposalHandler;
|
|
48
|
-
reExecuteTransactions(proposal: BlockProposal, txs: any[], l1ToL2Messages: Fr[]): Promise<any>;
|
|
47
|
+
reExecuteTransactions(proposal: BlockProposal, blockNumber: number, txs: any[], l1ToL2Messages: Fr[]): Promise<any>;
|
|
49
48
|
signWithAddress(addr: EthAddress, msg: TypedDataDefinition): Promise<Signature>;
|
|
50
49
|
getCoinbaseForAttestor(attestor: EthAddress): EthAddress;
|
|
51
50
|
getFeeRecipientForAttestor(attestor: EthAddress): AztecAddress;
|
package/dest/validator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
|
package/dest/validator.js
CHANGED
|
@@ -94,7 +94,9 @@ const SLASHABLE_BLOCK_PROPOSAL_VALIDATION_RESULT = [
|
|
|
94
94
|
}
|
|
95
95
|
static new(config, blockBuilder, epochCache, p2pClient, blockSource, l1ToL2MessageSource, txProvider, keyStoreManager, dateProvider = new DateProvider(), telemetry = getTelemetryClient()) {
|
|
96
96
|
const metrics = new ValidatorMetrics(telemetry);
|
|
97
|
-
const blockProposalValidator = new BlockProposalValidator(epochCache
|
|
97
|
+
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
98
|
+
txsPermitted: !config.disableTransactions
|
|
99
|
+
});
|
|
98
100
|
const blockProposalHandler = new BlockProposalHandler(blockBuilder, blockSource, l1ToL2MessageSource, txProvider, blockProposalValidator, config, metrics, dateProvider, telemetry);
|
|
99
101
|
const validator = new ValidatorClient(NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager), epochCache, p2pClient, blockProposalHandler, config, dateProvider, telemetry);
|
|
100
102
|
return validator;
|
|
@@ -106,8 +108,8 @@ const SLASHABLE_BLOCK_PROPOSAL_VALIDATION_RESULT = [
|
|
|
106
108
|
return this.blockProposalHandler;
|
|
107
109
|
}
|
|
108
110
|
// Proxy method for backwards compatibility with tests
|
|
109
|
-
reExecuteTransactions(proposal, txs, l1ToL2Messages) {
|
|
110
|
-
return this.blockProposalHandler.reexecuteTransactions(proposal, txs, l1ToL2Messages);
|
|
111
|
+
reExecuteTransactions(proposal, blockNumber, txs, l1ToL2Messages) {
|
|
112
|
+
return this.blockProposalHandler.reexecuteTransactions(proposal, blockNumber, txs, l1ToL2Messages);
|
|
111
113
|
}
|
|
112
114
|
signWithAddress(addr, msg) {
|
|
113
115
|
return this.keyStore.signTypedDataWithAddress(addr, msg);
|
|
@@ -172,7 +174,7 @@ const SLASHABLE_BLOCK_PROPOSAL_VALIDATION_RESULT = [
|
|
|
172
174
|
...proposal.toBlockInfo(),
|
|
173
175
|
proposer: proposer.toString()
|
|
174
176
|
};
|
|
175
|
-
this.log.info(`Received proposal for
|
|
177
|
+
this.log.info(`Received proposal for slot ${slotNumber}`, {
|
|
176
178
|
...proposalInfo,
|
|
177
179
|
txHashes: proposal.txHashes.map((t)=>t.toString())
|
|
178
180
|
});
|
|
@@ -192,7 +194,7 @@ const SLASHABLE_BLOCK_PROPOSAL_VALIDATION_RESULT = [
|
|
|
192
194
|
'state_mismatch',
|
|
193
195
|
'failed_txs',
|
|
194
196
|
'in_hash_mismatch',
|
|
195
|
-
'
|
|
197
|
+
'parent_block_wrong_slot'
|
|
196
198
|
];
|
|
197
199
|
if (badProposalReasons.includes(reason)) {
|
|
198
200
|
this.metrics.incFailedAttestationsBadProposal(1, reason);
|
|
@@ -214,7 +216,7 @@ const SLASHABLE_BLOCK_PROPOSAL_VALIDATION_RESULT = [
|
|
|
214
216
|
return undefined;
|
|
215
217
|
}
|
|
216
218
|
// Provided all of the above checks pass, we can attest to the proposal
|
|
217
|
-
this.log.info(`Attesting to proposal for
|
|
219
|
+
this.log.info(`Attesting to proposal for slot ${slotNumber}`, proposalInfo);
|
|
218
220
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
219
221
|
// If the above function does not throw an error, then we can attest to the proposal
|
|
220
222
|
return this.createBlockAttestationsFromProposal(proposal, inCommittee);
|
|
@@ -246,7 +248,7 @@ const SLASHABLE_BLOCK_PROPOSAL_VALIDATION_RESULT = [
|
|
|
246
248
|
this.log.verbose(`Already made a proposal for the same slot, skipping proposal`);
|
|
247
249
|
return Promise.resolve(undefined);
|
|
248
250
|
}
|
|
249
|
-
const newProposal = await this.validationService.createBlockProposal(
|
|
251
|
+
const newProposal = await this.validationService.createBlockProposal(header, archive, stateReference, txs, proposerAddress, {
|
|
250
252
|
...options,
|
|
251
253
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal
|
|
252
254
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/validator-client",
|
|
3
|
-
"version": "3.0.0-nightly.
|
|
3
|
+
"version": "3.0.0-nightly.20251025",
|
|
4
4
|
"main": "dest/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -64,16 +64,16 @@
|
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@aztec/constants": "3.0.0-nightly.
|
|
68
|
-
"@aztec/epoch-cache": "3.0.0-nightly.
|
|
69
|
-
"@aztec/ethereum": "3.0.0-nightly.
|
|
70
|
-
"@aztec/foundation": "3.0.0-nightly.
|
|
71
|
-
"@aztec/node-keystore": "3.0.0-nightly.
|
|
72
|
-
"@aztec/p2p": "3.0.0-nightly.
|
|
73
|
-
"@aztec/prover-client": "3.0.0-nightly.
|
|
74
|
-
"@aztec/slasher": "3.0.0-nightly.
|
|
75
|
-
"@aztec/stdlib": "3.0.0-nightly.
|
|
76
|
-
"@aztec/telemetry-client": "3.0.0-nightly.
|
|
67
|
+
"@aztec/constants": "3.0.0-nightly.20251025",
|
|
68
|
+
"@aztec/epoch-cache": "3.0.0-nightly.20251025",
|
|
69
|
+
"@aztec/ethereum": "3.0.0-nightly.20251025",
|
|
70
|
+
"@aztec/foundation": "3.0.0-nightly.20251025",
|
|
71
|
+
"@aztec/node-keystore": "3.0.0-nightly.20251025",
|
|
72
|
+
"@aztec/p2p": "3.0.0-nightly.20251025",
|
|
73
|
+
"@aztec/prover-client": "3.0.0-nightly.20251025",
|
|
74
|
+
"@aztec/slasher": "3.0.0-nightly.20251025",
|
|
75
|
+
"@aztec/stdlib": "3.0.0-nightly.20251025",
|
|
76
|
+
"@aztec/telemetry-client": "3.0.0-nightly.20251025",
|
|
77
77
|
"koa": "^2.16.1",
|
|
78
78
|
"koa-router": "^13.1.1",
|
|
79
79
|
"tslib": "^2.4.0",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
+
import { TimeoutError } from '@aztec/foundation/error';
|
|
2
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
@@ -7,12 +8,12 @@ import type { P2P, PeerId } from '@aztec/p2p';
|
|
|
7
8
|
import { TxProvider } from '@aztec/p2p';
|
|
8
9
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
9
10
|
import { computeInHashFromL1ToL2Messages } from '@aztec/prover-client/helpers';
|
|
10
|
-
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
11
|
+
import type { L2Block, L2BlockSource } from '@aztec/stdlib/block';
|
|
11
12
|
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
12
13
|
import type { IFullNodeBlockBuilder, ValidatorClientFullConfig } from '@aztec/stdlib/interfaces/server';
|
|
13
14
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
14
15
|
import { type BlockProposal, ConsensusPayload } from '@aztec/stdlib/p2p';
|
|
15
|
-
import { type FailedTx, GlobalVariables, type Tx } from '@aztec/stdlib/tx';
|
|
16
|
+
import { BlockHeader, type FailedTx, GlobalVariables, type Tx } from '@aztec/stdlib/tx';
|
|
16
17
|
import {
|
|
17
18
|
ReExFailedTxsError,
|
|
18
19
|
ReExStateMismatchError,
|
|
@@ -26,7 +27,7 @@ import type { ValidatorMetrics } from './metrics.js';
|
|
|
26
27
|
export type BlockProposalValidationFailureReason =
|
|
27
28
|
| 'invalid_proposal'
|
|
28
29
|
| 'parent_block_not_found'
|
|
29
|
-
| '
|
|
30
|
+
| 'parent_block_wrong_slot'
|
|
30
31
|
| 'in_hash_mismatch'
|
|
31
32
|
| 'block_number_already_exists'
|
|
32
33
|
| 'txs_not_available'
|
|
@@ -35,16 +36,27 @@ export type BlockProposalValidationFailureReason =
|
|
|
35
36
|
| 'timeout'
|
|
36
37
|
| 'unknown_error';
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
type ReexecuteTransactionsResult = {
|
|
40
|
+
block: L2Block;
|
|
41
|
+
failedTxs: FailedTx[];
|
|
42
|
+
reexecutionTimeMs: number;
|
|
43
|
+
totalManaUsed: number;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type BlockProposalValidationSuccessResult = {
|
|
47
|
+
isValid: true;
|
|
48
|
+
blockNumber: number;
|
|
49
|
+
reexecutionResult?: ReexecuteTransactionsResult;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type BlockProposalValidationFailureResult = {
|
|
53
|
+
isValid: false;
|
|
54
|
+
reason: BlockProposalValidationFailureReason;
|
|
55
|
+
blockNumber?: number;
|
|
56
|
+
reexecutionResult?: ReexecuteTransactionsResult;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type BlockProposalValidationResult = BlockProposalValidationSuccessResult | BlockProposalValidationFailureResult;
|
|
48
60
|
|
|
49
61
|
export class BlockProposalHandler {
|
|
50
62
|
public readonly tracer: Tracer;
|
|
@@ -68,16 +80,16 @@ export class BlockProposalHandler {
|
|
|
68
80
|
const handler = async (proposal: BlockProposal, proposalSender: PeerId) => {
|
|
69
81
|
try {
|
|
70
82
|
const result = await this.handleBlockProposal(proposal, proposalSender, true);
|
|
71
|
-
if (result.isValid
|
|
83
|
+
if (result.isValid) {
|
|
72
84
|
this.log.info(`Non-validator reexecution completed for slot ${proposal.slotNumber.toBigInt()}`, {
|
|
73
|
-
blockNumber:
|
|
74
|
-
reexecutionTimeMs: result.reexecutionResult
|
|
75
|
-
totalManaUsed: result.reexecutionResult
|
|
76
|
-
numTxs: result.reexecutionResult
|
|
85
|
+
blockNumber: result.blockNumber,
|
|
86
|
+
reexecutionTimeMs: result.reexecutionResult?.reexecutionTimeMs,
|
|
87
|
+
totalManaUsed: result.reexecutionResult?.totalManaUsed,
|
|
88
|
+
numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0,
|
|
77
89
|
});
|
|
78
90
|
} else {
|
|
79
91
|
this.log.warn(`Non-validator reexecution failed for slot ${proposal.slotNumber.toBigInt()}`, {
|
|
80
|
-
blockNumber:
|
|
92
|
+
blockNumber: result.blockNumber,
|
|
81
93
|
reason: result.reason,
|
|
82
94
|
});
|
|
83
95
|
}
|
|
@@ -97,8 +109,8 @@ export class BlockProposalHandler {
|
|
|
97
109
|
shouldReexecute: boolean,
|
|
98
110
|
): Promise<BlockProposalValidationResult> {
|
|
99
111
|
const slotNumber = proposal.slotNumber.toBigInt();
|
|
100
|
-
const blockNumber = proposal.blockNumber;
|
|
101
112
|
const proposer = proposal.getSender();
|
|
113
|
+
const config = this.blockBuilder.getConfig();
|
|
102
114
|
|
|
103
115
|
// Reject proposals with invalid signatures
|
|
104
116
|
if (!proposer) {
|
|
@@ -120,49 +132,40 @@ export class BlockProposalHandler {
|
|
|
120
132
|
return { isValid: false, reason: 'invalid_proposal' };
|
|
121
133
|
}
|
|
122
134
|
|
|
123
|
-
// Collect txs from the proposal. We start doing this as early as possible,
|
|
124
|
-
// and we do it even if we don't plan to re-execute the txs, so that we have them
|
|
125
|
-
// if another node needs them.
|
|
126
|
-
const config = this.blockBuilder.getConfig();
|
|
127
|
-
const { txs, missingTxs } = await this.txProvider.getTxsForBlockProposal(proposal, {
|
|
128
|
-
pinnedPeer: proposalSender,
|
|
129
|
-
deadline: this.getReexecutionDeadline(proposal, config),
|
|
130
|
-
});
|
|
131
|
-
|
|
132
135
|
// Check that the parent proposal is a block we know, otherwise reexecution would fail
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
(await this.blockSource.getBlock(blockNumber - 1)) ??
|
|
139
|
-
(timeoutDurationMs <= 0
|
|
140
|
-
? undefined
|
|
141
|
-
: await retryUntil(
|
|
142
|
-
async () => {
|
|
143
|
-
await this.blockSource.syncImmediate();
|
|
144
|
-
return await this.blockSource.getBlock(blockNumber - 1);
|
|
145
|
-
},
|
|
146
|
-
'Force Archiver Sync',
|
|
147
|
-
timeoutDurationMs / 1000,
|
|
148
|
-
0.5,
|
|
149
|
-
));
|
|
136
|
+
const parentBlockHeader = await this.getParentBlock(proposal);
|
|
137
|
+
if (parentBlockHeader === undefined) {
|
|
138
|
+
this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
|
|
139
|
+
return { isValid: false, reason: 'parent_block_not_found' };
|
|
140
|
+
}
|
|
150
141
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
142
|
+
// Check that the parent block's slot is less than the proposal's slot (should not happen, but we check anyway)
|
|
143
|
+
if (parentBlockHeader !== 'genesis' && parentBlockHeader.getSlot() >= slotNumber) {
|
|
144
|
+
this.log.warn(`Parent block slot is greater than or equal to proposal slot, skipping processing`, {
|
|
145
|
+
parentBlockSlot: parentBlockHeader.getSlot().toString(),
|
|
146
|
+
proposalSlot: slotNumber.toString(),
|
|
147
|
+
...proposalInfo,
|
|
148
|
+
});
|
|
149
|
+
return { isValid: false, reason: 'parent_block_wrong_slot' };
|
|
150
|
+
}
|
|
155
151
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
152
|
+
// Compute the block number based on the parent block
|
|
153
|
+
const blockNumber = parentBlockHeader === 'genesis' ? INITIAL_L2_BLOCK_NUM : parentBlockHeader.getBlockNumber() + 1;
|
|
154
|
+
|
|
155
|
+
// Check that this block number does not exist already
|
|
156
|
+
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
157
|
+
if (existingBlock) {
|
|
158
|
+
this.log.warn(`Block number ${blockNumber} already exists, skipping processing`, proposalInfo);
|
|
159
|
+
return { isValid: false, blockNumber, reason: 'block_number_already_exists' };
|
|
164
160
|
}
|
|
165
161
|
|
|
162
|
+
// Collect txs from the proposal. We start doing this as early as possible,
|
|
163
|
+
// and we do it even if we don't plan to re-execute the txs, so that we have them if another node needs them.
|
|
164
|
+
const { txs, missingTxs } = await this.txProvider.getTxsForBlockProposal(proposal, blockNumber, {
|
|
165
|
+
pinnedPeer: proposalSender,
|
|
166
|
+
deadline: this.getReexecutionDeadline(slotNumber, config),
|
|
167
|
+
});
|
|
168
|
+
|
|
166
169
|
// Check that I have the same set of l1ToL2Messages as the proposal
|
|
167
170
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
168
171
|
const computedInHash = await computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
@@ -173,20 +176,13 @@ export class BlockProposalHandler {
|
|
|
173
176
|
computedInHash: computedInHash.toString(),
|
|
174
177
|
...proposalInfo,
|
|
175
178
|
});
|
|
176
|
-
return { isValid: false, reason: 'in_hash_mismatch' };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check that this block number does not exist already
|
|
180
|
-
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
181
|
-
if (existingBlock) {
|
|
182
|
-
this.log.warn(`Block number ${blockNumber} already exists, skipping processing`, proposalInfo);
|
|
183
|
-
return { isValid: false, reason: 'block_number_already_exists' };
|
|
179
|
+
return { isValid: false, blockNumber, reason: 'in_hash_mismatch' };
|
|
184
180
|
}
|
|
185
181
|
|
|
186
182
|
// Check that all of the transactions in the proposal are available
|
|
187
183
|
if (missingTxs.length > 0) {
|
|
188
184
|
this.log.warn(`Missing ${missingTxs.length} txs to process proposal`, { ...proposalInfo, missingTxs });
|
|
189
|
-
return { isValid: false, reason: 'txs_not_available' };
|
|
185
|
+
return { isValid: false, blockNumber, reason: 'txs_not_available' };
|
|
190
186
|
}
|
|
191
187
|
|
|
192
188
|
// Try re-executing the transactions in the proposal if needed
|
|
@@ -194,23 +190,57 @@ export class BlockProposalHandler {
|
|
|
194
190
|
if (shouldReexecute) {
|
|
195
191
|
try {
|
|
196
192
|
this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
|
|
197
|
-
reexecutionResult = await this.reexecuteTransactions(proposal, txs, l1ToL2Messages);
|
|
193
|
+
reexecutionResult = await this.reexecuteTransactions(proposal, blockNumber, txs, l1ToL2Messages);
|
|
198
194
|
} catch (error) {
|
|
199
195
|
this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
|
|
200
196
|
const reason = this.getReexecuteFailureReason(error);
|
|
201
|
-
return { isValid: false, reason, reexecutionResult };
|
|
197
|
+
return { isValid: false, blockNumber, reason, reexecutionResult };
|
|
202
198
|
}
|
|
203
199
|
}
|
|
204
200
|
|
|
205
201
|
this.log.info(`Successfully processed proposal for slot ${slotNumber}`, proposalInfo);
|
|
206
|
-
return { isValid: true, reexecutionResult };
|
|
202
|
+
return { isValid: true, blockNumber, reexecutionResult };
|
|
207
203
|
}
|
|
208
204
|
|
|
209
|
-
private
|
|
210
|
-
proposal
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
205
|
+
private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockHeader | undefined> {
|
|
206
|
+
const parentArchive = proposal.payload.header.lastArchiveRoot;
|
|
207
|
+
const slot = proposal.slotNumber.toBigInt();
|
|
208
|
+
const config = this.blockBuilder.getConfig();
|
|
209
|
+
const { genesisArchiveRoot } = await this.blockSource.getGenesisValues();
|
|
210
|
+
|
|
211
|
+
if (parentArchive.equals(genesisArchiveRoot)) {
|
|
212
|
+
return 'genesis';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const deadline = this.getReexecutionDeadline(slot, config);
|
|
216
|
+
const currentTime = this.dateProvider.now();
|
|
217
|
+
const timeoutDurationMs = deadline.getTime() - currentTime;
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
return (
|
|
221
|
+
(await this.blockSource.getBlockHeaderByArchive(parentArchive)) ??
|
|
222
|
+
(timeoutDurationMs <= 0
|
|
223
|
+
? undefined
|
|
224
|
+
: await retryUntil(
|
|
225
|
+
() =>
|
|
226
|
+
this.blockSource.syncImmediate().then(() => this.blockSource.getBlockHeaderByArchive(parentArchive)),
|
|
227
|
+
'force archiver sync',
|
|
228
|
+
timeoutDurationMs / 1000,
|
|
229
|
+
0.5,
|
|
230
|
+
))
|
|
231
|
+
);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
if (err instanceof TimeoutError) {
|
|
234
|
+
this.log.debug(`Timed out getting parent block by archive root`, { parentArchive });
|
|
235
|
+
} else {
|
|
236
|
+
this.log.error('Error getting parent block by archive root', err, { parentArchive });
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private getReexecutionDeadline(slot: bigint, config: { l1GenesisTime: bigint; slotDuration: number }): Date {
|
|
243
|
+
const nextSlotTimestampSeconds = Number(getTimestampForSlot(slot + 1n, config));
|
|
214
244
|
const msNeededForPropagationAndPublishing = this.config.validatorReexecuteDeadlineMs;
|
|
215
245
|
return new Date(nextSlotTimestampSeconds * 1000 - msNeededForPropagationAndPublishing);
|
|
216
246
|
}
|
|
@@ -222,21 +252,17 @@ export class BlockProposalHandler {
|
|
|
222
252
|
return 'failed_txs';
|
|
223
253
|
} else if (err instanceof ReExTimeoutError) {
|
|
224
254
|
return 'timeout';
|
|
225
|
-
} else
|
|
255
|
+
} else {
|
|
226
256
|
return 'unknown_error';
|
|
227
257
|
}
|
|
228
258
|
}
|
|
229
259
|
|
|
230
260
|
async reexecuteTransactions(
|
|
231
261
|
proposal: BlockProposal,
|
|
262
|
+
blockNumber: number,
|
|
232
263
|
txs: Tx[],
|
|
233
264
|
l1ToL2Messages: Fr[],
|
|
234
|
-
): Promise<{
|
|
235
|
-
block: any;
|
|
236
|
-
failedTxs: FailedTx[];
|
|
237
|
-
reexecutionTimeMs: number;
|
|
238
|
-
totalManaUsed: number;
|
|
239
|
-
}> {
|
|
265
|
+
): Promise<ReexecuteTransactionsResult> {
|
|
240
266
|
const { header } = proposal.payload;
|
|
241
267
|
const { txHashes } = proposal;
|
|
242
268
|
|
|
@@ -257,14 +283,14 @@ export class BlockProposalHandler {
|
|
|
257
283
|
coinbase: proposal.payload.header.coinbase, // set arbitrarily by the proposer
|
|
258
284
|
feeRecipient: proposal.payload.header.feeRecipient, // set arbitrarily by the proposer
|
|
259
285
|
gasFees: proposal.payload.header.gasFees, // validated by the rollup contract
|
|
260
|
-
blockNumber
|
|
286
|
+
blockNumber, // computed from the parent block and checked it does not exist in archiver
|
|
261
287
|
timestamp: header.timestamp, // checked in the rollup contract against the slot number
|
|
262
288
|
chainId: new Fr(config.l1ChainId),
|
|
263
289
|
version: new Fr(config.rollupVersion),
|
|
264
290
|
});
|
|
265
291
|
|
|
266
292
|
const { block, failedTxs } = await this.blockBuilder.buildBlock(txs, l1ToL2Messages, globalVariables, {
|
|
267
|
-
deadline: this.getReexecutionDeadline(proposal, config),
|
|
293
|
+
deadline: this.getReexecutionDeadline(proposal.payload.header.slotNumber.toBigInt(), config),
|
|
268
294
|
});
|
|
269
295
|
|
|
270
296
|
const numFailedTxs = failedTxs.length;
|
|
@@ -28,7 +28,6 @@ export class ValidationService {
|
|
|
28
28
|
/**
|
|
29
29
|
* Create a block proposal with the given header, archive, and transactions
|
|
30
30
|
*
|
|
31
|
-
* @param blockNumber - The block number this proposal is for
|
|
32
31
|
* @param header - The block header
|
|
33
32
|
* @param archive - The archive of the current block
|
|
34
33
|
* @param txs - TxHash[] ordered list of transactions
|
|
@@ -37,7 +36,6 @@ export class ValidationService {
|
|
|
37
36
|
* @returns A block proposal signing the above information (not the current implementation!!!)
|
|
38
37
|
*/
|
|
39
38
|
async createBlockProposal(
|
|
40
|
-
blockNumber: number,
|
|
41
39
|
header: CheckpointHeader,
|
|
42
40
|
archive: Fr,
|
|
43
41
|
stateReference: StateReference,
|
|
@@ -59,11 +57,10 @@ export class ValidationService {
|
|
|
59
57
|
// For testing: corrupt the state reference to trigger state_mismatch validation failure
|
|
60
58
|
if (options.broadcastInvalidBlockProposal) {
|
|
61
59
|
unfreeze(stateReference.partial).noteHashTree = AppendOnlyTreeSnapshot.random();
|
|
62
|
-
this.log.warn(`Creating INVALID block proposal for
|
|
60
|
+
this.log.warn(`Creating INVALID block proposal for slot ${header.slotNumber.toBigInt()}`);
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
return BlockProposal.createProposalFromSigner(
|
|
66
|
-
blockNumber,
|
|
67
64
|
new ConsensusPayload(header, archive, stateReference),
|
|
68
65
|
txHashes,
|
|
69
66
|
options.publishFullTxs ? txs : undefined,
|
|
@@ -88,7 +85,7 @@ export class ValidationService {
|
|
|
88
85
|
const signatures = await Promise.all(
|
|
89
86
|
attestors.map(attestor => this.keyStore.signMessageWithAddress(attestor, buf)),
|
|
90
87
|
);
|
|
91
|
-
return signatures.map(sig => new BlockAttestation(proposal.
|
|
88
|
+
return signatures.map(sig => new BlockAttestation(proposal.payload, sig, proposal.signature));
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
async signAttestationsAndSigners(
|
package/src/factory.ts
CHANGED
|
@@ -24,7 +24,9 @@ export function createBlockProposalHandler(
|
|
|
24
24
|
},
|
|
25
25
|
) {
|
|
26
26
|
const metrics = new ValidatorMetrics(deps.telemetry);
|
|
27
|
-
const blockProposalValidator = new BlockProposalValidator(deps.epochCache
|
|
27
|
+
const blockProposalValidator = new BlockProposalValidator(deps.epochCache, {
|
|
28
|
+
txsPermitted: !config.disableTransactions,
|
|
29
|
+
});
|
|
28
30
|
return new BlockProposalHandler(
|
|
29
31
|
deps.blockBuilder,
|
|
30
32
|
deps.blockSource,
|
package/src/validator.ts
CHANGED
|
@@ -9,13 +9,7 @@ import { DateProvider } from '@aztec/foundation/timer';
|
|
|
9
9
|
import type { KeystoreManager } from '@aztec/node-keystore';
|
|
10
10
|
import type { P2P, PeerId, TxProvider } from '@aztec/p2p';
|
|
11
11
|
import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol } from '@aztec/p2p';
|
|
12
|
-
import {
|
|
13
|
-
OffenseType,
|
|
14
|
-
type SlasherConfig,
|
|
15
|
-
WANT_TO_SLASH_EVENT,
|
|
16
|
-
type Watcher,
|
|
17
|
-
type WatcherEmitter,
|
|
18
|
-
} from '@aztec/slasher';
|
|
12
|
+
import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
|
|
19
13
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
20
14
|
import type { CommitteeAttestationsAndSigners, L2BlockSource } from '@aztec/stdlib/block';
|
|
21
15
|
import type { IFullNodeBlockBuilder, Validator, ValidatorClientFullConfig } from '@aztec/stdlib/interfaces/server';
|
|
@@ -30,7 +24,6 @@ import { EventEmitter } from 'events';
|
|
|
30
24
|
import type { TypedDataDefinition } from 'viem';
|
|
31
25
|
|
|
32
26
|
import { BlockProposalHandler, type BlockProposalValidationFailureReason } from './block_proposal_handler.js';
|
|
33
|
-
import type { ValidatorClientConfig } from './config.js';
|
|
34
27
|
import { ValidationService } from './duties/validation_service.js';
|
|
35
28
|
import { NodeKeystoreAdapter } from './key_store/node_keystore_adapter.js';
|
|
36
29
|
import { ValidatorMetrics } from './metrics.js';
|
|
@@ -140,7 +133,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
140
133
|
}
|
|
141
134
|
|
|
142
135
|
static new(
|
|
143
|
-
config:
|
|
136
|
+
config: ValidatorClientFullConfig,
|
|
144
137
|
blockBuilder: IFullNodeBlockBuilder,
|
|
145
138
|
epochCache: EpochCache,
|
|
146
139
|
p2pClient: P2P,
|
|
@@ -152,7 +145,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
152
145
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
153
146
|
) {
|
|
154
147
|
const metrics = new ValidatorMetrics(telemetry);
|
|
155
|
-
const blockProposalValidator = new BlockProposalValidator(epochCache
|
|
148
|
+
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
149
|
+
txsPermitted: !config.disableTransactions,
|
|
150
|
+
});
|
|
156
151
|
const blockProposalHandler = new BlockProposalHandler(
|
|
157
152
|
blockBuilder,
|
|
158
153
|
blockSource,
|
|
@@ -189,8 +184,13 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
189
184
|
}
|
|
190
185
|
|
|
191
186
|
// Proxy method for backwards compatibility with tests
|
|
192
|
-
public reExecuteTransactions(
|
|
193
|
-
|
|
187
|
+
public reExecuteTransactions(
|
|
188
|
+
proposal: BlockProposal,
|
|
189
|
+
blockNumber: number,
|
|
190
|
+
txs: any[],
|
|
191
|
+
l1ToL2Messages: Fr[],
|
|
192
|
+
): Promise<any> {
|
|
193
|
+
return this.blockProposalHandler.reexecuteTransactions(proposal, blockNumber, txs, l1ToL2Messages);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
public signWithAddress(addr: EthAddress, msg: TypedDataDefinition) {
|
|
@@ -273,7 +273,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
273
273
|
const partOfCommittee = inCommittee.length > 0;
|
|
274
274
|
|
|
275
275
|
const proposalInfo = { ...proposal.toBlockInfo(), proposer: proposer.toString() };
|
|
276
|
-
this.log.info(`Received proposal for
|
|
276
|
+
this.log.info(`Received proposal for slot ${slotNumber}`, {
|
|
277
277
|
...proposalInfo,
|
|
278
278
|
txHashes: proposal.txHashes.map(t => t.toString()),
|
|
279
279
|
});
|
|
@@ -304,7 +304,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
304
304
|
'state_mismatch',
|
|
305
305
|
'failed_txs',
|
|
306
306
|
'in_hash_mismatch',
|
|
307
|
-
'
|
|
307
|
+
'parent_block_wrong_slot',
|
|
308
308
|
];
|
|
309
309
|
|
|
310
310
|
if (badProposalReasons.includes(reason as BlockProposalValidationFailureReason)) {
|
|
@@ -334,7 +334,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
// Provided all of the above checks pass, we can attest to the proposal
|
|
337
|
-
this.log.info(`Attesting to proposal for
|
|
337
|
+
this.log.info(`Attesting to proposal for slot ${slotNumber}`, proposalInfo);
|
|
338
338
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
339
339
|
|
|
340
340
|
// If the above function does not throw an error, then we can attest to the proposal
|
|
@@ -383,7 +383,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
const newProposal = await this.validationService.createBlockProposal(
|
|
386
|
-
blockNumber,
|
|
387
386
|
header,
|
|
388
387
|
archive,
|
|
389
388
|
stateReference,
|