@aztec/p2p 0.0.1-commit.e310a4c8 → 0.0.1-commit.e558bd1c
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 +3 -3
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +5 -3
- package/dest/client/interface.d.ts +9 -2
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +7 -4
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +17 -7
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
- package/dest/config.d.ts +9 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +3 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +94 -87
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +411 -3
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
- 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 +351 -85
- package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
- package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/index.js +1 -2
- package/dest/mem_pools/index.d.ts +2 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/index.js +1 -1
- package/dest/mem_pools/interface.d.ts +3 -3
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/archive/index.d.ts +2 -0
- package/dest/mem_pools/tx_pool_v2/archive/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/index.js +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +43 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +103 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +47 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +119 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +17 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +90 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +19 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +89 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +10 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +11 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +131 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +17 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +63 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +17 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +91 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +16 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +70 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +20 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +63 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +19 -0
- package/dest/mem_pools/tx_pool_v2/index.d.ts +5 -0
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/index.js +4 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +197 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.js +6 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +71 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +95 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts +26 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.js +70 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +99 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +332 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +55 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +156 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +69 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +748 -0
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/services/dummy_service.d.ts +6 -2
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +3 -0
- package/dest/services/index.d.ts +2 -1
- package/dest/services/index.d.ts.map +1 -1
- package/dest/services/index.js +1 -0
- package/dest/services/libp2p/libp2p_service.d.ts +74 -33
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +299 -228
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +4 -4
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +8 -8
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +6 -4
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +16 -11
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +15 -10
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +12 -11
- package/dest/services/service.d.ts +18 -1
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +3 -3
- package/dest/services/tx_collection/config.js +3 -3
- package/dest/services/tx_collection/fast_tx_collection.d.ts +4 -5
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +10 -14
- package/dest/services/tx_collection/index.d.ts +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +12 -12
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +4 -5
- package/dest/services/tx_file_store/config.d.ts +18 -0
- package/dest/services/tx_file_store/config.d.ts.map +1 -0
- package/dest/services/tx_file_store/config.js +26 -0
- package/dest/services/tx_file_store/index.d.ts +4 -0
- package/dest/services/tx_file_store/index.d.ts.map +1 -0
- package/dest/services/tx_file_store/index.js +3 -0
- package/dest/services/tx_file_store/instrumentation.d.ts +15 -0
- package/dest/services/tx_file_store/instrumentation.d.ts.map +1 -0
- package/dest/services/tx_file_store/instrumentation.js +29 -0
- package/dest/services/tx_file_store/tx_file_store.d.ts +47 -0
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -0
- package/dest/services/tx_file_store/tx_file_store.js +149 -0
- package/dest/test-helpers/testbench-utils.d.ts +10 -16
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +32 -30
- package/dest/testbench/p2p_client_testbench_worker.js +1 -1
- package/package.json +14 -14
- package/src/client/factory.ts +7 -4
- package/src/client/interface.ts +13 -1
- package/src/client/p2p_client.ts +25 -8
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
- package/src/config.ts +8 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +444 -90
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +436 -100
- package/src/mem_pools/attestation_pool/index.ts +9 -2
- package/src/mem_pools/index.ts +1 -1
- package/src/mem_pools/interface.ts +2 -2
- package/src/mem_pools/tx_pool_v2/README.md +209 -0
- package/src/mem_pools/tx_pool_v2/archive/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +120 -0
- package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +147 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +118 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +111 -0
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +23 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +164 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +74 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +101 -0
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +86 -0
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +72 -0
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +31 -0
- package/src/mem_pools/tx_pool_v2/index.ts +11 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +227 -0
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +161 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_bench_metrics.ts +77 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +417 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +212 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +882 -0
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
- package/src/services/dummy_service.ts +6 -0
- package/src/services/index.ts +1 -0
- package/src/services/libp2p/libp2p_service.ts +304 -230
- package/src/services/reqresp/batch-tx-requester/README.md +7 -7
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +11 -11
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +22 -13
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +21 -15
- package/src/services/service.ts +20 -0
- package/src/services/tx_collection/config.ts +6 -6
- package/src/services/tx_collection/fast_tx_collection.ts +14 -24
- package/src/services/tx_collection/index.ts +1 -1
- package/src/services/tx_collection/proposal_tx_collector.ts +12 -14
- package/src/services/tx_file_store/config.ts +43 -0
- package/src/services/tx_file_store/index.ts +3 -0
- package/src/services/tx_file_store/instrumentation.ts +36 -0
- package/src/services/tx_file_store/tx_file_store.ts +173 -0
- package/src/test-helpers/testbench-utils.ts +18 -39
- package/src/testbench/p2p_client_testbench_worker.ts +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
|
@@ -1,52 +1,296 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { toArray } from '@aztec/foundation/iterable';
|
|
4
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
|
|
6
|
+
import {
|
|
3
7
|
BlockProposal,
|
|
4
8
|
CheckpointAttestation,
|
|
5
9
|
CheckpointProposal,
|
|
6
|
-
CheckpointProposalCore,
|
|
10
|
+
type CheckpointProposalCore,
|
|
7
11
|
} from '@aztec/stdlib/p2p';
|
|
12
|
+
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
13
|
+
|
|
14
|
+
import { PoolInstrumentation, PoolName, type PoolStatsCallback } from '../instrumentation.js';
|
|
15
|
+
|
|
16
|
+
/** Result of trying to add an item (proposal or attestation) to the pool */
|
|
17
|
+
export type TryAddResult = {
|
|
18
|
+
/** Whether the item was added */
|
|
19
|
+
added: boolean;
|
|
20
|
+
/** Whether the exact item already existed */
|
|
21
|
+
alreadyExists: boolean;
|
|
22
|
+
/** Total items for this position - used for duplicate detection */
|
|
23
|
+
totalForPosition: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const MAX_PROPOSALS_PER_SLOT = 5;
|
|
27
|
+
export const MAX_PROPOSALS_PER_POSITION = 3;
|
|
28
|
+
export const ATTESTATION_CAP_BUFFER = 10;
|
|
29
|
+
|
|
30
|
+
/** Public API interface for attestation pools. Used for typing mocks and test implementations. */
|
|
31
|
+
export type AttestationPoolApi = Pick<
|
|
32
|
+
AttestationPool,
|
|
33
|
+
| 'tryAddBlockProposal'
|
|
34
|
+
| 'getBlockProposal'
|
|
35
|
+
| 'tryAddCheckpointProposal'
|
|
36
|
+
| 'getCheckpointProposal'
|
|
37
|
+
| 'addOwnCheckpointAttestations'
|
|
38
|
+
| 'tryAddCheckpointAttestation'
|
|
39
|
+
| 'deleteOlderThan'
|
|
40
|
+
| 'getCheckpointAttestationsForSlot'
|
|
41
|
+
| 'getCheckpointAttestationsForSlotAndProposal'
|
|
42
|
+
| 'isEmpty'
|
|
43
|
+
>;
|
|
8
44
|
|
|
9
45
|
/**
|
|
10
|
-
*
|
|
46
|
+
* Pool for storing attestations and proposals collected by a validator.
|
|
11
47
|
*
|
|
12
|
-
* Attestations
|
|
48
|
+
* Attestations and proposals observed via the p2p network are stored for requests
|
|
13
49
|
* from the validator to produce a block, or to serve to other peers.
|
|
14
50
|
*/
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
51
|
+
export class AttestationPool {
|
|
52
|
+
private metrics: PoolInstrumentation<CheckpointAttestation>;
|
|
53
|
+
|
|
54
|
+
// Checkpoint attestations from attestation key (slot-proposalId-signer) to serialized CheckpointAttestation
|
|
55
|
+
// Keys are lexicographically sortable allowing range queries by slot or by (slot, proposalId)
|
|
56
|
+
private checkpointAttestations: AztecAsyncMap<string, Buffer>;
|
|
57
|
+
|
|
58
|
+
// Checkpoint proposals from proposal archive to serialized CheckpointProposal
|
|
59
|
+
private checkpointProposals: AztecAsyncMap<string, Buffer>;
|
|
60
|
+
|
|
61
|
+
// Checkpoint proposals indexed by slot for querying all proposals in a slot
|
|
62
|
+
// Key: slot number, Value: proposal archive strings
|
|
63
|
+
private checkpointProposalsForSlot: AztecAsyncMultiMap<number, string>;
|
|
64
|
+
|
|
65
|
+
// Block proposals from proposal archive to serialized BlockProposal
|
|
66
|
+
private blockProposals: AztecAsyncMap<string, Buffer>;
|
|
67
|
+
|
|
68
|
+
// Block proposals indexed by slot and index-within-checkpoint for duplicate detection
|
|
69
|
+
// Key: (slot << 10) | indexWithinCheckpoint, Value: archive string
|
|
70
|
+
private blockProposalsForSlotAndIndex: AztecAsyncMultiMap<number, string>;
|
|
71
|
+
|
|
72
|
+
constructor(
|
|
73
|
+
private store: AztecAsyncKVStore,
|
|
74
|
+
telemetry: TelemetryClient = getTelemetryClient(),
|
|
75
|
+
private log = createLogger('aztec:attestation_pool'),
|
|
76
|
+
) {
|
|
77
|
+
// Initialize block proposal storage
|
|
78
|
+
this.blockProposals = store.openMap('proposals');
|
|
79
|
+
this.blockProposalsForSlotAndIndex = store.openMultiMap('block_proposals_for_slot_and_index');
|
|
80
|
+
|
|
81
|
+
// Initialize checkpoint attestations storage
|
|
82
|
+
this.checkpointAttestations = store.openMap('checkpoint_attestations');
|
|
83
|
+
|
|
84
|
+
// Initialize checkpoint proposal storage
|
|
85
|
+
this.checkpointProposals = store.openMap('checkpoint_proposals');
|
|
86
|
+
this.checkpointProposalsForSlot = store.openMultiMap('checkpoint_proposals_for_slot');
|
|
87
|
+
|
|
88
|
+
this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL, this.poolStats);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private poolStats: PoolStatsCallback = async () => {
|
|
92
|
+
return {
|
|
93
|
+
itemCount: await this.checkpointAttestations.sizeAsync(),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/** Returns whether the pool is empty. */
|
|
98
|
+
public async isEmpty(): Promise<boolean> {
|
|
99
|
+
for await (const _ of this.checkpointAttestations.entriesAsync()) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
for await (const _ of this.blockProposals.entriesAsync()) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private getProposalKey(slot: number | bigint | Fr | string, proposalId: Fr | string | Buffer): string {
|
|
109
|
+
const slotStr = typeof slot === 'string' ? slot : new Fr(slot).toString();
|
|
110
|
+
const proposalIdStr =
|
|
111
|
+
typeof proposalId === 'string'
|
|
112
|
+
? proposalId
|
|
113
|
+
: Buffer.isBuffer(proposalId)
|
|
114
|
+
? Fr.fromBuffer(proposalId).toString()
|
|
115
|
+
: proposalId.toString();
|
|
116
|
+
|
|
117
|
+
return `${slotStr}-${proposalIdStr}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private getAttestationKey(slot: number | bigint | Fr | string, proposalId: Fr | string, address: string): string {
|
|
121
|
+
return `${this.getProposalKey(slot, proposalId)}-${address}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Returns range bounds for querying all attestations for a given slot. */
|
|
125
|
+
private getAttestationKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
|
|
126
|
+
const slotStr = new Fr(slot).toString();
|
|
127
|
+
return { start: `${slotStr}-`, end: `${slotStr}-Z` }; // 'Z' sorts after any hex character
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Returns range bounds for querying all attestations for a given (slot, proposalId). */
|
|
131
|
+
private getAttestationKeyRangeForProposal(slot: SlotNumber, proposalId: string): { start: string; end: string } {
|
|
132
|
+
const proposalKey = this.getProposalKey(slot, proposalId);
|
|
133
|
+
return { start: `${proposalKey}-`, end: `${proposalKey}-Z` };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Number of bits reserved for indexWithinCheckpoint in position keys. */
|
|
137
|
+
private static readonly INDEX_BITS = 10;
|
|
138
|
+
/** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */
|
|
139
|
+
private static readonly MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
|
|
140
|
+
|
|
141
|
+
/** Creates a position key for block proposals: (slot << 10) | indexWithinCheckpoint. */
|
|
142
|
+
private getBlockPositionKey(slot: number, indexWithinCheckpoint: number): number {
|
|
143
|
+
if (indexWithinCheckpoint > AttestationPool.MAX_INDEX) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Value for indexWithinCheckpoint ${indexWithinCheckpoint} exceeds maximum ${AttestationPool.MAX_INDEX}`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return (slot << AttestationPool.INDEX_BITS) | indexWithinCheckpoint;
|
|
149
|
+
}
|
|
20
150
|
|
|
21
151
|
/**
|
|
22
|
-
*
|
|
152
|
+
* Attempts to add a block proposal to the pool.
|
|
23
153
|
*
|
|
24
|
-
*
|
|
154
|
+
* This method performs validation and addition in a single call:
|
|
155
|
+
* - Checks if the proposal already exists (returns alreadyExists: true if so)
|
|
156
|
+
* - Checks if the position has reached the proposal cap (returns added: false if so)
|
|
157
|
+
* - Adds the proposal if validation passes
|
|
25
158
|
*
|
|
26
|
-
* @
|
|
159
|
+
* @param blockProposal - The block proposal to add
|
|
160
|
+
* @returns Result indicating whether the proposal was added and duplicate detection info
|
|
27
161
|
*/
|
|
28
|
-
|
|
162
|
+
public async tryAddBlockProposal(blockProposal: BlockProposal): Promise<TryAddResult> {
|
|
163
|
+
return await this.store.transactionAsync(async () => {
|
|
164
|
+
const proposalId = blockProposal.archive.toString();
|
|
165
|
+
|
|
166
|
+
// Check if already exists
|
|
167
|
+
const alreadyExists = await this.blockProposals.hasAsync(proposalId);
|
|
168
|
+
if (alreadyExists) {
|
|
169
|
+
const totalForPosition = await this.getBlockProposalCountForPosition(
|
|
170
|
+
blockProposal.slotNumber,
|
|
171
|
+
blockProposal.indexWithinCheckpoint,
|
|
172
|
+
);
|
|
173
|
+
return { added: false, alreadyExists: true, totalForPosition };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Get current count for position and check cap, do not add if exceeded
|
|
177
|
+
const totalForPosition = await this.getBlockProposalCountForPosition(
|
|
178
|
+
blockProposal.slotNumber,
|
|
179
|
+
blockProposal.indexWithinCheckpoint,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (totalForPosition >= MAX_PROPOSALS_PER_POSITION) {
|
|
183
|
+
return { added: false, alreadyExists: false, totalForPosition };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Add the proposal
|
|
187
|
+
await this.addBlockProposal(blockProposal);
|
|
188
|
+
|
|
189
|
+
this.log.debug(
|
|
190
|
+
`Added block proposal for slot ${blockProposal.slotNumber} and index ${blockProposal.indexWithinCheckpoint}`,
|
|
191
|
+
{
|
|
192
|
+
proposalId,
|
|
193
|
+
slotNumber: blockProposal.slotNumber,
|
|
194
|
+
indexWithinCheckpoint: blockProposal.indexWithinCheckpoint,
|
|
195
|
+
},
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return { added: true, alreadyExists: false, totalForPosition: totalForPosition + 1 };
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Gets the count of block proposals for a given position (slot, indexWithinCheckpoint). */
|
|
203
|
+
private getBlockProposalCountForPosition(
|
|
204
|
+
slot: SlotNumber,
|
|
205
|
+
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
206
|
+
): Promise<number> {
|
|
207
|
+
const positionKey = this.getBlockPositionKey(slot, indexWithinCheckpoint);
|
|
208
|
+
return this.blockProposalsForSlotAndIndex.getValueCountAsync(positionKey);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Internal method - must be called within a transaction. */
|
|
212
|
+
private async addBlockProposal(blockProposal: BlockProposal): Promise<void> {
|
|
213
|
+
const proposalId = blockProposal.archive.toString();
|
|
214
|
+
// Strip signedTxs before storing to avoid persisting full tx data
|
|
215
|
+
await this.blockProposals.set(proposalId, blockProposal.withoutSignedTxs().toBuffer());
|
|
216
|
+
|
|
217
|
+
// Index by slot and position for duplicate detection
|
|
218
|
+
const positionKey = this.getBlockPositionKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
|
|
219
|
+
await this.blockProposalsForSlotAndIndex.set(positionKey, proposalId);
|
|
220
|
+
}
|
|
29
221
|
|
|
30
222
|
/**
|
|
31
|
-
*
|
|
223
|
+
* Get block proposal by its ID.
|
|
32
224
|
*
|
|
33
|
-
* @param
|
|
225
|
+
* @param id - The ID of the block proposal to retrieve. The ID is proposal.payload.archive
|
|
34
226
|
*
|
|
35
|
-
* @return
|
|
227
|
+
* @return The block proposal if it exists, otherwise undefined.
|
|
36
228
|
*/
|
|
37
|
-
|
|
229
|
+
public async getBlockProposal(id: string): Promise<BlockProposal | undefined> {
|
|
230
|
+
const buffer = await this.blockProposals.getAsync(id);
|
|
231
|
+
try {
|
|
232
|
+
if (buffer && buffer.length > 0) {
|
|
233
|
+
return BlockProposal.fromBuffer(buffer);
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
38
241
|
|
|
39
242
|
/**
|
|
40
|
-
*
|
|
243
|
+
* Attempts to add a checkpoint proposal to the pool.
|
|
244
|
+
*
|
|
245
|
+
* This method performs validation and addition in a single call:
|
|
246
|
+
* - Checks if the proposal already exists (returns alreadyExists: true if so)
|
|
247
|
+
* - Checks if the slot has reached the proposal cap (returns added: false if so)
|
|
248
|
+
* - Adds the proposal if validation passes
|
|
41
249
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
250
|
+
* Note: This method only handles the CheckpointProposalCore. If the original
|
|
251
|
+
* CheckpointProposal contains a lastBlock, the caller should extract it via
|
|
252
|
+
* getBlockProposal() and add it separately via tryAddBlockProposal().
|
|
45
253
|
*
|
|
46
|
-
* @param proposal - The checkpoint proposal to add
|
|
47
|
-
* @
|
|
254
|
+
* @param proposal - The checkpoint proposal core to add
|
|
255
|
+
* @returns Result indicating whether the proposal was added and duplicate detection info
|
|
48
256
|
*/
|
|
49
|
-
|
|
257
|
+
public async tryAddCheckpointProposal(proposal: CheckpointProposalCore): Promise<TryAddResult> {
|
|
258
|
+
return await this.store.transactionAsync(async () => {
|
|
259
|
+
const proposalId = proposal.archive.toString();
|
|
260
|
+
|
|
261
|
+
// Check if already exists
|
|
262
|
+
const alreadyExists = await this.checkpointProposals.hasAsync(proposalId);
|
|
263
|
+
if (alreadyExists) {
|
|
264
|
+
const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
|
|
265
|
+
return { added: false, alreadyExists: true, totalForPosition };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Get current count for slot and check cap
|
|
269
|
+
const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
|
|
270
|
+
if (totalForPosition >= MAX_PROPOSALS_PER_SLOT) {
|
|
271
|
+
return { added: false, alreadyExists: false, totalForPosition };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Add the proposal if cap not exceeded
|
|
275
|
+
await this.addCheckpointProposal(proposal);
|
|
276
|
+
|
|
277
|
+
this.log.debug(`Added checkpoint proposal for slot ${proposal.slotNumber}`, {
|
|
278
|
+
proposalId,
|
|
279
|
+
slotNumber: proposal.slotNumber,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return { added: true, alreadyExists: false, totalForPosition: totalForPosition + 1 };
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Internal method - must be called within a transaction. */
|
|
287
|
+
private async addCheckpointProposal(proposal: CheckpointProposalCore): Promise<void> {
|
|
288
|
+
const slotKey = proposal.slotNumber;
|
|
289
|
+
const proposalId = proposal.archive.toString();
|
|
290
|
+
|
|
291
|
+
await this.checkpointProposalsForSlot.set(slotKey, proposalId);
|
|
292
|
+
await this.checkpointProposals.set(proposalId, proposal.toBuffer());
|
|
293
|
+
}
|
|
50
294
|
|
|
51
295
|
/**
|
|
52
296
|
* Get checkpoint proposal by its ID.
|
|
@@ -57,101 +301,211 @@ export interface AttestationPool {
|
|
|
57
301
|
* @param id - The ID of the checkpoint proposal to retrieve (proposal.archive)
|
|
58
302
|
* @return The checkpoint proposal core if it exists, otherwise undefined.
|
|
59
303
|
*/
|
|
60
|
-
getCheckpointProposal(id: string): Promise<CheckpointProposalCore | undefined
|
|
304
|
+
public async getCheckpointProposal(id: string): Promise<CheckpointProposalCore | undefined> {
|
|
305
|
+
const buffer = await this.checkpointProposals.getAsync(id);
|
|
306
|
+
try {
|
|
307
|
+
if (buffer && buffer.length > 0) {
|
|
308
|
+
return CheckpointProposal.fromBuffer(buffer);
|
|
309
|
+
}
|
|
310
|
+
} catch {
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
61
313
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*
|
|
65
|
-
* @param idOrProposal - The ID of the checkpoint proposal or the proposal itself
|
|
66
|
-
* @return True if the proposal exists, false otherwise.
|
|
67
|
-
*/
|
|
68
|
-
hasCheckpointProposal(idOrProposal: string | CheckpointProposal): Promise<boolean>;
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
69
316
|
|
|
70
317
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @param attestations - Checkpoint attestations to add into the pool
|
|
318
|
+
* Adds own checkpoint attestations to the pool.
|
|
319
|
+
* Skips validations on number of checkpoint attestations stored for the given slot.
|
|
74
320
|
*/
|
|
75
|
-
|
|
321
|
+
public async addOwnCheckpointAttestations(attestations: CheckpointAttestation[]): Promise<void> {
|
|
322
|
+
await this.store.transactionAsync(async () => {
|
|
323
|
+
for (const attestation of attestations) {
|
|
324
|
+
const slotNumber = attestation.payload.header.slotNumber;
|
|
325
|
+
const proposalId = attestation.archive;
|
|
326
|
+
const sender = attestation.getSender();
|
|
76
327
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
328
|
+
// Skip attestations with invalid signatures
|
|
329
|
+
if (!sender) {
|
|
330
|
+
this.log.warn(`Skipping own checkpoint attestation with invalid signature for slot ${slotNumber}`, {
|
|
331
|
+
signature: attestation.signature.toString(),
|
|
332
|
+
slotNumber,
|
|
333
|
+
proposalId,
|
|
334
|
+
});
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const address = sender.toString();
|
|
339
|
+
|
|
340
|
+
await this.checkpointAttestations.set(
|
|
341
|
+
this.getAttestationKey(slotNumber, proposalId, address),
|
|
342
|
+
attestation.toBuffer(),
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
this.log.debug(`Added own checkpoint attestation for slot ${slotNumber} from ${address}`, {
|
|
346
|
+
signature: attestation.signature.toString(),
|
|
347
|
+
slotNumber,
|
|
348
|
+
address,
|
|
349
|
+
proposalId,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
83
354
|
|
|
84
355
|
/**
|
|
85
|
-
* Get all checkpoint attestations for a given slot
|
|
356
|
+
* Get all checkpoint attestations for a given slot.
|
|
86
357
|
*
|
|
87
358
|
* @param slot - The slot to query
|
|
88
359
|
* @return CheckpointAttestations
|
|
89
360
|
*/
|
|
90
|
-
getCheckpointAttestationsForSlot(slot: SlotNumber): Promise<CheckpointAttestation[]
|
|
361
|
+
public async getCheckpointAttestationsForSlot(slot: SlotNumber): Promise<CheckpointAttestation[]> {
|
|
362
|
+
const range = this.getAttestationKeyRangeForSlot(slot);
|
|
363
|
+
const attestations: CheckpointAttestation[] = [];
|
|
364
|
+
|
|
365
|
+
for await (const [_, buf] of this.checkpointAttestations.entriesAsync(range)) {
|
|
366
|
+
attestations.push(CheckpointAttestation.fromBuffer(buf));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return attestations;
|
|
370
|
+
}
|
|
91
371
|
|
|
92
372
|
/**
|
|
93
|
-
* Get checkpoint attestations for slot and given proposal
|
|
373
|
+
* Get checkpoint attestations for slot and given proposal.
|
|
94
374
|
*
|
|
95
375
|
* @param slot - The slot to query
|
|
96
376
|
* @param proposalId - The proposal to query
|
|
97
377
|
* @return CheckpointAttestations
|
|
98
378
|
*/
|
|
99
|
-
getCheckpointAttestationsForSlotAndProposal(
|
|
379
|
+
public async getCheckpointAttestationsForSlotAndProposal(
|
|
380
|
+
slot: SlotNumber,
|
|
381
|
+
proposalId: string,
|
|
382
|
+
): Promise<CheckpointAttestation[]> {
|
|
383
|
+
const range = this.getAttestationKeyRangeForProposal(slot, proposalId);
|
|
384
|
+
const attestations: CheckpointAttestation[] = [];
|
|
100
385
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
* @param attestation - The attestation to check
|
|
105
|
-
* @return True if the attestation exists, false otherwise
|
|
106
|
-
*/
|
|
107
|
-
hasCheckpointAttestation(attestation: CheckpointAttestation): Promise<boolean>;
|
|
386
|
+
for await (const [_, buf] of this.checkpointAttestations.entriesAsync(range)) {
|
|
387
|
+
attestations.push(CheckpointAttestation.fromBuffer(buf));
|
|
388
|
+
}
|
|
108
389
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
* - True if the proposal already exists, allow overwrite to keep parity with tests.
|
|
112
|
-
* - True if the slot is below the proposal cap.
|
|
113
|
-
* - False if the slot is at/above cap and this would be a new unique proposal.
|
|
114
|
-
*
|
|
115
|
-
* @param block - The block proposal to check
|
|
116
|
-
* @returns True if the proposal can be added (or already exists), false otherwise.
|
|
117
|
-
*/
|
|
118
|
-
canAddProposal(block: BlockProposal): Promise<boolean>;
|
|
390
|
+
return attestations;
|
|
391
|
+
}
|
|
119
392
|
|
|
120
393
|
/**
|
|
121
|
-
*
|
|
394
|
+
* Delete all pool data (attestations, proposals) older than the given slot.
|
|
122
395
|
*
|
|
123
|
-
* @param
|
|
124
|
-
* @returns True if the proposal can be added, false otherwise.
|
|
396
|
+
* @param oldestSlot - The oldest slot to keep.
|
|
125
397
|
*/
|
|
126
|
-
|
|
398
|
+
public async deleteOlderThan(oldestSlot: SlotNumber): Promise<void> {
|
|
399
|
+
let numberOfAttestations = 0;
|
|
400
|
+
let numberOfCheckpointProposals = 0;
|
|
401
|
+
let numberOfBlockProposals = 0;
|
|
402
|
+
|
|
403
|
+
await this.store.transactionAsync(async () => {
|
|
404
|
+
// Delete checkpoint attestations with slot < oldestSlot
|
|
405
|
+
// Attestation keys start with Fr(slot).toString(), so we use end bound of Fr(oldestSlot).toString()
|
|
406
|
+
const attestationEndKey = new Fr(oldestSlot).toString();
|
|
407
|
+
for await (const key of this.checkpointAttestations.keysAsync({ end: attestationEndKey })) {
|
|
408
|
+
await this.checkpointAttestations.delete(key);
|
|
409
|
+
numberOfAttestations++;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Delete checkpoint proposals for slots < oldestSlot, using checkpointProposalsForSlot as index
|
|
413
|
+
for await (const slot of this.checkpointProposalsForSlot.keysAsync({ end: oldestSlot })) {
|
|
414
|
+
const proposalIds = await toArray(this.checkpointProposalsForSlot.getValuesAsync(slot));
|
|
415
|
+
for (const proposalId of proposalIds) {
|
|
416
|
+
await this.checkpointProposals.delete(proposalId);
|
|
417
|
+
numberOfCheckpointProposals++;
|
|
418
|
+
}
|
|
419
|
+
await this.checkpointProposalsForSlot.delete(slot);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Delete block proposals for slots < oldestSlot, using blockProposalsForSlotAndIndex as index
|
|
423
|
+
// Key format: (slot << INDEX_BITS) | indexWithinCheckpoint
|
|
424
|
+
const blockPositionEndKey = oldestSlot << AttestationPool.INDEX_BITS;
|
|
425
|
+
for await (const positionKey of this.blockProposalsForSlotAndIndex.keysAsync({ end: blockPositionEndKey })) {
|
|
426
|
+
const proposalIds = await toArray(this.blockProposalsForSlotAndIndex.getValuesAsync(positionKey));
|
|
427
|
+
for (const proposalId of proposalIds) {
|
|
428
|
+
await this.blockProposals.delete(proposalId);
|
|
429
|
+
numberOfBlockProposals++;
|
|
430
|
+
}
|
|
431
|
+
await this.blockProposalsForSlotAndIndex.delete(positionKey);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
this.log.verbose(`Deleted old pool data`, {
|
|
436
|
+
oldestSlot,
|
|
437
|
+
numberOfAttestations,
|
|
438
|
+
numberOfCheckpointProposals,
|
|
439
|
+
numberOfBlockProposals,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
127
442
|
|
|
128
443
|
/**
|
|
129
|
-
*
|
|
444
|
+
* Attempts to add a checkpoint attestation to the pool.
|
|
130
445
|
*
|
|
131
|
-
*
|
|
446
|
+
* This method performs validation and addition in a single call:
|
|
447
|
+
* - Checks if the attestation already exists (returns alreadyExists: true if so)
|
|
448
|
+
* - Checks if the (slot, proposalId) has reached the attestation cap (returns added: false if so)
|
|
449
|
+
* - Adds the attestation if validation passes
|
|
450
|
+
*
|
|
451
|
+
* @param attestation - The checkpoint attestation to add
|
|
132
452
|
* @param committeeSize - Committee size for the attestation's slot
|
|
133
|
-
* @returns
|
|
453
|
+
* @returns Result indicating whether the attestation was added and existence info
|
|
134
454
|
*/
|
|
135
|
-
|
|
455
|
+
public async tryAddCheckpointAttestation(
|
|
456
|
+
attestation: CheckpointAttestation,
|
|
457
|
+
committeeSize: number,
|
|
458
|
+
): Promise<TryAddResult> {
|
|
459
|
+
const slotNumber = attestation.payload.header.slotNumber;
|
|
460
|
+
const proposalId = attestation.archive.toString();
|
|
461
|
+
const sender = attestation.getSender();
|
|
136
462
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
* @param slot - The slot to check
|
|
141
|
-
* @returns True if the cap has been reached, false otherwise.
|
|
142
|
-
*/
|
|
143
|
-
hasReachedCheckpointProposalCap(slot: SlotNumber): Promise<boolean>;
|
|
463
|
+
if (!sender) {
|
|
464
|
+
return { added: false, alreadyExists: false, totalForPosition: 0 };
|
|
465
|
+
}
|
|
144
466
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
* @param slot - The slot to check
|
|
149
|
-
* @param proposalId - The proposal to check
|
|
150
|
-
* @param committeeSize - Committee size for the slot
|
|
151
|
-
* @returns True if the cap has been reached, false otherwise.
|
|
152
|
-
*/
|
|
153
|
-
hasReachedCheckpointAttestationCap(slot: SlotNumber, proposalId: string, committeeSize: number): Promise<boolean>;
|
|
467
|
+
return await this.store.transactionAsync(async () => {
|
|
468
|
+
const key = this.getAttestationKey(slotNumber, proposalId, sender.toString());
|
|
469
|
+
const alreadyExists = await this.checkpointAttestations.hasAsync(key);
|
|
154
470
|
|
|
155
|
-
|
|
156
|
-
|
|
471
|
+
if (alreadyExists) {
|
|
472
|
+
const total = await this.getAttestationCount(slotNumber, proposalId);
|
|
473
|
+
return { added: false, alreadyExists: true, totalForPosition: total };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const limit = committeeSize + ATTESTATION_CAP_BUFFER;
|
|
477
|
+
const currentCount = await this.getAttestationCount(slotNumber, proposalId);
|
|
478
|
+
|
|
479
|
+
if (currentCount >= limit) {
|
|
480
|
+
return { added: false, alreadyExists: false, totalForPosition: currentCount };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
await this.checkpointAttestations.set(key, attestation.toBuffer());
|
|
484
|
+
|
|
485
|
+
this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${sender.toString()}`, {
|
|
486
|
+
signature: attestation.signature.toString(),
|
|
487
|
+
slotNumber,
|
|
488
|
+
address: sender.toString(),
|
|
489
|
+
proposalId,
|
|
490
|
+
});
|
|
491
|
+
return { added: true, alreadyExists: false, totalForPosition: currentCount + 1 };
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Gets the count of attestations for a given (slot, proposalId). */
|
|
496
|
+
private async getAttestationCount(slot: SlotNumber, proposalId: string): Promise<number> {
|
|
497
|
+
const range = this.getAttestationKeyRangeForProposal(slot, proposalId);
|
|
498
|
+
let count = 0;
|
|
499
|
+
for await (const _ of this.checkpointAttestations.keysAsync(range)) {
|
|
500
|
+
count++;
|
|
501
|
+
}
|
|
502
|
+
return count;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** Creates an AttestationPool backed by a temporary store for testing. */
|
|
507
|
+
export async function createTestAttestationPool(telemetry?: TelemetryClient): Promise<AttestationPool> {
|
|
508
|
+
const { openTmpStore } = await import('@aztec/kv-store/lmdb-v2');
|
|
509
|
+
const store = await openTmpStore('test-attestation-pool');
|
|
510
|
+
return new AttestationPool(store, telemetry);
|
|
157
511
|
}
|