@aztec/p2p 0.0.1-commit.808bf7f90 → 0.0.1-commit.8227e42
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/client/factory.d.ts +1 -1
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +8 -20
- package/dest/client/interface.d.ts +3 -10
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +2 -10
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +2 -57
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +5 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +9 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +12 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +2 -2
- 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 +1 -6
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
- package/dest/msg_validators/tx_validator/factory.d.ts +114 -6
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +219 -58
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +58 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +72 -35
- package/dest/msg_validators/tx_validator/index.d.ts +2 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
- package/dest/services/dummy_service.d.ts +2 -3
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +1 -4
- package/dest/services/libp2p/libp2p_service.d.ts +10 -7
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +57 -70
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +14 -37
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +17 -11
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +49 -15
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
- package/dest/services/service.d.ts +2 -2
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_provider.d.ts +3 -3
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +4 -4
- package/dest/test-helpers/testbench-utils.d.ts +2 -2
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/client/factory.ts +13 -33
- package/src/client/interface.ts +2 -10
- package/src/client/p2p_client.ts +2 -76
- package/src/mem_pools/tx_pool_v2/interfaces.ts +4 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +13 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +1 -1
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +2 -8
- package/src/msg_validators/tx_validator/README.md +115 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -3
- package/src/msg_validators/tx_validator/factory.ts +353 -77
- package/src/msg_validators/tx_validator/gas_validator.ts +84 -29
- package/src/msg_validators/tx_validator/index.ts +1 -0
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/services/dummy_service.ts +1 -5
- package/src/services/libp2p/libp2p_service.ts +70 -79
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +14 -42
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +63 -24
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
- package/src/services/service.ts +1 -1
- package/src/services/tx_provider.ts +2 -2
- package/src/test-helpers/testbench-utils.ts +1 -1
|
@@ -69,9 +69,11 @@ import {
|
|
|
69
69
|
} from '../../msg_validators/index.js';
|
|
70
70
|
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
71
71
|
import {
|
|
72
|
-
type
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
type TransactionValidator,
|
|
73
|
+
createFirstStageTxValidationsForGossipedTransactions,
|
|
74
|
+
createSecondStageTxValidationsForGossipedTransactions,
|
|
75
|
+
createTxValidatorForBlockProposalReceivedTxs,
|
|
76
|
+
createTxValidatorForReqResponseReceivedTxs,
|
|
75
77
|
} from '../../msg_validators/tx_validator/factory.js';
|
|
76
78
|
import { GossipSubEvent } from '../../types/index.js';
|
|
77
79
|
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
@@ -87,6 +89,9 @@ import { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
|
87
89
|
import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
|
|
88
90
|
import type { P2PReqRespConfig } from '../reqresp/config.js';
|
|
89
91
|
import {
|
|
92
|
+
AuthRequest,
|
|
93
|
+
BlockTxsRequest,
|
|
94
|
+
BlockTxsResponse,
|
|
90
95
|
DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
91
96
|
type ReqRespInterface,
|
|
92
97
|
type ReqRespResponse,
|
|
@@ -94,14 +99,9 @@ import {
|
|
|
94
99
|
type ReqRespSubProtocolHandler,
|
|
95
100
|
type ReqRespSubProtocolHandlers,
|
|
96
101
|
type ReqRespSubProtocolValidators,
|
|
102
|
+
StatusMessage,
|
|
97
103
|
type SubProtocolMap,
|
|
98
104
|
ValidationError,
|
|
99
|
-
} from '../reqresp/index.js';
|
|
100
|
-
import {
|
|
101
|
-
AuthRequest,
|
|
102
|
-
BlockTxsRequest,
|
|
103
|
-
BlockTxsResponse,
|
|
104
|
-
StatusMessage,
|
|
105
105
|
pingHandler,
|
|
106
106
|
reqGoodbyeHandler,
|
|
107
107
|
reqRespBlockHandler,
|
|
@@ -906,15 +906,45 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
906
906
|
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
907
907
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
908
908
|
const tx = Tx.fromBuffer(payloadData);
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
909
|
+
|
|
910
|
+
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
911
|
+
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
912
|
+
|
|
913
|
+
// Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
|
|
914
|
+
const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
915
|
+
const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
|
|
916
|
+
if (!firstStageOutcome.allPassed) {
|
|
917
|
+
const { name } = firstStageOutcome.failure;
|
|
918
|
+
let { severity } = firstStageOutcome.failure;
|
|
919
|
+
|
|
920
|
+
// Double spend validator has a special case handler. We perform more detailed examination
|
|
921
|
+
// as to how recently the nullifier was entered into the tree and if the transaction should
|
|
922
|
+
// have 'known' the nullifier existed. This determines the severity of the penalty applied to the peer.
|
|
923
|
+
if (name === 'doubleSpendValidator') {
|
|
924
|
+
const txBlockNumber = BlockNumber(currentBlockNumber + 1);
|
|
925
|
+
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
this.peerManager.penalizePeer(source, severity);
|
|
929
|
+
return { result: TopicValidatorResult.Reject };
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
|
|
933
|
+
const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
|
|
934
|
+
if (canAdd === 'ignored') {
|
|
935
|
+
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// Stage 2: expensive proof verification
|
|
939
|
+
const secondStageValidators = this.createSecondStageMessageValidators();
|
|
940
|
+
const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
|
|
941
|
+
if (!secondStageOutcome.allPassed) {
|
|
942
|
+
const { severity } = secondStageOutcome.failure;
|
|
943
|
+
this.peerManager.penalizePeer(source, severity);
|
|
914
944
|
return { result: TopicValidatorResult.Reject };
|
|
915
945
|
}
|
|
916
946
|
|
|
917
|
-
//
|
|
947
|
+
// Pool add: persist the tx
|
|
918
948
|
const txHash = tx.getTxHash();
|
|
919
949
|
const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
|
|
920
950
|
|
|
@@ -922,7 +952,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
922
952
|
const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
|
|
923
953
|
|
|
924
954
|
this.logger.trace(`Validate propagated tx`, {
|
|
925
|
-
isValid,
|
|
926
955
|
wasAccepted,
|
|
927
956
|
wasIgnored,
|
|
928
957
|
[Attributes.P2P_ID]: source.toString(),
|
|
@@ -1537,43 +1566,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1537
1566
|
}
|
|
1538
1567
|
|
|
1539
1568
|
protected createRequestedTxValidator(): TxValidator {
|
|
1540
|
-
return
|
|
1569
|
+
return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
|
|
1541
1570
|
l1ChainId: this.config.l1ChainId,
|
|
1542
1571
|
rollupVersion: this.config.rollupVersion,
|
|
1543
1572
|
});
|
|
1544
1573
|
}
|
|
1545
1574
|
|
|
1546
|
-
@trackSpan('Libp2pService.validatePropagatedTx', tx => ({
|
|
1547
|
-
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
1548
|
-
}))
|
|
1549
|
-
protected async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
|
|
1550
|
-
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
1551
|
-
|
|
1552
|
-
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1553
|
-
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1554
|
-
const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
1555
|
-
|
|
1556
|
-
for (const validator of messageValidators) {
|
|
1557
|
-
const outcome = await this.runValidations(tx, validator);
|
|
1558
|
-
|
|
1559
|
-
if (outcome.allPassed) {
|
|
1560
|
-
continue;
|
|
1561
|
-
}
|
|
1562
|
-
const { name } = outcome.failure;
|
|
1563
|
-
let { severity } = outcome.failure;
|
|
1564
|
-
|
|
1565
|
-
// Double spend validator has a special case handler
|
|
1566
|
-
if (name === 'doubleSpendValidator') {
|
|
1567
|
-
const txBlockNumber = BlockNumber(currentBlockNumber + 1); // tx is expected to be in the next block
|
|
1568
|
-
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
this.peerManager.penalizePeer(peerId, severity);
|
|
1572
|
-
return false;
|
|
1573
|
-
}
|
|
1574
|
-
return true;
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
1575
|
private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
|
|
1578
1576
|
if (blockNumber === this.feesCache?.blockNumber) {
|
|
1579
1577
|
return this.feesCache.gasFees;
|
|
@@ -1601,60 +1599,53 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1601
1599
|
};
|
|
1602
1600
|
}
|
|
1603
1601
|
|
|
1604
|
-
public async
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1602
|
+
public async validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void> {
|
|
1603
|
+
const validator = createTxValidatorForBlockProposalReceivedTxs(
|
|
1604
|
+
this.proofVerifier,
|
|
1605
|
+
{ l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
|
|
1606
|
+
this.logger.getBindings(),
|
|
1607
|
+
);
|
|
1610
1608
|
|
|
1611
|
-
await Promise.all(
|
|
1609
|
+
const results = await Promise.all(
|
|
1612
1610
|
txs.map(async tx => {
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
if (!outcome.allPassed) {
|
|
1616
|
-
throw new Error('Invalid tx detected', { cause: { outcome } });
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1611
|
+
const result = await validator.validateTx(tx);
|
|
1612
|
+
return result.result !== 'invalid';
|
|
1619
1613
|
}),
|
|
1620
1614
|
);
|
|
1615
|
+
if (results.some(value => value === false)) {
|
|
1616
|
+
throw new Error('Invalid tx detected');
|
|
1617
|
+
}
|
|
1621
1618
|
}
|
|
1622
1619
|
|
|
1623
|
-
/**
|
|
1624
|
-
|
|
1625
|
-
*
|
|
1626
|
-
* Each validator is a pair of a validator and a severity.
|
|
1627
|
-
* If a validator fails, the peer is penalized with the severity of the validator.
|
|
1628
|
-
*
|
|
1629
|
-
* @param currentBlockNumber - The current synced block number.
|
|
1630
|
-
* @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
|
|
1631
|
-
* @returns The message validators.
|
|
1632
|
-
*/
|
|
1633
|
-
private async createMessageValidators(
|
|
1620
|
+
/** Creates the first stage (fast) validators for gossiped transactions. */
|
|
1621
|
+
protected async createFirstStageMessageValidators(
|
|
1634
1622
|
currentBlockNumber: BlockNumber,
|
|
1635
1623
|
nextSlotTimestamp: UInt64,
|
|
1636
|
-
): Promise<Record<string,
|
|
1624
|
+
): Promise<Record<string, TransactionValidator>> {
|
|
1637
1625
|
const gasFees = await this.getGasFees(currentBlockNumber);
|
|
1638
1626
|
const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
|
|
1627
|
+
const blockNumber = BlockNumber(currentBlockNumber + 1);
|
|
1639
1628
|
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
return createTxMessageValidators(
|
|
1629
|
+
return createFirstStageTxValidationsForGossipedTransactions(
|
|
1643
1630
|
nextSlotTimestamp,
|
|
1644
|
-
|
|
1631
|
+
blockNumber,
|
|
1645
1632
|
this.worldStateSynchronizer,
|
|
1646
1633
|
gasFees,
|
|
1647
1634
|
this.config.l1ChainId,
|
|
1648
1635
|
this.config.rollupVersion,
|
|
1649
1636
|
protocolContractsHash,
|
|
1650
1637
|
this.archiver,
|
|
1651
|
-
this.proofVerifier,
|
|
1652
1638
|
!this.config.disableTransactions,
|
|
1653
1639
|
allowedInSetup,
|
|
1654
1640
|
this.logger.getBindings(),
|
|
1655
1641
|
);
|
|
1656
1642
|
}
|
|
1657
1643
|
|
|
1644
|
+
/** Creates the second stage (expensive proof verification) validators for gossiped transactions. */
|
|
1645
|
+
protected createSecondStageMessageValidators(): Record<string, TransactionValidator> {
|
|
1646
|
+
return createSecondStageTxValidationsForGossipedTransactions(this.proofVerifier, this.logger.getBindings());
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1658
1649
|
/**
|
|
1659
1650
|
* Run validations on a tx.
|
|
1660
1651
|
* @param tx - The tx to validate.
|
|
@@ -1663,7 +1654,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1663
1654
|
*/
|
|
1664
1655
|
private async runValidations(
|
|
1665
1656
|
tx: Tx,
|
|
1666
|
-
messageValidators: Record<string,
|
|
1657
|
+
messageValidators: Record<string, TransactionValidator>,
|
|
1667
1658
|
): Promise<ValidationOutcome> {
|
|
1668
1659
|
const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
|
|
1669
1660
|
const { result } = await validator.validateTx(tx);
|
|
@@ -8,7 +8,6 @@ import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
|
8
8
|
import { Tx, TxArray, TxHash } from '@aztec/stdlib/tx';
|
|
9
9
|
|
|
10
10
|
import type { PeerId } from '@libp2p/interface';
|
|
11
|
-
import { peerIdFromString } from '@libp2p/peer-id';
|
|
12
11
|
|
|
13
12
|
import type { IMissingTxsTracker } from '../../tx_collection/missing_txs_tracker.js';
|
|
14
13
|
import { ReqRespSubProtocol } from '.././interface.js';
|
|
@@ -90,10 +89,9 @@ export class BatchTxRequester {
|
|
|
90
89
|
if (this.opts.peerCollection) {
|
|
91
90
|
this.peers = this.opts.peerCollection;
|
|
92
91
|
} else {
|
|
93
|
-
const initialPeers = this.p2pService.connectionSampler.getPeerListSortedByConnectionCountAsc();
|
|
94
92
|
const badPeerThreshold = this.opts.badPeerThreshold ?? DEFAULT_BATCH_TX_REQUESTER_BAD_PEER_THRESHOLD;
|
|
95
93
|
this.peers = new PeerCollection(
|
|
96
|
-
|
|
94
|
+
this.p2pService.connectionSampler,
|
|
97
95
|
this.pinnedPeer,
|
|
98
96
|
this.dateProvider,
|
|
99
97
|
badPeerThreshold,
|
|
@@ -227,7 +225,6 @@ export class BatchTxRequester {
|
|
|
227
225
|
* Starts dumb worker loops
|
|
228
226
|
* */
|
|
229
227
|
private async dumbRequester() {
|
|
230
|
-
const nextPeerIndex = this.makeRoundRobinIndexer();
|
|
231
228
|
const nextBatchIndex = this.makeRoundRobinIndexer();
|
|
232
229
|
|
|
233
230
|
// Chunk missing tx hashes into batches of txBatchSize, wrapping around to ensure no peer gets less than txBatchSize
|
|
@@ -263,15 +260,9 @@ export class BatchTxRequester {
|
|
|
263
260
|
return { blockRequest, txs };
|
|
264
261
|
};
|
|
265
262
|
|
|
266
|
-
const
|
|
267
|
-
const peers = this.peers.getDumbPeersToQuery();
|
|
268
|
-
const idx = nextPeerIndex(() => peers.length);
|
|
269
|
-
return idx === undefined ? undefined : peerIdFromString(peers[idx]);
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const workerCount = Math.min(this.dumbParallelWorkerCount, this.peers.getAllPeers().size);
|
|
263
|
+
const workerCount = this.dumbParallelWorkerCount;
|
|
273
264
|
const workers = Array.from({ length: workerCount }, (_, index) =>
|
|
274
|
-
this.dumbWorkerLoop(
|
|
265
|
+
this.dumbWorkerLoop(this.peers.nextDumbPeerToQuery.bind(this.peers), makeRequest, index + 1),
|
|
275
266
|
);
|
|
276
267
|
|
|
277
268
|
await Promise.allSettled(workers);
|
|
@@ -332,14 +323,6 @@ export class BatchTxRequester {
|
|
|
332
323
|
* Starts smart worker loops
|
|
333
324
|
* */
|
|
334
325
|
private async smartRequester() {
|
|
335
|
-
const nextPeerIndex = this.makeRoundRobinIndexer();
|
|
336
|
-
|
|
337
|
-
const nextPeer = () => {
|
|
338
|
-
const peers = this.peers.getSmartPeersToQuery();
|
|
339
|
-
const idx = nextPeerIndex(() => peers.length);
|
|
340
|
-
return idx === undefined ? undefined : peerIdFromString(peers[idx]);
|
|
341
|
-
};
|
|
342
|
-
|
|
343
326
|
const makeRequest = (pid: PeerId) => {
|
|
344
327
|
const txs = this.txsMetadata.getTxsToRequestFromThePeer(pid);
|
|
345
328
|
const blockRequest = BlockTxsRequest.fromTxsSourceAndMissingTxs(this.blockTxsSource, txs);
|
|
@@ -350,9 +333,8 @@ export class BatchTxRequester {
|
|
|
350
333
|
return { blockRequest, txs };
|
|
351
334
|
};
|
|
352
335
|
|
|
353
|
-
const workers = Array.from(
|
|
354
|
-
|
|
355
|
-
(_, index) => this.smartWorkerLoop(nextPeer, makeRequest, index + 1),
|
|
336
|
+
const workers = Array.from({ length: this.smartParallelWorkerCount }, (_, index) =>
|
|
337
|
+
this.smartWorkerLoop(this.peers.nextSmartPeerToQuery.bind(this.peers), makeRequest, index + 1),
|
|
356
338
|
);
|
|
357
339
|
|
|
358
340
|
await Promise.allSettled(workers);
|
|
@@ -387,26 +369,18 @@ export class BatchTxRequester {
|
|
|
387
369
|
if (weRanOutOfPeersToQuery) {
|
|
388
370
|
this.logger.debug(`Worker loop smart: No more peers to query`);
|
|
389
371
|
|
|
390
|
-
// If
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const thereAreSomeRateLimitedSmartPeers = nextSmartPeerDelay !== undefined;
|
|
397
|
-
if (thereAreSomeRateLimitedSmartPeers) {
|
|
398
|
-
await this.sleepClampedToDeadline(nextSmartPeerDelay);
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
this.logger.debug(`Worker loop smart: No more smart peers to query killing ${workerIndex}`);
|
|
403
|
-
break;
|
|
372
|
+
// If we have rate limited peers wait for them.
|
|
373
|
+
const nextSmartPeerDelay = this.peers.getNextSmartPeerAvailabilityDelayMs();
|
|
374
|
+
const thereAreSomeRateLimitedSmartPeers = nextSmartPeerDelay !== undefined;
|
|
375
|
+
if (thereAreSomeRateLimitedSmartPeers) {
|
|
376
|
+
await this.sleepClampedToDeadline(nextSmartPeerDelay);
|
|
377
|
+
continue;
|
|
404
378
|
}
|
|
405
379
|
|
|
406
|
-
// Otherwise there are still some dumb peers that could become smart.
|
|
407
380
|
// We end up here when all known smart peers became temporarily unavailable via combination of
|
|
408
381
|
// (bad, in-flight, or rate-limited) or in some weird scenario all current smart peers turn bad which is permanent
|
|
409
|
-
// but dumb peers
|
|
382
|
+
// but there are dumb peers that could be promoted
|
|
383
|
+
// or new peer can join as dumb and be promoted later
|
|
410
384
|
//
|
|
411
385
|
// When a dumb peer responds with valid txIndices, it gets
|
|
412
386
|
// promoted to smart and releases the semaphore, waking this worker.
|
|
@@ -599,9 +573,7 @@ export class BatchTxRequester {
|
|
|
599
573
|
this.markTxsPeerHas(peerId, response);
|
|
600
574
|
|
|
601
575
|
// Unblock smart workers
|
|
602
|
-
|
|
603
|
-
this.smartRequesterSemaphore.release();
|
|
604
|
-
}
|
|
576
|
+
this.smartRequesterSemaphore.release();
|
|
605
577
|
}
|
|
606
578
|
|
|
607
579
|
private isBlockResponseValid(response: BlockTxsResponse): boolean {
|
|
@@ -2,18 +2,22 @@ import type { DateProvider } from '@aztec/foundation/timer';
|
|
|
2
2
|
import type { PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
3
3
|
|
|
4
4
|
import type { PeerId } from '@libp2p/interface';
|
|
5
|
+
import { peerIdFromString } from '@libp2p/peer-id';
|
|
5
6
|
|
|
7
|
+
import type { ConnectionSampler } from '../connection-sampler/connection_sampler.js';
|
|
6
8
|
import { DEFAULT_BATCH_TX_REQUESTER_BAD_PEER_THRESHOLD } from './config.js';
|
|
7
9
|
import type { IPeerPenalizer } from './interface.js';
|
|
8
10
|
|
|
9
11
|
export const RATE_LIMIT_EXCEEDED_PEER_CACHE_TTL = 1000; // 1s
|
|
10
12
|
|
|
11
13
|
export interface IPeerCollection {
|
|
12
|
-
getAllPeers(): Set<string>;
|
|
13
|
-
getSmartPeers(): Set<string>;
|
|
14
14
|
markPeerSmart(peerId: PeerId): void;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
|
|
16
|
+
/** Sample next peer in round-robin fashion. No smart peers if returns undefined */
|
|
17
|
+
nextSmartPeerToQuery(): PeerId | undefined;
|
|
18
|
+
/** Sample next peer in round-robin fashion. No dumb peers if returns undefined */
|
|
19
|
+
nextDumbPeerToQuery(): PeerId | undefined;
|
|
20
|
+
|
|
17
21
|
thereAreSomeDumbRatelimitExceededPeers(): boolean;
|
|
18
22
|
penalisePeer(peerId: PeerId, severity: PeerErrorSeverity): void;
|
|
19
23
|
unMarkPeerAsBad(peerId: PeerId): void;
|
|
@@ -28,8 +32,6 @@ export interface IPeerCollection {
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
export class PeerCollection implements IPeerCollection {
|
|
31
|
-
private readonly peers;
|
|
32
|
-
|
|
33
35
|
private readonly smartPeers = new Set<string>();
|
|
34
36
|
private readonly inFlightPeers = new Set<string>();
|
|
35
37
|
private readonly rateLimitExceededPeers = new Map<string, number>();
|
|
@@ -37,46 +39,60 @@ export class PeerCollection implements IPeerCollection {
|
|
|
37
39
|
private readonly badPeers = new Set<string>();
|
|
38
40
|
|
|
39
41
|
constructor(
|
|
40
|
-
|
|
42
|
+
private readonly connectionSampler: Pick<ConnectionSampler, 'getPeerListSortedByConnectionCountAsc'>,
|
|
41
43
|
private readonly pinnedPeerId: PeerId | undefined,
|
|
42
44
|
private readonly dateProvider: DateProvider,
|
|
43
45
|
private readonly badPeerThreshold: number = DEFAULT_BATCH_TX_REQUESTER_BAD_PEER_THRESHOLD,
|
|
44
46
|
private readonly peerPenalizer?: IPeerPenalizer,
|
|
45
47
|
) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Pinned peer is treaded specially, always mark it as in-flight
|
|
48
|
+
// Pinned peer is treated specially, always mark it as in-flight
|
|
49
49
|
// and never return it as part of smart/dumb peers
|
|
50
50
|
if (this.pinnedPeerId) {
|
|
51
51
|
const peerIdStr = this.pinnedPeerId.toString();
|
|
52
52
|
this.inFlightPeers.add(peerIdStr);
|
|
53
|
-
this.peers.delete(peerIdStr);
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
public
|
|
58
|
-
|
|
56
|
+
public markPeerSmart(peerId: PeerId): void {
|
|
57
|
+
this.smartPeers.add(peerId.toString());
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
// We keep track of all peers that are queried for peer sampling algorithm
|
|
61
|
+
private queriedSmartPeers: Set<string> = new Set<string>();
|
|
62
|
+
private queriedDumbPeers: Set<string> = new Set<string>();
|
|
63
|
+
|
|
64
|
+
private static nextPeer(allPeers: Set<string>, queried: Set<string>): PeerId | undefined {
|
|
65
|
+
if (allPeers.size === 0) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const availablePeers = allPeers.difference(queried);
|
|
69
|
+
let [first] = availablePeers;
|
|
70
|
+
if (first === undefined) {
|
|
71
|
+
// We queried all peers. Start over
|
|
72
|
+
[first] = allPeers;
|
|
73
|
+
queried.clear();
|
|
74
|
+
}
|
|
75
|
+
queried.add(first);
|
|
76
|
+
return peerIdFromString(first);
|
|
63
77
|
}
|
|
64
78
|
|
|
65
|
-
public
|
|
66
|
-
this.
|
|
79
|
+
public nextSmartPeerToQuery(): PeerId | undefined {
|
|
80
|
+
return PeerCollection.nextPeer(this.availableSmartPeers, this.queriedSmartPeers);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public nextDumbPeerToQuery(): PeerId | undefined {
|
|
84
|
+
return PeerCollection.nextPeer(this.availableDumbPeers, this.queriedDumbPeers);
|
|
67
85
|
}
|
|
68
86
|
|
|
69
|
-
|
|
70
|
-
return
|
|
87
|
+
private get availableSmartPeers(): Set<string> {
|
|
88
|
+
return this.peers.intersection(
|
|
71
89
|
this.smartPeers.difference(this.getBadPeers().union(this.inFlightPeers).union(this.getRateLimitExceededPeers())),
|
|
72
90
|
);
|
|
73
91
|
}
|
|
74
92
|
|
|
75
|
-
|
|
76
|
-
return
|
|
77
|
-
this.
|
|
78
|
-
this.smartPeers.union(this.getBadPeers()).union(this.inFlightPeers).union(this.getRateLimitExceededPeers()),
|
|
79
|
-
),
|
|
93
|
+
private get availableDumbPeers(): Set<string> {
|
|
94
|
+
return this.peers.difference(
|
|
95
|
+
this.smartPeers.union(this.getBadPeers()).union(this.inFlightPeers).union(this.getRateLimitExceededPeers()),
|
|
80
96
|
);
|
|
81
97
|
}
|
|
82
98
|
|
|
@@ -202,4 +218,27 @@ export class PeerCollection implements IPeerCollection {
|
|
|
202
218
|
|
|
203
219
|
return minExpiry! - now;
|
|
204
220
|
}
|
|
221
|
+
|
|
222
|
+
private orderedPeers: Set<string> = new Set();
|
|
223
|
+
|
|
224
|
+
private get peers(): Set<string> {
|
|
225
|
+
const pinnedStr = this.pinnedPeerId?.toString();
|
|
226
|
+
const currentlyConnected = new Set(
|
|
227
|
+
this.connectionSampler
|
|
228
|
+
.getPeerListSortedByConnectionCountAsc()
|
|
229
|
+
.map(p => p.toString())
|
|
230
|
+
.filter(p => p !== pinnedStr),
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// Remove disconnected peers, preserving order of the rest.
|
|
234
|
+
this.orderedPeers = this.orderedPeers.intersection(currentlyConnected);
|
|
235
|
+
|
|
236
|
+
// Append newly connected peers at the end (lowest priority).
|
|
237
|
+
for (const peer of currentlyConnected) {
|
|
238
|
+
if (!this.orderedPeers.has(peer)) {
|
|
239
|
+
this.orderedPeers.add(peer);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return this.orderedPeers;
|
|
243
|
+
}
|
|
205
244
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ClientProtocolCircuitVerifier } from '@aztec/stdlib/interfaces/server';
|
|
2
2
|
import { Tx, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { createTxValidatorForReqResponseReceivedTxs } from '../../../msg_validators/index.js';
|
|
5
5
|
|
|
6
6
|
export interface BatchRequestTxValidatorConfig {
|
|
7
7
|
l1ChainId: number;
|
|
@@ -29,7 +29,7 @@ export class BatchRequestTxValidator implements IBatchRequestTxValidator {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
static createRequestedTxValidator(config: BatchRequestTxValidatorConfig): TxValidator {
|
|
32
|
-
return
|
|
32
|
+
return createTxValidatorForReqResponseReceivedTxs(config.proofVerifier, {
|
|
33
33
|
l1ChainId: config.l1ChainId,
|
|
34
34
|
rollupVersion: config.rollupVersion,
|
|
35
35
|
});
|
package/src/services/service.ts
CHANGED
|
@@ -139,7 +139,7 @@ export interface P2PService {
|
|
|
139
139
|
/** Returns the number of peers in the GossipSub mesh for a given topic type. */
|
|
140
140
|
getGossipMeshPeerCount(topicType: TopicType): number;
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void>;
|
|
143
143
|
|
|
144
144
|
addReqRespSubProtocol(
|
|
145
145
|
subProtocol: ReqRespSubProtocol,
|
|
@@ -25,7 +25,7 @@ export class TxProvider implements ITxProvider {
|
|
|
25
25
|
constructor(
|
|
26
26
|
private txCollection: TxCollection,
|
|
27
27
|
private txPool: TxPoolV2,
|
|
28
|
-
private
|
|
28
|
+
private blockProposalTransactionValidator: Pick<P2PClient, 'validateTxsReceivedInBlockProposal'>,
|
|
29
29
|
private log: Logger = createLogger('p2p:tx-collector'),
|
|
30
30
|
client: TelemetryClient = getTelemetryClient(),
|
|
31
31
|
) {
|
|
@@ -227,7 +227,7 @@ export class TxProvider implements ITxProvider {
|
|
|
227
227
|
if (txs.length === 0) {
|
|
228
228
|
return;
|
|
229
229
|
}
|
|
230
|
-
await this.
|
|
230
|
+
await this.blockProposalTransactionValidator.validateTxsReceivedInBlockProposal(txs);
|
|
231
231
|
await this.txPool.addProtectedTxs(txs, blockHeader);
|
|
232
232
|
}
|
|
233
233
|
}
|
|
@@ -76,7 +76,7 @@ export class InMemoryTxPool extends EventEmitter implements TxPoolV2 {
|
|
|
76
76
|
return Promise.resolve({ accepted, ignored: [], rejected: [] });
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
79
|
+
canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
80
80
|
const key = tx.getTxHash().toString();
|
|
81
81
|
if (this.txsByHash.has(key)) {
|
|
82
82
|
return Promise.resolve('ignored');
|