@aztec/p2p 0.0.1-commit.3e3d0c9cd → 0.0.1-commit.3f296a7d2
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/p2p_client.d.ts +1 -1
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +6 -4
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +3 -3
- package/dest/config.d.ts +7 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +10 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +3 -3
- package/dest/mem_pools/attestation_pool/attestation_pool.js +3 -3
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +6 -6
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +4 -4
- 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 +5 -4
- package/dest/msg_validators/clock_tolerance.d.ts +1 -1
- package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
- package/dest/msg_validators/clock_tolerance.js +4 -3
- 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 +5 -5
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
- 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 +35 -2
- package/dest/msg_validators/tx_validator/factory.d.ts +1 -1
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +8 -2
- package/dest/msg_validators/tx_validator/phases_validator.js +1 -1
- package/dest/services/encoding.d.ts +5 -1
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +7 -1
- package/dest/services/libp2p/libp2p_service.d.ts +4 -9
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +85 -53
- 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 +4 -2
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +4 -7
- 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 +43 -56
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +1 -2
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +4 -4
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.js +7 -7
- package/dest/services/reqresp/reqresp.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +16 -8
- package/dest/services/tx_collection/fast_tx_collection.d.ts +1 -4
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +57 -73
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +6 -7
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +4 -4
- package/dest/services/tx_collection/request_tracker.d.ts +53 -0
- package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
- package/dest/services/tx_collection/request_tracker.js +84 -0
- package/dest/services/tx_collection/slow_tx_collection.js +1 -1
- package/dest/services/tx_collection/tx_collection.d.ts +3 -6
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- 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 +20 -2
- package/dest/testbench/p2p_client_testbench_worker.js +3 -3
- package/package.json +14 -14
- package/src/client/p2p_client.ts +6 -4
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +3 -5
- package/src/config.ts +17 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +3 -3
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +6 -6
- package/src/mem_pools/tx_pool_v2/interfaces.ts +4 -4
- package/src/msg_validators/attestation_validator/README.md +1 -1
- package/src/msg_validators/attestation_validator/attestation_validator.ts +5 -4
- package/src/msg_validators/clock_tolerance.ts +4 -3
- package/src/msg_validators/proposal_validator/README.md +3 -3
- package/src/msg_validators/proposal_validator/proposal_validator.ts +6 -5
- package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
- package/src/msg_validators/tx_validator/data_validator.ts +42 -1
- package/src/msg_validators/tx_validator/factory.ts +7 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +1 -1
- package/src/services/encoding.ts +9 -1
- package/src/services/libp2p/libp2p_service.ts +80 -64
- package/src/services/peer-manager/peer_manager.ts +5 -2
- package/src/services/reqresp/batch-tx-requester/README.md +46 -7
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +35 -60
- package/src/services/reqresp/batch-tx-requester/interface.ts +0 -1
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +6 -6
- package/src/services/reqresp/reqresp.ts +18 -10
- package/src/services/tx_collection/fast_tx_collection.ts +57 -83
- package/src/services/tx_collection/proposal_tx_collector.ts +8 -13
- package/src/services/tx_collection/request_tracker.ts +127 -0
- package/src/services/tx_collection/slow_tx_collection.ts +1 -1
- package/src/services/tx_collection/tx_collection.ts +3 -5
- package/src/test-helpers/testbench-utils.ts +28 -3
- package/src/testbench/p2p_client_testbench_worker.ts +3 -5
- package/dest/services/tx_collection/missing_txs_tracker.d.ts +0 -32
- package/dest/services/tx_collection/missing_txs_tracker.d.ts.map +0 -1
- package/dest/services/tx_collection/missing_txs_tracker.js +0 -27
- package/src/services/tx_collection/missing_txs_tracker.ts +0 -52
package/src/services/encoding.ts
CHANGED
|
@@ -9,6 +9,14 @@ import { webcrypto } from 'node:crypto';
|
|
|
9
9
|
import { compressSync, uncompressSync } from 'snappy';
|
|
10
10
|
import xxhashFactory from 'xxhash-wasm';
|
|
11
11
|
|
|
12
|
+
/** Thrown when a Snappy-compressed response exceeds the allowed decompressed size. */
|
|
13
|
+
export class OversizedSnappyResponseError extends Error {
|
|
14
|
+
constructor(decompressedSize: number, maxSizeKb: number) {
|
|
15
|
+
super(`Decompressed size ${decompressedSize} exceeds maximum allowed size of ${maxSizeKb}kb`);
|
|
16
|
+
this.name = 'OversizedSnappyResponseError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
// Load WASM
|
|
13
21
|
const xxhash = await xxhashFactory();
|
|
14
22
|
|
|
@@ -86,7 +94,7 @@ export class SnappyTransform implements DataTransform {
|
|
|
86
94
|
const { decompressedSize } = readSnappyPreamble(data);
|
|
87
95
|
if (decompressedSize > maxSizeKb * 1024) {
|
|
88
96
|
this.logger.warn(`Decompressed size ${decompressedSize} exceeds maximum allowed size of ${maxSizeKb}kb`);
|
|
89
|
-
throw new
|
|
97
|
+
throw new OversizedSnappyResponseError(decompressedSize, maxSizeKb);
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
return Buffer.from(uncompressSync(data, { asBuffer: true }));
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
type CheckpointProposalCore,
|
|
19
19
|
type Gossipable,
|
|
20
20
|
P2PMessage,
|
|
21
|
-
type ValidationResult as P2PValidationResult,
|
|
22
21
|
PeerErrorSeverity,
|
|
23
22
|
PeerErrorSeverityByHarshness,
|
|
24
23
|
TopicType,
|
|
@@ -131,7 +130,7 @@ type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: Vali
|
|
|
131
130
|
// REFACTOR: Unify with the type above
|
|
132
131
|
type ReceivedMessageValidationResult<T, M = undefined> =
|
|
133
132
|
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
|
|
134
|
-
| { obj?: T; result: TopicValidatorResult.Reject; metadata?: M };
|
|
133
|
+
| { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
|
|
135
134
|
|
|
136
135
|
/**
|
|
137
136
|
* Lib P2P implementation of the P2PService interface.
|
|
@@ -226,7 +225,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
226
225
|
|
|
227
226
|
const proposalValidatorOpts = {
|
|
228
227
|
txsPermitted: !config.disableTransactions,
|
|
229
|
-
maxTxsPerBlock: config.validateMaxTxsPerBlock,
|
|
228
|
+
maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
|
|
230
229
|
};
|
|
231
230
|
this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
|
|
232
231
|
this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
|
|
@@ -883,30 +882,56 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
883
882
|
source: PeerId,
|
|
884
883
|
topicType: TopicType,
|
|
885
884
|
): Promise<ReceivedMessageValidationResult<T, M>> {
|
|
886
|
-
|
|
885
|
+
// Default to reject result with a penalty if validation function throws an error
|
|
886
|
+
let resultAndObj: ReceivedMessageValidationResult<T, M> = {
|
|
887
|
+
result: TopicValidatorResult.Reject,
|
|
888
|
+
severity: PeerErrorSeverity.MidToleranceError,
|
|
889
|
+
};
|
|
887
890
|
const timer = new Timer();
|
|
888
891
|
try {
|
|
889
892
|
resultAndObj = await validationFunc();
|
|
890
893
|
} catch (err) {
|
|
891
|
-
this.
|
|
892
|
-
this.logger.error(`Error deserializing and validating gossipsub message`, err, {
|
|
893
|
-
msgId,
|
|
894
|
-
source: source.toString(),
|
|
895
|
-
topicType,
|
|
896
|
-
});
|
|
894
|
+
this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
|
|
897
895
|
}
|
|
898
896
|
|
|
899
897
|
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
898
|
+
this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
|
|
900
899
|
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
900
|
+
} else if (resultAndObj.result === TopicValidatorResult.Reject) {
|
|
901
|
+
this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
|
|
902
|
+
msgId,
|
|
903
|
+
source: source.toString(),
|
|
904
|
+
topicType,
|
|
905
|
+
severity: resultAndObj.severity,
|
|
906
|
+
});
|
|
907
|
+
this.peerManager.penalizePeer(source, resultAndObj.severity);
|
|
908
|
+
} else {
|
|
909
|
+
this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
|
|
901
910
|
}
|
|
902
911
|
|
|
903
912
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
|
|
904
913
|
return resultAndObj;
|
|
905
914
|
}
|
|
906
915
|
|
|
916
|
+
private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
|
|
917
|
+
try {
|
|
918
|
+
return deserializeFunc();
|
|
919
|
+
} catch (err) {
|
|
920
|
+
this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
|
|
921
|
+
err,
|
|
922
|
+
msgId,
|
|
923
|
+
source: source.toString(),
|
|
924
|
+
});
|
|
925
|
+
return undefined;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
907
929
|
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
908
930
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
909
|
-
const tx = Tx.fromBuffer(payloadData);
|
|
931
|
+
const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
|
|
932
|
+
if (!tx) {
|
|
933
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
|
|
934
|
+
}
|
|
910
935
|
|
|
911
936
|
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
912
937
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
@@ -931,8 +956,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
931
956
|
severity,
|
|
932
957
|
source: source.toString(),
|
|
933
958
|
});
|
|
934
|
-
|
|
935
|
-
return { result: TopicValidatorResult.Reject };
|
|
959
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
936
960
|
}
|
|
937
961
|
|
|
938
962
|
// Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
|
|
@@ -954,8 +978,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
954
978
|
severity,
|
|
955
979
|
source: source.toString(),
|
|
956
980
|
});
|
|
957
|
-
|
|
958
|
-
return { result: TopicValidatorResult.Reject };
|
|
981
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
959
982
|
}
|
|
960
983
|
|
|
961
984
|
// Pool add: persist the tx
|
|
@@ -976,7 +999,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
976
999
|
} else if (wasIgnored) {
|
|
977
1000
|
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
978
1001
|
} else {
|
|
979
|
-
|
|
1002
|
+
this.logger.warn(`Gossiped tx ${txHash.toString()} unexpectedly rejected by pool`, {
|
|
1003
|
+
source: source.toString(),
|
|
1004
|
+
txHash: txHash.toString(),
|
|
1005
|
+
});
|
|
1006
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
980
1007
|
}
|
|
981
1008
|
};
|
|
982
1009
|
|
|
@@ -1006,7 +1033,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1006
1033
|
source: PeerId,
|
|
1007
1034
|
): Promise<void> {
|
|
1008
1035
|
const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
|
|
1009
|
-
() =>
|
|
1036
|
+
() => {
|
|
1037
|
+
const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
|
|
1038
|
+
if (!attestation) {
|
|
1039
|
+
return Promise.resolve({
|
|
1040
|
+
result: TopicValidatorResult.Reject,
|
|
1041
|
+
severity: PeerErrorSeverity.LowToleranceError,
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
return this.validateAndStoreCheckpointAttestation(source, attestation);
|
|
1045
|
+
},
|
|
1010
1046
|
msgId,
|
|
1011
1047
|
source,
|
|
1012
1048
|
TopicType.checkpoint_attestation,
|
|
@@ -1039,8 +1075,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1039
1075
|
|
|
1040
1076
|
if (validationResult.result === 'reject') {
|
|
1041
1077
|
this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1042
|
-
|
|
1043
|
-
return { result: TopicValidatorResult.Reject };
|
|
1078
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1044
1079
|
}
|
|
1045
1080
|
|
|
1046
1081
|
if (validationResult.result === 'ignore') {
|
|
@@ -1066,16 +1101,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1066
1101
|
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1067
1102
|
}
|
|
1068
1103
|
|
|
1069
|
-
// Could not add (cap reached for signer),
|
|
1104
|
+
// Could not add (cap reached for signer), penalize and do not re-broadcast
|
|
1070
1105
|
if (!added) {
|
|
1071
|
-
this.logger.warn(`
|
|
1106
|
+
this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
|
|
1072
1107
|
slot: slot.toString(),
|
|
1073
1108
|
archive: attestation.archive.toString(),
|
|
1074
1109
|
source: peerId.toString(),
|
|
1075
1110
|
attester: attestation.getSender()?.toString(),
|
|
1076
1111
|
count,
|
|
1077
1112
|
});
|
|
1078
|
-
return { result: TopicValidatorResult.
|
|
1113
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
1079
1114
|
}
|
|
1080
1115
|
|
|
1081
1116
|
// Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
|
|
@@ -1130,8 +1165,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1130
1165
|
|
|
1131
1166
|
if (validationResult.result === 'reject') {
|
|
1132
1167
|
this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
1133
|
-
|
|
1134
|
-
return { result: TopicValidatorResult.Reject };
|
|
1168
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1135
1169
|
}
|
|
1136
1170
|
|
|
1137
1171
|
if (validationResult.result === 'ignore') {
|
|
@@ -1155,7 +1189,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1155
1189
|
|
|
1156
1190
|
// Too many blocks received for this slot and index, penalize peer and do not re-broadcast
|
|
1157
1191
|
if (!added) {
|
|
1158
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
|
|
1159
1192
|
this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
|
|
1160
1193
|
...block.toBlockInfo(),
|
|
1161
1194
|
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
@@ -1163,7 +1196,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1163
1196
|
proposer: block.getSender()?.toString(),
|
|
1164
1197
|
source: peerId.toString(),
|
|
1165
1198
|
});
|
|
1166
|
-
return {
|
|
1199
|
+
return {
|
|
1200
|
+
result: TopicValidatorResult.Reject,
|
|
1201
|
+
metadata: { isEquivocated },
|
|
1202
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1203
|
+
};
|
|
1167
1204
|
}
|
|
1168
1205
|
|
|
1169
1206
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
@@ -1256,8 +1293,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1256
1293
|
|
|
1257
1294
|
if (validationResult.result === 'reject') {
|
|
1258
1295
|
this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
|
|
1259
|
-
|
|
1260
|
-
return { result: TopicValidatorResult.Reject };
|
|
1296
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1261
1297
|
}
|
|
1262
1298
|
|
|
1263
1299
|
if (validationResult.result === 'ignore') {
|
|
@@ -1272,20 +1308,21 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1272
1308
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1273
1309
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1274
1310
|
});
|
|
1275
|
-
const
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
metadata: { isEquivocated } = {},
|
|
1279
|
-
} = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1280
|
-
if (result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1311
|
+
const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1312
|
+
const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
|
|
1313
|
+
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1281
1314
|
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1282
1315
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1283
1316
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1284
1317
|
isEquivocated,
|
|
1285
|
-
result,
|
|
1318
|
+
result: blockProposalResult.result,
|
|
1286
1319
|
});
|
|
1287
|
-
return {
|
|
1288
|
-
|
|
1320
|
+
return {
|
|
1321
|
+
result: TopicValidatorResult.Reject,
|
|
1322
|
+
severity:
|
|
1323
|
+
'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
|
|
1324
|
+
};
|
|
1325
|
+
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
|
|
1289
1326
|
processBlock = true;
|
|
1290
1327
|
}
|
|
1291
1328
|
}
|
|
@@ -1312,13 +1349,17 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1312
1349
|
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
|
|
1313
1350
|
// Note: We still return the checkpoint obj so the lastBlock can be processed if valid
|
|
1314
1351
|
if (!added) {
|
|
1315
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
|
|
1316
1352
|
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
1317
1353
|
...checkpoint.toCheckpointInfo(),
|
|
1318
1354
|
count,
|
|
1319
1355
|
source: peerId.toString(),
|
|
1320
1356
|
});
|
|
1321
|
-
return {
|
|
1357
|
+
return {
|
|
1358
|
+
result: TopicValidatorResult.Reject,
|
|
1359
|
+
obj: checkpoint,
|
|
1360
|
+
metadata: { isEquivocated, processBlock },
|
|
1361
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1362
|
+
};
|
|
1322
1363
|
}
|
|
1323
1364
|
|
|
1324
1365
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
@@ -1742,31 +1783,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1742
1783
|
return PeerErrorSeverity.HighToleranceError;
|
|
1743
1784
|
}
|
|
1744
1785
|
|
|
1745
|
-
/**
|
|
1746
|
-
* Validate a checkpoint attestation.
|
|
1747
|
-
*
|
|
1748
|
-
* @param attestation - The checkpoint attestation to validate.
|
|
1749
|
-
* @returns True if the checkpoint attestation is valid, false otherwise.
|
|
1750
|
-
*/
|
|
1751
|
-
@trackSpan('Libp2pService.validateCheckpointAttestation', async (_, attestation) => ({
|
|
1752
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
1753
|
-
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
1754
|
-
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1755
|
-
}))
|
|
1756
|
-
public async validateCheckpointAttestation(
|
|
1757
|
-
peerId: PeerId,
|
|
1758
|
-
attestation: CheckpointAttestation,
|
|
1759
|
-
): Promise<P2PValidationResult> {
|
|
1760
|
-
const result = await this.checkpointAttestationValidator.validate(attestation);
|
|
1761
|
-
|
|
1762
|
-
if (result.result === 'reject') {
|
|
1763
|
-
this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1764
|
-
this.peerManager.penalizePeer(peerId, result.severity);
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
return result;
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
1786
|
public getPeerScore(peerId: PeerId): number {
|
|
1771
1787
|
return this.node.services.pubsub.score.score(peerId.toString());
|
|
1772
1788
|
}
|
|
@@ -32,7 +32,7 @@ import { PeerScoreState, type PeerScoring } from './peer_scoring.js';
|
|
|
32
32
|
const MAX_DIAL_ATTEMPTS = 3;
|
|
33
33
|
const MAX_CACHED_PEERS = 100;
|
|
34
34
|
const MAX_CACHED_PEER_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
35
|
-
const
|
|
35
|
+
const DEFAULT_FAILED_PEER_BAN_TIME_MS = 5 * 60 * 1000; // 5 minutes timeout after failing MAX_DIAL_ATTEMPTS
|
|
36
36
|
const GOODBYE_DIAL_TIMEOUT_MS = 1000;
|
|
37
37
|
const FAILED_AUTH_HANDSHAKE_EXPIRY_MS = 60 * 60 * 1000; // 1 hour
|
|
38
38
|
|
|
@@ -776,7 +776,8 @@ export class PeerManager implements PeerManagerInterface {
|
|
|
776
776
|
// Add to timed out peers
|
|
777
777
|
this.timedOutPeers.set(id, {
|
|
778
778
|
peerId: id,
|
|
779
|
-
timeoutUntilMs:
|
|
779
|
+
timeoutUntilMs:
|
|
780
|
+
this.dateProvider.now() + (this.config.peerFailedBanTimeMs ?? DEFAULT_FAILED_PEER_BAN_TIME_MS),
|
|
780
781
|
});
|
|
781
782
|
}
|
|
782
783
|
}
|
|
@@ -938,6 +939,8 @@ export class PeerManager implements PeerManagerInterface {
|
|
|
938
939
|
`Received auth for validator ${sender.toString()} from peer ${peerIdString}, but this validator is already authenticated to peer ${peerForAddress.toString()}`,
|
|
939
940
|
{ ...logData, address: sender.toString() },
|
|
940
941
|
);
|
|
942
|
+
this.markAuthHandshakeFailed(peerId);
|
|
943
|
+
this.markPeerForDisconnect(peerId);
|
|
941
944
|
return;
|
|
942
945
|
}
|
|
943
946
|
|
|
@@ -170,6 +170,37 @@ class BlockTxsResponse {
|
|
|
170
170
|
|
|
171
171
|
The `BitVector` is a compact representation where each bit corresponds to a transaction index in the block proposal. This allows efficient capability advertisement without repeating full hashes.
|
|
172
172
|
|
|
173
|
+
## Cancellation
|
|
174
|
+
|
|
175
|
+
All cancellation is managed by a single `RequestTracker` instance, shared across the entire collection
|
|
176
|
+
flow. The `RequestTracker` owns the deadline, tracks which txs are still missing, and exposes a
|
|
177
|
+
`cancellationToken` promise that resolves when the request should stop (deadline hit, all txs fetched,
|
|
178
|
+
or external `cancel()` call).
|
|
179
|
+
|
|
180
|
+
Cancellation propagates from the deepest stack level upward:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
RequestTracker.finish()
|
|
184
|
+
├── resolves cancellationToken promise
|
|
185
|
+
│
|
|
186
|
+
├── BatchTxRequester workers (deepest)
|
|
187
|
+
│ ├── shouldStop() checks requestTracker.cancelled → exit loop
|
|
188
|
+
│ ├── sleepClampedToDeadline races sleep vs cancellationToken → wakes
|
|
189
|
+
│ └── semaphore.acquire races vs cancellationToken → wakes
|
|
190
|
+
│ │
|
|
191
|
+
│ ▼ workers settle → txQueue.end() → generator returns
|
|
192
|
+
│
|
|
193
|
+
├── Node collection loops
|
|
194
|
+
│ ├── notFinished() checks requestTracker.cancelled → exit loop
|
|
195
|
+
│ └── inter-retry sleep races vs cancellationToken → wakes
|
|
196
|
+
│ │
|
|
197
|
+
│ ▼ all node loops settle
|
|
198
|
+
│
|
|
199
|
+
└── collectFast (outermost)
|
|
200
|
+
awaits Promise.allSettled([reqresp, nodes]) → settles after inner tasks
|
|
201
|
+
finally: requestTracker.cancel() (idempotent), cleanup
|
|
202
|
+
```
|
|
203
|
+
|
|
173
204
|
## Key Files
|
|
174
205
|
|
|
175
206
|
| File | Description |
|
|
@@ -179,15 +210,16 @@ The `BitVector` is a compact representation where each bit corresponds to a tran
|
|
|
179
210
|
| `peer_collection.ts` | Manages peer classification (dumb/smart/bad) and rate limiting |
|
|
180
211
|
| `interface.ts` | Type definitions for dependencies |
|
|
181
212
|
| `../protocols/block_txs/` | Wire protocol definitions (`BlockTxsRequest`, `BlockTxsResponse`, `BitVector`) |
|
|
213
|
+
| `../../tx_collection/request_tracker.ts` | Centralized deadline, missing tx tracking, and cancellation signal |
|
|
182
214
|
|
|
183
215
|
## Stopping Conditions
|
|
184
216
|
|
|
185
|
-
The `BatchTxRequester` stops when any of these conditions are met
|
|
217
|
+
The `BatchTxRequester` stops when any of these conditions are met, all managed by the `RequestTracker`:
|
|
186
218
|
|
|
187
|
-
1. **All transactions fetched** -
|
|
188
|
-
2. **Deadline exceeded** -
|
|
189
|
-
3. **
|
|
190
|
-
4. **No transactions to fetch** -
|
|
219
|
+
1. **All transactions fetched** - `markFetched()` removes the last missing tx, triggering `finish()`
|
|
220
|
+
2. **Deadline exceeded** - `setTimeout` in `RequestTracker` fires, triggering `finish()`
|
|
221
|
+
3. **External cancellation** - `RequestTracker.cancel()` called (e.g., from `stop()`, `stopCollectingForBlocksUpTo`)
|
|
222
|
+
4. **No transactions to fetch** - Empty hash set at construction, `RequestTracker` finishes immediately
|
|
191
223
|
|
|
192
224
|
## Configuration
|
|
193
225
|
|
|
@@ -228,11 +260,15 @@ Request to peer fails
|
|
|
228
260
|
## Usage Example
|
|
229
261
|
|
|
230
262
|
```typescript
|
|
263
|
+
const requestTracker = RequestTracker.create(
|
|
264
|
+
missingTxHashes, // TxHash[] - what we need
|
|
265
|
+
new Date(Date.now() + 5_000), // deadline
|
|
266
|
+
);
|
|
267
|
+
|
|
231
268
|
const requester = new BatchTxRequester(
|
|
232
|
-
|
|
269
|
+
requestTracker, // IRequestTracker - tracks missing txs, deadline, and cancellation
|
|
233
270
|
blockTxsSource, // BlockTxsSource - the proposal or block we need txs for
|
|
234
271
|
pinnedPeer, // PeerId | undefined - peer expected to have the txs
|
|
235
|
-
timeoutMs, // number - how long to try
|
|
236
272
|
p2pService, // BatchTxRequesterLibP2PService
|
|
237
273
|
);
|
|
238
274
|
|
|
@@ -273,6 +309,8 @@ const txs = await BatchTxRequester.collectAllTxs(requester.run());
|
|
|
273
309
|
│ 1. Try RPC nodes first (fast) │ │ Periodic polling of RPC nodes │
|
|
274
310
|
│ 2. Fall back to BatchTxRequester │ │ and peers for missing txs │
|
|
275
311
|
│ │ │ │
|
|
312
|
+
│ Creates RequestTracker per │ │ │
|
|
313
|
+
│ request with deadline │ │ │
|
|
276
314
|
└───────────────────┬───────────────┘ └─────────────────────────────────────┘
|
|
277
315
|
│
|
|
278
316
|
│ For 'proposal' and 'block' requests
|
|
@@ -281,6 +319,7 @@ const txs = await BatchTxRequester.collectAllTxs(requester.run());
|
|
|
281
319
|
│ BatchTxRequester │
|
|
282
320
|
│ │
|
|
283
321
|
│ Aggressive parallel fetching from multiple peers │
|
|
322
|
+
│ Shares RequestTracker with FastTxCollection for unified cancellation │
|
|
284
323
|
│ Uses BLOCK_TXS sub-protocol for efficient batching │
|
|
285
324
|
└───────────────────┬─────────────────────────────────────────────────────────┘
|
|
286
325
|
│
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { chunkWrapAround } from '@aztec/foundation/collection';
|
|
2
|
-
import { TimeoutError } from '@aztec/foundation/error';
|
|
3
2
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
3
|
import { FifoMemoryQueue, type ISemaphore, Semaphore } from '@aztec/foundation/queue';
|
|
5
4
|
import { sleep } from '@aztec/foundation/sleep';
|
|
6
|
-
import { DateProvider
|
|
5
|
+
import { DateProvider } from '@aztec/foundation/timer';
|
|
7
6
|
import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
8
7
|
import { Tx, TxArray, TxHash } from '@aztec/stdlib/tx';
|
|
9
8
|
|
|
10
9
|
import type { PeerId } from '@libp2p/interface';
|
|
11
10
|
|
|
12
|
-
import type {
|
|
11
|
+
import type { IRequestTracker } from '../../tx_collection/request_tracker.js';
|
|
13
12
|
import { ReqRespSubProtocol } from '.././interface.js';
|
|
14
13
|
import { BlockTxsRequest, BlockTxsResponse, type BlockTxsSource } from '.././protocols/index.js';
|
|
15
14
|
import { ReqRespStatus } from '.././status.js';
|
|
@@ -42,16 +41,14 @@ import { BatchRequestTxValidator, type IBatchRequestTxValidator } from './tx_val
|
|
|
42
41
|
* - Is the peer which was unable to send us successful response N times in a row
|
|
43
42
|
* */
|
|
44
43
|
export class BatchTxRequester {
|
|
44
|
+
private readonly requestTracker: IRequestTracker;
|
|
45
45
|
private readonly blockTxsSource: BlockTxsSource;
|
|
46
46
|
private readonly pinnedPeer: PeerId | undefined;
|
|
47
|
-
private readonly timeoutMs: number;
|
|
48
47
|
private readonly p2pService: BatchTxRequesterLibP2PService;
|
|
49
48
|
private readonly logger: Logger;
|
|
50
|
-
private readonly dateProvider: DateProvider;
|
|
51
49
|
private readonly opts: BatchTxRequesterOptions;
|
|
52
50
|
private readonly peers: IPeerCollection;
|
|
53
51
|
private readonly txsMetadata: ITxMetadataCollection;
|
|
54
|
-
private readonly deadline: number;
|
|
55
52
|
private readonly smartRequesterSemaphore: ISemaphore;
|
|
56
53
|
private readonly txQueue: FifoMemoryQueue<Tx>;
|
|
57
54
|
private readonly txValidator: IBatchRequestTxValidator;
|
|
@@ -60,21 +57,19 @@ export class BatchTxRequester {
|
|
|
60
57
|
private readonly txBatchSize: number;
|
|
61
58
|
|
|
62
59
|
constructor(
|
|
63
|
-
|
|
60
|
+
requestTracker: IRequestTracker,
|
|
64
61
|
blockTxsSource: BlockTxsSource,
|
|
65
62
|
pinnedPeer: PeerId | undefined,
|
|
66
|
-
timeoutMs: number,
|
|
67
63
|
p2pService: BatchTxRequesterLibP2PService,
|
|
68
64
|
logger?: Logger,
|
|
69
65
|
dateProvider?: DateProvider,
|
|
70
66
|
opts?: BatchTxRequesterOptions,
|
|
71
67
|
) {
|
|
68
|
+
this.requestTracker = requestTracker;
|
|
72
69
|
this.blockTxsSource = blockTxsSource;
|
|
73
70
|
this.pinnedPeer = pinnedPeer;
|
|
74
|
-
this.timeoutMs = timeoutMs;
|
|
75
71
|
this.p2pService = p2pService;
|
|
76
72
|
this.logger = logger ?? createLogger('p2p:reqresp_batch');
|
|
77
|
-
this.dateProvider = dateProvider ?? new DateProvider();
|
|
78
73
|
this.opts = opts ?? {};
|
|
79
74
|
|
|
80
75
|
this.smartParallelWorkerCount =
|
|
@@ -82,7 +77,6 @@ export class BatchTxRequester {
|
|
|
82
77
|
this.dumbParallelWorkerCount =
|
|
83
78
|
this.opts.dumbParallelWorkerCount ?? DEFAULT_BATCH_TX_REQUESTER_DUMB_PARALLEL_WORKER_COUNT;
|
|
84
79
|
this.txBatchSize = this.opts.txBatchSize ?? DEFAULT_BATCH_TX_REQUESTER_TX_BATCH_SIZE;
|
|
85
|
-
this.deadline = this.dateProvider.now() + this.timeoutMs;
|
|
86
80
|
this.txQueue = new FifoMemoryQueue(this.logger);
|
|
87
81
|
this.txValidator = this.opts.txValidator ?? new BatchRequestTxValidator(this.p2pService.txValidatorConfig);
|
|
88
82
|
|
|
@@ -93,12 +87,12 @@ export class BatchTxRequester {
|
|
|
93
87
|
this.peers = new PeerCollection(
|
|
94
88
|
this.p2pService.connectionSampler,
|
|
95
89
|
this.pinnedPeer,
|
|
96
|
-
|
|
90
|
+
dateProvider ?? new DateProvider(),
|
|
97
91
|
badPeerThreshold,
|
|
98
92
|
this.p2pService.peerScoring,
|
|
99
93
|
);
|
|
100
94
|
}
|
|
101
|
-
this.txsMetadata = new MissingTxMetadataCollection(
|
|
95
|
+
this.txsMetadata = new MissingTxMetadataCollection(requestTracker, this.txBatchSize);
|
|
102
96
|
this.smartRequesterSemaphore = this.opts.semaphore ?? new Semaphore(0);
|
|
103
97
|
}
|
|
104
98
|
|
|
@@ -106,40 +100,30 @@ export class BatchTxRequester {
|
|
|
106
100
|
* Fetches all missing transactions and yields them one by one
|
|
107
101
|
* */
|
|
108
102
|
public async *run(): AsyncGenerator<Tx, Tx | undefined, unknown> {
|
|
109
|
-
// Our timeout is represented in milliseconds but queue expects seconds
|
|
110
|
-
// We also want to make sure we wait at least 1 second in case of very low timeouts
|
|
111
|
-
const timeoutQueueAfter = Math.max(Math.ceil(this.timeoutMs / 1_000), 1);
|
|
112
103
|
try {
|
|
113
104
|
if (this.txsMetadata.getMissingTxHashes().size === 0) {
|
|
114
105
|
return undefined;
|
|
115
106
|
}
|
|
116
107
|
|
|
117
|
-
// Start workers in background
|
|
118
|
-
const workersPromise =
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
|
|
108
|
+
// Start workers in background. Workers stop themselves via requestTracker.checkCancelled().
|
|
109
|
+
const workersPromise = Promise.allSettled([
|
|
110
|
+
this.smartRequester(),
|
|
111
|
+
this.dumbRequester(),
|
|
112
|
+
this.pinnedPeerRequester(),
|
|
113
|
+
]).finally(() => {
|
|
122
114
|
this.txQueue.end();
|
|
123
115
|
});
|
|
124
116
|
|
|
117
|
+
// Yield txs as workers put them on the queue. The queue's end() drains remaining items
|
|
118
|
+
// before returning null, so we don't lose any txs.
|
|
125
119
|
while (true) {
|
|
126
|
-
const tx = await this.txQueue.get(
|
|
120
|
+
const tx = await this.txQueue.get();
|
|
127
121
|
|
|
128
|
-
// null indicates that the queue has ended
|
|
129
122
|
if (tx === null) {
|
|
130
123
|
break;
|
|
131
124
|
}
|
|
132
125
|
|
|
133
126
|
yield tx;
|
|
134
|
-
|
|
135
|
-
if (this.shouldStop()) {
|
|
136
|
-
// Drain queue before ending
|
|
137
|
-
let remaining;
|
|
138
|
-
while ((remaining = this.txQueue.getImmediate()) !== undefined) {
|
|
139
|
-
yield remaining;
|
|
140
|
-
}
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
127
|
}
|
|
144
128
|
|
|
145
129
|
this.unlockSmartRequesterSemaphores();
|
|
@@ -360,7 +344,10 @@ export class BatchTxRequester {
|
|
|
360
344
|
) {
|
|
361
345
|
try {
|
|
362
346
|
this.logger.trace(`Smart worker ${workerIndex} started`);
|
|
363
|
-
await
|
|
347
|
+
await Promise.race([this.smartRequesterSemaphore.acquire(), this.requestTracker.cancellationToken]);
|
|
348
|
+
if (this.requestTracker.checkCancelled()) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
364
351
|
this.logger.trace(`Smart worker ${workerIndex} acquired semaphore`);
|
|
365
352
|
|
|
366
353
|
while (!this.shouldStop()) {
|
|
@@ -384,7 +371,10 @@ export class BatchTxRequester {
|
|
|
384
371
|
//
|
|
385
372
|
// When a dumb peer responds with valid txIndices, it gets
|
|
386
373
|
// promoted to smart and releases the semaphore, waking this worker.
|
|
387
|
-
await
|
|
374
|
+
await Promise.race([this.smartRequesterSemaphore.acquire(), this.requestTracker.cancellationToken]);
|
|
375
|
+
if (this.requestTracker.checkCancelled()) {
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
388
378
|
this.logger.debug(`Worker loop smart: acquired next smart peer`);
|
|
389
379
|
continue;
|
|
390
380
|
}
|
|
@@ -411,11 +401,7 @@ export class BatchTxRequester {
|
|
|
411
401
|
});
|
|
412
402
|
}
|
|
413
403
|
} catch (err: any) {
|
|
414
|
-
|
|
415
|
-
this.logger.debug(`Smart worker ${workerIndex} timed out waiting for semaphore`);
|
|
416
|
-
} else {
|
|
417
|
-
this.logger.error(`Smart worker ${workerIndex} encountered an error: ${err}`);
|
|
418
|
-
}
|
|
404
|
+
this.logger.error(`Smart worker ${workerIndex} encountered an error: ${err}`);
|
|
419
405
|
} finally {
|
|
420
406
|
this.logger.debug(`Smart worker ${workerIndex} finished`);
|
|
421
407
|
}
|
|
@@ -528,6 +514,9 @@ export class BatchTxRequester {
|
|
|
528
514
|
});
|
|
529
515
|
|
|
530
516
|
if (hasInvalidTx) {
|
|
517
|
+
this.logger.warn(`Penalizing peer ${peerId.toString()} for sending invalid transactions in batch response`, {
|
|
518
|
+
peerId,
|
|
519
|
+
});
|
|
531
520
|
this.peers.penalisePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
532
521
|
} else {
|
|
533
522
|
// If we have received successful response from the peer, they have "redeemed" themselves and not considered bad anymore
|
|
@@ -651,27 +640,14 @@ export class BatchTxRequester {
|
|
|
651
640
|
}
|
|
652
641
|
|
|
653
642
|
/*
|
|
654
|
-
*
|
|
655
|
-
|
|
656
|
-
return this.txsMetadata.getMissingTxHashes().size == 0;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/*
|
|
660
|
-
* Checks if the BatchTxRequester should stop fetching missing txs
|
|
661
|
-
* Conditions for stopping are:
|
|
662
|
-
* - There have been no missing transactions to start with
|
|
663
|
-
* - All transactions have been fetched
|
|
664
|
-
* - The deadline has been hit (no more time to fetch)
|
|
665
|
-
* - This process has been cancelled via abortSignal
|
|
666
|
-
*
|
|
667
|
-
* @returns true if BatchTxRequester should stop, otherwise false*/
|
|
643
|
+
* Checks if the BatchTxRequester should stop fetching missing txs.
|
|
644
|
+
* Delegates to requestTracker which covers: deadline hit, all txs fetched, or external cancellation. */
|
|
668
645
|
private shouldStop() {
|
|
669
|
-
|
|
670
|
-
if (aborted) {
|
|
646
|
+
if (this.requestTracker.checkCancelled()) {
|
|
671
647
|
this.unlockSmartRequesterSemaphores();
|
|
672
648
|
}
|
|
673
649
|
|
|
674
|
-
return
|
|
650
|
+
return this.requestTracker.checkCancelled();
|
|
675
651
|
}
|
|
676
652
|
|
|
677
653
|
/*
|
|
@@ -689,10 +665,9 @@ export class BatchTxRequester {
|
|
|
689
665
|
* This ensures we don't sleep past the deadline.
|
|
690
666
|
* */
|
|
691
667
|
private async sleepClampedToDeadline(durationMs: number) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (thereIsTimeRemaining) {
|
|
695
|
-
await sleep(Math.min(durationMs, remaining));
|
|
668
|
+
if (this.requestTracker.checkCancelled()) {
|
|
669
|
+
return;
|
|
696
670
|
}
|
|
671
|
+
await Promise.race([sleep(durationMs), this.requestTracker.cancellationToken]);
|
|
697
672
|
}
|
|
698
673
|
}
|
|
@@ -49,7 +49,6 @@ export interface BatchTxRequesterOptions {
|
|
|
49
49
|
//Injectable for testing purposes
|
|
50
50
|
semaphore?: ISemaphore;
|
|
51
51
|
peerCollection?: IPeerCollection;
|
|
52
|
-
abortSignal?: AbortSignal;
|
|
53
52
|
/** Optional tx validator for testing - if not provided, one is created from p2pService.txValidatorConfig */
|
|
54
53
|
txValidator?: IBatchRequestTxValidator;
|
|
55
54
|
}
|