@aztec/p2p 5.0.0-private.20260319 → 5.0.0-rc.2
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/README.md +4 -1
- package/dest/bootstrap/bootstrap.d.ts +1 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +22 -6
- package/dest/client/factory.d.ts +4 -3
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +33 -24
- package/dest/client/interface.d.ts +43 -11
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +33 -19
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +100 -44
- package/dest/config.d.ts +137 -107
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +64 -23
- package/dest/errors/p2p-service.error.d.ts +9 -0
- package/dest/errors/p2p-service.error.d.ts.map +1 -0
- package/dest/errors/p2p-service.error.js +10 -0
- package/dest/errors/reqresp.error.d.ts +1 -20
- package/dest/errors/reqresp.error.d.ts.map +1 -1
- package/dest/errors/reqresp.error.js +0 -21
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +98 -56
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +265 -196
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +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 +175 -59
- 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 +6 -4
- package/dest/mem_pools/index.d.ts +1 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.d.ts +1 -1
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +17 -1
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +1 -5
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +1 -7
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +6 -6
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +62 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +22 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +13 -6
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +7 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +10 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +8 -3
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +7 -4
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +8 -3
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +153 -111
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -16
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +8 -3
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +8 -3
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +25 -3
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +77 -14
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +8 -15
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +3 -3
- package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
- package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +36 -2
- package/dest/msg_validators/tx_validator/factory.d.ts +14 -15
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +36 -17
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +47 -10
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +65 -48
- package/dest/msg_validators/tx_validator/index.d.ts +3 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +2 -0
- package/dest/msg_validators/tx_validator/phases_validator.js +3 -3
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +5 -5
- package/dest/services/discv5/discV5_service.d.ts +13 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +119 -23
- package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
- package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
- package/dest/services/discv5/persisted_enr_store.js +145 -0
- package/dest/services/dummy_service.d.ts +15 -15
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +15 -16
- package/dest/services/encoding.d.ts +8 -8
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +11 -15
- package/dest/services/gossipsub/topic_score_params.d.ts +6 -12
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +4 -14
- package/dest/services/libp2p/instrumentation.d.ts +3 -1
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
- package/dest/services/libp2p/instrumentation.js +14 -0
- package/dest/services/libp2p/libp2p_service.d.ts +44 -39
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +383 -278
- package/dest/services/peer-manager/peer_manager.d.ts +20 -3
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +79 -23
- package/dest/services/peer-manager/peer_scoring.d.ts +34 -2
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +102 -13
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
- 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 +38 -44
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +8 -5
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
- package/dest/services/reqresp/config.d.ts +3 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/interface.d.ts +16 -18
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +10 -20
- package/dest/services/reqresp/metrics.d.ts +1 -1
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/metrics.js +0 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
- 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 +31 -28
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
- 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 +42 -24
- package/dest/services/reqresp/protocols/index.d.ts +1 -2
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +0 -1
- package/dest/services/reqresp/protocols/tx.d.ts +1 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +1 -3
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
- package/dest/services/reqresp/reqresp.d.ts +7 -29
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +26 -207
- package/dest/services/service.d.ts +25 -12
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +2 -23
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +2 -55
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +12 -28
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_collection.js +43 -83
- package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.js +39 -29
- package/dest/services/tx_collection/index.d.ts +2 -3
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +0 -1
- package/dest/services/tx_collection/instrumentation.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
- package/dest/services/tx_collection/instrumentation.js +0 -2
- package/dest/services/tx_collection/tx_collection.d.ts +35 -51
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +275 -119
- package/dest/services/tx_collection/tx_collection_sink.d.ts +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +2 -2
- package/dest/services/tx_collection/tx_source.d.ts +6 -5
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +13 -8
- package/dest/services/tx_file_store/tx_file_store.d.ts +1 -3
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
- package/dest/services/tx_file_store/tx_file_store.js +4 -14
- package/dest/services/tx_provider.d.ts +3 -1
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +3 -0
- package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +5 -2
- package/dest/test-helpers/mock-pubsub.d.ts +23 -9
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +44 -44
- package/dest/test-helpers/reqresp-nodes.d.ts +4 -5
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +16 -18
- package/dest/test-helpers/test_tx_provider.d.ts +3 -1
- package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
- package/dest/test-helpers/test_tx_provider.js +3 -0
- package/dest/test-helpers/testbench-utils.d.ts +14 -14
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +25 -13
- package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +102 -41
- package/dest/testbench/worker_client_manager.d.ts +10 -6
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +51 -9
- package/dest/util.d.ts +11 -6
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +35 -14
- package/dest/versioning.d.ts +3 -6
- package/dest/versioning.d.ts.map +1 -1
- package/dest/versioning.js +3 -24
- package/package.json +16 -16
- package/src/bootstrap/bootstrap.ts +17 -3
- package/src/client/factory.ts +52 -24
- package/src/client/interface.ts +38 -17
- package/src/client/p2p_client.ts +118 -76
- package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
- package/src/config.ts +130 -31
- package/src/errors/p2p-service.error.ts +11 -0
- package/src/errors/reqresp.error.ts +0 -25
- package/src/index.ts +0 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +319 -238
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +198 -62
- package/src/mem_pools/attestation_pool/mocks.ts +13 -8
- package/src/mem_pools/index.ts +0 -3
- package/src/mem_pools/instrumentation.ts +5 -1
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +1 -9
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +7 -7
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +65 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/interfaces.ts +18 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +20 -8
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +12 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +8 -4
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +163 -117
- package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -15
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
- package/src/msg_validators/proposal_validator/README.md +1 -1
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +15 -3
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +26 -3
- package/src/msg_validators/proposal_validator/proposal_validator.ts +89 -11
- package/src/msg_validators/tx_validator/README.md +13 -3
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -12
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +3 -3
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
- package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
- package/src/msg_validators/tx_validator/data_validator.ts +44 -1
- package/src/msg_validators/tx_validator/factory.ts +44 -20
- package/src/msg_validators/tx_validator/gas_validator.ts +99 -54
- package/src/msg_validators/tx_validator/index.ts +2 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +3 -3
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
- package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
- package/src/services/data_store.ts +5 -13
- package/src/services/discv5/discV5_service.ts +126 -14
- package/src/services/discv5/persisted_enr_store.ts +158 -0
- package/src/services/dummy_service.ts +20 -39
- package/src/services/encoding.ts +11 -21
- package/src/services/gossipsub/README.md +33 -11
- package/src/services/gossipsub/topic_score_params.ts +9 -18
- package/src/services/libp2p/instrumentation.ts +14 -0
- package/src/services/libp2p/libp2p_service.ts +436 -311
- package/src/services/peer-manager/peer_manager.ts +86 -22
- package/src/services/peer-manager/peer_scoring.ts +102 -8
- package/src/services/reqresp/README.md +0 -14
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +42 -52
- package/src/services/reqresp/batch-tx-requester/interface.ts +11 -4
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
- package/src/services/reqresp/config.ts +2 -2
- package/src/services/reqresp/interface.ts +21 -47
- package/src/services/reqresp/metrics.ts +0 -1
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +34 -30
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
- package/src/services/reqresp/protocols/index.ts +0 -1
- package/src/services/reqresp/protocols/tx.ts +1 -3
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
- package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
- package/src/services/reqresp/reqresp.ts +29 -252
- package/src/services/service.ts +31 -28
- package/src/services/tx_collection/config.ts +3 -80
- package/src/services/tx_collection/file_store_tx_collection.ts +54 -103
- package/src/services/tx_collection/file_store_tx_source.ts +43 -31
- package/src/services/tx_collection/index.ts +1 -6
- package/src/services/tx_collection/instrumentation.ts +1 -7
- package/src/services/tx_collection/tx_collection.ts +330 -173
- package/src/services/tx_collection/tx_collection_sink.ts +2 -2
- package/src/services/tx_collection/tx_source.ts +10 -8
- package/src/services/tx_file_store/tx_file_store.ts +5 -17
- package/src/services/tx_provider.ts +5 -0
- package/src/test-helpers/make-test-p2p-clients.ts +3 -0
- package/src/test-helpers/mock-pubsub.ts +46 -60
- package/src/test-helpers/reqresp-nodes.ts +12 -22
- package/src/test-helpers/test_tx_provider.ts +5 -0
- package/src/test-helpers/testbench-utils.ts +31 -26
- package/src/testbench/p2p_client_testbench_worker.ts +110 -55
- package/src/testbench/worker_client_manager.ts +59 -20
- package/src/util.ts +32 -17
- package/src/versioning.ts +3 -33
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -305
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -123
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
- package/dest/mem_pools/tx_pool/index.d.ts +0 -3
- package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/index.js +0 -2
- package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
- package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/priority.js +0 -15
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
- package/dest/msg_validators/clock_tolerance.d.ts +0 -21
- package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
- package/dest/msg_validators/clock_tolerance.js +0 -38
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
- package/dest/services/reqresp/protocols/block.d.ts +0 -9
- package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/block.js +0 -32
- package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -51
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/fast_tx_collection.js +0 -311
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
- package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -57
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/slow_tx_collection.js +0 -211
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -344
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
- package/src/mem_pools/tx_pool/README.md +0 -270
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
- package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
- package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
- package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
- package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
- package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
- package/src/mem_pools/tx_pool/index.ts +0 -2
- package/src/mem_pools/tx_pool/priority.ts +0 -20
- package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
- package/src/msg_validators/clock_tolerance.ts +0 -52
- package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
- package/src/services/reqresp/protocols/block.ts +0 -37
- package/src/services/tx_collection/fast_tx_collection.ts +0 -361
- package/src/services/tx_collection/proposal_tx_collector.ts +0 -108
- package/src/services/tx_collection/slow_tx_collection.ts +0 -266
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
1
|
+
import type { BlockProposalHash, CheckpointProposalHash, SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
2
|
import { toArray } from '@aztec/foundation/iterable';
|
|
4
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
4
|
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
|
|
@@ -15,17 +14,22 @@ import { PoolInstrumentation, PoolName, type PoolStatsCallback } from '../instru
|
|
|
15
14
|
|
|
16
15
|
/** Result of trying to add an item (proposal or attestation) to the pool */
|
|
17
16
|
export type TryAddResult = {
|
|
18
|
-
/** Whether the item was
|
|
17
|
+
/** Whether the item was accepted into pool state. False when it already existed, was invalid, or hit a cap. */
|
|
19
18
|
added: boolean;
|
|
20
|
-
/** Whether the exact
|
|
19
|
+
/** Whether the exact signed payload (matched by payload hash) already existed in the pool. */
|
|
21
20
|
alreadyExists: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* - tryAddBlockProposal:
|
|
24
|
-
* - tryAddCheckpointProposal:
|
|
25
|
-
* - tryAddCheckpointAttestation:
|
|
21
|
+
/** Number of distinct signed-payload hashes seen for the position. Meaning varies by method:
|
|
22
|
+
* - tryAddBlockProposal: distinct payload hashes at (slot, indexWithinCheckpoint)
|
|
23
|
+
* - tryAddCheckpointProposal: distinct payload hashes at slot
|
|
24
|
+
* - tryAddCheckpointAttestation: distinct payload hashes by this signer for this slot */
|
|
26
25
|
count: number;
|
|
27
26
|
};
|
|
28
27
|
|
|
28
|
+
export type ProposalsForSlot = {
|
|
29
|
+
blockProposals: BlockProposal[];
|
|
30
|
+
checkpointProposals: CheckpointProposalCore[];
|
|
31
|
+
};
|
|
32
|
+
|
|
29
33
|
export const MAX_CHECKPOINT_PROPOSALS_PER_SLOT = 2;
|
|
30
34
|
export const MAX_BLOCK_PROPOSALS_PER_POSITION = 2;
|
|
31
35
|
/** Maximum attestations a single signer can make per slot before being rejected. */
|
|
@@ -35,9 +39,11 @@ export const MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER = 2;
|
|
|
35
39
|
export type AttestationPoolApi = Pick<
|
|
36
40
|
AttestationPool,
|
|
37
41
|
| 'tryAddBlockProposal'
|
|
38
|
-
| '
|
|
42
|
+
| 'getBlockProposalByArchive'
|
|
43
|
+
| 'getProposalsForSlot'
|
|
39
44
|
| 'tryAddCheckpointProposal'
|
|
40
45
|
| 'getCheckpointProposal'
|
|
46
|
+
| 'hasCheckpointProposalForSlot'
|
|
41
47
|
| 'addOwnCheckpointAttestations'
|
|
42
48
|
| 'tryAddCheckpointAttestation'
|
|
43
49
|
| 'deleteOlderThan'
|
|
@@ -52,31 +58,45 @@ export type AttestationPoolApi = Pick<
|
|
|
52
58
|
*
|
|
53
59
|
* Attestations and proposals observed via the p2p network are stored for requests
|
|
54
60
|
* from the validator to produce a block, or to serve to other peers.
|
|
61
|
+
*
|
|
62
|
+
* Equivocation detection: distinct *signed payload hashes* arriving at the same
|
|
63
|
+
* position are tracked in the matching index multimap so the equivocation count
|
|
64
|
+
* reaches 2 even when archive collides on `feeAssetPriceModifier` variants.
|
|
65
|
+
* Proposal bytes are retained per accepted payload hash, up to the same equivocation
|
|
66
|
+
* caps, for slashing watchers that need signed P2P proposals.
|
|
55
67
|
*/
|
|
56
68
|
export class AttestationPool {
|
|
57
69
|
private metrics: PoolInstrumentation<CheckpointAttestation>;
|
|
58
70
|
|
|
59
|
-
// Checkpoint attestations from
|
|
60
|
-
//
|
|
61
|
-
|
|
71
|
+
// Checkpoint attestations from `${paddedSlot}-${signer}` to serialized CheckpointAttestation.
|
|
72
|
+
// Stores the first attestation seen per (slot, signer); subsequent distinct payload
|
|
73
|
+
// hashes from the same signer are tracked only in `attestationHashesPerSlotAndSigner`
|
|
74
|
+
// for equivocation detection.
|
|
75
|
+
private attestationPerSlotAndSigner: AztecAsyncMap<string, Buffer>;
|
|
62
76
|
|
|
63
|
-
//
|
|
64
|
-
|
|
77
|
+
// Distinct payload hashes seen per (slot, signer) for tracking attestation equivocations.
|
|
78
|
+
// Key: `${paddedSlot}-${signerAddress}`, Value: CheckpointProposalHash (`0x`-prefixed hex)
|
|
79
|
+
private attestationHashesPerSlotAndSigner: AztecAsyncMultiMap<string, CheckpointProposalHash>;
|
|
65
80
|
|
|
66
|
-
// Checkpoint proposals
|
|
67
|
-
//
|
|
68
|
-
private
|
|
81
|
+
// Checkpoint proposals from `${paddedSlot}-${payloadHash}` to serialized CheckpointProposalCore.
|
|
82
|
+
// Stores every accepted distinct payload up to MAX_CHECKPOINT_PROPOSALS_PER_SLOT.
|
|
83
|
+
private checkpointProposalsPerSlotAndHash: AztecAsyncMap<string, Buffer>;
|
|
69
84
|
|
|
70
|
-
//
|
|
71
|
-
|
|
85
|
+
// Distinct payload hashes seen per slot. Hash collision = duplicate.
|
|
86
|
+
// Hash count reaching 2 = equivocation.
|
|
87
|
+
// Key: slot number, Value: CheckpointProposalHash (`0x`-prefixed hex)
|
|
88
|
+
private checkpointProposalHashesPerSlot: AztecAsyncMultiMap<number, CheckpointProposalHash>;
|
|
72
89
|
|
|
73
|
-
// Block proposals
|
|
74
|
-
//
|
|
75
|
-
private
|
|
90
|
+
// Block proposals from `${paddedSlot}-${paddedIndex}-${payloadHash}` to serialized BlockProposal.
|
|
91
|
+
// Stores every accepted distinct payload up to MAX_BLOCK_PROPOSALS_PER_POSITION.
|
|
92
|
+
private blockProposalsPerSlotIndexAndHash: AztecAsyncMap<string, Buffer>;
|
|
76
93
|
|
|
77
|
-
//
|
|
78
|
-
// Key:
|
|
79
|
-
private
|
|
94
|
+
// Distinct payload hashes seen per (slot, indexWithinCheckpoint).
|
|
95
|
+
// Key: slot * (1 << INDEX_BITS) + indexWithinCheckpoint, Value: BlockProposalHash (`0x`-prefixed hex)
|
|
96
|
+
private blockProposalHashesPerSlotAndIndex: AztecAsyncMultiMap<number, BlockProposalHash>;
|
|
97
|
+
|
|
98
|
+
// Secondary index from archive root to all retained block proposal keys.
|
|
99
|
+
private blockProposalKeysPerArchive: AztecAsyncMultiMap<string, string>;
|
|
80
100
|
|
|
81
101
|
constructor(
|
|
82
102
|
private store: AztecAsyncKVStore,
|
|
@@ -84,128 +104,159 @@ export class AttestationPool {
|
|
|
84
104
|
private log = createLogger('aztec:attestation_pool'),
|
|
85
105
|
) {
|
|
86
106
|
// Initialize block proposal storage
|
|
87
|
-
this.
|
|
88
|
-
this.
|
|
107
|
+
this.blockProposalsPerSlotIndexAndHash = store.openMap('block_proposals_by_slot_index_and_hash');
|
|
108
|
+
this.blockProposalHashesPerSlotAndIndex = store.openMultiMap('block_proposals_for_slot_and_index');
|
|
109
|
+
this.blockProposalKeysPerArchive = store.openMultiMap('block_proposals_by_archive');
|
|
89
110
|
|
|
90
111
|
// Initialize checkpoint attestations storage
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
112
|
+
this.attestationPerSlotAndSigner = store.openMap('checkpoint_attestations');
|
|
113
|
+
this.attestationHashesPerSlotAndSigner = store.openMultiMap('checkpoint_attestations_per_slot_and_signer');
|
|
93
114
|
|
|
94
115
|
// Initialize checkpoint proposal storage
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
116
|
+
this.checkpointProposalsPerSlotAndHash = store.openMap('checkpoint_proposals_by_slot_and_hash');
|
|
117
|
+
this.checkpointProposalHashesPerSlot = store.openMultiMap('checkpoint_proposals_for_slot');
|
|
97
118
|
|
|
98
119
|
this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL, this.poolStats);
|
|
99
120
|
}
|
|
100
121
|
|
|
101
122
|
private poolStats: PoolStatsCallback = async () => {
|
|
102
123
|
return {
|
|
103
|
-
itemCount: await this.
|
|
124
|
+
itemCount: await this.attestationPerSlotAndSigner.sizeAsync(),
|
|
104
125
|
};
|
|
105
126
|
};
|
|
106
127
|
|
|
107
128
|
/** Returns whether the pool is empty. */
|
|
108
129
|
public async isEmpty(): Promise<boolean> {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return
|
|
130
|
+
const [attestationCount, blockProposalCount, checkpointProposalCount] = await Promise.all([
|
|
131
|
+
this.attestationPerSlotAndSigner.sizeAsync(),
|
|
132
|
+
this.blockProposalsPerSlotIndexAndHash.sizeAsync(),
|
|
133
|
+
this.checkpointProposalsPerSlotAndHash.sizeAsync(),
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
return attestationCount === 0 && blockProposalCount === 0 && checkpointProposalCount === 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Number of bits reserved for indexWithinCheckpoint in position keys. */
|
|
140
|
+
private static readonly INDEX_BITS = 10;
|
|
141
|
+
/** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */
|
|
142
|
+
private static readonly MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
|
|
143
|
+
/** Decimal digits used to left-pad slot numbers in string keys.
|
|
144
|
+
* 10 digits ≈ 3500 years at 36 s/slot, leaving ample headroom. */
|
|
145
|
+
private static readonly SLOT_PAD_DIGITS = 10;
|
|
146
|
+
|
|
147
|
+
/** Fixed-width decimal slot string for use in composite string keys. */
|
|
148
|
+
private slotPaddedKey(slot: SlotNumber | number): string {
|
|
149
|
+
return slot.toString().padStart(AttestationPool.SLOT_PAD_DIGITS, '0');
|
|
116
150
|
}
|
|
117
151
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
? proposalId
|
|
123
|
-
: Buffer.isBuffer(proposalId)
|
|
124
|
-
? Fr.fromBuffer(proposalId).toString()
|
|
125
|
-
: proposalId.toString();
|
|
152
|
+
/** Fixed-width decimal index string for use in composite string keys. */
|
|
153
|
+
private indexPaddedKey(indexWithinCheckpoint: number): string {
|
|
154
|
+
return indexWithinCheckpoint.toString().padStart(4, '0');
|
|
155
|
+
}
|
|
126
156
|
|
|
127
|
-
|
|
157
|
+
/** Key for retained block proposals. */
|
|
158
|
+
private getBlockProposalKey(
|
|
159
|
+
slot: SlotNumber | number,
|
|
160
|
+
indexWithinCheckpoint: number,
|
|
161
|
+
payloadHash: BlockProposalHash,
|
|
162
|
+
): string {
|
|
163
|
+
return `${this.slotPaddedKey(slot)}-${this.indexPaddedKey(indexWithinCheckpoint)}-${payloadHash}`;
|
|
128
164
|
}
|
|
129
165
|
|
|
130
|
-
|
|
131
|
-
|
|
166
|
+
/** Range bounds for all retained block proposals in a slot. */
|
|
167
|
+
private getBlockProposalKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
|
|
168
|
+
return { start: `${this.slotPaddedKey(slot)}-`, end: `${this.slotPaddedKey(slot + 1)}-` };
|
|
132
169
|
}
|
|
133
170
|
|
|
134
|
-
/**
|
|
135
|
-
private
|
|
136
|
-
|
|
137
|
-
return { start: `${slotStr}-`, end: `${slotStr}-Z` }; // 'Z' sorts after any hex character
|
|
171
|
+
/** Key for retained checkpoint proposals. */
|
|
172
|
+
private getCheckpointProposalKey(slot: SlotNumber | number, payloadHash: CheckpointProposalHash): string {
|
|
173
|
+
return `${this.slotPaddedKey(slot)}-${payloadHash}`;
|
|
138
174
|
}
|
|
139
175
|
|
|
140
|
-
/**
|
|
141
|
-
private
|
|
142
|
-
|
|
143
|
-
return { start: `${proposalKey}-`, end: `${proposalKey}-Z` };
|
|
176
|
+
/** Range bounds for all retained checkpoint proposals in a slot. */
|
|
177
|
+
private getCheckpointProposalKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
|
|
178
|
+
return { start: `${this.slotPaddedKey(slot)}-`, end: `${this.slotPaddedKey(slot + 1)}-` };
|
|
144
179
|
}
|
|
145
180
|
|
|
146
|
-
/**
|
|
181
|
+
/** Key for the per-(slot, signer) attestation main store and equivocation index. */
|
|
147
182
|
private getSlotSignerKey(slot: SlotNumber, signerAddress: string): string {
|
|
148
|
-
|
|
149
|
-
return `${slotStr}-${signerAddress}`;
|
|
183
|
+
return `${this.slotPaddedKey(slot)}-${signerAddress}`;
|
|
150
184
|
}
|
|
151
185
|
|
|
152
|
-
/**
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Returns range bounds for querying all attestations for a given slot.
|
|
188
|
+
* Fixed-width padding ensures the slot prefix sorts cleanly, so using the next
|
|
189
|
+
* slot's prefix as the upper bound captures exactly the current slot's entries.
|
|
190
|
+
*/
|
|
191
|
+
private getAttestationKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
|
|
192
|
+
return { start: `${this.slotPaddedKey(slot)}-`, end: `${this.slotPaddedKey(slot + 1)}-` };
|
|
193
|
+
}
|
|
156
194
|
|
|
157
|
-
/** Creates a position key for block proposals:
|
|
195
|
+
/** Creates a position key for block proposals: slot * 1024 + indexWithinCheckpoint.
|
|
196
|
+
* Uses multiplication instead of bit-shift to avoid 32-bit signed integer overflow
|
|
197
|
+
* (bit-shift overflows after slot ~2^21, roughly 278 days of uptime). */
|
|
158
198
|
private getBlockPositionKey(slot: number, indexWithinCheckpoint: number): number {
|
|
159
199
|
if (indexWithinCheckpoint > AttestationPool.MAX_INDEX) {
|
|
160
200
|
throw new Error(
|
|
161
201
|
`Value for indexWithinCheckpoint ${indexWithinCheckpoint} exceeds maximum ${AttestationPool.MAX_INDEX}`,
|
|
162
202
|
);
|
|
163
203
|
}
|
|
164
|
-
return
|
|
204
|
+
return slot * (1 << AttestationPool.INDEX_BITS) + indexWithinCheckpoint;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Returns true if the multimap already contains the given value for the given key. */
|
|
208
|
+
private async multimapHasValue<TKey extends number | string, TValue extends string>(
|
|
209
|
+
map: AztecAsyncMultiMap<TKey, TValue>,
|
|
210
|
+
key: TKey,
|
|
211
|
+
value: TValue,
|
|
212
|
+
): Promise<boolean> {
|
|
213
|
+
const values = await toArray(map.getValuesAsync(key));
|
|
214
|
+
return values.includes(value);
|
|
165
215
|
}
|
|
166
216
|
|
|
167
217
|
/**
|
|
168
218
|
* Attempts to add a block proposal to the pool.
|
|
169
219
|
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* -
|
|
173
|
-
*
|
|
220
|
+
* - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
|
|
221
|
+
* exact same signed payload returns `alreadyExists: true`.
|
|
222
|
+
* - Distinct payload hashes at the same `(slot, indexWithinCheckpoint)` are tracked
|
|
223
|
+
* in the equivocation index and retained up to the cap.
|
|
174
224
|
*
|
|
175
225
|
* @param blockProposal - The block proposal to add
|
|
176
226
|
* @returns Result indicating whether the proposal was added and duplicate detection info
|
|
177
227
|
*/
|
|
178
228
|
public async tryAddBlockProposal(blockProposal: BlockProposal): Promise<TryAddResult> {
|
|
179
229
|
return await this.store.transactionAsync(async () => {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
const count = await this.
|
|
186
|
-
blockProposal.slotNumber,
|
|
187
|
-
blockProposal.indexWithinCheckpoint,
|
|
188
|
-
);
|
|
230
|
+
const positionKey = this.getBlockPositionKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
|
|
231
|
+
const payloadHash = blockProposal.getPayloadHash();
|
|
232
|
+
|
|
233
|
+
// Hash already tracked => exact same signed payload was already received.
|
|
234
|
+
if (await this.multimapHasValue(this.blockProposalHashesPerSlotAndIndex, positionKey, payloadHash)) {
|
|
235
|
+
const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
|
|
189
236
|
return { added: false, alreadyExists: true, count };
|
|
190
237
|
}
|
|
191
238
|
|
|
192
|
-
//
|
|
193
|
-
const count = await this.
|
|
194
|
-
blockProposal.slotNumber,
|
|
195
|
-
blockProposal.indexWithinCheckpoint,
|
|
196
|
-
);
|
|
197
|
-
|
|
239
|
+
// Cap reached for this position (no more new payload hashes accepted).
|
|
240
|
+
const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
|
|
198
241
|
if (count >= MAX_BLOCK_PROPOSALS_PER_POSITION) {
|
|
199
242
|
return { added: false, alreadyExists: false, count };
|
|
200
243
|
}
|
|
201
244
|
|
|
202
|
-
//
|
|
203
|
-
await this.
|
|
245
|
+
// Track the new payload hash for equivocation detection.
|
|
246
|
+
await this.blockProposalHashesPerSlotAndIndex.set(positionKey, payloadHash);
|
|
247
|
+
const proposalKey = this.getBlockProposalKey(
|
|
248
|
+
blockProposal.slotNumber,
|
|
249
|
+
blockProposal.indexWithinCheckpoint,
|
|
250
|
+
payloadHash,
|
|
251
|
+
);
|
|
252
|
+
await this.blockProposalsPerSlotIndexAndHash.set(proposalKey, blockProposal.withoutSignedTxs().toBuffer());
|
|
253
|
+
await this.blockProposalKeysPerArchive.set(blockProposal.archive.toString(), proposalKey);
|
|
204
254
|
|
|
205
255
|
this.log.debug(
|
|
206
256
|
`Added block proposal for slot ${blockProposal.slotNumber} and index ${blockProposal.indexWithinCheckpoint}`,
|
|
207
257
|
{
|
|
208
|
-
|
|
258
|
+
archive: blockProposal.archive.toString(),
|
|
259
|
+
payloadHash,
|
|
209
260
|
slotNumber: blockProposal.slotNumber,
|
|
210
261
|
indexWithinCheckpoint: blockProposal.indexWithinCheckpoint,
|
|
211
262
|
},
|
|
@@ -215,60 +266,77 @@ export class AttestationPool {
|
|
|
215
266
|
});
|
|
216
267
|
}
|
|
217
268
|
|
|
218
|
-
/** Gets the count of block proposals for a given position (slot, indexWithinCheckpoint). */
|
|
219
|
-
private getBlockProposalCountForPosition(
|
|
220
|
-
slot: SlotNumber,
|
|
221
|
-
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
222
|
-
): Promise<number> {
|
|
223
|
-
const positionKey = this.getBlockPositionKey(slot, indexWithinCheckpoint);
|
|
224
|
-
return this.blockProposalsForSlotAndIndex.getValueCountAsync(positionKey);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** Internal method - must be called within a transaction. */
|
|
228
|
-
private async addBlockProposal(blockProposal: BlockProposal): Promise<void> {
|
|
229
|
-
const proposalId = blockProposal.archive.toString();
|
|
230
|
-
// Strip signedTxs before storing to avoid persisting full tx data
|
|
231
|
-
await this.blockProposals.set(proposalId, blockProposal.withoutSignedTxs().toBuffer());
|
|
232
|
-
|
|
233
|
-
// Index by slot and position for duplicate detection
|
|
234
|
-
const positionKey = this.getBlockPositionKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
|
|
235
|
-
await this.blockProposalsForSlotAndIndex.set(positionKey, proposalId);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
269
|
/**
|
|
239
|
-
* Get block proposal by
|
|
270
|
+
* Get block proposal by archive root.
|
|
240
271
|
*
|
|
241
|
-
*
|
|
272
|
+
* Resolves the archive root through the archive index and returns the first
|
|
273
|
+
* retained proposal for that archive. This lookup is used by block-txs req/resp,
|
|
274
|
+
* where any retained proposal for the requested archive gives the tx hash list.
|
|
242
275
|
*
|
|
243
|
-
* @
|
|
276
|
+
* @param archiveRoot - The archive root to look up
|
|
277
|
+
* @return The block proposal if it exists and its archive matches, otherwise undefined.
|
|
244
278
|
*/
|
|
245
|
-
public async
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
if (buffer
|
|
249
|
-
|
|
279
|
+
public async getBlockProposalByArchive(archiveRoot: string): Promise<BlockProposal | undefined> {
|
|
280
|
+
for await (const proposalKey of this.blockProposalKeysPerArchive.getValuesAsync(archiveRoot)) {
|
|
281
|
+
const buffer = await this.blockProposalsPerSlotIndexAndHash.getAsync(proposalKey);
|
|
282
|
+
if (!buffer || buffer.length === 0) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const proposal = BlockProposal.fromBuffer(buffer);
|
|
287
|
+
if (proposal.archive.toString() === archiveRoot) {
|
|
288
|
+
return proposal;
|
|
289
|
+
}
|
|
290
|
+
} catch {
|
|
291
|
+
continue;
|
|
250
292
|
}
|
|
251
|
-
} catch {
|
|
252
|
-
return undefined;
|
|
253
293
|
}
|
|
254
|
-
|
|
255
294
|
return undefined;
|
|
256
295
|
}
|
|
257
296
|
|
|
297
|
+
/** Returns retained signed proposals for a slot. */
|
|
298
|
+
public async getProposalsForSlot(slot: SlotNumber): Promise<ProposalsForSlot> {
|
|
299
|
+
const blockProposals: BlockProposal[] = [];
|
|
300
|
+
const checkpointProposals: CheckpointProposalCore[] = [];
|
|
301
|
+
|
|
302
|
+
for await (const [_, buffer] of this.blockProposalsPerSlotIndexAndHash.entriesAsync(
|
|
303
|
+
this.getBlockProposalKeyRangeForSlot(slot),
|
|
304
|
+
)) {
|
|
305
|
+
try {
|
|
306
|
+
blockProposals.push(BlockProposal.fromBuffer(buffer));
|
|
307
|
+
} catch {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
for await (const [_, buffer] of this.checkpointProposalsPerSlotAndHash.entriesAsync(
|
|
313
|
+
this.getCheckpointProposalKeyRangeForSlot(slot),
|
|
314
|
+
)) {
|
|
315
|
+
try {
|
|
316
|
+
checkpointProposals.push(CheckpointProposal.fromBuffer(buffer));
|
|
317
|
+
} catch {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return { blockProposals, checkpointProposals };
|
|
323
|
+
}
|
|
324
|
+
|
|
258
325
|
/** Checks if any block proposals exist for a given slot (at index 0). */
|
|
259
326
|
public async hasBlockProposalsForSlot(slot: SlotNumber): Promise<boolean> {
|
|
260
327
|
const positionKey = this.getBlockPositionKey(slot, 0);
|
|
261
|
-
const count = await this.
|
|
328
|
+
const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
|
|
262
329
|
return count > 0;
|
|
263
330
|
}
|
|
264
331
|
|
|
265
332
|
/**
|
|
266
333
|
* Attempts to add a checkpoint proposal to the pool.
|
|
267
334
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
* -
|
|
271
|
-
*
|
|
335
|
+
* - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
|
|
336
|
+
* exact same signed payload returns `alreadyExists: true`.
|
|
337
|
+
* - Distinct payload hashes at the same slot are tracked in the equivocation index.
|
|
338
|
+
* Distinct payload bytes are retained up to the same cap so slashing watchers
|
|
339
|
+
* can recover signed proposals.
|
|
272
340
|
*
|
|
273
341
|
* Note: This method only handles the CheckpointProposalCore. If the original
|
|
274
342
|
* CheckpointProposal contains a lastBlock, the caller should extract it via
|
|
@@ -279,73 +347,77 @@ export class AttestationPool {
|
|
|
279
347
|
*/
|
|
280
348
|
public async tryAddCheckpointProposal(proposal: CheckpointProposalCore): Promise<TryAddResult> {
|
|
281
349
|
return await this.store.transactionAsync(async () => {
|
|
282
|
-
const
|
|
350
|
+
const slot = proposal.slotNumber;
|
|
351
|
+
const payloadHash = proposal.getPayloadHash();
|
|
283
352
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (alreadyExists) {
|
|
287
|
-
const count = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
|
|
353
|
+
if (await this.multimapHasValue(this.checkpointProposalHashesPerSlot, slot, payloadHash)) {
|
|
354
|
+
const count = await this.checkpointProposalHashesPerSlot.getValueCountAsync(slot);
|
|
288
355
|
return { added: false, alreadyExists: true, count };
|
|
289
356
|
}
|
|
290
357
|
|
|
291
|
-
|
|
292
|
-
const count = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
|
|
358
|
+
const count = await this.checkpointProposalHashesPerSlot.getValueCountAsync(slot);
|
|
293
359
|
if (count >= MAX_CHECKPOINT_PROPOSALS_PER_SLOT) {
|
|
294
360
|
return { added: false, alreadyExists: false, count };
|
|
295
361
|
}
|
|
296
362
|
|
|
297
|
-
//
|
|
298
|
-
await this.
|
|
363
|
+
// Track the new payload hash for equivocation detection.
|
|
364
|
+
await this.checkpointProposalHashesPerSlot.set(slot, payloadHash);
|
|
365
|
+
await this.checkpointProposalsPerSlotAndHash.set(
|
|
366
|
+
this.getCheckpointProposalKey(slot, payloadHash),
|
|
367
|
+
proposal.toBuffer(),
|
|
368
|
+
);
|
|
299
369
|
|
|
300
|
-
this.log.debug(`Added checkpoint proposal for slot ${
|
|
301
|
-
|
|
302
|
-
|
|
370
|
+
this.log.debug(`Added checkpoint proposal for slot ${slot}`, {
|
|
371
|
+
archive: proposal.archive.toString(),
|
|
372
|
+
payloadHash,
|
|
373
|
+
slotNumber: slot,
|
|
303
374
|
});
|
|
304
375
|
|
|
305
376
|
return { added: true, alreadyExists: false, count: count + 1 };
|
|
306
377
|
});
|
|
307
378
|
}
|
|
308
379
|
|
|
309
|
-
/** Internal method - must be called within a transaction. */
|
|
310
|
-
private async addCheckpointProposal(proposal: CheckpointProposalCore): Promise<void> {
|
|
311
|
-
const slotKey = proposal.slotNumber;
|
|
312
|
-
const proposalId = proposal.archive.toString();
|
|
313
|
-
|
|
314
|
-
await this.checkpointProposalsForSlot.set(slotKey, proposalId);
|
|
315
|
-
await this.checkpointProposals.set(proposalId, proposal.toBuffer());
|
|
316
|
-
}
|
|
317
|
-
|
|
318
380
|
/**
|
|
319
|
-
* Get checkpoint proposal
|
|
381
|
+
* Get a retained checkpoint proposal stored for the given slot.
|
|
382
|
+
* If multiple proposals were retained for an equivocation, returns the lowest
|
|
383
|
+
* payload hash deterministically.
|
|
320
384
|
*
|
|
321
385
|
* Returns a CheckpointProposalCore (without lastBlock info) since the lastBlock
|
|
322
386
|
* is extracted and stored separately as a BlockProposal when added.
|
|
323
387
|
*
|
|
324
|
-
* @param
|
|
325
|
-
* @return The checkpoint proposal core if
|
|
388
|
+
* @param slot - The slot to look up
|
|
389
|
+
* @return The checkpoint proposal core if one is stored, otherwise undefined.
|
|
326
390
|
*/
|
|
327
|
-
public async getCheckpointProposal(
|
|
328
|
-
const buffer
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
391
|
+
public async getCheckpointProposal(slot: SlotNumber): Promise<CheckpointProposalCore | undefined> {
|
|
392
|
+
for await (const [_, buffer] of this.checkpointProposalsPerSlotAndHash.entriesAsync(
|
|
393
|
+
this.getCheckpointProposalKeyRangeForSlot(slot),
|
|
394
|
+
)) {
|
|
395
|
+
try {
|
|
396
|
+
if (buffer && buffer.length > 0) {
|
|
397
|
+
return CheckpointProposal.fromBuffer(buffer);
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
continue;
|
|
332
401
|
}
|
|
333
|
-
} catch {
|
|
334
|
-
return undefined;
|
|
335
402
|
}
|
|
336
403
|
|
|
337
404
|
return undefined;
|
|
338
405
|
}
|
|
339
406
|
|
|
407
|
+
/** Returns whether any checkpoint proposal is retained for the given slot. */
|
|
408
|
+
public async hasCheckpointProposalForSlot(slot: SlotNumber): Promise<boolean> {
|
|
409
|
+
return (await this.getCheckpointProposal(slot)) !== undefined;
|
|
410
|
+
}
|
|
411
|
+
|
|
340
412
|
/**
|
|
341
413
|
* Adds own checkpoint attestations to the pool.
|
|
342
|
-
* Skips
|
|
414
|
+
* Skips per-signer cap and equivocation tracking; the caller is trusted.
|
|
415
|
+
* Each (slot, signer) gets a single stored attestation; later additions overwrite.
|
|
343
416
|
*/
|
|
344
417
|
public async addOwnCheckpointAttestations(attestations: CheckpointAttestation[]): Promise<void> {
|
|
345
418
|
await this.store.transactionAsync(async () => {
|
|
346
419
|
for (const attestation of attestations) {
|
|
347
420
|
const slotNumber = attestation.payload.header.slotNumber;
|
|
348
|
-
const proposalId = attestation.archive;
|
|
349
421
|
const sender = attestation.getSender();
|
|
350
422
|
|
|
351
423
|
// Skip attestations with invalid signatures
|
|
@@ -353,22 +425,30 @@ export class AttestationPool {
|
|
|
353
425
|
this.log.warn(`Skipping own checkpoint attestation with invalid signature for slot ${slotNumber}`, {
|
|
354
426
|
signature: attestation.signature.toString(),
|
|
355
427
|
slotNumber,
|
|
356
|
-
|
|
428
|
+
archive: attestation.archive.toString(),
|
|
357
429
|
});
|
|
358
430
|
continue;
|
|
359
431
|
}
|
|
360
432
|
|
|
361
433
|
const address = sender.toString();
|
|
362
|
-
const ownKey = this.
|
|
434
|
+
const ownKey = this.getSlotSignerKey(slotNumber, address);
|
|
435
|
+
const payloadHash = attestation.getPayloadHash();
|
|
363
436
|
|
|
364
|
-
await this.
|
|
437
|
+
await this.attestationPerSlotAndSigner.set(ownKey, attestation.toBuffer());
|
|
365
438
|
this.metrics.trackMempoolItemAdded(ownKey);
|
|
366
439
|
|
|
440
|
+
// Track our own payload hash so that an equivocating attestation from another
|
|
441
|
+
// peer at the same (slot, signer) is detected as a duplicate.
|
|
442
|
+
if (!(await this.multimapHasValue(this.attestationHashesPerSlotAndSigner, ownKey, payloadHash))) {
|
|
443
|
+
await this.attestationHashesPerSlotAndSigner.set(ownKey, payloadHash);
|
|
444
|
+
}
|
|
445
|
+
|
|
367
446
|
this.log.debug(`Added own checkpoint attestation for slot ${slotNumber} from ${address}`, {
|
|
368
447
|
signature: attestation.signature.toString(),
|
|
369
448
|
slotNumber,
|
|
370
449
|
address,
|
|
371
|
-
|
|
450
|
+
archive: attestation.archive.toString(),
|
|
451
|
+
payloadHash,
|
|
372
452
|
});
|
|
373
453
|
}
|
|
374
454
|
});
|
|
@@ -377,6 +457,10 @@ export class AttestationPool {
|
|
|
377
457
|
/**
|
|
378
458
|
* Get all checkpoint attestations for a given slot.
|
|
379
459
|
*
|
|
460
|
+
* Returns one attestation per (slot, signer) — the first seen for each signer.
|
|
461
|
+
* Later equivocating attestations from the same signer are tracked in the index
|
|
462
|
+
* but their bytes are not retained.
|
|
463
|
+
*
|
|
380
464
|
* @param slot - The slot to query
|
|
381
465
|
* @return CheckpointAttestations
|
|
382
466
|
*/
|
|
@@ -384,7 +468,7 @@ export class AttestationPool {
|
|
|
384
468
|
const range = this.getAttestationKeyRangeForSlot(slot);
|
|
385
469
|
const attestations: CheckpointAttestation[] = [];
|
|
386
470
|
|
|
387
|
-
for await (const [_, buf] of this.
|
|
471
|
+
for await (const [_, buf] of this.attestationPerSlotAndSigner.entriesAsync(range)) {
|
|
388
472
|
attestations.push(CheckpointAttestation.fromBuffer(buf));
|
|
389
473
|
}
|
|
390
474
|
|
|
@@ -392,24 +476,19 @@ export class AttestationPool {
|
|
|
392
476
|
}
|
|
393
477
|
|
|
394
478
|
/**
|
|
395
|
-
* Get checkpoint attestations for slot
|
|
479
|
+
* Get checkpoint attestations for a slot whose signed payload matches the given
|
|
480
|
+
* proposal payload hash.
|
|
396
481
|
*
|
|
397
482
|
* @param slot - The slot to query
|
|
398
|
-
* @param
|
|
399
|
-
* @return CheckpointAttestations
|
|
483
|
+
* @param proposalPayloadHash - Hex-encoded keccak256 of the target proposal's signed payload
|
|
484
|
+
* @return CheckpointAttestations whose `getPayloadHash()` matches `proposalPayloadHash`
|
|
400
485
|
*/
|
|
401
486
|
public async getCheckpointAttestationsForSlotAndProposal(
|
|
402
487
|
slot: SlotNumber,
|
|
403
|
-
|
|
488
|
+
proposalPayloadHash: CheckpointProposalHash,
|
|
404
489
|
): Promise<CheckpointAttestation[]> {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
for await (const [_, buf] of this.checkpointAttestations.entriesAsync(range)) {
|
|
409
|
-
attestations.push(CheckpointAttestation.fromBuffer(buf));
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return attestations;
|
|
490
|
+
const all = await this.getCheckpointAttestationsForSlot(slot);
|
|
491
|
+
return all.filter(att => att.getPayloadHash() === proposalPayloadHash);
|
|
413
492
|
}
|
|
414
493
|
|
|
415
494
|
/**
|
|
@@ -423,43 +502,51 @@ export class AttestationPool {
|
|
|
423
502
|
let numberOfBlockProposals = 0;
|
|
424
503
|
|
|
425
504
|
await this.store.transactionAsync(async () => {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
505
|
+
const oldestSlotPadded = this.slotPaddedKey(oldestSlot);
|
|
506
|
+
|
|
507
|
+
// Delete checkpoint attestations whose key < `${oldestSlotPadded}-`. Fixed-width
|
|
508
|
+
// decimal padding means the slot prefix sorts strictly before any key at that slot.
|
|
509
|
+
for await (const key of this.attestationPerSlotAndSigner.keysAsync({ end: `${oldestSlotPadded}-` })) {
|
|
510
|
+
await this.attestationPerSlotAndSigner.delete(key);
|
|
431
511
|
this.metrics.trackMempoolItemRemoved(key);
|
|
432
512
|
numberOfAttestations++;
|
|
433
513
|
}
|
|
434
514
|
|
|
435
|
-
// Clean up per-signer-per-slot index
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const slotSignerEndKey = new Fr(oldestSlot).toString();
|
|
439
|
-
for await (const key of this.checkpointAttestationsPerSlotAndSigner.keysAsync({ end: slotSignerEndKey })) {
|
|
440
|
-
await this.checkpointAttestationsPerSlotAndSigner.delete(key);
|
|
515
|
+
// Clean up per-signer-per-slot index using the same end bound.
|
|
516
|
+
for await (const key of this.attestationHashesPerSlotAndSigner.keysAsync({ end: `${oldestSlotPadded}-` })) {
|
|
517
|
+
await this.attestationHashesPerSlotAndSigner.delete(key);
|
|
441
518
|
}
|
|
442
519
|
|
|
443
|
-
// Delete checkpoint proposals for slots < oldestSlot
|
|
444
|
-
for await (const slot of this.
|
|
445
|
-
|
|
446
|
-
for (const proposalId of proposalIds) {
|
|
447
|
-
await this.checkpointProposals.delete(proposalId);
|
|
448
|
-
numberOfCheckpointProposals++;
|
|
449
|
-
}
|
|
450
|
-
await this.checkpointProposalsForSlot.delete(slot);
|
|
520
|
+
// Delete checkpoint proposals for slots < oldestSlot.
|
|
521
|
+
for await (const slot of this.checkpointProposalHashesPerSlot.keysAsync({ end: oldestSlot })) {
|
|
522
|
+
await this.checkpointProposalHashesPerSlot.delete(slot);
|
|
451
523
|
}
|
|
452
524
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
525
|
+
for await (const key of this.checkpointProposalsPerSlotAndHash.keysAsync({
|
|
526
|
+
end: `${oldestSlotPadded}-`,
|
|
527
|
+
})) {
|
|
528
|
+
await this.checkpointProposalsPerSlotAndHash.delete(key);
|
|
529
|
+
numberOfCheckpointProposals++;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Delete block proposals for slots < oldestSlot, using blockProposalHashesPerSlotAndIndex as index.
|
|
533
|
+
// Key format: slot * (1 << INDEX_BITS) + indexWithinCheckpoint
|
|
534
|
+
const blockPositionEndKey = oldestSlot * (1 << AttestationPool.INDEX_BITS);
|
|
535
|
+
for await (const positionKey of this.blockProposalHashesPerSlotAndIndex.keysAsync({ end: blockPositionEndKey })) {
|
|
536
|
+
await this.blockProposalHashesPerSlotAndIndex.delete(positionKey);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
for await (const [key, buffer] of this.blockProposalsPerSlotIndexAndHash.entriesAsync({
|
|
540
|
+
end: `${oldestSlotPadded}-`,
|
|
541
|
+
})) {
|
|
542
|
+
try {
|
|
543
|
+
const proposal = BlockProposal.fromBuffer(buffer);
|
|
544
|
+
await this.blockProposalKeysPerArchive.deleteValue(proposal.archive.toString(), key);
|
|
545
|
+
} catch {
|
|
546
|
+
// ignore decode errors when cleaning up
|
|
461
547
|
}
|
|
462
|
-
await this.
|
|
548
|
+
await this.blockProposalsPerSlotIndexAndHash.delete(key);
|
|
549
|
+
numberOfBlockProposals++;
|
|
463
550
|
}
|
|
464
551
|
});
|
|
465
552
|
|
|
@@ -474,18 +561,19 @@ export class AttestationPool {
|
|
|
474
561
|
/**
|
|
475
562
|
* Attempts to add a checkpoint attestation to the pool.
|
|
476
563
|
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
* -
|
|
480
|
-
*
|
|
564
|
+
* - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
|
|
565
|
+
* exact same signed payload from the same signer returns `alreadyExists: true`.
|
|
566
|
+
* - Distinct payload hashes from the same (slot, signer) are tracked in the
|
|
567
|
+
* equivocation index. The first one's bytes are stored; later distinct hashes
|
|
568
|
+
* bump `count` so libp2p can fire its duplicate callback.
|
|
481
569
|
*
|
|
482
570
|
* @param attestation - The checkpoint attestation to add
|
|
483
|
-
* @returns Result indicating whether the attestation was added, existence info,
|
|
484
|
-
*
|
|
571
|
+
* @returns Result indicating whether the attestation was added, existence info,
|
|
572
|
+
* and number of distinct payload hashes by this signer for this slot
|
|
573
|
+
* (for equivocation detection).
|
|
485
574
|
*/
|
|
486
575
|
public async tryAddCheckpointAttestation(attestation: CheckpointAttestation): Promise<TryAddResult> {
|
|
487
576
|
const slotNumber = attestation.payload.header.slotNumber;
|
|
488
|
-
const proposalId = attestation.archive.toString();
|
|
489
577
|
const sender = attestation.getSender();
|
|
490
578
|
|
|
491
579
|
if (!sender) {
|
|
@@ -493,28 +581,23 @@ export class AttestationPool {
|
|
|
493
581
|
}
|
|
494
582
|
|
|
495
583
|
const signerAddress = sender.toString();
|
|
584
|
+
const slotSignerKey = this.getSlotSignerKey(slotNumber, signerAddress);
|
|
585
|
+
const payloadHash = attestation.getPayloadHash();
|
|
496
586
|
|
|
497
587
|
return await this.store.transactionAsync(async () => {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
// Get count of attestations by this signer for this slot (for duplicate detection)
|
|
502
|
-
const signerAttestationCount = await this.getSignerAttestationCountForSlot(slotNumber, signerAddress);
|
|
503
|
-
|
|
504
|
-
if (alreadyExists) {
|
|
505
|
-
return {
|
|
506
|
-
added: false,
|
|
507
|
-
alreadyExists: true,
|
|
508
|
-
count: signerAttestationCount,
|
|
509
|
-
};
|
|
588
|
+
if (await this.multimapHasValue(this.attestationHashesPerSlotAndSigner, slotSignerKey, payloadHash)) {
|
|
589
|
+
const count = await this.attestationHashesPerSlotAndSigner.getValueCountAsync(slotSignerKey);
|
|
590
|
+
return { added: false, alreadyExists: true, count };
|
|
510
591
|
}
|
|
511
592
|
|
|
512
|
-
|
|
593
|
+
const signerAttestationCount = await this.attestationHashesPerSlotAndSigner.getValueCountAsync(slotSignerKey);
|
|
594
|
+
|
|
513
595
|
if (signerAttestationCount >= MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER) {
|
|
514
596
|
this.log.debug(`Rejecting attestation: signer ${signerAddress} exceeded per-slot cap for slot ${slotNumber}`, {
|
|
515
597
|
slotNumber,
|
|
516
598
|
signerAddress,
|
|
517
|
-
|
|
599
|
+
archive: attestation.archive.toString(),
|
|
600
|
+
payloadHash,
|
|
518
601
|
signerAttestationCount,
|
|
519
602
|
});
|
|
520
603
|
return {
|
|
@@ -524,22 +607,26 @@ export class AttestationPool {
|
|
|
524
607
|
};
|
|
525
608
|
}
|
|
526
609
|
|
|
527
|
-
//
|
|
528
|
-
await this.
|
|
529
|
-
this.metrics.trackMempoolItemAdded(key);
|
|
610
|
+
// Track the new payload hash for equivocation detection.
|
|
611
|
+
await this.attestationHashesPerSlotAndSigner.set(slotSignerKey, payloadHash);
|
|
530
612
|
|
|
531
|
-
//
|
|
532
|
-
|
|
533
|
-
await this.
|
|
613
|
+
// Only the first distinct payload at (slot, signer) is stored; later
|
|
614
|
+
// equivocations are detected via the multimap but their bytes are not retained.
|
|
615
|
+
const alreadyHasStored = await this.attestationPerSlotAndSigner.hasAsync(slotSignerKey);
|
|
616
|
+
if (!alreadyHasStored) {
|
|
617
|
+
await this.attestationPerSlotAndSigner.set(slotSignerKey, attestation.toBuffer());
|
|
618
|
+
this.metrics.trackMempoolItemAdded(slotSignerKey);
|
|
619
|
+
}
|
|
534
620
|
|
|
535
621
|
this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${signerAddress}`, {
|
|
536
622
|
signature: attestation.signature.toString(),
|
|
537
623
|
slotNumber,
|
|
538
624
|
address: signerAddress,
|
|
539
|
-
|
|
625
|
+
archive: attestation.archive.toString(),
|
|
626
|
+
payloadHash,
|
|
627
|
+
stored: !alreadyHasStored,
|
|
540
628
|
});
|
|
541
629
|
|
|
542
|
-
// Return the new count
|
|
543
630
|
return {
|
|
544
631
|
added: true,
|
|
545
632
|
alreadyExists: false,
|
|
@@ -547,12 +634,6 @@ export class AttestationPool {
|
|
|
547
634
|
};
|
|
548
635
|
});
|
|
549
636
|
}
|
|
550
|
-
|
|
551
|
-
/** Gets the count of attestations by a specific signer for a given slot. */
|
|
552
|
-
private async getSignerAttestationCountForSlot(slot: SlotNumber, signerAddress: string): Promise<number> {
|
|
553
|
-
const slotSignerKey = this.getSlotSignerKey(slot, signerAddress);
|
|
554
|
-
return await this.checkpointAttestationsPerSlotAndSigner.getValueCountAsync(slotSignerKey);
|
|
555
|
-
}
|
|
556
637
|
}
|
|
557
638
|
|
|
558
639
|
/** Creates an AttestationPool backed by a temporary store for testing. */
|