@aztec/p2p 1.2.1 → 2.0.0-nightly.20250813
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 +5 -1
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +29 -12
- package/dest/client/interface.d.ts +8 -13
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +18 -22
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +86 -83
- package/dest/config.d.ts +30 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +34 -1
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +13 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +117 -10
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +4 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +22 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +4 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +21 -1
- package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +2 -10
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +8 -3
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +64 -37
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +8 -3
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +18 -10
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +11 -2
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +73 -44
- package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.js +2 -2
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +35 -59
- package/dest/msg_validators/tx_validator/double_spend_validator.js +2 -2
- package/dest/msg_validators/tx_validator/gas_validator.js +4 -4
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +21 -21
- package/dest/msg_validators/tx_validator/phases_validator.js +3 -3
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +3 -3
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +4 -1
- package/dest/services/dummy_service.d.ts +13 -3
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +26 -3
- package/dest/services/index.d.ts +3 -1
- package/dest/services/index.d.ts.map +1 -1
- package/dest/services/index.js +3 -1
- package/dest/services/libp2p/libp2p_service.d.ts +13 -20
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +123 -46
- package/dest/services/peer-manager/interface.d.ts +8 -1
- package/dest/services/peer-manager/interface.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts +70 -3
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +369 -39
- package/dest/services/reqresp/config.d.ts +3 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/config.js +3 -3
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +3 -4
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/connection-sampler/connection_sampler.js +35 -52
- package/dest/services/reqresp/index.d.ts +2 -1
- package/dest/services/reqresp/index.d.ts.map +1 -1
- package/dest/services/reqresp/index.js +2 -1
- package/dest/services/reqresp/interface.d.ts +61 -10
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +41 -6
- package/dest/services/reqresp/protocols/auth.d.ts +43 -0
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/auth.js +71 -0
- package/dest/services/reqresp/protocols/block.d.ts +5 -0
- package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block.js +28 -5
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +30 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +75 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +11 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +39 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +49 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +75 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts +4 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/index.js +3 -0
- package/dest/services/reqresp/protocols/goodbye.js +3 -5
- package/dest/services/reqresp/protocols/index.d.ts +2 -0
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +2 -0
- package/dest/services/reqresp/protocols/status.d.ts +2 -0
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/status.js +7 -0
- package/dest/services/reqresp/protocols/tx.d.ts +12 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +34 -6
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +20 -0
- package/dest/services/reqresp/reqresp.d.ts +37 -39
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +220 -220
- package/dest/services/reqresp/status.d.ts +8 -3
- package/dest/services/reqresp/status.d.ts.map +1 -1
- package/dest/services/reqresp/status.js +6 -2
- package/dest/services/service.d.ts +10 -10
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +25 -0
- package/dest/services/tx_collection/config.d.ts.map +1 -0
- package/dest/services/tx_collection/config.js +58 -0
- package/dest/services/tx_collection/fast_tx_collection.d.ts +56 -0
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/fast_tx_collection.js +295 -0
- package/dest/services/tx_collection/index.d.ts +3 -0
- package/dest/services/tx_collection/index.d.ts.map +1 -0
- package/dest/services/tx_collection/index.js +2 -0
- package/dest/services/tx_collection/instrumentation.d.ts +10 -0
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -0
- package/dest/services/tx_collection/instrumentation.js +34 -0
- package/dest/services/tx_collection/slow_tx_collection.d.ts +54 -0
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/slow_tx_collection.js +176 -0
- package/dest/services/tx_collection/tx_collection.d.ts +109 -0
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection.js +127 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts +30 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection_sink.js +81 -0
- package/dest/services/tx_collection/tx_source.d.ts +18 -0
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_source.js +31 -0
- package/dest/services/tx_provider.d.ts +49 -0
- package/dest/services/tx_provider.d.ts.map +1 -0
- package/dest/services/tx_provider.js +206 -0
- package/dest/services/{tx_collect_instrumentation.d.ts → tx_provider_instrumentation.d.ts} +2 -2
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -0
- package/dest/services/{tx_collect_instrumentation.js → tx_provider_instrumentation.js} +5 -5
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +4 -3
- package/dest/test-helpers/mock-pubsub.d.ts +2 -1
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +2 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +8 -4
- package/dest/testbench/p2p_client_testbench_worker.js +11 -5
- package/dest/util.d.ts +1 -1
- package/dest/util.d.ts.map +1 -1
- package/package.json +14 -15
- package/src/client/factory.ts +87 -12
- package/src/client/interface.ts +19 -15
- package/src/client/p2p_client.ts +108 -92
- package/src/config.ts +52 -1
- package/src/index.ts +1 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +15 -1
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +155 -4
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +28 -1
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +28 -2
- package/src/mem_pools/attestation_pool/mocks.ts +1 -3
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +59 -41
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +19 -9
- package/src/mem_pools/tx_pool/tx_pool.ts +7 -2
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +63 -40
- package/src/msg_validators/attestation_validator/attestation_validator.ts +1 -1
- package/src/msg_validators/tx_validator/block_header_validator.ts +2 -2
- package/src/msg_validators/tx_validator/data_validator.ts +36 -27
- package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
- package/src/msg_validators/tx_validator/gas_validator.ts +4 -4
- package/src/msg_validators/tx_validator/metadata_validator.ts +22 -28
- package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -2
- package/src/services/discv5/discV5_service.ts +4 -1
- package/src/services/dummy_service.ts +44 -4
- package/src/services/index.ts +3 -1
- package/src/services/libp2p/libp2p_service.ts +147 -55
- package/src/services/peer-manager/interface.ts +10 -1
- package/src/services/peer-manager/peer_manager.ts +441 -41
- package/src/services/reqresp/config.ts +3 -3
- package/src/services/reqresp/connection-sampler/connection_sampler.ts +38 -63
- package/src/services/reqresp/index.ts +2 -0
- package/src/services/reqresp/interface.ts +63 -17
- package/src/services/reqresp/protocols/auth.ts +83 -0
- package/src/services/reqresp/protocols/block.ts +24 -3
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +90 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +53 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +79 -0
- package/src/services/reqresp/protocols/block_txs/index.ts +3 -0
- package/src/services/reqresp/protocols/goodbye.ts +3 -3
- package/src/services/reqresp/protocols/index.ts +2 -0
- package/src/services/reqresp/protocols/status.ts +20 -0
- package/src/services/reqresp/protocols/tx.ts +35 -6
- package/src/services/reqresp/rate-limiter/rate_limits.ts +20 -0
- package/src/services/reqresp/reqresp.ts +294 -264
- package/src/services/reqresp/status.ts +9 -3
- package/src/services/service.ts +23 -14
- package/src/services/tx_collection/config.ts +84 -0
- package/src/services/tx_collection/fast_tx_collection.ts +338 -0
- package/src/services/tx_collection/index.ts +2 -0
- package/src/services/tx_collection/instrumentation.ts +43 -0
- package/src/services/tx_collection/slow_tx_collection.ts +232 -0
- package/src/services/tx_collection/tx_collection.ts +214 -0
- package/src/services/tx_collection/tx_collection_sink.ts +98 -0
- package/src/services/tx_collection/tx_source.ts +37 -0
- package/src/services/tx_provider.ts +215 -0
- package/src/services/{tx_collect_instrumentation.ts → tx_provider_instrumentation.ts} +5 -5
- package/src/test-helpers/make-test-p2p-clients.ts +4 -2
- package/src/test-helpers/mock-pubsub.ts +1 -0
- package/src/test-helpers/reqresp-nodes.ts +7 -1
- package/src/testbench/p2p_client_testbench_worker.ts +9 -2
- package/src/util.ts +1 -1
- package/dest/services/tx_collect_instrumentation.d.ts.map +0 -1
- package/dest/services/tx_collector.d.ts +0 -23
- package/dest/services/tx_collector.d.ts.map +0 -1
- package/dest/services/tx_collector.js +0 -95
- package/src/services/tx_collector.ts +0 -134
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { compactArray } from '@aztec/foundation/collection';
|
|
2
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { elapsed } from '@aztec/foundation/timer';
|
|
4
|
+
import type { BlockInfo, L2Block } from '@aztec/stdlib/block';
|
|
5
|
+
import type { ITxProvider } from '@aztec/stdlib/interfaces/server';
|
|
6
|
+
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
7
|
+
import { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
8
|
+
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
9
|
+
|
|
10
|
+
import type { PeerId } from '@libp2p/interface';
|
|
11
|
+
|
|
12
|
+
import type { P2PClient } from '../client/p2p_client.js';
|
|
13
|
+
import type { TxPool } from '../mem_pools/index.js';
|
|
14
|
+
import type { FastCollectionRequestInput, TxCollection } from './tx_collection/tx_collection.js';
|
|
15
|
+
import { TxProviderInstrumentation } from './tx_provider_instrumentation.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Gathers and returns txs given a block proposal, block, or their hashes.
|
|
19
|
+
* Loads available txs from the tx pool, and relies on a TxCollection service to collect txs from the network and other nodes.
|
|
20
|
+
*/
|
|
21
|
+
export class TxProvider implements ITxProvider {
|
|
22
|
+
protected instrumentation: TxProviderInstrumentation;
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
private txCollection: TxCollection,
|
|
26
|
+
private txPool: TxPool,
|
|
27
|
+
private txValidator: Pick<P2PClient, 'validate'>,
|
|
28
|
+
private log: Logger = createLogger('p2p:tx-collector'),
|
|
29
|
+
client: TelemetryClient = getTelemetryClient(),
|
|
30
|
+
) {
|
|
31
|
+
this.instrumentation = new TxProviderInstrumentation(client, 'TxProvider');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Returns txs from the tx pool given their hashes.*/
|
|
35
|
+
public async getAvailableTxs(txHashes: TxHash[]): Promise<{ txs: Tx[]; missingTxs: TxHash[] }> {
|
|
36
|
+
const response = await this.txPool.getTxsByHash(txHashes);
|
|
37
|
+
if (response.length !== txHashes.length) {
|
|
38
|
+
throw new Error(`Unexpected response size from tx pool: expected ${txHashes.length} but got ${response.length}`);
|
|
39
|
+
}
|
|
40
|
+
const txs: Tx[] = [];
|
|
41
|
+
const missingTxs: TxHash[] = [];
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < txHashes.length; i++) {
|
|
44
|
+
const tx = response[i];
|
|
45
|
+
if (tx === undefined) {
|
|
46
|
+
missingTxs.push(txHashes[i]);
|
|
47
|
+
} else {
|
|
48
|
+
txs.push(tx.setTxHash(txHashes[i]));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { txs, missingTxs };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Gathers txs from the tx pool, proposal body, remote rpc nodes, and reqresp. */
|
|
56
|
+
public getTxsForBlockProposal(
|
|
57
|
+
blockProposal: BlockProposal,
|
|
58
|
+
opts: { pinnedPeer: PeerId | undefined; deadline: Date },
|
|
59
|
+
): Promise<{ txs: Tx[]; missingTxs: TxHash[] }> {
|
|
60
|
+
return this.getOrderedTxsFromAllSources(
|
|
61
|
+
{ type: 'proposal', blockProposal },
|
|
62
|
+
blockProposal.toBlockInfo(),
|
|
63
|
+
blockProposal.txHashes,
|
|
64
|
+
{ ...opts, pinnedPeer: opts.pinnedPeer },
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Gathers txs from the tx pool, remote rpc nodes, and reqresp. */
|
|
69
|
+
public getTxsForBlock(block: L2Block, opts: { deadline: Date }): Promise<{ txs: Tx[]; missingTxs: TxHash[] }> {
|
|
70
|
+
return this.getOrderedTxsFromAllSources(
|
|
71
|
+
{ type: 'block', block },
|
|
72
|
+
block.toBlockInfo(),
|
|
73
|
+
block.body.txEffects.map(tx => tx.txHash),
|
|
74
|
+
{ ...opts, pinnedPeer: undefined },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async getOrderedTxsFromAllSources(
|
|
79
|
+
request: FastCollectionRequestInput,
|
|
80
|
+
blockInfo: BlockInfo,
|
|
81
|
+
txHashes: TxHash[],
|
|
82
|
+
opts: { pinnedPeer: PeerId | undefined; deadline: Date },
|
|
83
|
+
) {
|
|
84
|
+
const [durationMs, result] = await elapsed(() => this.getTxsFromAllSources(request, blockInfo, txHashes, opts));
|
|
85
|
+
const { missingTxHashes, txsFromMempool, txsFromNetwork, txsFromProposal } = result;
|
|
86
|
+
|
|
87
|
+
const txs = [...(txsFromMempool ?? []), ...(txsFromProposal ?? []), ...(txsFromNetwork ?? [])];
|
|
88
|
+
const missingTxs = missingTxHashes?.length ?? 0;
|
|
89
|
+
|
|
90
|
+
const level = missingTxs === 0 ? 'verbose' : 'warn';
|
|
91
|
+
this.log[level](`Retrieved ${txs.length} out of ${blockInfo.txCount} txs for ${request.type}`, {
|
|
92
|
+
...blockInfo,
|
|
93
|
+
txsFromProposal: txsFromProposal?.length,
|
|
94
|
+
txsFromMempool: txsFromMempool?.length,
|
|
95
|
+
txsFromNetwork: txsFromNetwork?.length,
|
|
96
|
+
missingTxs,
|
|
97
|
+
durationMs,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const orderedTxs = this.orderTxs(txs, txHashes);
|
|
101
|
+
if (orderedTxs.length + missingTxs !== txHashes.length) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Error collecting txs for ${request.type} with ${txHashes.length} txs: found ${orderedTxs.length} and flagged ${missingTxs} as missing`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { txs: orderedTxs, missingTxs: (missingTxHashes ?? []).map(TxHash.fromString), durationMs };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private orderTxs(txs: Tx[], order: TxHash[]): Tx[] {
|
|
111
|
+
const txsMap = new Map(txs.map(tx => [tx.txHash.toString(), tx]));
|
|
112
|
+
return order.map(hash => txsMap.get(hash.toString())!).filter(tx => tx !== undefined);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private async getTxsFromAllSources(
|
|
116
|
+
request: FastCollectionRequestInput,
|
|
117
|
+
blockInfo: BlockInfo,
|
|
118
|
+
txHashes: TxHash[],
|
|
119
|
+
opts: { pinnedPeer: PeerId | undefined; deadline: Date },
|
|
120
|
+
) {
|
|
121
|
+
const missingTxHashes = new Set(txHashes.map(txHash => txHash.toString()));
|
|
122
|
+
if (missingTxHashes.size === 0) {
|
|
123
|
+
this.log.debug(`Received request with no transactions`, blockInfo);
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// First go to our tx pool and fetch whatever txs we have there
|
|
128
|
+
// We go to the mempool first since those txs are already validated
|
|
129
|
+
const txsFromMempool = compactArray(await this.txPool.getTxsByHash(txHashes));
|
|
130
|
+
txsFromMempool.forEach(tx => missingTxHashes.delete(tx.getTxHash().toString()));
|
|
131
|
+
this.instrumentation.incTxsFromMempool(txsFromMempool.length);
|
|
132
|
+
this.log.debug(
|
|
133
|
+
`Retrieved ${txsFromMempool.length} txs from mempool for block proposal (${missingTxHashes.size} pending)`,
|
|
134
|
+
{ ...blockInfo, missingTxHashes: [...missingTxHashes] },
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (missingTxHashes.size === 0) {
|
|
138
|
+
return { txsFromMempool };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Take txs from the proposal body if there are any
|
|
142
|
+
// Note that we still have to validate these txs, but we do it in parallel with tx collection
|
|
143
|
+
const proposal = request.type === 'proposal' ? request.blockProposal : undefined;
|
|
144
|
+
const txsFromProposal = this.extractFromProposal(proposal, [...missingTxHashes]);
|
|
145
|
+
if (txsFromProposal.length > 0) {
|
|
146
|
+
this.instrumentation.incTxsFromProposals(txsFromProposal.length);
|
|
147
|
+
txsFromProposal.forEach(tx => missingTxHashes.delete(tx.txHash.toString()));
|
|
148
|
+
this.log.debug(`Retrieved ${txsFromProposal.length} txs from proposal body (${missingTxHashes.size} pending)`, {
|
|
149
|
+
...blockInfo,
|
|
150
|
+
missingTxHashes: [...missingTxHashes],
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (missingTxHashes.size === 0) {
|
|
155
|
+
await this.processProposalTxs(txsFromProposal);
|
|
156
|
+
return { txsFromMempool, txsFromProposal };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Start tx collection from the network if needed, while we validate the txs taken from the proposal in parallel
|
|
160
|
+
const [txsFromNetwork] = await Promise.all([
|
|
161
|
+
this.txCollection.collectFastFor(request, [...missingTxHashes], opts),
|
|
162
|
+
this.processProposalTxs(txsFromProposal),
|
|
163
|
+
] as const);
|
|
164
|
+
|
|
165
|
+
if (txsFromNetwork.length > 0) {
|
|
166
|
+
txsFromNetwork.forEach(tx => missingTxHashes.delete(tx.txHash.toString()));
|
|
167
|
+
this.instrumentation.incTxsFromP2P(txsFromNetwork.length);
|
|
168
|
+
this.log.debug(
|
|
169
|
+
`Retrieved ${txsFromNetwork.length} txs from network for block proposal (${missingTxHashes.size} pending)`,
|
|
170
|
+
{ ...blockInfo, missingTxHashes: [...missingTxHashes] },
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (missingTxHashes.size === 0) {
|
|
175
|
+
return { txsFromNetwork, txsFromMempool, txsFromProposal };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// We are still missing txs, make one last attempt to collect them from our pool, in case they showed up somehow else
|
|
179
|
+
const moreTxsFromPool = compactArray(await this.txPool.getTxsByHash([...missingTxHashes].map(TxHash.fromString)));
|
|
180
|
+
|
|
181
|
+
if (moreTxsFromPool.length > 0) {
|
|
182
|
+
this.instrumentation.incTxsFromMempool(moreTxsFromPool.length);
|
|
183
|
+
this.log.debug(
|
|
184
|
+
`Retrieved ${moreTxsFromPool.length} txs from pool retry for block proposal (${missingTxHashes.size} pending)`,
|
|
185
|
+
{ ...blockInfo, missingTxHashes: [...missingTxHashes] },
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (missingTxHashes.size > 0) {
|
|
190
|
+
this.instrumentation.incMissingTxs(missingTxHashes.size);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
txsFromNetwork,
|
|
195
|
+
txsFromMempool: [...txsFromMempool, ...moreTxsFromPool],
|
|
196
|
+
txsFromProposal,
|
|
197
|
+
missingTxHashes: [...missingTxHashes],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private extractFromProposal(proposal: BlockProposal | undefined, missingTxHashes: string[]): Tx[] {
|
|
202
|
+
if (!proposal) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
return compactArray(proposal.txs ?? []).filter(tx => missingTxHashes.includes(tx.getTxHash().toString()));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private async processProposalTxs(txs: Tx[]): Promise<void> {
|
|
209
|
+
if (txs.length === 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
await this.txValidator.validate(txs);
|
|
213
|
+
await this.txPool.addTxs(txs);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client';
|
|
2
2
|
|
|
3
|
-
export class
|
|
3
|
+
export class TxProviderInstrumentation {
|
|
4
4
|
private txFromProposalCount: UpDownCounter;
|
|
5
5
|
private txFromMempoolCount: UpDownCounter;
|
|
6
6
|
private txFromP2PCount: UpDownCounter;
|
|
@@ -9,19 +9,19 @@ export class TxCollectorInstrumentation {
|
|
|
9
9
|
constructor(client: TelemetryClient, name: string) {
|
|
10
10
|
const meter = client.getMeter(name);
|
|
11
11
|
|
|
12
|
-
this.txFromProposalCount = meter.createUpDownCounter(Metrics.
|
|
12
|
+
this.txFromProposalCount = meter.createUpDownCounter(Metrics.TX_PROVIDER_TXS_FROM_PROPOSALS_COUNT, {
|
|
13
13
|
description: 'The number of txs taken from block proposals',
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
this.txFromMempoolCount = meter.createUpDownCounter(Metrics.
|
|
16
|
+
this.txFromMempoolCount = meter.createUpDownCounter(Metrics.TX_PROVIDER_TXS_FROM_MEMPOOL_COUNT, {
|
|
17
17
|
description: 'The number of txs taken from the local mempool',
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
this.txFromP2PCount = meter.createUpDownCounter(Metrics.
|
|
20
|
+
this.txFromP2PCount = meter.createUpDownCounter(Metrics.TX_PROVIDER_TXS_FROM_P2P_COUNT, {
|
|
21
21
|
description: 'The number of txs taken from the p2p network',
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
this.missingTxsCount = meter.createUpDownCounter(Metrics.
|
|
24
|
+
this.missingTxsCount = meter.createUpDownCounter(Metrics.TX_PROVIDER_MISSING_TXS_COUNT, {
|
|
25
25
|
description: 'The number of txs not found anywhere',
|
|
26
26
|
});
|
|
27
27
|
}
|
|
@@ -2,6 +2,7 @@ import { MockL2BlockSource } from '@aztec/archiver/test';
|
|
|
2
2
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
3
3
|
import { SecretValue } from '@aztec/foundation/config';
|
|
4
4
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
5
6
|
import { sleep } from '@aztec/foundation/sleep';
|
|
6
7
|
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
7
8
|
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
|
|
@@ -105,6 +106,7 @@ export async function makeTestP2PClient(
|
|
|
105
106
|
mockEpochCache,
|
|
106
107
|
'test-p2p-client',
|
|
107
108
|
undefined,
|
|
109
|
+
undefined,
|
|
108
110
|
{
|
|
109
111
|
txPool: mockTxPool as unknown as TxPool,
|
|
110
112
|
attestationPool: mockAttestationPool as unknown as AttestationPool,
|
|
@@ -147,7 +149,7 @@ export async function makeAndStartTestP2PClients(numberOfPeers: number, testConf
|
|
|
147
149
|
clients.push(client);
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
await
|
|
152
|
+
await retryUntil(() => clients.every(c => c.isReady()), 'p2p clients started', 10, 0.5);
|
|
151
153
|
testConfig.logger?.info(`Created and started ${clients.length} P2P clients at ports ${ports.join(',')}`, {
|
|
152
154
|
ports,
|
|
153
155
|
peerEnrs,
|
|
@@ -206,5 +208,5 @@ export async function makeTestP2PClients(numberOfPeers: number, testConfig: Make
|
|
|
206
208
|
|
|
207
209
|
export async function startTestP2PClients(clients: P2PClient[]) {
|
|
208
210
|
await Promise.all(clients.map(c => c.start()));
|
|
209
|
-
await
|
|
211
|
+
await retryUntil(() => clients.every(c => c.isReady()), 'p2p clients started', 10, 0.5);
|
|
210
212
|
}
|
|
@@ -110,6 +110,7 @@ export class MockPubSub implements PubSubLibp2p {
|
|
|
110
110
|
class MockGossipSubService extends TypedEventEmitter<GossipsubEvents> implements GossipSubService {
|
|
111
111
|
private logger = createLogger('p2p:test:mock-gossipsub');
|
|
112
112
|
public subscribedTopics: Set<TopicStr> = new Set();
|
|
113
|
+
public readonly direct = new Set<string>();
|
|
113
114
|
|
|
114
115
|
constructor(
|
|
115
116
|
public peerId: PeerId,
|
|
@@ -151,10 +151,12 @@ export async function createTestLibP2PService<T extends P2PClientType>(
|
|
|
151
151
|
reqresp,
|
|
152
152
|
worldStateSynchronizer,
|
|
153
153
|
protocolVersion,
|
|
154
|
+
epochCache,
|
|
154
155
|
);
|
|
155
156
|
|
|
156
157
|
p2pNode.services.pubsub.score.params.appSpecificWeight = 10;
|
|
157
|
-
p2pNode.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
|
|
158
|
+
p2pNode.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
|
|
159
|
+
peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
|
|
158
160
|
|
|
159
161
|
return new LibP2PService<T>(
|
|
160
162
|
clientType,
|
|
@@ -188,6 +190,8 @@ export const MOCK_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = {
|
|
|
188
190
|
[ReqRespSubProtocol.TX]: (_msg: any) => Promise.resolve(Buffer.from('tx')),
|
|
189
191
|
[ReqRespSubProtocol.GOODBYE]: (_msg: any) => Promise.resolve(Buffer.from('goodbye')),
|
|
190
192
|
[ReqRespSubProtocol.BLOCK]: (_msg: any) => Promise.resolve(Buffer.from('block')),
|
|
193
|
+
[ReqRespSubProtocol.AUTH]: (_msg: any) => Promise.resolve(Buffer.from('auth')),
|
|
194
|
+
[ReqRespSubProtocol.BLOCK_TXS]: (_msg: any) => Promise.resolve(Buffer.from('block_txs')),
|
|
191
195
|
};
|
|
192
196
|
|
|
193
197
|
// By default, all requests are valid
|
|
@@ -198,6 +202,8 @@ export const MOCK_SUB_PROTOCOL_VALIDATORS: ReqRespSubProtocolValidators = {
|
|
|
198
202
|
[ReqRespSubProtocol.TX]: noopValidator,
|
|
199
203
|
[ReqRespSubProtocol.GOODBYE]: noopValidator,
|
|
200
204
|
[ReqRespSubProtocol.BLOCK]: noopValidator,
|
|
205
|
+
[ReqRespSubProtocol.AUTH]: noopValidator,
|
|
206
|
+
[ReqRespSubProtocol.BLOCK_TXS]: noopValidator,
|
|
201
207
|
};
|
|
202
208
|
|
|
203
209
|
/**
|
|
@@ -19,6 +19,7 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien
|
|
|
19
19
|
|
|
20
20
|
import type { Message, PeerId } from '@libp2p/interface';
|
|
21
21
|
import { TopicValidatorResult } from '@libp2p/interface';
|
|
22
|
+
import EventEmitter from 'events';
|
|
22
23
|
|
|
23
24
|
import type { P2PConfig } from '../config.js';
|
|
24
25
|
import { createP2PClient } from '../index.js';
|
|
@@ -35,7 +36,7 @@ import type { PubSubLibp2p } from '../util.js';
|
|
|
35
36
|
// Simple mock implementation
|
|
36
37
|
function mockTxPool(): TxPool {
|
|
37
38
|
// Mock all methods
|
|
38
|
-
|
|
39
|
+
const pool: Omit<TxPool, keyof EventEmitter> = {
|
|
39
40
|
isEmpty: () => Promise.resolve(false),
|
|
40
41
|
addTxs: () => Promise.resolve(1),
|
|
41
42
|
getTxByHash: () => Promise.resolve(undefined),
|
|
@@ -54,6 +55,7 @@ function mockTxPool(): TxPool {
|
|
|
54
55
|
updateConfig: () => {},
|
|
55
56
|
markTxsAsNonEvictable: () => Promise.resolve(),
|
|
56
57
|
};
|
|
58
|
+
return Object.assign(new EventEmitter(), pool);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
function mockAttestationPool(): AttestationPool {
|
|
@@ -66,6 +68,8 @@ function mockAttestationPool(): AttestationPool {
|
|
|
66
68
|
deleteAttestationsForSlotAndProposal: () => Promise.resolve(),
|
|
67
69
|
getAttestationsForSlot: () => Promise.resolve([]),
|
|
68
70
|
getAttestationsForSlotAndProposal: () => Promise.resolve([]),
|
|
71
|
+
addBlockProposal: () => Promise.resolve(),
|
|
72
|
+
getBlockProposal: () => Promise.resolve(undefined),
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -84,6 +88,8 @@ function mockEpochCache(): EpochCacheInterface {
|
|
|
84
88
|
}),
|
|
85
89
|
getEpochAndSlotInNextL1Slot: () => ({ epoch: 0n, slot: 0n, ts: 0n, now: 0n }),
|
|
86
90
|
isInCommittee: () => Promise.resolve(false),
|
|
91
|
+
getRegisteredValidators: () => Promise.resolve([]),
|
|
92
|
+
filterInCommittee: () => Promise.resolve([]),
|
|
87
93
|
};
|
|
88
94
|
}
|
|
89
95
|
|
|
@@ -154,7 +160,7 @@ class TestLibP2PService<T extends P2PClientType = P2PClientType.Full> extends Li
|
|
|
154
160
|
const tx = Tx.fromBuffer(p2pMessage.payload);
|
|
155
161
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Accept);
|
|
156
162
|
|
|
157
|
-
const txHash =
|
|
163
|
+
const txHash = tx.getTxHash();
|
|
158
164
|
const txHashString = txHash.toString();
|
|
159
165
|
this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()}.`);
|
|
160
166
|
await this.mempools.txPool.addTxs([tx]);
|
|
@@ -204,6 +210,7 @@ process.on('message', async msg => {
|
|
|
204
210
|
worldState,
|
|
205
211
|
epochCache,
|
|
206
212
|
'test-p2p-bench-worker',
|
|
213
|
+
undefined,
|
|
207
214
|
telemetry,
|
|
208
215
|
deps,
|
|
209
216
|
);
|
package/src/util.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface PubSubLibp2p extends Pick<Libp2p, 'status' | 'start' | 'stop' |
|
|
|
22
22
|
services: {
|
|
23
23
|
pubsub: Pick<
|
|
24
24
|
GossipSub,
|
|
25
|
-
'addEventListener' | 'removeEventListener' | 'publish' | 'subscribe' | 'reportMessageValidationResult'
|
|
25
|
+
'addEventListener' | 'removeEventListener' | 'publish' | 'subscribe' | 'reportMessageValidationResult' | 'direct'
|
|
26
26
|
> & { score: Pick<GossipSub['score'], 'score'> };
|
|
27
27
|
};
|
|
28
28
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tx_collect_instrumentation.d.ts","sourceRoot":"","sources":["../../src/services/tx_collect_instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAE5F,qBAAa,0BAA0B;IACrC,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,eAAe,CAAgB;gBAE3B,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM;IAoBjD,mBAAmB,CAAC,KAAK,EAAE,MAAM;IAIjC,iBAAiB,CAAC,KAAK,EAAE,MAAM;IAI/B,aAAa,CAAC,KAAK,EAAE,MAAM;IAI3B,aAAa,CAAC,KAAK,EAAE,MAAM;CAG5B"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { type Logger } from '@aztec/foundation/log';
|
|
2
|
-
import type { ITxCollector } from '@aztec/stdlib/interfaces/server';
|
|
3
|
-
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
4
|
-
import type { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
5
|
-
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
6
|
-
import type { PeerId } from '@libp2p/interface';
|
|
7
|
-
import type { P2PClient } from '../client/p2p_client.js';
|
|
8
|
-
export declare class TxCollector implements ITxCollector {
|
|
9
|
-
private p2pClient;
|
|
10
|
-
private log;
|
|
11
|
-
private instrumentation;
|
|
12
|
-
constructor(p2pClient: Pick<P2PClient, 'getTxsByHashFromPool' | 'hasTxsInPool' | 'getTxsByHash' | 'validate' | 'requestTxsByHash' | 'addTxsToPool'>, log?: Logger, client?: TelemetryClient);
|
|
13
|
-
private collectFromProposal;
|
|
14
|
-
collectForBlockProposal(proposal: BlockProposal, peerWhoSentTheProposal: PeerId | undefined): Promise<{
|
|
15
|
-
txs: Tx[];
|
|
16
|
-
missing?: TxHash[];
|
|
17
|
-
}>;
|
|
18
|
-
collectTransactions(txHashes: TxHash[], peerWhoSentTheProposal: PeerId | undefined): Promise<{
|
|
19
|
-
txs: Tx[];
|
|
20
|
-
missing?: TxHash[];
|
|
21
|
-
}>;
|
|
22
|
-
}
|
|
23
|
-
//# sourceMappingURL=tx_collector.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tx_collector.d.ts","sourceRoot":"","sources":["../../src/services/tx_collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAEnF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGzD,qBAAa,WAAY,YAAW,YAAY;IAG5C,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,GAAG;IANb,OAAO,CAAC,eAAe,CAA6B;gBAE1C,SAAS,EAAE,IAAI,CACrB,SAAS,EACT,sBAAsB,GAAG,cAAc,GAAG,cAAc,GAAG,UAAU,GAAG,kBAAkB,GAAG,cAAc,CAC5G,EACO,GAAG,GAAE,MAAyC,EACtD,MAAM,GAAE,eAAsC;YAMlC,mBAAmB;IAmD3B,uBAAuB,CAC3B,QAAQ,EAAE,aAAa,EACvB,sBAAsB,EAAE,MAAM,GAAG,SAAS,GACzC,OAAO,CAAC;QAAE,GAAG,EAAE,EAAE,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAmCvC,mBAAmB,CACvB,QAAQ,EAAE,MAAM,EAAE,EAClB,sBAAsB,EAAE,MAAM,GAAG,SAAS,GACzC,OAAO,CAAC;QAAE,GAAG,EAAE,EAAE,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAe9C"}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { compactArray } from '@aztec/foundation/collection';
|
|
2
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
4
|
-
import { TxCollectorInstrumentation } from './tx_collect_instrumentation.js';
|
|
5
|
-
export class TxCollector {
|
|
6
|
-
p2pClient;
|
|
7
|
-
log;
|
|
8
|
-
instrumentation;
|
|
9
|
-
constructor(p2pClient, log = createLogger('p2p:tx-collector'), client = getTelemetryClient()){
|
|
10
|
-
this.p2pClient = p2pClient;
|
|
11
|
-
this.log = log;
|
|
12
|
-
this.instrumentation = new TxCollectorInstrumentation(client, 'TxCollector');
|
|
13
|
-
}
|
|
14
|
-
// Checks the proposal for transactions we don't already have, validates them and adds them to our pool
|
|
15
|
-
async collectFromProposal(proposal) {
|
|
16
|
-
// Does this proposal have any transactions?
|
|
17
|
-
if (!proposal.txs || proposal.txs.length === 0) {
|
|
18
|
-
return 0;
|
|
19
|
-
}
|
|
20
|
-
const proposalHashes = new Set((proposal.payload.txHashes ?? []).map((txHash)=>txHash.toString()));
|
|
21
|
-
// Get the transactions from the proposal and their hashes
|
|
22
|
-
// also, we are only interested in txs that are part of the proposal
|
|
23
|
-
const txsFromProposal = compactArray(await Promise.all(proposal.txs.map((tx)=>tx === undefined ? Promise.resolve(undefined) : tx.getTxHash().then((hash)=>({
|
|
24
|
-
txHash: hash,
|
|
25
|
-
tx
|
|
26
|
-
}))))).filter((tx)=>proposalHashes.has(tx.txHash.toString()));
|
|
27
|
-
// Of the transactions from the proposal, retrieve those that we have in the pool already
|
|
28
|
-
const txsToValidate = [];
|
|
29
|
-
const txsWeAlreadyHave = await this.p2pClient.getTxsByHashFromPool(txsFromProposal.map((tx)=>tx.txHash));
|
|
30
|
-
// Txs we already have will have holes where we did not find them
|
|
31
|
-
// Where that is the case we need to validate the tx in the proposal
|
|
32
|
-
for(let i = 0; i < txsWeAlreadyHave.length; i++){
|
|
33
|
-
if (txsWeAlreadyHave[i] === undefined) {
|
|
34
|
-
txsToValidate.push(txsFromProposal[i].tx);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
// Now validate all the transactions from the proposal that we don't have
|
|
38
|
-
// This will throw if any of the transactions are invalid, this is probably correct, if someone sends us a proposal with invalid
|
|
39
|
-
// transactions we probably shouldn't spend any more effort on it
|
|
40
|
-
try {
|
|
41
|
-
await this.p2pClient.validate(txsToValidate);
|
|
42
|
-
} catch (err) {
|
|
43
|
-
this.log.error(`Received proposal with invalid transactions, skipping`);
|
|
44
|
-
throw err;
|
|
45
|
-
}
|
|
46
|
-
// Now store these transactions in our pool, provided these are the txs in proposal.payload.txHashes they will be pinned already
|
|
47
|
-
await this.p2pClient.addTxsToPool(txsToValidate);
|
|
48
|
-
return txsToValidate.length;
|
|
49
|
-
}
|
|
50
|
-
async collectForBlockProposal(proposal, peerWhoSentTheProposal) {
|
|
51
|
-
if (proposal.payload.txHashes.length === 0) {
|
|
52
|
-
this.log.verbose(`Received block proposal with no transactions, skipping transaction availability check`);
|
|
53
|
-
return {
|
|
54
|
-
txs: []
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
const txsInMempool = (await this.p2pClient.hasTxsInPool(proposal.payload.txHashes)).filter(Boolean).length;
|
|
58
|
-
this.instrumentation.incTxsFromMempool(txsInMempool);
|
|
59
|
-
// Take txs from the proposal if there are any
|
|
60
|
-
const txTakenFromProposal = await this.collectFromProposal(proposal);
|
|
61
|
-
this.instrumentation.incTxsFromProposals(txTakenFromProposal);
|
|
62
|
-
// Now get the txs we need, either from the pool or the p2p network
|
|
63
|
-
const txHashes = proposal.payload.txHashes;
|
|
64
|
-
const { txs, missing } = await this.collectTransactions(txHashes, peerWhoSentTheProposal);
|
|
65
|
-
this.instrumentation.incMissingTxs(missing?.length ?? 0);
|
|
66
|
-
const txsFromP2P = txHashes.length - txTakenFromProposal - txsInMempool - (missing?.length ?? 0);
|
|
67
|
-
this.instrumentation.incTxsFromP2P(txsFromP2P);
|
|
68
|
-
this.log.info(`Retrieved ${txs.length}/${txHashes.length} txs for block proposal`, {
|
|
69
|
-
blockNumber: proposal.blockNumber,
|
|
70
|
-
slotNumber: proposal.slotNumber.toNumber(),
|
|
71
|
-
totalTxsInProposal: txHashes.length,
|
|
72
|
-
txsFromProposal: txTakenFromProposal,
|
|
73
|
-
txsFromMempool: txsInMempool,
|
|
74
|
-
txsFromP2P,
|
|
75
|
-
missingTxs: missing?.length ?? 0
|
|
76
|
-
});
|
|
77
|
-
return {
|
|
78
|
-
txs,
|
|
79
|
-
missing
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
async collectTransactions(txHashes, peerWhoSentTheProposal) {
|
|
83
|
-
// This will request from the network any txs that are missing
|
|
84
|
-
// NOTE: this could still return missing txs so we need to (1) be careful to handle undefined and (2) keep the txs in the correct order for re-execution
|
|
85
|
-
const maybeRetrievedTxs = await this.p2pClient.getTxsByHash(txHashes, peerWhoSentTheProposal);
|
|
86
|
-
// Get the txs that we didn't get from the network, if any. This will be empty if we got them al
|
|
87
|
-
const missingTxs = compactArray(maybeRetrievedTxs.map((tx, index)=>tx === undefined ? txHashes[index] : undefined));
|
|
88
|
-
// if we found all txs, this is a noop. If we didn't find all txs then tell the validator to skip attestations because missingTxs.length > 0
|
|
89
|
-
const retrievedTxs = compactArray(maybeRetrievedTxs);
|
|
90
|
-
return {
|
|
91
|
-
txs: retrievedTxs,
|
|
92
|
-
missing: missingTxs
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { compactArray } from '@aztec/foundation/collection';
|
|
2
|
-
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import type { ITxCollector } from '@aztec/stdlib/interfaces/server';
|
|
4
|
-
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
5
|
-
import type { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
6
|
-
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
7
|
-
|
|
8
|
-
import type { PeerId } from '@libp2p/interface';
|
|
9
|
-
|
|
10
|
-
import type { P2PClient } from '../client/p2p_client.js';
|
|
11
|
-
import { TxCollectorInstrumentation } from './tx_collect_instrumentation.js';
|
|
12
|
-
|
|
13
|
-
export class TxCollector implements ITxCollector {
|
|
14
|
-
private instrumentation: TxCollectorInstrumentation;
|
|
15
|
-
constructor(
|
|
16
|
-
private p2pClient: Pick<
|
|
17
|
-
P2PClient,
|
|
18
|
-
'getTxsByHashFromPool' | 'hasTxsInPool' | 'getTxsByHash' | 'validate' | 'requestTxsByHash' | 'addTxsToPool'
|
|
19
|
-
>,
|
|
20
|
-
private log: Logger = createLogger('p2p:tx-collector'),
|
|
21
|
-
client: TelemetryClient = getTelemetryClient(),
|
|
22
|
-
) {
|
|
23
|
-
this.instrumentation = new TxCollectorInstrumentation(client, 'TxCollector');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Checks the proposal for transactions we don't already have, validates them and adds them to our pool
|
|
27
|
-
private async collectFromProposal(proposal: BlockProposal): Promise<number> {
|
|
28
|
-
// Does this proposal have any transactions?
|
|
29
|
-
if (!proposal.txs || proposal.txs.length === 0) {
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const proposalHashes = new Set<string>((proposal.payload.txHashes ?? []).map(txHash => txHash.toString()));
|
|
34
|
-
|
|
35
|
-
// Get the transactions from the proposal and their hashes
|
|
36
|
-
// also, we are only interested in txs that are part of the proposal
|
|
37
|
-
const txsFromProposal = compactArray(
|
|
38
|
-
await Promise.all(
|
|
39
|
-
proposal.txs.map(tx =>
|
|
40
|
-
tx === undefined
|
|
41
|
-
? Promise.resolve(undefined)
|
|
42
|
-
: tx.getTxHash().then(hash => ({
|
|
43
|
-
txHash: hash,
|
|
44
|
-
tx,
|
|
45
|
-
})),
|
|
46
|
-
),
|
|
47
|
-
),
|
|
48
|
-
).filter(tx => proposalHashes.has(tx.txHash.toString()));
|
|
49
|
-
|
|
50
|
-
// Of the transactions from the proposal, retrieve those that we have in the pool already
|
|
51
|
-
const txsToValidate = [];
|
|
52
|
-
const txsWeAlreadyHave = await this.p2pClient.getTxsByHashFromPool(txsFromProposal.map(tx => tx.txHash));
|
|
53
|
-
|
|
54
|
-
// Txs we already have will have holes where we did not find them
|
|
55
|
-
// Where that is the case we need to validate the tx in the proposal
|
|
56
|
-
for (let i = 0; i < txsWeAlreadyHave.length; i++) {
|
|
57
|
-
if (txsWeAlreadyHave[i] === undefined) {
|
|
58
|
-
txsToValidate.push(txsFromProposal[i].tx);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Now validate all the transactions from the proposal that we don't have
|
|
63
|
-
// This will throw if any of the transactions are invalid, this is probably correct, if someone sends us a proposal with invalid
|
|
64
|
-
// transactions we probably shouldn't spend any more effort on it
|
|
65
|
-
try {
|
|
66
|
-
await this.p2pClient.validate(txsToValidate);
|
|
67
|
-
} catch (err) {
|
|
68
|
-
this.log.error(`Received proposal with invalid transactions, skipping`);
|
|
69
|
-
throw err;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Now store these transactions in our pool, provided these are the txs in proposal.payload.txHashes they will be pinned already
|
|
73
|
-
await this.p2pClient.addTxsToPool(txsToValidate);
|
|
74
|
-
|
|
75
|
-
return txsToValidate.length;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async collectForBlockProposal(
|
|
79
|
-
proposal: BlockProposal,
|
|
80
|
-
peerWhoSentTheProposal: PeerId | undefined,
|
|
81
|
-
): Promise<{ txs: Tx[]; missing?: TxHash[] }> {
|
|
82
|
-
if (proposal.payload.txHashes.length === 0) {
|
|
83
|
-
this.log.verbose(`Received block proposal with no transactions, skipping transaction availability check`);
|
|
84
|
-
return { txs: [] };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const txsInMempool = (await this.p2pClient.hasTxsInPool(proposal.payload.txHashes)).filter(Boolean).length;
|
|
88
|
-
this.instrumentation.incTxsFromMempool(txsInMempool);
|
|
89
|
-
|
|
90
|
-
// Take txs from the proposal if there are any
|
|
91
|
-
const txTakenFromProposal = await this.collectFromProposal(proposal);
|
|
92
|
-
this.instrumentation.incTxsFromProposals(txTakenFromProposal);
|
|
93
|
-
|
|
94
|
-
// Now get the txs we need, either from the pool or the p2p network
|
|
95
|
-
const txHashes: TxHash[] = proposal.payload.txHashes;
|
|
96
|
-
const { txs, missing } = await this.collectTransactions(txHashes, peerWhoSentTheProposal);
|
|
97
|
-
|
|
98
|
-
this.instrumentation.incMissingTxs(missing?.length ?? 0);
|
|
99
|
-
|
|
100
|
-
const txsFromP2P = txHashes.length - txTakenFromProposal - txsInMempool - (missing?.length ?? 0);
|
|
101
|
-
this.instrumentation.incTxsFromP2P(txsFromP2P);
|
|
102
|
-
|
|
103
|
-
this.log.info(`Retrieved ${txs.length}/${txHashes.length} txs for block proposal`, {
|
|
104
|
-
blockNumber: proposal.blockNumber,
|
|
105
|
-
slotNumber: proposal.slotNumber.toNumber(),
|
|
106
|
-
totalTxsInProposal: txHashes.length,
|
|
107
|
-
txsFromProposal: txTakenFromProposal,
|
|
108
|
-
txsFromMempool: txsInMempool,
|
|
109
|
-
txsFromP2P,
|
|
110
|
-
missingTxs: missing?.length ?? 0,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
return { txs, missing };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async collectTransactions(
|
|
117
|
-
txHashes: TxHash[],
|
|
118
|
-
peerWhoSentTheProposal: PeerId | undefined,
|
|
119
|
-
): Promise<{ txs: Tx[]; missing?: TxHash[] }> {
|
|
120
|
-
// This will request from the network any txs that are missing
|
|
121
|
-
// NOTE: this could still return missing txs so we need to (1) be careful to handle undefined and (2) keep the txs in the correct order for re-execution
|
|
122
|
-
const maybeRetrievedTxs = await this.p2pClient.getTxsByHash(txHashes, peerWhoSentTheProposal);
|
|
123
|
-
|
|
124
|
-
// Get the txs that we didn't get from the network, if any. This will be empty if we got them al
|
|
125
|
-
const missingTxs = compactArray(
|
|
126
|
-
maybeRetrievedTxs.map((tx, index) => (tx === undefined ? txHashes[index] : undefined)),
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
// if we found all txs, this is a noop. If we didn't find all txs then tell the validator to skip attestations because missingTxs.length > 0
|
|
130
|
-
const retrievedTxs = compactArray(maybeRetrievedTxs);
|
|
131
|
-
|
|
132
|
-
return { txs: retrievedTxs, missing: missingTxs };
|
|
133
|
-
}
|
|
134
|
-
}
|