@aztec/p2p 0.72.1 → 0.74.0
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/bootstrap/bootstrap.d.ts +2 -2
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +1 -1
- package/dest/client/factory.d.ts +2 -2
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +4 -4
- package/dest/client/p2p_client.d.ts +13 -16
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +50 -62
- 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 +11 -19
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +6 -13
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +74 -80
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +5 -5
- package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +3 -3
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +9 -9
- 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 +59 -53
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +7 -7
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +17 -18
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +7 -7
- 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 +48 -47
- package/dest/mocks/index.js +3 -3
- package/dest/msg_validators/attestation_validator/attestation_validator.js +2 -2
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +2 -2
- package/dest/msg_validators/tx_validator/block_header_validator.js +3 -3
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +7 -7
- package/dest/msg_validators/tx_validator/double_spend_validator.js +3 -3
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +9 -9
- package/dest/services/data_store.d.ts +4 -4
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +7 -7
- package/dest/services/libp2p/libp2p_service.d.ts +3 -3
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +56 -35
- package/dest/services/peer-manager/peer_manager.d.ts +1 -0
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +6 -3
- package/dest/services/reqresp/protocols/tx.js +4 -4
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +2 -2
- package/dest/util.d.ts +2 -2
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +2 -2
- package/package.json +7 -7
- package/src/bootstrap/bootstrap.ts +2 -2
- package/src/client/factory.ts +5 -5
- package/src/client/p2p_client.ts +63 -75
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +14 -22
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +100 -94
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +4 -4
- package/src/mem_pools/attestation_pool/mocks.ts +3 -3
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +84 -72
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +26 -23
- package/src/mem_pools/tx_pool/tx_pool.ts +7 -7
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +50 -47
- package/src/mocks/index.ts +2 -2
- package/src/msg_validators/attestation_validator/attestation_validator.ts +1 -1
- package/src/msg_validators/block_proposal_validator/block_proposal_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 +12 -9
- package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
- package/src/msg_validators/tx_validator/metadata_validator.ts +8 -8
- package/src/services/data_store.ts +9 -9
- package/src/services/libp2p/libp2p_service.ts +70 -36
- package/src/services/peer-manager/peer_manager.ts +7 -2
- package/src/services/reqresp/protocols/tx.ts +3 -3
- package/src/services/reqresp/reqresp.ts +7 -1
- package/src/util.ts +7 -4
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { BlockAttestation } from '@aztec/circuit-types';
|
|
2
2
|
import { Fr } from '@aztec/foundation/fields';
|
|
3
|
+
import { toArray } from '@aztec/foundation/iterable';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
-
import { type
|
|
5
|
+
import { type AztecAsyncKVStore, type AztecAsyncMap, type AztecAsyncMultiMap } from '@aztec/kv-store';
|
|
5
6
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
6
7
|
|
|
7
8
|
import { PoolInstrumentation, PoolName } from '../instrumentation.js';
|
|
@@ -10,144 +11,149 @@ import { type AttestationPool } from './attestation_pool.js';
|
|
|
10
11
|
export class KvAttestationPool implements AttestationPool {
|
|
11
12
|
private metrics: PoolInstrumentation<BlockAttestation>;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
private
|
|
14
|
+
private attestations: AztecAsyncMap<string, Buffer>;
|
|
15
|
+
private proposalsForSlot: AztecAsyncMultiMap<string, string>;
|
|
16
|
+
private attestationsForProposal: AztecAsyncMultiMap<string, string>;
|
|
15
17
|
|
|
16
18
|
constructor(
|
|
17
|
-
private store:
|
|
19
|
+
private store: AztecAsyncKVStore,
|
|
18
20
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
19
21
|
private log = createLogger('aztec:attestation_pool'),
|
|
20
22
|
) {
|
|
21
|
-
this.attestations = store.
|
|
23
|
+
this.attestations = store.openMap('attestations');
|
|
24
|
+
this.proposalsForSlot = store.openMultiMap('proposals_for_slot');
|
|
25
|
+
this.attestationsForProposal = store.openMultiMap('attestations_for_proposal');
|
|
26
|
+
|
|
22
27
|
this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL);
|
|
23
28
|
}
|
|
24
29
|
|
|
25
|
-
private
|
|
26
|
-
|
|
30
|
+
private getProposalKey(slot: number | bigint | Fr | string, proposalId: Fr | string | Buffer): string {
|
|
31
|
+
const slotStr = typeof slot === 'string' ? slot : new Fr(slot).toString();
|
|
32
|
+
const proposalIdStr =
|
|
33
|
+
typeof proposalId === 'string'
|
|
34
|
+
? proposalId
|
|
35
|
+
: Buffer.isBuffer(proposalId)
|
|
36
|
+
? Fr.fromBuffer(proposalId).toString()
|
|
37
|
+
: proposalId.toString();
|
|
38
|
+
|
|
39
|
+
return `${slotStr}-${proposalIdStr}`;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
* Essentially a nested mapping of address -> attestation
|
|
33
|
-
*
|
|
34
|
-
* @param slot - The slot to get the proposal map for
|
|
35
|
-
* @param proposalId - The proposalId to get the map for
|
|
36
|
-
* @returns The proposal map
|
|
37
|
-
*/
|
|
38
|
-
private getProposalMap(slot: string, proposalId: string): AztecMapWithSize<string, Buffer> {
|
|
39
|
-
const mapKey = this.getProposalMapKey(slot, proposalId);
|
|
40
|
-
return this.store.openMapWithSize(mapKey);
|
|
42
|
+
private getAttestationKey(slot: number | bigint | Fr | string, proposalId: Fr | string, address: string): string {
|
|
43
|
+
return `${this.getProposalKey(slot, proposalId)}-${address}`;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
public async addAttestations(attestations: BlockAttestation[]): Promise<void> {
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
await this.store.transactionAsync(async () => {
|
|
48
|
+
for (const attestation of attestations) {
|
|
49
|
+
const slotNumber = attestation.payload.header.globalVariables.slotNumber;
|
|
50
|
+
const proposalId = attestation.archive;
|
|
51
|
+
const address = (await attestation.getSender()).toString();
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
await this.attestations.set(slotNumber, proposalId);
|
|
53
|
+
await this.attestations.set(this.getAttestationKey(slotNumber, proposalId, address), attestation.toBuffer());
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
await this.proposalsForSlot.set(slotNumber.toString(), proposalId.toString());
|
|
56
|
+
await this.attestationsForProposal.set(
|
|
57
|
+
this.getProposalKey(slotNumber, proposalId),
|
|
58
|
+
this.getAttestationKey(slotNumber, proposalId, address),
|
|
59
|
+
);
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
this.log.verbose(`Added attestation for slot ${slotNumber} from ${address}`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
58
64
|
|
|
59
65
|
this.metrics.recordAddedObjects(attestations.length);
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
public getAttestationsForSlot(slot: bigint, proposalId: string): Promise<BlockAttestation[]> {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
return Promise.resolve(attestationsArray);
|
|
68
|
-
}
|
|
68
|
+
public async getAttestationsForSlot(slot: bigint, proposalId: string): Promise<BlockAttestation[]> {
|
|
69
|
+
const attestationIds = await toArray(
|
|
70
|
+
this.attestationsForProposal.getValuesAsync(this.getProposalKey(slot, proposalId)),
|
|
71
|
+
);
|
|
72
|
+
const attestations: BlockAttestation[] = [];
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
const
|
|
74
|
+
// alternatively iterate this.attestaions starting from slot-proposal-EthAddress.zero
|
|
75
|
+
for (const id of attestationIds) {
|
|
76
|
+
const buf = await this.attestations.getAsync(id);
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
olderThan.push(slot);
|
|
78
|
+
if (!buf) {
|
|
79
|
+
// this should not happen unless we lost writes
|
|
80
|
+
throw new Error('Attestation not found ' + id);
|
|
77
81
|
}
|
|
82
|
+
|
|
83
|
+
const attestation = BlockAttestation.fromBuffer(buf);
|
|
84
|
+
attestations.push(attestation);
|
|
78
85
|
}
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
return Promise.resolve();
|
|
87
|
+
return attestations;
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
public async
|
|
85
|
-
const
|
|
90
|
+
public async deleteAttestationsOlderThan(oldestSlot: bigint): Promise<void> {
|
|
91
|
+
const olderThan = await toArray(this.proposalsForSlot.keysAsync({ end: new Fr(oldestSlot).toString() }));
|
|
92
|
+
for (const oldSlot of olderThan) {
|
|
93
|
+
await this.deleteAttestationsForSlot(BigInt(oldSlot));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
86
96
|
|
|
87
|
-
|
|
97
|
+
public async deleteAttestationsForSlot(slot: bigint): Promise<void> {
|
|
98
|
+
const slotFr = new Fr(slot);
|
|
88
99
|
let numberOfAttestations = 0;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (proposalIds) {
|
|
100
|
+
await this.store.transactionAsync(async () => {
|
|
101
|
+
const proposalIds = await toArray(this.proposalsForSlot.getValuesAsync(slotFr.toString()));
|
|
92
102
|
for (const proposalId of proposalIds) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
}
|
|
103
|
+
const attestations = await toArray(
|
|
104
|
+
this.attestationsForProposal.getValuesAsync(this.getProposalKey(slotFr, proposalId)),
|
|
105
|
+
);
|
|
98
106
|
|
|
99
|
-
|
|
107
|
+
numberOfAttestations += attestations.length;
|
|
108
|
+
for (const attestation of attestations) {
|
|
109
|
+
await this.attestations.delete(attestation);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await this.attestationsForProposal.delete(this.getProposalKey(slotFr, proposalId));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
100
115
|
|
|
101
116
|
this.log.verbose(`Removed ${numberOfAttestations} attestations for slot ${slot}`);
|
|
102
117
|
this.metrics.recordRemovedObjects(numberOfAttestations);
|
|
103
|
-
return Promise.resolve();
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
public async deleteAttestationsForSlotAndProposal(slot: bigint, proposalId: string): Promise<void> {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const numberOfAttestations = proposalMap.size();
|
|
119
|
-
deletionPromises.push(proposalMap.clear());
|
|
121
|
+
let numberOfAttestations = 0;
|
|
122
|
+
await this.store.transactionAsync(async () => {
|
|
123
|
+
const slotString = new Fr(slot).toString();
|
|
124
|
+
const attestations = await toArray(
|
|
125
|
+
this.attestationsForProposal.getValuesAsync(this.getProposalKey(slot, proposalId)),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
numberOfAttestations += attestations.length;
|
|
129
|
+
for (const attestation of attestations) {
|
|
130
|
+
await this.attestations.delete(attestation);
|
|
131
|
+
}
|
|
120
132
|
|
|
121
|
-
this.
|
|
122
|
-
this.
|
|
123
|
-
}
|
|
133
|
+
await this.proposalsForSlot.deleteValue(slotString, proposalId);
|
|
134
|
+
await this.attestationsForProposal.delete(this.getProposalKey(slotString, proposalId));
|
|
135
|
+
});
|
|
124
136
|
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
this.log.verbose(`Removed ${numberOfAttestations} attestations for slot ${slot} and proposal ${proposalId}`);
|
|
138
|
+
this.metrics.recordRemovedObjects(numberOfAttestations);
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
public async deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
await this.store.transactionAsync(async () => {
|
|
143
|
+
for (const attestation of attestations) {
|
|
144
|
+
const slotNumber = attestation.payload.header.globalVariables.slotNumber;
|
|
145
|
+
const proposalId = attestation.archive;
|
|
146
|
+
const address = (await attestation.getSender()).toString();
|
|
147
|
+
|
|
148
|
+
await this.attestations.delete(this.getAttestationKey(slotNumber, proposalId, address));
|
|
149
|
+
await this.attestationsForProposal.deleteValue(
|
|
150
|
+
this.getProposalKey(slotNumber, proposalId),
|
|
151
|
+
this.getAttestationKey(slotNumber, proposalId, address),
|
|
152
|
+
);
|
|
136
153
|
|
|
137
|
-
if (proposalMap) {
|
|
138
|
-
const address = attestation.getSender().toString();
|
|
139
|
-
deletionPromises.push(proposalMap.delete(address));
|
|
140
154
|
this.log.debug(`Deleted attestation for slot ${slotNumber} from ${address}`);
|
|
141
155
|
}
|
|
142
|
-
|
|
143
|
-
if (proposalMap.size() === 0) {
|
|
144
|
-
deletionPromises.push(this.attestations.deleteValue(slotNumber, proposalId));
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
await Promise.all(deletionPromises);
|
|
149
|
-
|
|
156
|
+
});
|
|
150
157
|
this.metrics.recordRemovedObjects(attestations.length);
|
|
151
|
-
return Promise.resolve();
|
|
152
158
|
}
|
|
153
159
|
}
|
|
@@ -26,13 +26,13 @@ export class InMemoryAttestationPool implements AttestationPool {
|
|
|
26
26
|
return Promise.resolve([]);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
public addAttestations(attestations: BlockAttestation[]): Promise<void> {
|
|
29
|
+
public async addAttestations(attestations: BlockAttestation[]): Promise<void> {
|
|
30
30
|
for (const attestation of attestations) {
|
|
31
31
|
// Perf: order and group by slot before insertion
|
|
32
32
|
const slotNumber = attestation.payload.header.globalVariables.slotNumber;
|
|
33
33
|
|
|
34
34
|
const proposalId = attestation.archive.toString();
|
|
35
|
-
const address = attestation.getSender();
|
|
35
|
+
const address = await attestation.getSender();
|
|
36
36
|
|
|
37
37
|
const slotAttestationMap = getSlotOrDefault(this.attestations, slotNumber.toBigInt());
|
|
38
38
|
const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
|
|
@@ -105,7 +105,7 @@ export class InMemoryAttestationPool implements AttestationPool {
|
|
|
105
105
|
return Promise.resolve();
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
public deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
|
|
108
|
+
public async deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
|
|
109
109
|
for (const attestation of attestations) {
|
|
110
110
|
const slotNumber = attestation.payload.header.globalVariables.slotNumber;
|
|
111
111
|
const slotAttestationMap = this.attestations.get(slotNumber.toBigInt());
|
|
@@ -113,7 +113,7 @@ export class InMemoryAttestationPool implements AttestationPool {
|
|
|
113
113
|
const proposalId = attestation.archive.toString();
|
|
114
114
|
const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
|
|
115
115
|
if (proposalAttestationMap) {
|
|
116
|
-
const address = attestation.getSender();
|
|
116
|
+
const address = await attestation.getSender();
|
|
117
117
|
proposalAttestationMap.delete(address.toString());
|
|
118
118
|
this.log.debug(`Deleted attestation for slot ${slotNumber} from ${address}`);
|
|
119
119
|
}
|
|
@@ -27,17 +27,17 @@ export const generateAccount = (): LocalAccount => {
|
|
|
27
27
|
* @param slot The slot number the attestation is for
|
|
28
28
|
* @returns A Block Attestation
|
|
29
29
|
*/
|
|
30
|
-
export const mockAttestation = (
|
|
30
|
+
export const mockAttestation = async (
|
|
31
31
|
signer: Secp256k1Signer,
|
|
32
32
|
slot: number = 0,
|
|
33
33
|
archive: Fr = Fr.random(),
|
|
34
34
|
txs: TxHash[] = [0, 1, 2, 3, 4, 5].map(() => TxHash.random()),
|
|
35
|
-
): BlockAttestation => {
|
|
35
|
+
): Promise<BlockAttestation> => {
|
|
36
36
|
// Use arbitrary numbers for all other than slot
|
|
37
37
|
const header = makeHeader(1, 2, slot);
|
|
38
38
|
const payload = new ConsensusPayload(header, archive, txs);
|
|
39
39
|
|
|
40
|
-
const hash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
|
|
40
|
+
const hash = await getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
|
|
41
41
|
const signature = signer.sign(hash);
|
|
42
42
|
|
|
43
43
|
return new BlockAttestation(payload, signature);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Tx, TxHash } from '@aztec/circuit-types';
|
|
2
2
|
import { type TxAddedToPoolStats } from '@aztec/circuit-types/stats';
|
|
3
3
|
import { ClientIvcProof } from '@aztec/circuits.js';
|
|
4
|
+
import { toArray } from '@aztec/foundation/iterable';
|
|
4
5
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
5
|
-
import {
|
|
6
|
+
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
|
|
6
7
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
7
8
|
|
|
8
9
|
import { PoolInstrumentation, PoolName } from '../instrumentation.js';
|
|
@@ -13,25 +14,25 @@ import { type TxPool } from './tx_pool.js';
|
|
|
13
14
|
* KV implementation of the Transaction Pool.
|
|
14
15
|
*/
|
|
15
16
|
export class AztecKVTxPool implements TxPool {
|
|
16
|
-
#store:
|
|
17
|
+
#store: AztecAsyncKVStore;
|
|
17
18
|
|
|
18
19
|
/** Our tx pool, stored as a Map, with K: tx hash and V: the transaction. */
|
|
19
|
-
#txs:
|
|
20
|
+
#txs: AztecAsyncMap<string, Buffer>;
|
|
20
21
|
|
|
21
22
|
/** Index from tx hash to the block number in which they were mined, filtered by mined txs. */
|
|
22
|
-
#minedTxHashToBlock:
|
|
23
|
+
#minedTxHashToBlock: AztecAsyncMap<string, number>;
|
|
23
24
|
|
|
24
25
|
/** Index from tx priority (stored as hex) to its tx hash, filtered by pending txs. */
|
|
25
|
-
#pendingTxPriorityToHash:
|
|
26
|
+
#pendingTxPriorityToHash: AztecAsyncMultiMap<string, string>;
|
|
26
27
|
|
|
27
28
|
/** KV store for archived txs. */
|
|
28
|
-
#archive:
|
|
29
|
+
#archive: AztecAsyncKVStore;
|
|
29
30
|
|
|
30
31
|
/** Archived txs map for future lookup. */
|
|
31
|
-
#archivedTxs:
|
|
32
|
+
#archivedTxs: AztecAsyncMap<string, Buffer>;
|
|
32
33
|
|
|
33
34
|
/** Indexes of the archived txs by insertion order. */
|
|
34
|
-
#archivedTxIndices:
|
|
35
|
+
#archivedTxIndices: AztecAsyncMap<number, string>;
|
|
35
36
|
|
|
36
37
|
/** Number of txs to archive. */
|
|
37
38
|
#archivedTxLimit: number;
|
|
@@ -49,8 +50,8 @@ export class AztecKVTxPool implements TxPool {
|
|
|
49
50
|
* @param log - A logger.
|
|
50
51
|
*/
|
|
51
52
|
constructor(
|
|
52
|
-
store:
|
|
53
|
-
archive:
|
|
53
|
+
store: AztecAsyncKVStore,
|
|
54
|
+
archive: AztecAsyncKVStore,
|
|
54
55
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
55
56
|
archivedTxLimit: number = 0,
|
|
56
57
|
log = createLogger('p2p:tx_pool'),
|
|
@@ -75,16 +76,16 @@ export class AztecKVTxPool implements TxPool {
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
let deletedPending = 0;
|
|
78
|
-
return this.#store.
|
|
79
|
+
return this.#store.transactionAsync(async () => {
|
|
79
80
|
for (const hash of txHashes) {
|
|
80
81
|
const key = hash.toString();
|
|
81
|
-
|
|
82
|
+
await this.#minedTxHashToBlock.set(key, blockNumber);
|
|
82
83
|
|
|
83
|
-
const tx = this.getTxByHash(hash);
|
|
84
|
+
const tx = await this.getTxByHash(hash);
|
|
84
85
|
if (tx) {
|
|
85
86
|
deletedPending++;
|
|
86
87
|
const fee = getPendingTxPriority(tx);
|
|
87
|
-
|
|
88
|
+
await this.#pendingTxPriorityToHash.deleteValue(fee, key);
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
this.#metrics.recordAddedObjects(txHashes.length, 'mined');
|
|
@@ -98,14 +99,14 @@ export class AztecKVTxPool implements TxPool {
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
let markedAsPending = 0;
|
|
101
|
-
return this.#store.
|
|
102
|
+
return this.#store.transactionAsync(async () => {
|
|
102
103
|
for (const hash of txHashes) {
|
|
103
104
|
const key = hash.toString();
|
|
104
|
-
|
|
105
|
+
await this.#minedTxHashToBlock.delete(key);
|
|
105
106
|
|
|
106
|
-
const tx = this.getTxByHash(hash);
|
|
107
|
+
const tx = await this.getTxByHash(hash);
|
|
107
108
|
if (tx) {
|
|
108
|
-
|
|
109
|
+
await this.#pendingTxPriorityToHash.set(getPendingTxPriority(tx), key);
|
|
109
110
|
markedAsPending++;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
@@ -115,22 +116,23 @@ export class AztecKVTxPool implements TxPool {
|
|
|
115
116
|
});
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
public getPendingTxHashes(): TxHash[] {
|
|
119
|
-
|
|
119
|
+
public async getPendingTxHashes(): Promise<TxHash[]> {
|
|
120
|
+
const vals = await toArray(this.#pendingTxPriorityToHash.valuesAsync({ reverse: true }));
|
|
121
|
+
return vals.map(x => TxHash.fromString(x));
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
public getMinedTxHashes(): [TxHash, number][] {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
blockNumber,
|
|
126
|
-
]);
|
|
124
|
+
public async getMinedTxHashes(): Promise<[TxHash, number][]> {
|
|
125
|
+
const vals = await toArray(this.#minedTxHashToBlock.entriesAsync());
|
|
126
|
+
return vals.map(([txHash, blockNumber]) => [TxHash.fromString(txHash), blockNumber]);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
public getTxStatus(txHash: TxHash): 'pending' | 'mined' | undefined {
|
|
129
|
+
public async getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined> {
|
|
130
130
|
const key = txHash.toString();
|
|
131
|
-
|
|
131
|
+
const [isMined, isKnown] = await Promise.all([this.#minedTxHashToBlock.hasAsync(key), this.#txs.hasAsync(key)]);
|
|
132
|
+
|
|
133
|
+
if (isMined) {
|
|
132
134
|
return 'mined';
|
|
133
|
-
} else if (
|
|
135
|
+
} else if (isKnown) {
|
|
134
136
|
return 'pending';
|
|
135
137
|
} else {
|
|
136
138
|
return undefined;
|
|
@@ -142,8 +144,8 @@ export class AztecKVTxPool implements TxPool {
|
|
|
142
144
|
* @param txHash - The generated tx hash.
|
|
143
145
|
* @returns The transaction, if found, 'undefined' otherwise.
|
|
144
146
|
*/
|
|
145
|
-
public getTxByHash(txHash: TxHash): Tx | undefined {
|
|
146
|
-
const buffer = this.#txs.
|
|
147
|
+
public async getTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
148
|
+
const buffer = await this.#txs.getAsync(txHash.toString());
|
|
147
149
|
if (buffer) {
|
|
148
150
|
const tx = Tx.fromBuffer(buffer);
|
|
149
151
|
tx.setTxHash(txHash);
|
|
@@ -157,8 +159,8 @@ export class AztecKVTxPool implements TxPool {
|
|
|
157
159
|
* @param txHash - The tx hash.
|
|
158
160
|
* @returns The transaction metadata, if found, 'undefined' otherwise.
|
|
159
161
|
*/
|
|
160
|
-
public getArchivedTxByHash(txHash: TxHash): Tx | undefined {
|
|
161
|
-
const buffer = this.#archivedTxs.
|
|
162
|
+
public async getArchivedTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
163
|
+
const buffer = await this.#archivedTxs.getAsync(txHash.toString());
|
|
162
164
|
if (buffer) {
|
|
163
165
|
const tx = Tx.fromBuffer(buffer);
|
|
164
166
|
tx.setTxHash(txHash);
|
|
@@ -172,26 +174,31 @@ export class AztecKVTxPool implements TxPool {
|
|
|
172
174
|
* @param txs - An array of txs to be added to the pool.
|
|
173
175
|
* @returns Empty promise.
|
|
174
176
|
*/
|
|
175
|
-
public addTxs(txs: Tx[]): Promise<void> {
|
|
176
|
-
|
|
177
|
+
public async addTxs(txs: Tx[]): Promise<void> {
|
|
178
|
+
const hashesAndStats = await Promise.all(
|
|
179
|
+
txs.map(async tx => ({ txHash: await tx.getTxHash(), txStats: await tx.getStats() })),
|
|
180
|
+
);
|
|
181
|
+
await this.#store.transactionAsync(async () => {
|
|
177
182
|
let pendingCount = 0;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
183
|
+
await Promise.all(
|
|
184
|
+
txs.map(async (tx, i) => {
|
|
185
|
+
const { txHash, txStats } = hashesAndStats[i];
|
|
186
|
+
this.#log.verbose(`Adding tx ${txHash.toString()} to pool`, {
|
|
187
|
+
eventName: 'tx-added-to-pool',
|
|
188
|
+
...txStats,
|
|
189
|
+
} satisfies TxAddedToPoolStats);
|
|
190
|
+
|
|
191
|
+
const key = txHash.toString();
|
|
192
|
+
await this.#txs.set(key, tx.toBuffer());
|
|
193
|
+
|
|
194
|
+
if (!(await this.#minedTxHashToBlock.hasAsync(key))) {
|
|
195
|
+
pendingCount++;
|
|
196
|
+
// REFACTOR: Use an lmdb conditional write to avoid race conditions with this write tx
|
|
197
|
+
await this.#pendingTxPriorityToHash.set(getPendingTxPriority(tx), key);
|
|
198
|
+
this.#metrics.recordSize(tx);
|
|
199
|
+
}
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
195
202
|
|
|
196
203
|
this.#metrics.recordAddedObjects(pendingCount, 'pending');
|
|
197
204
|
});
|
|
@@ -207,16 +214,16 @@ export class AztecKVTxPool implements TxPool {
|
|
|
207
214
|
let minedDeleted = 0;
|
|
208
215
|
|
|
209
216
|
const deletedTxs: Tx[] = [];
|
|
210
|
-
const poolDbTx = this.#store.
|
|
217
|
+
const poolDbTx = this.#store.transactionAsync(async () => {
|
|
211
218
|
for (const hash of txHashes) {
|
|
212
219
|
const key = hash.toString();
|
|
213
|
-
const tx = this.getTxByHash(hash);
|
|
220
|
+
const tx = await this.getTxByHash(hash);
|
|
214
221
|
|
|
215
222
|
if (tx) {
|
|
216
223
|
const fee = getPendingTxPriority(tx);
|
|
217
|
-
|
|
224
|
+
await this.#pendingTxPriorityToHash.deleteValue(fee, key);
|
|
218
225
|
|
|
219
|
-
const isMined = this.#minedTxHashToBlock.
|
|
226
|
+
const isMined = await this.#minedTxHashToBlock.hasAsync(key);
|
|
220
227
|
if (isMined) {
|
|
221
228
|
minedDeleted++;
|
|
222
229
|
} else {
|
|
@@ -227,8 +234,8 @@ export class AztecKVTxPool implements TxPool {
|
|
|
227
234
|
deletedTxs.push(tx);
|
|
228
235
|
}
|
|
229
236
|
|
|
230
|
-
|
|
231
|
-
|
|
237
|
+
await this.#txs.delete(key);
|
|
238
|
+
await this.#minedTxHashToBlock.delete(key);
|
|
232
239
|
}
|
|
233
240
|
}
|
|
234
241
|
|
|
@@ -243,8 +250,9 @@ export class AztecKVTxPool implements TxPool {
|
|
|
243
250
|
* Gets all the transactions stored in the pool.
|
|
244
251
|
* @returns Array of tx objects in the order they were added to the pool.
|
|
245
252
|
*/
|
|
246
|
-
public getAllTxs(): Tx[] {
|
|
247
|
-
|
|
253
|
+
public async getAllTxs(): Promise<Tx[]> {
|
|
254
|
+
const vals = await toArray(this.#txs.entriesAsync());
|
|
255
|
+
return vals.map(([hash, buffer]) => {
|
|
248
256
|
const tx = Tx.fromBuffer(buffer);
|
|
249
257
|
tx.setTxHash(TxHash.fromString(hash));
|
|
250
258
|
return tx;
|
|
@@ -255,8 +263,9 @@ export class AztecKVTxPool implements TxPool {
|
|
|
255
263
|
* Gets the hashes of all transactions currently in the tx pool.
|
|
256
264
|
* @returns An array of transaction hashes found in the tx pool.
|
|
257
265
|
*/
|
|
258
|
-
public getAllTxHashes(): TxHash[] {
|
|
259
|
-
|
|
266
|
+
public async getAllTxHashes(): Promise<TxHash[]> {
|
|
267
|
+
const vals = await toArray(this.#txs.keysAsync());
|
|
268
|
+
return vals.map(x => TxHash.fromString(x));
|
|
260
269
|
}
|
|
261
270
|
|
|
262
271
|
/**
|
|
@@ -264,18 +273,21 @@ export class AztecKVTxPool implements TxPool {
|
|
|
264
273
|
* @param txs - The list of transactions to archive.
|
|
265
274
|
* @returns Empty promise.
|
|
266
275
|
*/
|
|
267
|
-
private archiveTxs(txs: Tx[]): Promise<void> {
|
|
268
|
-
|
|
276
|
+
private async archiveTxs(txs: Tx[]): Promise<void> {
|
|
277
|
+
const txHashes = await Promise.all(txs.map(tx => tx.getTxHash()));
|
|
278
|
+
await this.#archive.transactionAsync(async () => {
|
|
269
279
|
// calcualte the head and tail indices of the archived txs by insertion order.
|
|
270
|
-
let headIdx =
|
|
271
|
-
|
|
280
|
+
let headIdx =
|
|
281
|
+
((await this.#archivedTxIndices.entriesAsync({ limit: 1, reverse: true }).next()).value?.[0] ?? -1) + 1;
|
|
282
|
+
let tailIdx = (await this.#archivedTxIndices.entriesAsync({ limit: 1 }).next()).value?.[0] ?? 0;
|
|
272
283
|
|
|
273
|
-
for (
|
|
284
|
+
for (let i = 0; i < txs.length; i++) {
|
|
285
|
+
const tx = txs[i];
|
|
274
286
|
while (headIdx - tailIdx >= this.#archivedTxLimit) {
|
|
275
|
-
const txHash = this.#archivedTxIndices.
|
|
287
|
+
const txHash = await this.#archivedTxIndices.getAsync(tailIdx);
|
|
276
288
|
if (txHash) {
|
|
277
|
-
|
|
278
|
-
|
|
289
|
+
await this.#archivedTxs.delete(txHash);
|
|
290
|
+
await this.#archivedTxIndices.delete(tailIdx);
|
|
279
291
|
}
|
|
280
292
|
tailIdx++;
|
|
281
293
|
}
|
|
@@ -287,9 +299,9 @@ export class AztecKVTxPool implements TxPool {
|
|
|
287
299
|
tx.enqueuedPublicFunctionCalls,
|
|
288
300
|
tx.publicTeardownFunctionCall,
|
|
289
301
|
);
|
|
290
|
-
const txHash =
|
|
291
|
-
|
|
292
|
-
|
|
302
|
+
const txHash = txHashes[i].toString();
|
|
303
|
+
await this.#archivedTxs.set(txHash, archivedTx.toBuffer());
|
|
304
|
+
await this.#archivedTxIndices.set(headIdx, txHash);
|
|
293
305
|
headIdx++;
|
|
294
306
|
}
|
|
295
307
|
});
|