@aztec-labs/p2p 6.0.0-rc.1 → 6.0.0-rc.3
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/bootstrap/bootstrap.d.ts +2 -2
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +4 -2
- package/dest/client/factory.d.ts +3 -3
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +6 -6
- package/dest/client/interface.d.ts +1 -1
- package/dest/client/p2p_client.d.ts +1 -1
- package/dest/client/p2p_client.js +1 -1
- package/dest/config.d.ts +11 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +16 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +15 -3
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +15 -0
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +21 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +6 -2
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +9 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +10 -8
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +22 -11
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +6 -2
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +2 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +11 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +111 -44
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +3 -24
- package/dest/msg_validators/epoch_cache_lookup.d.ts +18 -0
- package/dest/msg_validators/epoch_cache_lookup.d.ts.map +1 -0
- package/dest/msg_validators/epoch_cache_lookup.js +29 -0
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +80 -82
- package/dest/msg_validators/tx_validator/data_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +4 -3
- package/dest/msg_validators/tx_validator/factory.d.ts +40 -11
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +36 -5
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +18 -10
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +37 -27
- package/dest/services/discv5/discV5_service.d.ts +2 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +3 -1
- package/dest/services/libp2p/libp2p_service.d.ts +18 -9
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +127 -29
- package/dest/services/peer-manager/peer_manager.d.ts +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +14 -0
- package/dest/services/tx_provider.d.ts +1 -1
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +3 -0
- package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +3 -1
- package/dest/test-helpers/mock-pubsub.js +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +3 -1
- package/dest/test-helpers/testbench-utils.d.ts +4 -2
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +6 -0
- package/dest/testbench/p2p_client_testbench_worker.js +6 -2
- package/package.json +14 -14
- package/src/bootstrap/bootstrap.ts +3 -2
- package/src/client/factory.ts +7 -7
- package/src/client/interface.ts +1 -1
- package/src/client/p2p_client.ts +1 -1
- package/src/config.ts +11 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +25 -0
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +31 -0
- package/src/mem_pools/tx_pool_v2/README.md +1 -1
- package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +13 -1
- package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +17 -9
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +6 -1
- package/src/mem_pools/tx_pool_v2/interfaces.ts +10 -4
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +95 -40
- package/src/msg_validators/attestation_validator/attestation_validator.ts +3 -23
- package/src/msg_validators/epoch_cache_lookup.ts +33 -0
- package/src/msg_validators/proposal_validator/README.md +3 -3
- package/src/msg_validators/proposal_validator/proposal_validator.ts +78 -77
- package/src/msg_validators/tx_validator/README.md +12 -8
- package/src/msg_validators/tx_validator/data_validator.ts +4 -3
- package/src/msg_validators/tx_validator/factory.ts +60 -14
- package/src/msg_validators/tx_validator/gas_validator.ts +46 -34
- package/src/services/discv5/discV5_service.ts +2 -1
- package/src/services/libp2p/libp2p_service.ts +138 -34
- package/src/services/peer-manager/peer_manager.ts +14 -0
- package/src/services/tx_provider.ts +3 -0
- package/src/test-helpers/make-test-p2p-clients.ts +5 -1
- package/src/test-helpers/mock-pubsub.ts +3 -3
- package/src/test-helpers/reqresp-nodes.ts +5 -1
- package/src/test-helpers/testbench-utils.ts +13 -1
- package/src/testbench/p2p_client_testbench_worker.ts +10 -2
|
@@ -36,7 +36,7 @@ import { getVKTreeRoot } from '@aztec-labs/noir-protocol-circuits-types/vk-tree'
|
|
|
36
36
|
import { ProtocolContractAddress, protocolContractsHash } from '@aztec-labs/protocol-contracts';
|
|
37
37
|
import type { BlockHash } from '@aztec-labs/stdlib/block';
|
|
38
38
|
import type { ContractDataSource } from '@aztec-labs/stdlib/contract';
|
|
39
|
-
import
|
|
39
|
+
import { GasFees } from '@aztec-labs/stdlib/gas';
|
|
40
40
|
import type {
|
|
41
41
|
AllowedElement,
|
|
42
42
|
ClientProtocolCircuitVerifier,
|
|
@@ -58,7 +58,7 @@ import { ContractInstanceTxValidator } from './contract_instance_validator.js';
|
|
|
58
58
|
import { DataTxValidator } from './data_validator.js';
|
|
59
59
|
import { DoubleSpendTxValidator, type NullifierSource } from './double_spend_validator.js';
|
|
60
60
|
import { MaxGasLimitsValidator, MinGasLimitsValidator } from './gas_limits_validator.js';
|
|
61
|
-
import { GasTxValidator, MaxFeePerGasValidator } from './gas_validator.js';
|
|
61
|
+
import { FeePayerBalanceValidator, GasTxValidator, MaxFeePerGasValidator } from './gas_validator.js';
|
|
62
62
|
import { MetadataTxValidator } from './metadata_validator.js';
|
|
63
63
|
import { NullifierCache } from './nullifier_cache.js';
|
|
64
64
|
import { AllowedSetupCallsMetaValidator, PhasesTxValidator } from './phases_validator.js';
|
|
@@ -69,22 +69,55 @@ import { TxProofValidator } from './tx_proof_validator.js';
|
|
|
69
69
|
import { TxValidationCache } from './tx_validation_cache.js';
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* with the associated severity level.
|
|
72
|
+
* Marks a gossip validator whose failure is not attributable to the sending peer: the tx is dropped without
|
|
73
|
+
* being added to the pool or propagated, and the peer's score is left alone.
|
|
75
74
|
*/
|
|
76
|
-
export
|
|
75
|
+
export const IgnoreWithoutPenalty = 'ignore-without-penalty';
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Consequence of a gossip validator failing: the sending peer is penalized with this severity, unless the
|
|
79
|
+
* failure is local policy rather than peer misbehavior, in which case it is {@link IgnoreWithoutPenalty}.
|
|
80
|
+
*/
|
|
81
|
+
export type GossipValidationFailure = PeerErrorSeverity | typeof IgnoreWithoutPenalty;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The fees a gossiped tx is checked against. A tx below `admission` is not relayed. A tx below `penaltyFloor` as
|
|
85
|
+
* well is priced lower than any view of the chain this node considers plausible, so its sender is penalized.
|
|
86
|
+
* `penaltyFloor` is never above `admission`.
|
|
87
|
+
*/
|
|
88
|
+
export type GossipMinFees = { admission: GasFees; penaltyFloor: GasFees };
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Fraction of the lower of the admission and L1-forward fees below which a gossiped tx gets its sender penalized.
|
|
92
|
+
* A peer can legitimately price the next block below both: one that has seen a checkpoint this node has not can
|
|
93
|
+
* see the congestion fee drop, and the L1 gas components move with every L1 block. Halving leaves room for
|
|
94
|
+
* several such steps while still penalizing zero-fee and negligible-fee spam.
|
|
95
|
+
*/
|
|
96
|
+
export const GOSSIP_FEE_PENALTY_FLOOR_FRACTION = 0.5;
|
|
97
|
+
|
|
98
|
+
/** Derives the gossip fee thresholds from this node's admission fee and its L1-forward fee. */
|
|
99
|
+
export function computeGossipMinFees(admission: GasFees, l1Forward: GasFees): GossipMinFees {
|
|
100
|
+
return { admission, penaltyFloor: GasFees.min(admission, l1Forward).mul(GOSSIP_FEE_PENALTY_FLOOR_FRACTION) };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A validator paired with the consequence of its failure.
|
|
105
|
+
* Used for gossip validation where each validator's failure triggers a peer penalization with the associated
|
|
106
|
+
* severity level. Only stages that can also fail on local policy widen `F` to include
|
|
107
|
+
* {@link IgnoreWithoutPenalty}.
|
|
108
|
+
*/
|
|
109
|
+
export interface TransactionValidator<F extends GossipValidationFailure = PeerErrorSeverity> {
|
|
77
110
|
validator: {
|
|
78
111
|
validateTx(tx: Tx): Promise<TxValidationResult>;
|
|
79
112
|
};
|
|
80
|
-
severity:
|
|
113
|
+
severity: F;
|
|
81
114
|
}
|
|
82
115
|
|
|
83
116
|
/**
|
|
84
117
|
* First stage of gossip validation — fast checks run before the pool pre-check.
|
|
85
118
|
*
|
|
86
|
-
* If any validator fails, the
|
|
87
|
-
*
|
|
119
|
+
* If any validator fails, the tx is rejected immediately, without consulting the pool or running proof
|
|
120
|
+
* verification. The peer is penalized unless the only failures are marked {@link IgnoreWithoutPenalty}.
|
|
88
121
|
*
|
|
89
122
|
* The `doubleSpendValidator` failure is special-cased by the caller (`handleGossipedTx`)
|
|
90
123
|
* to determine severity based on how recently the nullifier appeared. The caller reports the
|
|
@@ -94,7 +127,7 @@ export function createFirstStageTxValidationsForGossipedTransactions(
|
|
|
94
127
|
timestamp: UInt64,
|
|
95
128
|
blockNumber: BlockNumber,
|
|
96
129
|
worldStateSynchronizer: WorldStateSynchronizer,
|
|
97
|
-
gasFees:
|
|
130
|
+
gasFees: GossipMinFees,
|
|
98
131
|
l1ChainId: number,
|
|
99
132
|
rollupVersion: number,
|
|
100
133
|
protocolContractsHash: Fr,
|
|
@@ -103,7 +136,7 @@ export function createFirstStageTxValidationsForGossipedTransactions(
|
|
|
103
136
|
allowedInSetup: AllowedElement[] = [],
|
|
104
137
|
bindings?: LoggerBindings,
|
|
105
138
|
gasLimitOpts?: { maxTxL2Gas?: number; maxTxDAGas?: number },
|
|
106
|
-
): Record<string, TransactionValidator
|
|
139
|
+
): Record<string, TransactionValidator<GossipValidationFailure>> {
|
|
107
140
|
const merkleTree = worldStateSynchronizer.getCommitted();
|
|
108
141
|
|
|
109
142
|
return {
|
|
@@ -166,11 +199,24 @@ export function createFirstStageTxValidationsForGossipedTransactions(
|
|
|
166
199
|
validator: new MaxGasLimitsValidator<Tx>({ ...gasLimitOpts, bindings }),
|
|
167
200
|
severity: PeerErrorSeverity.MidToleranceError,
|
|
168
201
|
},
|
|
169
|
-
|
|
170
|
-
|
|
202
|
+
// The max-fee comparisons and the fee-payer balance check are separate entries because their failures mean
|
|
203
|
+
// different things. A tx below our admission fee is only invalid against a fee this node resolved: a peer
|
|
204
|
+
// ahead of us may be relaying against a lower checkpoint fee it legitimately sees, so the tx is dropped
|
|
205
|
+
// without penalizing the sender. That excuse only stretches down to the penalty floor, so a tx below it fails
|
|
206
|
+
// both entries and the harshest failure wins. An underfunded fee payer is invalid against shared state, so
|
|
207
|
+
// it stays penalized.
|
|
208
|
+
maxFeePerGasValidator: {
|
|
209
|
+
validator: new MaxFeePerGasValidator<Tx>(gasFees.admission, bindings),
|
|
210
|
+
severity: IgnoreWithoutPenalty,
|
|
211
|
+
},
|
|
212
|
+
maxFeePerGasFloorValidator: {
|
|
213
|
+
validator: new MaxFeePerGasValidator<Tx>(gasFees.penaltyFloor, bindings),
|
|
214
|
+
severity: PeerErrorSeverity.MidToleranceError,
|
|
215
|
+
},
|
|
216
|
+
feePayerBalanceValidator: {
|
|
217
|
+
validator: new FeePayerBalanceValidator(
|
|
171
218
|
new DatabasePublicStateSource(merkleTree),
|
|
172
219
|
ProtocolContractAddress.FeeJuice,
|
|
173
|
-
gasFees,
|
|
174
220
|
bindings,
|
|
175
221
|
),
|
|
176
222
|
severity: PeerErrorSeverity.MidToleranceError,
|
|
@@ -72,52 +72,25 @@ export class MaxFeePerGasValidator<T extends HasMaxFeePerGasData> implements TxV
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Validates that a transaction can
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* 1. **Max fee per gas** (delegates to {@link MaxFeePerGasValidator}) — rejects the tx if its
|
|
79
|
-
* maxFeesPerGas is below the current block's gas fees.
|
|
80
|
-
* 2. **Fee payer balance** — reads the fee payer's FeeJuice balance from public state,
|
|
81
|
-
* adds any pending claim from a setup-phase `_increase_public_balance` call, and
|
|
82
|
-
* rejects if the total is less than the tx's fee limit (gasLimits * maxFeePerGas).
|
|
83
|
-
*
|
|
84
|
-
* Gas limits are deliberately not checked here: they are owned by {@link MinGasLimitsValidator} and
|
|
85
|
-
* {@link MaxGasLimitsValidator}, which factories include separately so that exemptions (e.g. gas estimation)
|
|
86
|
-
* don't change fee enforcement.
|
|
75
|
+
* Validates that a transaction's fee payer can cover its fee limit: reads the fee payer's FeeJuice balance from
|
|
76
|
+
* public state, adds any pending claim from a setup-phase `_increase_public_balance` call, and rejects if the total
|
|
77
|
+
* is less than the tx's fee limit (gasLimits * maxFeePerGas).
|
|
87
78
|
*
|
|
88
|
-
*
|
|
79
|
+
* Does not check max fee per gas against any fee: a tx with zero max fees has a zero fee limit and passes here with
|
|
80
|
+
* no balance, so every caller must also run a {@link MaxFeePerGasValidator}, as {@link GasTxValidator} does.
|
|
89
81
|
*/
|
|
90
|
-
export class
|
|
82
|
+
export class FeePayerBalanceValidator implements TxValidator<Tx> {
|
|
91
83
|
#log: Logger;
|
|
92
84
|
#publicDataSource: PublicStateSource;
|
|
93
85
|
#feeJuiceAddress: AztecAddress;
|
|
94
|
-
#maxFeePerGasValidator: MaxFeePerGasValidator<Tx>;
|
|
95
86
|
|
|
96
|
-
constructor(
|
|
97
|
-
publicDataSource: PublicStateSource,
|
|
98
|
-
feeJuiceAddress: AztecAddress,
|
|
99
|
-
gasFees: GasFees,
|
|
100
|
-
bindings?: LoggerBindings,
|
|
101
|
-
) {
|
|
87
|
+
constructor(publicDataSource: PublicStateSource, feeJuiceAddress: AztecAddress, bindings?: LoggerBindings) {
|
|
102
88
|
this.#log = createLogger('sequencer:tx_validator:tx_gas', bindings);
|
|
103
89
|
this.#publicDataSource = publicDataSource;
|
|
104
90
|
this.#feeJuiceAddress = feeJuiceAddress;
|
|
105
|
-
this.#maxFeePerGasValidator = new MaxFeePerGasValidator(gasFees, bindings);
|
|
106
91
|
}
|
|
107
92
|
|
|
108
93
|
async validateTx(tx: Tx): Promise<TxValidationResult> {
|
|
109
|
-
const maxFeeValidation = this.#maxFeePerGasValidator.validateMaxFeePerGas(tx);
|
|
110
|
-
if (maxFeeValidation.result === 'invalid') {
|
|
111
|
-
return maxFeeValidation;
|
|
112
|
-
}
|
|
113
|
-
return await this.validateTxFee(tx);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Checks the fee payer has enough FeeJuice balance to cover the tx's fee limit.
|
|
118
|
-
* Accounts for any pending claim from a setup-phase `_increase_public_balance` call.
|
|
119
|
-
*/
|
|
120
|
-
public async validateTxFee(tx: Tx): Promise<TxValidationResult> {
|
|
121
94
|
const feePayer = tx.data.feePayer;
|
|
122
95
|
|
|
123
96
|
// Compute the maximum fee that this tx may pay, based on its gasLimits and maxFeePerGas
|
|
@@ -147,3 +120,42 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
147
120
|
return { result: 'valid' };
|
|
148
121
|
}
|
|
149
122
|
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Validates that a transaction can pay its gas fees.
|
|
126
|
+
*
|
|
127
|
+
* Runs two checks in order:
|
|
128
|
+
* 1. **Max fee per gas** (delegates to {@link MaxFeePerGasValidator}) — rejects the tx if its
|
|
129
|
+
* maxFeesPerGas is below the current block's gas fees.
|
|
130
|
+
* 2. **Fee payer balance** (delegates to {@link FeePayerBalanceValidator}) — rejects if the fee payer cannot
|
|
131
|
+
* cover the tx's fee limit (gasLimits * maxFeePerGas).
|
|
132
|
+
*
|
|
133
|
+
* Gas limits are deliberately not checked here: they are owned by {@link MinGasLimitsValidator} and
|
|
134
|
+
* {@link MaxGasLimitsValidator}, which factories include separately so that exemptions (e.g. gas estimation)
|
|
135
|
+
* don't change fee enforcement.
|
|
136
|
+
*
|
|
137
|
+
* Used by: RPC and block building validators. Gossip runs the two checks as separate entries, since their
|
|
138
|
+
* failures carry different peer penalties.
|
|
139
|
+
*/
|
|
140
|
+
export class GasTxValidator implements TxValidator<Tx> {
|
|
141
|
+
#maxFeePerGasValidator: MaxFeePerGasValidator<Tx>;
|
|
142
|
+
#feePayerBalanceValidator: FeePayerBalanceValidator;
|
|
143
|
+
|
|
144
|
+
constructor(
|
|
145
|
+
publicDataSource: PublicStateSource,
|
|
146
|
+
feeJuiceAddress: AztecAddress,
|
|
147
|
+
gasFees: GasFees,
|
|
148
|
+
bindings?: LoggerBindings,
|
|
149
|
+
) {
|
|
150
|
+
this.#maxFeePerGasValidator = new MaxFeePerGasValidator(gasFees, bindings);
|
|
151
|
+
this.#feePayerBalanceValidator = new FeePayerBalanceValidator(publicDataSource, feeJuiceAddress, bindings);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async validateTx(tx: Tx): Promise<TxValidationResult> {
|
|
155
|
+
const maxFeeValidation = this.#maxFeePerGasValidator.validateMaxFeePerGas(tx);
|
|
156
|
+
if (maxFeeValidation.result === 'invalid') {
|
|
157
|
+
return maxFeeValidation;
|
|
158
|
+
}
|
|
159
|
+
return await this.#feePayerBalanceValidator.validateTx(tx);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -10,7 +10,7 @@ import { Discv5, type Discv5EventEmitter, type IDiscv5CreateOptions } from '@net
|
|
|
10
10
|
import { ENR, SignableENR } from '@nethermindeth/enr';
|
|
11
11
|
import EventEmitter from 'events';
|
|
12
12
|
|
|
13
|
-
import type
|
|
13
|
+
import { DISCV5_RATE_LIMITER_OPTS, type P2PConfig } from '../../config.js';
|
|
14
14
|
import { createNodeENR } from '../../enr/generate-enr.js';
|
|
15
15
|
import { AZTEC_ENR_KEY, Discv5Event, PeerEvent } from '../../types/index.js';
|
|
16
16
|
import { convertToMultiaddr } from '../../util.js';
|
|
@@ -126,6 +126,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
|
|
|
126
126
|
...configOverrides.config,
|
|
127
127
|
},
|
|
128
128
|
metricsRegistry,
|
|
129
|
+
rateLimiterOpts: DISCV5_RATE_LIMITER_OPTS,
|
|
129
130
|
});
|
|
130
131
|
|
|
131
132
|
// Hook onto the onEstablished method to check the peer's version from the ENR,
|
|
@@ -9,7 +9,7 @@ import { protocolContractsHash } from '@aztec-labs/protocol-contracts';
|
|
|
9
9
|
import type { EthAddress, L2BlockSource } from '@aztec-labs/stdlib/block';
|
|
10
10
|
import { DEFAULT_MAX_BLOCKS_PER_CHECKPOINT } from '@aztec-labs/stdlib/config';
|
|
11
11
|
import type { ContractDataSource } from '@aztec-labs/stdlib/contract';
|
|
12
|
-
import { type
|
|
12
|
+
import { type TxAdmissionMinFeesProvider, getNetworkTxGasLimits } from '@aztec-labs/stdlib/gas';
|
|
13
13
|
import type {
|
|
14
14
|
ClientProtocolCircuitVerifier,
|
|
15
15
|
P2PConnectivity,
|
|
@@ -77,7 +77,11 @@ import {
|
|
|
77
77
|
} from '../../msg_validators/index.js';
|
|
78
78
|
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
79
79
|
import {
|
|
80
|
+
type GossipMinFees,
|
|
81
|
+
type GossipValidationFailure,
|
|
82
|
+
IgnoreWithoutPenalty,
|
|
80
83
|
type TransactionValidator,
|
|
84
|
+
computeGossipMinFees,
|
|
81
85
|
createFirstStageTxValidationsForGossipedTransactions,
|
|
82
86
|
createSecondStageTxValidationsForGossipedTransactions,
|
|
83
87
|
createTxValidatorForBlockProposalReceivedTxs,
|
|
@@ -143,13 +147,21 @@ function buildConsensusTimetable(
|
|
|
143
147
|
});
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
interface ValidationResult {
|
|
150
|
+
interface ValidationResult<F extends GossipValidationFailure> {
|
|
147
151
|
name: string;
|
|
148
152
|
isValid: TxValidationResult;
|
|
149
|
-
severity:
|
|
153
|
+
severity: F;
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Failure consequences ordered from mildest to harshest, so the outcome of a stage is the harshest failure it
|
|
158
|
+
* produced. A penalty-free ignore is the mildest: any penalized failure on the same tx wins over it.
|
|
159
|
+
*/
|
|
160
|
+
const GossipValidationFailureByHarshness = [IgnoreWithoutPenalty, ...PeerErrorSeverityByHarshness] as const;
|
|
161
|
+
|
|
162
|
+
type ValidationOutcome<F extends GossipValidationFailure> =
|
|
163
|
+
| { allPassed: true }
|
|
164
|
+
| { allPassed: false; failure: ValidationResult<F> };
|
|
153
165
|
|
|
154
166
|
// REFACTOR: Unify with the type above
|
|
155
167
|
type ReceivedMessageValidationResult<T, M = undefined> =
|
|
@@ -229,7 +241,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
229
241
|
private epochCache: EpochCacheInterface,
|
|
230
242
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
231
243
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
232
|
-
private
|
|
244
|
+
private nextBlockMinFeesProvider: TxAdmissionMinFeesProvider,
|
|
233
245
|
telemetry: TelemetryClient,
|
|
234
246
|
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
235
247
|
private txValidationCache?: TxValidationCache,
|
|
@@ -345,7 +357,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
345
357
|
proofVerifier: ClientProtocolCircuitVerifier;
|
|
346
358
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
347
359
|
peerStore: AztecAsyncKVStore;
|
|
348
|
-
|
|
360
|
+
nextBlockMinFeesProvider: TxAdmissionMinFeesProvider;
|
|
349
361
|
telemetry: TelemetryClient;
|
|
350
362
|
logger: Logger;
|
|
351
363
|
packageVersion: string;
|
|
@@ -359,7 +371,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
359
371
|
mempools,
|
|
360
372
|
proofVerifier,
|
|
361
373
|
peerStore,
|
|
362
|
-
|
|
374
|
+
nextBlockMinFeesProvider,
|
|
363
375
|
telemetry,
|
|
364
376
|
logger,
|
|
365
377
|
packageVersion,
|
|
@@ -587,7 +599,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
587
599
|
epochCache,
|
|
588
600
|
proofVerifier,
|
|
589
601
|
worldStateSynchronizer,
|
|
590
|
-
|
|
602
|
+
nextBlockMinFeesProvider,
|
|
591
603
|
telemetry,
|
|
592
604
|
logger,
|
|
593
605
|
txValidationCache,
|
|
@@ -1121,6 +1133,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1121
1133
|
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
|
|
1122
1134
|
}
|
|
1123
1135
|
|
|
1136
|
+
// Dedup on already-accepted content before validation: a tx hash already in the pool has been
|
|
1137
|
+
// validated, so a re-encoded copy carries no new work and is ignored without running the stages.
|
|
1138
|
+
if ((await this.mempools.txPool.hasTxs([tx.getTxHash()]))[0]) {
|
|
1139
|
+
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1124
1142
|
// Stage 1 setup: chain tip, epoch info, gas fees, and L1 constants reads
|
|
1125
1143
|
const { currentBlockNumber, firstStageValidators } = await timed('fast_validation_setup', async () => {
|
|
1126
1144
|
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
@@ -1138,6 +1156,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1138
1156
|
const { name } = firstStageOutcome.failure;
|
|
1139
1157
|
let { severity } = firstStageOutcome.failure;
|
|
1140
1158
|
|
|
1159
|
+
// The tx failed a check that only holds against this node's own view (it pays less than the fee we
|
|
1160
|
+
// resolved for the next block). Drop it without propagating, but leave the sender's score alone.
|
|
1161
|
+
if (severity === IgnoreWithoutPenalty) {
|
|
1162
|
+
this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: stage 1 local check failed`, {
|
|
1163
|
+
validator: name,
|
|
1164
|
+
source: source.toString(),
|
|
1165
|
+
});
|
|
1166
|
+
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1141
1169
|
// Double spend validator has a special case handler. We perform more detailed examination
|
|
1142
1170
|
// as to how recently the nullifier was entered into the tree and if the transaction should
|
|
1143
1171
|
// have 'known' the nullifier existed. This determines the severity of the penalty applied to the peer.
|
|
@@ -1228,7 +1256,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1228
1256
|
* Process a checkpoint attestation from a peer.
|
|
1229
1257
|
* Validates the attestation and adds it to the pool.
|
|
1230
1258
|
*/
|
|
1231
|
-
|
|
1259
|
+
protected async processCheckpointAttestationFromPeer(
|
|
1232
1260
|
payloadData: Buffer,
|
|
1233
1261
|
msgId: string,
|
|
1234
1262
|
source: PeerId,
|
|
@@ -1264,6 +1292,24 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1264
1292
|
);
|
|
1265
1293
|
}
|
|
1266
1294
|
|
|
1295
|
+
// Bounded set of accepted checkpoint-attestation identities (slot, payload hash, signature bytes), so a
|
|
1296
|
+
// re-encoded copy of an accepted attestation is ignored before the expensive signature recovery.
|
|
1297
|
+
private readonly recentAcceptedAttestations = new Set<string>();
|
|
1298
|
+
private static readonly MAX_RECENT_ATTESTATIONS = 8192;
|
|
1299
|
+
|
|
1300
|
+
private rememberAcceptedAttestation(key: string): void {
|
|
1301
|
+
if (this.recentAcceptedAttestations.has(key)) {
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
this.recentAcceptedAttestations.add(key);
|
|
1305
|
+
if (this.recentAcceptedAttestations.size > LibP2PService.MAX_RECENT_ATTESTATIONS) {
|
|
1306
|
+
const oldest = this.recentAcceptedAttestations.keys().next().value;
|
|
1307
|
+
if (oldest !== undefined) {
|
|
1308
|
+
this.recentAcceptedAttestations.delete(oldest);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1267
1313
|
/** Validates a checkpoint attestation and adds it to the pool. Penalizes the peer if validation fails. */
|
|
1268
1314
|
@trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId, attestation) => ({
|
|
1269
1315
|
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
@@ -1272,6 +1318,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1272
1318
|
peerId: PeerId,
|
|
1273
1319
|
attestation: CheckpointAttestation,
|
|
1274
1320
|
): Promise<ReceivedMessageValidationResult<CheckpointAttestation>> {
|
|
1321
|
+
const attestationKey = `${attestation.payload.header.slotNumber}:${attestation
|
|
1322
|
+
.getPayloadHash()
|
|
1323
|
+
.toString()}:${attestation.signature.toBuffer().toString('hex')}`;
|
|
1324
|
+
// Dedup on already-accepted content before validation: a re-encoded copy of an accepted attestation
|
|
1325
|
+
// is ignored here, before the expensive signature recovery in validate().
|
|
1326
|
+
if (this.recentAcceptedAttestations.has(attestationKey)) {
|
|
1327
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1275
1330
|
const validationResult = await this.checkpointAttestationValidator.validate(attestation);
|
|
1276
1331
|
|
|
1277
1332
|
if (validationResult.result === 'reject') {
|
|
@@ -1302,16 +1357,18 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1302
1357
|
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1303
1358
|
}
|
|
1304
1359
|
|
|
1305
|
-
//
|
|
1360
|
+
// Local per-(slot, signer) retention cap is full. That is receiver-local state, not evidence the sender
|
|
1361
|
+
// forwarded invalid data (it could not know our cache was full), so drop the extra payload without
|
|
1362
|
+
// penalizing the peer. Do not re-broadcast.
|
|
1306
1363
|
if (!added) {
|
|
1307
|
-
this.logger.
|
|
1364
|
+
this.logger.debug(`Ignoring checkpoint attestation exceeding per-signer cap`, {
|
|
1308
1365
|
slot: slot.toString(),
|
|
1309
1366
|
archive: attestation.archive.toString(),
|
|
1310
1367
|
source: peerId.toString(),
|
|
1311
1368
|
attester: attestation.getSender()?.toString(),
|
|
1312
1369
|
count,
|
|
1313
1370
|
});
|
|
1314
|
-
return { result: TopicValidatorResult.
|
|
1371
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1315
1372
|
}
|
|
1316
1373
|
|
|
1317
1374
|
// Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
|
|
@@ -1330,6 +1387,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1330
1387
|
}
|
|
1331
1388
|
|
|
1332
1389
|
// Attestation was added successfully - accept it so other nodes can also detect the equivocation
|
|
1390
|
+
this.rememberAcceptedAttestation(attestationKey);
|
|
1333
1391
|
this.checkpointAttestationCallback?.(attestation);
|
|
1334
1392
|
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
1335
1393
|
}
|
|
@@ -1339,7 +1397,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1339
1397
|
result,
|
|
1340
1398
|
obj: block,
|
|
1341
1399
|
metadata: { isEquivocated, isOversized } = {},
|
|
1342
|
-
} = await this.validateReceivedMessage<
|
|
1400
|
+
} = await this.validateReceivedMessage<
|
|
1401
|
+
BlockProposal,
|
|
1402
|
+
{ isEquivocated: boolean; isOversized: boolean; capFull?: boolean }
|
|
1403
|
+
>(
|
|
1343
1404
|
() => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
|
|
1344
1405
|
msgId,
|
|
1345
1406
|
source,
|
|
@@ -1363,7 +1424,22 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1363
1424
|
protected async validateAndStoreBlockProposal(
|
|
1364
1425
|
peerId: PeerId,
|
|
1365
1426
|
block: BlockProposal,
|
|
1366
|
-
): Promise<
|
|
1427
|
+
): Promise<
|
|
1428
|
+
ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean; capFull?: boolean }>
|
|
1429
|
+
> {
|
|
1430
|
+
// Dedup on already-accepted content before validation: an exact signed payload already stored at
|
|
1431
|
+
// this (slot, index) has been validated, so a re-encoded copy is ignored without re-running it. A
|
|
1432
|
+
// different payload hash at the same position is not stored, so equivocation still takes the full path.
|
|
1433
|
+
if (
|
|
1434
|
+
await this.mempools.attestationPool.hasBlockProposal(
|
|
1435
|
+
block.slotNumber,
|
|
1436
|
+
block.indexWithinCheckpoint,
|
|
1437
|
+
block.getPayloadHash(),
|
|
1438
|
+
)
|
|
1439
|
+
) {
|
|
1440
|
+
return { result: TopicValidatorResult.Ignore, obj: block };
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1367
1443
|
const validationResult = await this.blockProposalValidator.validate(block);
|
|
1368
1444
|
|
|
1369
1445
|
if (validationResult.result === 'reject') {
|
|
@@ -1396,9 +1472,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1396
1472
|
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1397
1473
|
}
|
|
1398
1474
|
|
|
1399
|
-
//
|
|
1475
|
+
// Local per-position retention cap is full. That is receiver-local state, not evidence the
|
|
1476
|
+
// sender forwarded invalid data (it could not know our cache was full), so drop the extra
|
|
1477
|
+
// payload without penalizing the peer. Do not re-broadcast.
|
|
1400
1478
|
if (!added) {
|
|
1401
|
-
this.logger.
|
|
1479
|
+
this.logger.debug(`Ignoring block proposal exceeding per-position cap`, {
|
|
1402
1480
|
...block.toBlockInfo(),
|
|
1403
1481
|
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1404
1482
|
count,
|
|
@@ -1406,9 +1484,14 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1406
1484
|
source: peerId.toString(),
|
|
1407
1485
|
});
|
|
1408
1486
|
return {
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1487
|
+
// isEquivocated is false here even when count > 1: a full cache is a receiver-local drop, not
|
|
1488
|
+
// a fresh equivocation by this block. Genuine equivocation is already captured on the add
|
|
1489
|
+
// (duplicateProposalCallback below). capFull tells the checkpoint path, whose terminal block
|
|
1490
|
+
// this may be, to ignore the whole checkpoint too, rather than store and re-broadcast it while
|
|
1491
|
+
// its terminal block was dropped.
|
|
1492
|
+
result: TopicValidatorResult.Ignore,
|
|
1493
|
+
obj: block,
|
|
1494
|
+
metadata: { isEquivocated: false, isOversized, capFull: true },
|
|
1412
1495
|
};
|
|
1413
1496
|
}
|
|
1414
1497
|
|
|
@@ -1558,6 +1641,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1558
1641
|
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1559
1642
|
>
|
|
1560
1643
|
> {
|
|
1644
|
+
// Dedup on already-accepted content before validation: an exact signed checkpoint payload already
|
|
1645
|
+
// stored for this slot has been validated, so a re-encoded copy is ignored without re-running it. A
|
|
1646
|
+
// different payload hash at the same slot is not stored, so equivocation still takes the full path.
|
|
1647
|
+
if (await this.mempools.attestationPool.hasCheckpointProposal(checkpoint.slotNumber, checkpoint.getPayloadHash())) {
|
|
1648
|
+
return { result: TopicValidatorResult.Ignore, obj: checkpoint };
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1561
1651
|
const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1562
1652
|
|
|
1563
1653
|
if (validationResult.result === 'reject') {
|
|
@@ -1579,14 +1669,14 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1579
1669
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1580
1670
|
});
|
|
1581
1671
|
const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1582
|
-
const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } =
|
|
1672
|
+
const { obj, metadata: { isEquivocated, isOversized: blockIsOversized, capFull: blockCapFull } = {} } =
|
|
1673
|
+
blockProposalResult;
|
|
1583
1674
|
isOversized = blockIsOversized ?? false;
|
|
1584
1675
|
|
|
1585
|
-
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj
|
|
1676
|
+
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj) {
|
|
1586
1677
|
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1587
1678
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1588
1679
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1589
|
-
isEquivocated,
|
|
1590
1680
|
result: blockProposalResult.result,
|
|
1591
1681
|
});
|
|
1592
1682
|
return {
|
|
@@ -1594,6 +1684,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1594
1684
|
severity:
|
|
1595
1685
|
'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
|
|
1596
1686
|
};
|
|
1687
|
+
} else if (blockCapFull || isEquivocated) {
|
|
1688
|
+
// Both cases depend on our local cache, not on anything the relaying peer could check, so do not
|
|
1689
|
+
// penalize it. Genuine equivocation was already reported at the block level. Drop the checkpoint:
|
|
1690
|
+
// no store, no re-broadcast, no processing or attestation.
|
|
1691
|
+
this.logger.debug(`Ignoring checkpoint whose terminal block was a receiver-local drop`, {
|
|
1692
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1693
|
+
[Attributes.P2P_ID]: peerId.toString(),
|
|
1694
|
+
});
|
|
1695
|
+
return { result: TopicValidatorResult.Ignore, obj: checkpoint };
|
|
1597
1696
|
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
|
|
1598
1697
|
// An oversized terminal block is re-broadcast as slashing evidence but never processed.
|
|
1599
1698
|
processBlock = true;
|
|
@@ -1619,19 +1718,19 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1619
1718
|
};
|
|
1620
1719
|
}
|
|
1621
1720
|
|
|
1622
|
-
//
|
|
1623
|
-
//
|
|
1721
|
+
// Local per-slot retention cap is full. That is receiver-local state, not evidence the sender
|
|
1722
|
+
// forwarded invalid data, so drop the extra payload without penalizing the peer. Do not
|
|
1723
|
+
// re-broadcast. We still return the checkpoint obj so the lastBlock can be processed if valid.
|
|
1624
1724
|
if (!added) {
|
|
1625
|
-
this.logger.
|
|
1725
|
+
this.logger.debug(`Ignoring checkpoint proposal exceeding per-slot cap`, {
|
|
1626
1726
|
...checkpoint.toCheckpointInfo(),
|
|
1627
1727
|
count,
|
|
1628
1728
|
source: peerId.toString(),
|
|
1629
1729
|
});
|
|
1630
1730
|
return {
|
|
1631
|
-
result: TopicValidatorResult.
|
|
1731
|
+
result: TopicValidatorResult.Ignore,
|
|
1632
1732
|
obj: checkpoint,
|
|
1633
1733
|
metadata: { isEquivocated, processBlock, isOversized },
|
|
1634
|
-
severity: PeerErrorSeverity.HighToleranceError,
|
|
1635
1734
|
};
|
|
1636
1735
|
}
|
|
1637
1736
|
|
|
@@ -1805,8 +1904,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1805
1904
|
}
|
|
1806
1905
|
}
|
|
1807
1906
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1907
|
+
/** The fees a gossiped transaction is checked against (see {@link computeGossipMinFees}). */
|
|
1908
|
+
protected async getGasFees(): Promise<GossipMinFees> {
|
|
1909
|
+
const [admission, l1Forward] = await Promise.all([
|
|
1910
|
+
this.nextBlockMinFeesProvider.getAdmissionMinFees(),
|
|
1911
|
+
this.nextBlockMinFeesProvider.getL1ForwardMinFees(),
|
|
1912
|
+
]);
|
|
1913
|
+
return computeGossipMinFees(admission, l1Forward);
|
|
1810
1914
|
}
|
|
1811
1915
|
|
|
1812
1916
|
/**
|
|
@@ -1852,7 +1956,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1852
1956
|
protected async createFirstStageMessageValidators(
|
|
1853
1957
|
currentBlockNumber: BlockNumber,
|
|
1854
1958
|
nextSlotTimestamp: UInt64,
|
|
1855
|
-
): Promise<Record<string, TransactionValidator
|
|
1959
|
+
): Promise<Record<string, TransactionValidator<GossipValidationFailure>>> {
|
|
1856
1960
|
const gasFees = await this.getGasFees();
|
|
1857
1961
|
const allowedInSetup = [
|
|
1858
1962
|
...(await getDefaultAllowedSetupFunctions()),
|
|
@@ -1892,10 +1996,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1892
1996
|
* @param messageValidators - The message validators to run.
|
|
1893
1997
|
* @returns The validation outcome.
|
|
1894
1998
|
*/
|
|
1895
|
-
private async runValidations(
|
|
1999
|
+
private async runValidations<F extends GossipValidationFailure>(
|
|
1896
2000
|
tx: Tx,
|
|
1897
|
-
messageValidators: Record<string, TransactionValidator
|
|
1898
|
-
): Promise<ValidationOutcome
|
|
2001
|
+
messageValidators: Record<string, TransactionValidator<F>>,
|
|
2002
|
+
): Promise<ValidationOutcome<F>> {
|
|
1899
2003
|
// Gossip validation stays exhaustive within each stage because peer scoring uses the harshest failure severity;
|
|
1900
2004
|
// failing fast would make the penalty depend on validator order. The stage boundaries themselves fail fast.
|
|
1901
2005
|
const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
|
|
@@ -1908,7 +2012,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1908
2012
|
const failures = allValidations.filter(x => !x.isValid);
|
|
1909
2013
|
if (failures.length > 0) {
|
|
1910
2014
|
// Pick the most severe failure (lowest tolerance = harshest penalty)
|
|
1911
|
-
const failed = maxBy(failures, f =>
|
|
2015
|
+
const failed = maxBy(failures, f => GossipValidationFailureByHarshness.indexOf(f.severity))!;
|
|
1912
2016
|
return {
|
|
1913
2017
|
allPassed: false,
|
|
1914
2018
|
failure: {
|
|
@@ -923,6 +923,20 @@ export class PeerManager implements PeerManagerInterface {
|
|
|
923
923
|
timeoutUntilMs:
|
|
924
924
|
this.dateProvider.now() + (this.config.peerFailedBanTimeMs ?? DEFAULT_FAILED_PEER_BAN_TIME_MS),
|
|
925
925
|
});
|
|
926
|
+
// Drop the address-book record written before the dial. Nothing else deletes /peers/
|
|
927
|
+
// rows, so a stream of unreachable discovered peers would otherwise grow the durable
|
|
928
|
+
// peerstore without bound. Keep operator-configured peers (trusted, private, preferred),
|
|
929
|
+
// and keep any peer we currently hold a connection to: an inbound connect or a concurrent
|
|
930
|
+
// rediscovery may have refreshed the record while this dial was failing.
|
|
931
|
+
if (!this.isProtectedPeer(peer.peerId) && this.libP2PNode.getConnections(peer.peerId).length === 0) {
|
|
932
|
+
try {
|
|
933
|
+
await this.libP2PNode.peerStore.delete(peer.peerId);
|
|
934
|
+
} catch (deleteError) {
|
|
935
|
+
this.logger.trace(`Failed to remove dropped peer ${id} from the peer store`, {
|
|
936
|
+
error: inspect(deleteError),
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
}
|
|
926
940
|
}
|
|
927
941
|
}
|
|
928
942
|
}
|
|
@@ -189,6 +189,9 @@ export class TxProvider implements ITxProvider {
|
|
|
189
189
|
const moreTxsFromPool = compactArray(await this.txPool.getTxsByHash([...missingTxHashes].map(TxHash.fromString)));
|
|
190
190
|
|
|
191
191
|
if (moreTxsFromPool.length > 0) {
|
|
192
|
+
for (const tx of moreTxsFromPool) {
|
|
193
|
+
missingTxHashes.delete(tx.getTxHash().toString());
|
|
194
|
+
}
|
|
192
195
|
this.instrumentation.incTxsFromMempool(moreTxsFromPool.length);
|
|
193
196
|
this.log.debug(
|
|
194
197
|
`Retrieved ${moreTxsFromPool.length} txs from pool retry for block proposal (${missingTxHashes.size} pending)`,
|