@aztec/p2p 0.0.1-commit.35158ae7e → 0.0.1-commit.3750d92a7
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 +6 -5
- package/dest/client/interface.d.ts +9 -2
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +3 -2
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +30 -10
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +14 -3
- package/dest/config.d.ts +103 -99
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +10 -5
- package/dest/errors/p2p-service.error.d.ts +9 -0
- package/dest/errors/p2p-service.error.d.ts.map +1 -0
- package/dest/errors/p2p-service.error.js +10 -0
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +4 -2
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +8 -5
- package/dest/mem_pools/index.d.ts +1 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +1 -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 +6 -2
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +5 -2
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +17 -9
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +4 -2
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +2 -2
- package/dest/msg_validators/clock_tolerance.d.ts +12 -1
- package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
- package/dest/msg_validators/clock_tolerance.js +50 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +16 -8
- package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +11 -9
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +5 -5
- package/dest/services/discv5/discV5_service.d.ts +1 -1
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +2 -4
- package/dest/services/dummy_service.d.ts +6 -3
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +6 -1
- package/dest/services/gossipsub/topic_score_params.d.ts +13 -2
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +21 -4
- package/dest/services/libp2p/libp2p_service.d.ts +12 -20
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +42 -97
- package/dest/services/peer-manager/peer_manager.d.ts +6 -2
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +33 -8
- package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +32 -10
- package/dest/services/reqresp/config.d.ts +3 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/interface.d.ts +14 -9
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +10 -11
- package/dest/services/reqresp/metrics.d.ts +1 -1
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/metrics.js +0 -1
- package/dest/services/reqresp/protocols/index.d.ts +1 -2
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +0 -1
- package/dest/services/reqresp/protocols/tx.d.ts +1 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +1 -3
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
- package/dest/services/reqresp/reqresp.d.ts +4 -2
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +11 -2
- package/dest/services/service.d.ts +5 -8
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.js +39 -29
- package/dest/services/tx_collection/tx_source.d.ts +6 -5
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +9 -7
- package/dest/test-helpers/mock-pubsub.d.ts +11 -8
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +36 -19
- 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 +1 -2
- package/dest/test-helpers/testbench-utils.d.ts +1 -1
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +1 -0
- package/dest/testbench/p2p_client_testbench_worker.js +63 -12
- package/dest/testbench/worker_client_manager.d.ts +8 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +49 -1
- package/dest/util.d.ts +3 -8
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +9 -2
- package/package.json +14 -14
- package/src/client/factory.ts +8 -3
- package/src/client/interface.ts +9 -1
- package/src/client/p2p_client.ts +34 -11
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +15 -3
- package/src/config.ts +18 -6
- package/src/errors/p2p-service.error.ts +11 -0
- package/src/index.ts +0 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +9 -5
- package/src/mem_pools/index.ts +0 -3
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +8 -2
- package/src/msg_validators/attestation_validator/attestation_validator.ts +18 -7
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +4 -1
- package/src/msg_validators/clock_tolerance.ts +68 -0
- package/src/msg_validators/proposal_validator/README.md +1 -1
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +4 -1
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +4 -1
- package/src/msg_validators/proposal_validator/proposal_validator.ts +13 -7
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/gas_validator.ts +25 -9
- package/src/services/data_store.ts +5 -13
- package/src/services/discv5/discV5_service.ts +2 -4
- package/src/services/dummy_service.ts +8 -2
- package/src/services/gossipsub/topic_score_params.ts +36 -4
- package/src/services/libp2p/libp2p_service.ts +47 -106
- package/src/services/peer-manager/peer_manager.ts +38 -8
- package/src/services/peer-manager/peer_scoring.ts +27 -5
- package/src/services/reqresp/config.ts +2 -2
- package/src/services/reqresp/interface.ts +21 -11
- package/src/services/reqresp/metrics.ts +0 -1
- package/src/services/reqresp/protocols/index.ts +0 -1
- package/src/services/reqresp/protocols/tx.ts +1 -3
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
- package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
- package/src/services/reqresp/reqresp.ts +18 -1
- package/src/services/service.ts +6 -8
- package/src/services/tx_collection/file_store_tx_source.ts +43 -31
- package/src/services/tx_collection/tx_source.ts +8 -7
- package/src/test-helpers/mock-pubsub.ts +31 -14
- package/src/test-helpers/reqresp-nodes.ts +2 -2
- package/src/test-helpers/testbench-utils.ts +1 -0
- package/src/testbench/p2p_client_testbench_worker.ts +67 -9
- package/src/testbench/worker_client_manager.ts +55 -1
- package/src/util.ts +12 -8
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -123
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
- package/dest/mem_pools/tx_pool/index.d.ts +0 -3
- package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/index.js +0 -2
- package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
- package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/priority.js +0 -15
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
- package/dest/services/reqresp/protocols/block.d.ts +0 -9
- package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/block.js +0 -32
- package/src/mem_pools/tx_pool/README.md +0 -270
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
- package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
- package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
- package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
- package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
- package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
- package/src/mem_pools/tx_pool/index.ts +0 -2
- package/src/mem_pools/tx_pool/priority.ts +0 -20
- package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
- package/src/services/reqresp/protocols/block.ts +0 -37
|
@@ -51,9 +51,7 @@ export function reqRespTxHandler(mempools: MemPools): ReqRespSubProtocolHandler
|
|
|
51
51
|
* Per: https://github.com/AztecProtocol/aztec-packages/issues/15149#issuecomment-2999054485
|
|
52
52
|
* we define Q as max number of transactions per batch, the comment explains why we use 8.
|
|
53
53
|
*/
|
|
54
|
-
|
|
55
|
-
//more info: https://github.com/AztecProtocol/aztec-packages/pull/15516#pullrequestreview-2995474321
|
|
56
|
-
export function chunkTxHashesRequest(hashes: TxHash[], chunkSize = 1): Array<TxHashArray> {
|
|
54
|
+
export function chunkTxHashesRequest(hashes: TxHash[], chunkSize = 8): Array<TxHashArray> {
|
|
57
55
|
return chunk(hashes, chunkSize).map(chunk => new TxHashArray(...chunk));
|
|
58
56
|
}
|
|
59
57
|
|
|
@@ -97,9 +97,10 @@ export function prettyPrintRateLimitStatus(status: RateLimitStatus) {
|
|
|
97
97
|
* 2. Individual rate limits for each peer.
|
|
98
98
|
*
|
|
99
99
|
* How it works:
|
|
100
|
-
* - When a request comes in, it first checks against the
|
|
101
|
-
* - If the
|
|
102
|
-
* - The request is only allowed if both the
|
|
100
|
+
* - When a request comes in, it first checks against the peer's individual rate limit.
|
|
101
|
+
* - If the peer limit allows, it then checks against the global rate limit.
|
|
102
|
+
* - The request is only allowed if both the peer-specific and global limits allow it.
|
|
103
|
+
* - Checking peer limit first ensures a rate-limited peer cannot exhaust the global quota.
|
|
103
104
|
* - It automatically creates and manages rate limiters for new peers as they make requests.
|
|
104
105
|
* - It periodically cleans up rate limiters for inactive peers to conserve memory.
|
|
105
106
|
*
|
|
@@ -119,10 +120,6 @@ export class SubProtocolRateLimiter {
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
allow(peerId: PeerId): RateLimitStatus {
|
|
122
|
-
if (!this.globalLimiter.allow()) {
|
|
123
|
-
return RateLimitStatus.DeniedGlobal;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
123
|
const peerIdStr = peerId.toString();
|
|
127
124
|
let peerLimiter: PeerRateLimiter | undefined = this.peerLimiters.get(peerIdStr);
|
|
128
125
|
if (!peerLimiter) {
|
|
@@ -135,10 +132,17 @@ export class SubProtocolRateLimiter {
|
|
|
135
132
|
} else {
|
|
136
133
|
peerLimiter.lastAccess = Date.now();
|
|
137
134
|
}
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
|
|
136
|
+
// Check peer limit first: a rate-limited peer must not consume global quota,
|
|
137
|
+
// otherwise one spamming peer can starve all others by exhausting the global bucket.
|
|
138
|
+
if (!peerLimiter.limiter.allow()) {
|
|
140
139
|
return RateLimitStatus.DeniedPeer;
|
|
141
140
|
}
|
|
141
|
+
|
|
142
|
+
if (!this.globalLimiter.allow()) {
|
|
143
|
+
return RateLimitStatus.DeniedGlobal;
|
|
144
|
+
}
|
|
145
|
+
|
|
142
146
|
return RateLimitStatus.Allowed;
|
|
143
147
|
}
|
|
144
148
|
|
|
@@ -42,16 +42,6 @@ export const DEFAULT_RATE_LIMITS: ReqRespSubProtocolRateLimits = {
|
|
|
42
42
|
quotaCount: 200,
|
|
43
43
|
},
|
|
44
44
|
},
|
|
45
|
-
[ReqRespSubProtocol.BLOCK]: {
|
|
46
|
-
peerLimit: {
|
|
47
|
-
quotaTimeMs: 1000,
|
|
48
|
-
quotaCount: 2,
|
|
49
|
-
},
|
|
50
|
-
globalLimit: {
|
|
51
|
-
quotaTimeMs: 1000,
|
|
52
|
-
quotaCount: 5,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
45
|
[ReqRespSubProtocol.GOODBYE]: {
|
|
56
46
|
peerLimit: {
|
|
57
47
|
quotaTimeMs: 1000,
|
|
@@ -34,7 +34,9 @@ import {
|
|
|
34
34
|
type ReqRespSubProtocolHandlers,
|
|
35
35
|
type ReqRespSubProtocolRateLimits,
|
|
36
36
|
type ReqRespSubProtocolValidators,
|
|
37
|
+
type ShouldRejectPeer,
|
|
37
38
|
type SubProtocolMap,
|
|
39
|
+
UNAUTHENTICATED_ALLOWED_PROTOCOLS,
|
|
38
40
|
responseFromBuffer,
|
|
39
41
|
subProtocolSizeCalculators,
|
|
40
42
|
} from './interface.js';
|
|
@@ -72,6 +74,8 @@ export class ReqResp implements ReqRespInterface {
|
|
|
72
74
|
|
|
73
75
|
private snappyTransform: SnappyTransform;
|
|
74
76
|
|
|
77
|
+
private shouldRejectPeer: ShouldRejectPeer | undefined;
|
|
78
|
+
|
|
75
79
|
private metrics: ReqRespMetrics;
|
|
76
80
|
|
|
77
81
|
constructor(
|
|
@@ -108,6 +112,10 @@ export class ReqResp implements ReqRespInterface {
|
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
public setShouldRejectPeer(checker: ShouldRejectPeer): void {
|
|
116
|
+
this.shouldRejectPeer = checker;
|
|
117
|
+
}
|
|
118
|
+
|
|
111
119
|
get tracer() {
|
|
112
120
|
return this.metrics.tracer;
|
|
113
121
|
}
|
|
@@ -462,7 +470,7 @@ export class ReqResp implements ReqRespInterface {
|
|
|
462
470
|
);
|
|
463
471
|
return resp;
|
|
464
472
|
} catch (e: any) {
|
|
465
|
-
this.logger.
|
|
473
|
+
this.logger.debug(`SUBPROTOCOL: ${subProtocol}\n`, e);
|
|
466
474
|
// On error we immediately abort the stream, this is preferred way,
|
|
467
475
|
// because it signals to the sender that error happened, whereas
|
|
468
476
|
// closing the stream only closes our side and is much slower
|
|
@@ -596,6 +604,15 @@ export class ReqResp implements ReqRespInterface {
|
|
|
596
604
|
throw new ReqRespStatusError(ReqRespStatus.RATE_LIMIT_EXCEEDED);
|
|
597
605
|
}
|
|
598
606
|
|
|
607
|
+
// When p2pAllowOnlyValidators is enabled, reject unauthenticated peers on data protocols
|
|
608
|
+
if (
|
|
609
|
+
!UNAUTHENTICATED_ALLOWED_PROTOCOLS.has(protocol) &&
|
|
610
|
+
(this.shouldRejectPeer?.(connection.remotePeer.toString()) ?? false)
|
|
611
|
+
) {
|
|
612
|
+
this.logger.debug(`Rejecting unauthenticated peer ${connection.remotePeer} on gated protocol ${protocol}`);
|
|
613
|
+
throw new ReqRespStatusError(ReqRespStatus.FAILURE);
|
|
614
|
+
}
|
|
615
|
+
|
|
599
616
|
await this.processStream(protocol, incomingStream);
|
|
600
617
|
} catch (err: any) {
|
|
601
618
|
this.metrics.recordResponseError(protocol);
|
package/src/services/service.ts
CHANGED
|
@@ -117,7 +117,12 @@ export interface P2PService {
|
|
|
117
117
|
// Leaky abstraction: fix https://github.com/AztecProtocol/aztec-packages/issues/7963
|
|
118
118
|
registerBlockReceivedCallback(callback: P2PBlockReceivedCallback): void;
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback): void;
|
|
121
|
+
|
|
122
|
+
registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback): void;
|
|
123
|
+
|
|
124
|
+
/** Fires the all-nodes checkpoint callback for our own proposal (gossipsub doesn't deliver own messages). */
|
|
125
|
+
notifyOwnCheckpointProposal(checkpoint: CheckpointProposalCore): Promise<void>;
|
|
121
126
|
|
|
122
127
|
/**
|
|
123
128
|
* Registers a callback invoked when a duplicate proposal is detected (equivocation).
|
|
@@ -196,13 +201,6 @@ export interface PeerDiscoveryService extends EventEmitter {
|
|
|
196
201
|
on(event: 'peer:discovered', listener: (enr: ENR) => void): this;
|
|
197
202
|
emit(event: 'peer:discovered', enr: ENR): boolean;
|
|
198
203
|
|
|
199
|
-
/**
|
|
200
|
-
* Event emitted when our public IP is discovered or changes via discv5 peer interactions.
|
|
201
|
-
* Only emitted when enrUpdate is enabled (i.e. queryForIp=true and no static p2pIp).
|
|
202
|
-
*/
|
|
203
|
-
on(event: 'ip:changed', listener: (ip: string) => void): this;
|
|
204
|
-
emit(event: 'ip:changed', ip: string): boolean;
|
|
205
|
-
|
|
206
204
|
getStatus(): PeerDiscoveryState;
|
|
207
205
|
|
|
208
206
|
getEnr(): ENR | undefined;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { partitionAsync } from '@aztec/foundation/collection';
|
|
1
2
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
2
3
|
import { Timer } from '@aztec/foundation/timer';
|
|
3
4
|
import { type ReadOnlyFileStore, createReadOnlyFileStore } from '@aztec/stdlib/file-store';
|
|
4
|
-
import { Tx, type TxHash } from '@aztec/stdlib/tx';
|
|
5
|
+
import { Tx, type TxHash, type TxValidator } from '@aztec/stdlib/tx';
|
|
5
6
|
import {
|
|
6
7
|
type Histogram,
|
|
7
8
|
Metrics,
|
|
@@ -23,6 +24,7 @@ export class FileStoreTxSource implements TxSource {
|
|
|
23
24
|
private readonly fileStore: ReadOnlyFileStore,
|
|
24
25
|
private readonly baseUrl: string,
|
|
25
26
|
private readonly basePath: string,
|
|
27
|
+
private readonly txValidator: TxValidator,
|
|
26
28
|
private readonly log: Logger,
|
|
27
29
|
telemetry: TelemetryClient,
|
|
28
30
|
) {
|
|
@@ -44,6 +46,7 @@ export class FileStoreTxSource implements TxSource {
|
|
|
44
46
|
public static async create(
|
|
45
47
|
url: string,
|
|
46
48
|
basePath: string,
|
|
49
|
+
txValidator: TxValidator,
|
|
47
50
|
log: Logger = createLogger('p2p:file_store_tx_source'),
|
|
48
51
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
49
52
|
): Promise<FileStoreTxSource | undefined> {
|
|
@@ -53,7 +56,7 @@ export class FileStoreTxSource implements TxSource {
|
|
|
53
56
|
log.warn(`Failed to create file store for URL: ${url}`);
|
|
54
57
|
return undefined;
|
|
55
58
|
}
|
|
56
|
-
return new FileStoreTxSource(fileStore, url, basePath, log, telemetry);
|
|
59
|
+
return new FileStoreTxSource(fileStore, url, basePath, txValidator, log, telemetry);
|
|
57
60
|
} catch (err) {
|
|
58
61
|
log.warn(`Error creating file store for URL: ${url}`, { error: err });
|
|
59
62
|
return undefined;
|
|
@@ -65,35 +68,41 @@ export class FileStoreTxSource implements TxSource {
|
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
public async getTxsByHash(txHashes: TxHash[]): Promise<TxSourceCollectionResult> {
|
|
68
|
-
const
|
|
71
|
+
const results = await Promise.all(
|
|
72
|
+
txHashes.map(async txHash => {
|
|
73
|
+
const path = `${this.basePath}/txs/${txHash.toString()}.bin`;
|
|
74
|
+
const timer = new Timer();
|
|
75
|
+
try {
|
|
76
|
+
const buffer = await this.fileStore.read(path);
|
|
77
|
+
const tx = Tx.fromBuffer(buffer);
|
|
78
|
+
return { tx, downloadDuration: timer.ms(), downloadSize: buffer.length };
|
|
79
|
+
} catch {
|
|
80
|
+
this.downloadsFailed.add(1);
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const txs = results.filter(tx => tx !== undefined);
|
|
87
|
+
const [validTxs, invalidTxs] = await partitionAsync(
|
|
88
|
+
txs,
|
|
89
|
+
async ({ tx, downloadDuration, downloadSize }): Promise<boolean> => {
|
|
90
|
+
const valid = await this.txValidator.validateTx(tx);
|
|
91
|
+
if (valid.result === 'valid') {
|
|
92
|
+
this.downloadsSuccess.add(1);
|
|
93
|
+
this.downloadDuration.record(Math.ceil(downloadDuration));
|
|
94
|
+
this.downloadSize.record(downloadSize);
|
|
95
|
+
return true;
|
|
96
|
+
} else {
|
|
97
|
+
this.downloadsFailed.add(1);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
69
103
|
return {
|
|
70
|
-
validTxs: (
|
|
71
|
-
|
|
72
|
-
txHashes.map(async txHash => {
|
|
73
|
-
const path = `${this.basePath}/txs/${txHash.toString()}.bin`;
|
|
74
|
-
const timer = new Timer();
|
|
75
|
-
try {
|
|
76
|
-
const buffer = await this.fileStore.read(path);
|
|
77
|
-
const tx = Tx.fromBuffer(buffer);
|
|
78
|
-
if ((await tx.validateTxHash()) && txHash.equals(tx.txHash)) {
|
|
79
|
-
this.downloadsSuccess.add(1);
|
|
80
|
-
this.downloadDuration.record(Math.ceil(timer.ms()));
|
|
81
|
-
this.downloadSize.record(buffer.length);
|
|
82
|
-
return tx;
|
|
83
|
-
} else {
|
|
84
|
-
invalidTxHashes.push(tx.txHash.toString());
|
|
85
|
-
this.downloadsFailed.add(1);
|
|
86
|
-
return undefined;
|
|
87
|
-
}
|
|
88
|
-
} catch {
|
|
89
|
-
// Tx not found or error reading - return undefined
|
|
90
|
-
this.downloadsFailed.add(1);
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
}),
|
|
94
|
-
)
|
|
95
|
-
).filter(tx => tx !== undefined),
|
|
96
|
-
invalidTxHashes: invalidTxHashes,
|
|
104
|
+
validTxs: validTxs.map(({ tx }) => tx),
|
|
105
|
+
invalidTxHashes: invalidTxs.map(({ tx }) => tx.getTxHash().toString()),
|
|
97
106
|
};
|
|
98
107
|
}
|
|
99
108
|
}
|
|
@@ -109,9 +118,12 @@ export class FileStoreTxSource implements TxSource {
|
|
|
109
118
|
export async function createFileStoreTxSources(
|
|
110
119
|
urls: string[],
|
|
111
120
|
basePath: string,
|
|
121
|
+
txValidator: TxValidator,
|
|
112
122
|
log: Logger = createLogger('p2p:file_store_tx_source'),
|
|
113
123
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
114
124
|
): Promise<FileStoreTxSource[]> {
|
|
115
|
-
const sources = await Promise.all(
|
|
125
|
+
const sources = await Promise.all(
|
|
126
|
+
urls.map(url => FileStoreTxSource.create(url, basePath, txValidator, log, telemetry)),
|
|
127
|
+
);
|
|
116
128
|
return sources.filter((s): s is FileStoreTxSource => s !== undefined);
|
|
117
129
|
}
|
|
@@ -2,7 +2,7 @@ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
|
2
2
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
3
3
|
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
4
4
|
import { type AztecNode, createAztecNodeClient } from '@aztec/stdlib/interfaces/client';
|
|
5
|
-
import type { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
5
|
+
import type { Tx, TxHash, TxValidator } from '@aztec/stdlib/tx';
|
|
6
6
|
import { type ComponentsVersions, getComponentsVersionsFromConfig } from '@aztec/stdlib/versioning';
|
|
7
7
|
import { makeTracedFetch } from '@aztec/telemetry-client';
|
|
8
8
|
|
|
@@ -16,12 +16,13 @@ export interface TxSource {
|
|
|
16
16
|
export class NodeRpcTxSource implements TxSource {
|
|
17
17
|
constructor(
|
|
18
18
|
private readonly client: Pick<AztecNode, 'getTxsByHash'>,
|
|
19
|
+
private readonly txValidator: TxValidator,
|
|
19
20
|
private readonly info: string,
|
|
20
21
|
) {}
|
|
21
22
|
|
|
22
|
-
public static fromUrl(nodeUrl: string, versions: ComponentsVersions): NodeRpcTxSource {
|
|
23
|
+
public static fromUrl(nodeUrl: string, txValidator: TxValidator, versions: ComponentsVersions): NodeRpcTxSource {
|
|
23
24
|
const client = createAztecNodeClient(nodeUrl, versions, makeTracedFetch([1, 2, 3], false));
|
|
24
|
-
return new NodeRpcTxSource(client, nodeUrl);
|
|
25
|
+
return new NodeRpcTxSource(client, txValidator, nodeUrl);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
public getInfo() {
|
|
@@ -38,8 +39,8 @@ export class NodeRpcTxSource implements TxSource {
|
|
|
38
39
|
const invalidTxHashes: string[] = [];
|
|
39
40
|
await Promise.all(
|
|
40
41
|
txs.map(async tx => {
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
42
|
+
const validation = await this.txValidator.validateTx(tx);
|
|
43
|
+
if (validation.result === 'valid') {
|
|
43
44
|
validTxs.push(tx);
|
|
44
45
|
} else {
|
|
45
46
|
invalidTxHashes.push(tx.getTxHash().toString());
|
|
@@ -50,7 +51,7 @@ export class NodeRpcTxSource implements TxSource {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export function createNodeRpcTxSources(urls: string[], chainConfig: ChainConfig) {
|
|
54
|
+
export function createNodeRpcTxSources(urls: string[], txValidator: TxValidator, chainConfig: ChainConfig) {
|
|
54
55
|
const versions = getComponentsVersionsFromConfig(chainConfig, protocolContractsHash, getVKTreeRoot());
|
|
55
|
-
return urls.map(url => NodeRpcTxSource.fromUrl(url, versions));
|
|
56
|
+
return urls.map(url => NodeRpcTxSource.fromUrl(url, txValidator, versions));
|
|
56
57
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
2
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
3
4
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
4
5
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
5
6
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
type TopicValidatorResult,
|
|
16
17
|
TypedEventEmitter,
|
|
17
18
|
} from '@libp2p/interface';
|
|
18
|
-
import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
|
|
19
19
|
|
|
20
20
|
import type { P2PConfig } from '../config.js';
|
|
21
21
|
import type { MemPools } from '../mem_pools/interface.js';
|
|
@@ -101,6 +101,7 @@ class MockReqResp implements ReqRespInterface {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
updateConfig(_config: Partial<P2PReqRespConfig>): void {}
|
|
104
|
+
setShouldRejectPeer(): void {}
|
|
104
105
|
|
|
105
106
|
start(
|
|
106
107
|
subProtocolHandlers: Partial<ReqRespSubProtocolHandlers>,
|
|
@@ -139,6 +140,11 @@ class MockReqResp implements ReqRespInterface {
|
|
|
139
140
|
const responses: InstanceType<SubProtocolMap[SubProtocol]['response']>[] = [];
|
|
140
141
|
const peers = this.network.getReqRespPeers().filter(p => !p.peerId.equals(this.peerId));
|
|
141
142
|
const targetPeers = pinnedPeer ? peers.filter(p => p.peerId.equals(pinnedPeer)) : peers;
|
|
143
|
+
const delayMs = this.network.getPropagationDelayMs();
|
|
144
|
+
|
|
145
|
+
if (delayMs > 0) {
|
|
146
|
+
await sleep(delayMs);
|
|
147
|
+
}
|
|
142
148
|
|
|
143
149
|
for (const request of requests) {
|
|
144
150
|
const requestBuffer = request.toBuffer();
|
|
@@ -175,7 +181,12 @@ class MockReqResp implements ReqRespInterface {
|
|
|
175
181
|
return { status: ReqRespStatus.SUCCESS, data: Buffer.from([]) };
|
|
176
182
|
}
|
|
177
183
|
try {
|
|
184
|
+
const delayMs = this.network.getPropagationDelayMs();
|
|
185
|
+
if (delayMs > 0) {
|
|
186
|
+
await sleep(delayMs);
|
|
187
|
+
}
|
|
178
188
|
const data = await handler(this.peerId, payload);
|
|
189
|
+
|
|
179
190
|
return { status: ReqRespStatus.SUCCESS, data };
|
|
180
191
|
} catch {
|
|
181
192
|
return { status: ReqRespStatus.FAILURE };
|
|
@@ -213,14 +224,6 @@ export class MockPubSub implements PubSubLibp2p {
|
|
|
213
224
|
get services() {
|
|
214
225
|
return {
|
|
215
226
|
pubsub: this.gossipSub,
|
|
216
|
-
components: {
|
|
217
|
-
addressManager: {
|
|
218
|
-
removeObservedAddr: () => {},
|
|
219
|
-
addObservedAddr: () => {},
|
|
220
|
-
confirmObservedAddr: () => {},
|
|
221
|
-
} as unknown as AddressManager,
|
|
222
|
-
connectionManager: {} as unknown as ConnectionManager,
|
|
223
|
-
},
|
|
224
227
|
};
|
|
225
228
|
}
|
|
226
229
|
|
|
@@ -251,10 +254,10 @@ class MockGossipSubService extends TypedEventEmitter<GossipsubEvents> implements
|
|
|
251
254
|
score: (_peerId: PeerIdStr) => 0,
|
|
252
255
|
};
|
|
253
256
|
|
|
254
|
-
publish(topic: TopicStr, data: Uint8Array, _opts?: PublishOpts): Promise<PublishResult> {
|
|
257
|
+
async publish(topic: TopicStr, data: Uint8Array, _opts?: PublishOpts): Promise<PublishResult> {
|
|
255
258
|
this.logger.debug(`Publishing message on topic ${topic}`, { topic, sender: this.peerId.toString() });
|
|
256
|
-
this.network.publishToPeers(topic, data, this.peerId);
|
|
257
|
-
return
|
|
259
|
+
await this.network.publishToPeers(topic, data, this.peerId);
|
|
260
|
+
return { recipients: this.network.getPeers().filter(peer => !this.peerId.equals(peer)) };
|
|
258
261
|
}
|
|
259
262
|
|
|
260
263
|
receive(msg: GossipsubMessage) {
|
|
@@ -290,7 +293,8 @@ class MockGossipSubService extends TypedEventEmitter<GossipsubEvents> implements
|
|
|
290
293
|
|
|
291
294
|
/**
|
|
292
295
|
* Mock gossip sub network used for testing.
|
|
293
|
-
* All instances of MockGossipSubService connected to the same network
|
|
296
|
+
* All instances of MockGossipSubService connected to the same network receive the same messages,
|
|
297
|
+
* optionally delayed by a configurable propagation time.
|
|
294
298
|
*/
|
|
295
299
|
export class MockGossipSubNetwork {
|
|
296
300
|
private peers: MockGossipSubService[] = [];
|
|
@@ -299,6 +303,15 @@ export class MockGossipSubNetwork {
|
|
|
299
303
|
|
|
300
304
|
private logger = createLogger('p2p:test:mock-gossipsub-network');
|
|
301
305
|
|
|
306
|
+
constructor(
|
|
307
|
+
/** Artificial propagation delay in milliseconds applied to each message delivery. */
|
|
308
|
+
private propagationDelayMs: number = 0,
|
|
309
|
+
) {}
|
|
310
|
+
|
|
311
|
+
public getPropagationDelayMs(): number {
|
|
312
|
+
return this.propagationDelayMs;
|
|
313
|
+
}
|
|
314
|
+
|
|
302
315
|
public getPeers(): PeerId[] {
|
|
303
316
|
return this.peers.map(peer => peer.peerId);
|
|
304
317
|
}
|
|
@@ -315,7 +328,7 @@ export class MockGossipSubNetwork {
|
|
|
315
328
|
return this.reqRespPeers;
|
|
316
329
|
}
|
|
317
330
|
|
|
318
|
-
public publishToPeers(topic: TopicStr, data: Uint8Array, sender: PeerId): void {
|
|
331
|
+
public async publishToPeers(topic: TopicStr, data: Uint8Array, sender: PeerId): Promise<void> {
|
|
319
332
|
const msgId = (this.nextMsgId++).toString();
|
|
320
333
|
this.logger.debug(`Network is distributing message on topic ${topic}`, {
|
|
321
334
|
topic,
|
|
@@ -324,6 +337,10 @@ export class MockGossipSubNetwork {
|
|
|
324
337
|
msgId,
|
|
325
338
|
});
|
|
326
339
|
|
|
340
|
+
if (this.propagationDelayMs > 0) {
|
|
341
|
+
await sleep(this.propagationDelayMs);
|
|
342
|
+
}
|
|
343
|
+
|
|
327
344
|
const gossipSubMsg: GossipsubMessage = { msgId, msg: { type: 'unsigned', topic, data }, propagationSource: sender };
|
|
328
345
|
for (const peer of this.peers) {
|
|
329
346
|
if (peer.subscribedTopics.has(topic)) {
|
|
@@ -153,6 +153,8 @@ export async function createTestLibP2PService(
|
|
|
153
153
|
epochCache,
|
|
154
154
|
);
|
|
155
155
|
|
|
156
|
+
reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
|
|
157
|
+
|
|
156
158
|
p2pNode.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
|
|
157
159
|
p2pNode.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
|
|
158
160
|
peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
|
|
@@ -187,7 +189,6 @@ export const MOCK_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = {
|
|
|
187
189
|
[ReqRespSubProtocol.STATUS]: (_msg: any) => Promise.resolve(Buffer.from('status')),
|
|
188
190
|
[ReqRespSubProtocol.TX]: (_msg: any) => Promise.resolve(Buffer.from('tx')),
|
|
189
191
|
[ReqRespSubProtocol.GOODBYE]: (_msg: any) => Promise.resolve(Buffer.from('goodbye')),
|
|
190
|
-
[ReqRespSubProtocol.BLOCK]: (_msg: any) => Promise.resolve(Buffer.from('block')),
|
|
191
192
|
[ReqRespSubProtocol.AUTH]: (_msg: any) => Promise.resolve(Buffer.from('auth')),
|
|
192
193
|
[ReqRespSubProtocol.BLOCK_TXS]: (_msg: any) => Promise.resolve(Buffer.from('block_txs')),
|
|
193
194
|
};
|
|
@@ -199,7 +200,6 @@ export const MOCK_SUB_PROTOCOL_VALIDATORS: ReqRespSubProtocolValidators = {
|
|
|
199
200
|
[ReqRespSubProtocol.STATUS]: noopValidator,
|
|
200
201
|
[ReqRespSubProtocol.TX]: noopValidator,
|
|
201
202
|
[ReqRespSubProtocol.GOODBYE]: noopValidator,
|
|
202
|
-
[ReqRespSubProtocol.BLOCK]: noopValidator,
|
|
203
203
|
[ReqRespSubProtocol.AUTH]: noopValidator,
|
|
204
204
|
[ReqRespSubProtocol.BLOCK_TXS]: noopValidator,
|
|
205
205
|
};
|
|
@@ -287,6 +287,7 @@ export function createMockEpochCache(): EpochCacheInterface {
|
|
|
287
287
|
nowMs: 0n,
|
|
288
288
|
}),
|
|
289
289
|
isProposerPipeliningEnabled: () => false,
|
|
290
|
+
pipeliningOffset: () => 0,
|
|
290
291
|
computeProposerIndex: () => 0n,
|
|
291
292
|
getCurrentAndNextSlot: () => ({ currentSlot: SlotNumber.ZERO, nextSlot: SlotNumber.ZERO }),
|
|
292
293
|
getTargetAndNextSlot: () => ({ targetSlot: SlotNumber.ZERO, nextSlot: SlotNumber.ZERO }),
|
|
@@ -204,6 +204,25 @@ function installUnlimitedRateLimits(client: P2PClient): void {
|
|
|
204
204
|
rateLimiter.allow = () => RateLimitStatus.Allowed;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
/** Resets peer scores to prevent cross-case contamination in benchmarks. */
|
|
208
|
+
function resetPeerScores(client: P2PClient): void {
|
|
209
|
+
const peerManager = (client as any).p2pService.peerManager;
|
|
210
|
+
const peerScoring = peerManager?.peerScoring;
|
|
211
|
+
if (peerScoring?.resetAllScores) {
|
|
212
|
+
peerScoring.resetAllScores();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Returns the number of connected peers for connectivity checks. */
|
|
217
|
+
function getConnectedPeerCount(client: P2PClient): number {
|
|
218
|
+
const p2pService = (client as any).p2pService;
|
|
219
|
+
const connectionSampler = p2pService?.reqresp?.getConnectionSampler?.();
|
|
220
|
+
if (connectionSampler?.getPeerListSortedByConnectionCountAsc) {
|
|
221
|
+
return connectionSampler.getPeerListSortedByConnectionCountAsc().length;
|
|
222
|
+
}
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
207
226
|
async function runAggregatorBenchmark(
|
|
208
227
|
client: P2PClient,
|
|
209
228
|
blockProposal: BlockProposal,
|
|
@@ -321,6 +340,37 @@ let workerConfig: P2PConfig | null = null;
|
|
|
321
340
|
let workerLogger: Logger | null = null;
|
|
322
341
|
let kvStore: Awaited<ReturnType<typeof openTmpStore>> | null = null;
|
|
323
342
|
|
|
343
|
+
async function stopWorker() {
|
|
344
|
+
try {
|
|
345
|
+
if (workerClient) {
|
|
346
|
+
await workerClient.stop();
|
|
347
|
+
workerClient = null;
|
|
348
|
+
}
|
|
349
|
+
} catch (e) {
|
|
350
|
+
workerLogger?.error('Error stopping worker client', e);
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
if (kvStore?.close) {
|
|
354
|
+
await kvStore.close();
|
|
355
|
+
kvStore = null;
|
|
356
|
+
}
|
|
357
|
+
} catch (e) {
|
|
358
|
+
workerLogger?.error('Error closing kv store', e);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function gracefulExit(code: number = 0) {
|
|
363
|
+
try {
|
|
364
|
+
if (process.connected) {
|
|
365
|
+
process.disconnect();
|
|
366
|
+
}
|
|
367
|
+
} catch {
|
|
368
|
+
// IPC channel already closed
|
|
369
|
+
}
|
|
370
|
+
// Safety fallback if lingering handles prevent the event loop from draining
|
|
371
|
+
setTimeout(() => process.exit(code), 5000).unref();
|
|
372
|
+
}
|
|
373
|
+
|
|
324
374
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
325
375
|
process.on('message', async msg => {
|
|
326
376
|
const {
|
|
@@ -410,13 +460,8 @@ process.on('message', async msg => {
|
|
|
410
460
|
const cmd = msg as any;
|
|
411
461
|
switch (cmd.type) {
|
|
412
462
|
case 'STOP':
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
if (kvStore?.close) {
|
|
417
|
-
await kvStore.close();
|
|
418
|
-
}
|
|
419
|
-
process.exit(0);
|
|
463
|
+
await stopWorker();
|
|
464
|
+
gracefulExit(0);
|
|
420
465
|
break;
|
|
421
466
|
|
|
422
467
|
case 'SEND_TX':
|
|
@@ -426,6 +471,13 @@ process.on('message', async msg => {
|
|
|
426
471
|
}
|
|
427
472
|
break;
|
|
428
473
|
|
|
474
|
+
case 'GET_PEER_COUNT':
|
|
475
|
+
process.send!({
|
|
476
|
+
type: 'PEER_COUNT',
|
|
477
|
+
count: workerClient ? getConnectedPeerCount(workerClient) : 0,
|
|
478
|
+
});
|
|
479
|
+
break;
|
|
480
|
+
|
|
429
481
|
case 'BENCH_REQRESP': {
|
|
430
482
|
const benchCmd = cmd as BenchReqRespCommand;
|
|
431
483
|
if (!workerClient || !workerTxPool || !workerAttestationPool || !workerConfig || !workerLogger) {
|
|
@@ -442,6 +494,7 @@ process.on('message', async msg => {
|
|
|
442
494
|
// Reset state before each benchmark run to avoid cross-run contamination
|
|
443
495
|
workerTxPool.resetState();
|
|
444
496
|
workerAttestationPool.resetState();
|
|
497
|
+
resetPeerScores(workerClient);
|
|
445
498
|
|
|
446
499
|
installUnlimitedRateLimits(workerClient);
|
|
447
500
|
|
|
@@ -493,7 +546,12 @@ process.on('message', async msg => {
|
|
|
493
546
|
}
|
|
494
547
|
}
|
|
495
548
|
} catch (err: any) {
|
|
496
|
-
|
|
497
|
-
|
|
549
|
+
try {
|
|
550
|
+
process.send!({ type: 'ERROR', error: err.message });
|
|
551
|
+
} catch {
|
|
552
|
+
// IPC channel may be closed
|
|
553
|
+
}
|
|
554
|
+
await stopWorker();
|
|
555
|
+
gracefulExit(1);
|
|
498
556
|
}
|
|
499
557
|
});
|
|
@@ -72,7 +72,6 @@ class WorkerClientManager {
|
|
|
72
72
|
destroy() {
|
|
73
73
|
this.cleanup().catch((error: Error) => {
|
|
74
74
|
this.logger.error('Failed to cleanup worker client manager', error);
|
|
75
|
-
process.exit(1);
|
|
76
75
|
});
|
|
77
76
|
}
|
|
78
77
|
|
|
@@ -425,6 +424,61 @@ class WorkerClientManager {
|
|
|
425
424
|
this.logger.info('All worker processes cleaned up');
|
|
426
425
|
}
|
|
427
426
|
|
|
427
|
+
/**
|
|
428
|
+
* Checks that the aggregator (client 0) has sufficient peer connections before running a benchmark.
|
|
429
|
+
* This prevents benchmark cases from starting with degraded connectivity after a previous case
|
|
430
|
+
* caused connection failures.
|
|
431
|
+
*/
|
|
432
|
+
async waitForConnectivity(minPeers: number, timeoutMs: number = 15_000): Promise<number> {
|
|
433
|
+
const waitInterval = 1000;
|
|
434
|
+
let waited = 0;
|
|
435
|
+
|
|
436
|
+
while (waited < timeoutMs) {
|
|
437
|
+
const count = await this.getPeerCount(0, 5000);
|
|
438
|
+
if (count >= minPeers) {
|
|
439
|
+
this.logger.info(`Connectivity check passed: ${count}/${minPeers} peers connected`);
|
|
440
|
+
return count;
|
|
441
|
+
}
|
|
442
|
+
this.logger.debug(`Waiting for connectivity: ${count}/${minPeers} (waited ${waited}ms)`);
|
|
443
|
+
await sleep(waitInterval);
|
|
444
|
+
waited += waitInterval;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const finalCount = await this.getPeerCount(0, 5000);
|
|
448
|
+
this.logger.warn(`Connectivity check: only ${finalCount}/${minPeers} peers after ${timeoutMs}ms`);
|
|
449
|
+
return finalCount;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private getPeerCount(clientIndex: number, timeoutMs: number): Promise<number> {
|
|
453
|
+
return new Promise<number>(resolve => {
|
|
454
|
+
let resolved = false;
|
|
455
|
+
|
|
456
|
+
const handler = (msg: any) => {
|
|
457
|
+
if (resolved) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (msg.type === 'PEER_COUNT') {
|
|
461
|
+
resolved = true;
|
|
462
|
+
clearTimeout(timeout);
|
|
463
|
+
this.processes[clientIndex].off('message', handler);
|
|
464
|
+
resolve(msg.count as number);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const timeout = setTimeout(() => {
|
|
469
|
+
if (resolved) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
resolved = true;
|
|
473
|
+
this.processes[clientIndex].off('message', handler);
|
|
474
|
+
resolve(0);
|
|
475
|
+
}, timeoutMs);
|
|
476
|
+
|
|
477
|
+
this.processes[clientIndex].on('message', handler);
|
|
478
|
+
this.processes[clientIndex].send({ type: 'GET_PEER_COUNT' });
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
428
482
|
/**
|
|
429
483
|
* Run a req/resp benchmark across all worker clients.
|
|
430
484
|
*
|