@aztec/p2p 0.87.7 → 0.87.8
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/interface.d.ts +1 -1
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +2 -2
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +3 -3
- 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 +0 -9
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +1 -0
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +13 -8
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +1 -0
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +7 -6
- package/dest/mem_pools/instrumentation.d.ts +7 -11
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +25 -37
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +2 -1
- 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 +22 -38
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +1 -0
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +13 -21
- package/dest/services/encoding.d.ts +2 -0
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +9 -1
- 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 +9 -5
- package/dest/services/tx_collect_instrumentation.d.ts +13 -0
- package/dest/services/tx_collect_instrumentation.d.ts.map +1 -0
- package/dest/services/tx_collect_instrumentation.js +34 -0
- package/dest/services/tx_collector.d.ts +6 -2
- package/dest/services/tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collector.js +61 -49
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -0
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +6 -0
- package/package.json +12 -12
- package/src/client/interface.ts +1 -1
- package/src/client/p2p_client.ts +3 -3
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +0 -14
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +17 -12
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +8 -7
- package/src/mem_pools/instrumentation.ts +32 -46
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +23 -58
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +14 -26
- package/src/services/encoding.ts +9 -1
- package/src/services/reqresp/reqresp.ts +6 -6
- package/src/services/tx_collect_instrumentation.ts +44 -0
- package/src/services/tx_collector.ts +87 -66
- package/src/test-helpers/reqresp-nodes.ts +6 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client';
|
|
2
|
+
|
|
3
|
+
export class TxCollectorInstrumentation {
|
|
4
|
+
private txFromProposalCount: UpDownCounter;
|
|
5
|
+
private txFromMempoolCount: UpDownCounter;
|
|
6
|
+
private txFromP2PCount: UpDownCounter;
|
|
7
|
+
private missingTxsCount: UpDownCounter;
|
|
8
|
+
|
|
9
|
+
constructor(client: TelemetryClient, name: string) {
|
|
10
|
+
const meter = client.getMeter(name);
|
|
11
|
+
|
|
12
|
+
this.txFromProposalCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_TXS_FROM_PROPOSALS_COUNT, {
|
|
13
|
+
description: 'The number of txs taken from block proposals',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
this.txFromMempoolCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_TXS_FROM_MEMPOOL_COUNT, {
|
|
17
|
+
description: 'The number of txs taken from the local mempool',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
this.txFromP2PCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_TXS_FROM_P2P_COUNT, {
|
|
21
|
+
description: 'The number of txs taken from the p2p network',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
this.missingTxsCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_MISSING_TXS_COUNT, {
|
|
25
|
+
description: 'The number of txs not found anywhere',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
incTxsFromProposals(count: number) {
|
|
30
|
+
this.txFromProposalCount.add(count);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
incTxsFromMempool(count: number) {
|
|
34
|
+
this.txFromMempoolCount.add(count);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
incTxsFromP2P(count: number) {
|
|
38
|
+
this.txFromP2PCount.add(count);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
incMissingTxs(count: number) {
|
|
42
|
+
this.missingTxsCount.add(count);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -2,102 +2,123 @@ import { compactArray } from '@aztec/foundation/collection';
|
|
|
2
2
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
3
|
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
4
4
|
import type { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
5
|
+
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
6
|
+
|
|
7
|
+
import type { PeerId } from '@libp2p/interface';
|
|
5
8
|
|
|
6
9
|
import type { P2PClient } from '../client/p2p_client.js';
|
|
10
|
+
import { TxCollectorInstrumentation } from './tx_collect_instrumentation.js';
|
|
7
11
|
|
|
8
12
|
export class TxCollector {
|
|
13
|
+
private instrumentation: TxCollectorInstrumentation;
|
|
14
|
+
|
|
9
15
|
constructor(
|
|
10
16
|
private p2pClient: Pick<
|
|
11
17
|
P2PClient,
|
|
12
|
-
'getTxsByHashFromPool' | 'hasTxsInPool' | 'getTxsByHash' | 'validate' | 'requestTxsByHash'
|
|
18
|
+
'getTxsByHashFromPool' | 'hasTxsInPool' | 'getTxsByHash' | 'validate' | 'requestTxsByHash' | 'addTxsToPool'
|
|
13
19
|
>,
|
|
14
20
|
private log: Logger = createLogger('p2p:tx-collector'),
|
|
15
|
-
|
|
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
|
+
}
|
|
16
77
|
|
|
17
78
|
async collectForBlockProposal(
|
|
18
79
|
proposal: BlockProposal,
|
|
19
|
-
peerWhoSentTheProposal:
|
|
80
|
+
peerWhoSentTheProposal: PeerId | undefined,
|
|
20
81
|
): Promise<{ txs: Tx[]; missing?: TxHash[] }> {
|
|
21
82
|
if (proposal.payload.txHashes.length === 0) {
|
|
22
83
|
this.log.verbose(`Received block proposal with no transactions, skipping transaction availability check`);
|
|
23
84
|
return { txs: [] };
|
|
24
85
|
}
|
|
25
|
-
// Is this a new style proposal?
|
|
26
|
-
if (proposal.txs && proposal.txs.length > 0 && proposal.txs.length === proposal.payload.txHashes.length) {
|
|
27
|
-
// Yes, any txs that we already have we should use
|
|
28
|
-
this.log.info(`Using new style proposal with ${proposal.txs.length} transactions`);
|
|
29
|
-
|
|
30
|
-
// Request from the pool based on the signed hashes in the payload
|
|
31
|
-
const hashesFromPayload = proposal.payload.txHashes;
|
|
32
|
-
const txsToUse = await this.p2pClient.getTxsByHashFromPool(hashesFromPayload);
|
|
33
|
-
|
|
34
|
-
const missingTxs = txsToUse.filter(tx => tx === undefined).length;
|
|
35
|
-
if (missingTxs > 0) {
|
|
36
|
-
this.log.verbose(
|
|
37
|
-
`Missing ${missingTxs}/${hashesFromPayload.length} transactions in the tx pool, will attempt to take from the proposal`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
86
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// Fill any holes with txs in the proposal, provided their hash matches the hash in the payload
|
|
44
|
-
for (let i = 0; i < txsToUse.length; i++) {
|
|
45
|
-
if (txsToUse[i] === undefined) {
|
|
46
|
-
// We don't have the transaction, take from the proposal, provided the hash is the same
|
|
47
|
-
const hashOfTxInProposal = await proposal.txs[i].getTxHash();
|
|
48
|
-
if (hashOfTxInProposal.equals(hashesFromPayload[i])) {
|
|
49
|
-
// Hash is equal, we can use the tx from the proposal
|
|
50
|
-
txsToUse[i] = proposal.txs[i];
|
|
51
|
-
usedFromProposal++;
|
|
52
|
-
} else {
|
|
53
|
-
this.log.warn(
|
|
54
|
-
`Unable to take tx: ${hashOfTxInProposal.toString()} from the proposal, it does not match payload hash: ${hashesFromPayload[
|
|
55
|
-
i
|
|
56
|
-
].toString()}`,
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// See if we still have any holes, if there are then we were not successful and will try the old method
|
|
63
|
-
if (txsToUse.some(tx => tx === undefined)) {
|
|
64
|
-
this.log.warn(`Failed to use transactions from proposal. Falling back to old proposal logic`);
|
|
65
|
-
} else {
|
|
66
|
-
this.log.info(
|
|
67
|
-
`Successfully used ${usedFromProposal}/${hashesFromPayload.length} transactions from the proposal`,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
await this.p2pClient.validate(txsToUse as Tx[]);
|
|
71
|
-
return { txs: txsToUse as Tx[] };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
87
|
+
const txsInMempool = (await this.p2pClient.hasTxsInPool(proposal.payload.txHashes)).filter(Boolean).length;
|
|
88
|
+
this.instrumentation.incTxsFromMempool(txsInMempool);
|
|
74
89
|
|
|
75
|
-
|
|
90
|
+
// Take txs from the proposal if there are any
|
|
91
|
+
const txTakenFromProposal = await this.collectFromProposal(proposal);
|
|
92
|
+
this.instrumentation.incTxsFromProposals(txTakenFromProposal);
|
|
76
93
|
|
|
77
|
-
//
|
|
78
|
-
// This will request from network any txs that are missing
|
|
94
|
+
// Now get the txs we need, either from the pool or the p2p network
|
|
79
95
|
const txHashes: TxHash[] = proposal.payload.txHashes;
|
|
80
96
|
|
|
81
|
-
// This part is just for logging that we are requesting from the network
|
|
82
|
-
const availability = await this.p2pClient.hasTxsInPool(txHashes);
|
|
83
|
-
const notAvailable = availability.filter(availability => availability === false);
|
|
84
|
-
if (notAvailable.length) {
|
|
85
|
-
this.log.verbose(
|
|
86
|
-
`Missing ${notAvailable.length} transactions in the tx pool, will need to request from the network`,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
97
|
// This will request from the network any txs that are missing
|
|
91
98
|
// 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
|
|
92
99
|
const maybeRetrievedTxs = await this.p2pClient.getTxsByHash(txHashes, peerWhoSentTheProposal);
|
|
100
|
+
|
|
101
|
+
// Get the txs that we didn't get from the network, if any. This will be empty if we got them al
|
|
93
102
|
const missingTxs = compactArray(
|
|
94
103
|
maybeRetrievedTxs.map((tx, index) => (tx === undefined ? txHashes[index] : undefined)),
|
|
95
104
|
);
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
this.instrumentation.incMissingTxs(missingTxs.length);
|
|
106
|
+
|
|
107
|
+
const txsFromP2P = txHashes.length - txTakenFromProposal - txsInMempool - missingTxs.length;
|
|
108
|
+
this.instrumentation.incTxsFromP2P(txsFromP2P);
|
|
98
109
|
|
|
99
|
-
|
|
110
|
+
// 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
|
|
111
|
+
const retrievedTxs = compactArray(maybeRetrievedTxs);
|
|
100
112
|
|
|
113
|
+
this.log.info(`Retrieved ${retrievedTxs.length}/${txHashes.length} txs for block proposal`, {
|
|
114
|
+
blockNumber: proposal.blockNumber.toNumber(),
|
|
115
|
+
slotNumber: proposal.slotNumber.toNumber(),
|
|
116
|
+
totalTxsInProposal: txHashes.length,
|
|
117
|
+
txsFromProposal: txTakenFromProposal,
|
|
118
|
+
txsFromMempool: txsInMempool,
|
|
119
|
+
txsFromP2P,
|
|
120
|
+
missingTxs: missingTxs.length,
|
|
121
|
+
});
|
|
101
122
|
return { txs: retrievedTxs, missing: missingTxs };
|
|
102
123
|
}
|
|
103
124
|
}
|
|
@@ -225,11 +225,17 @@ export const connectToPeers = async (nodes: ReqRespNode[]): Promise<void> => {
|
|
|
225
225
|
|
|
226
226
|
// Mock circuit verifier for testing - reimplementation from bb to avoid dependency
|
|
227
227
|
export class AlwaysTrueCircuitVerifier implements ClientProtocolCircuitVerifier {
|
|
228
|
+
stop(): Promise<void> {
|
|
229
|
+
return Promise.resolve();
|
|
230
|
+
}
|
|
228
231
|
verifyProof(_tx: Tx): Promise<boolean> {
|
|
229
232
|
return Promise.resolve(true);
|
|
230
233
|
}
|
|
231
234
|
}
|
|
232
235
|
export class AlwaysFalseCircuitVerifier implements ClientProtocolCircuitVerifier {
|
|
236
|
+
stop(): Promise<void> {
|
|
237
|
+
return Promise.resolve();
|
|
238
|
+
}
|
|
233
239
|
verifyProof(_tx: Tx): Promise<boolean> {
|
|
234
240
|
return Promise.resolve(false);
|
|
235
241
|
}
|