@aztec/p2p 0.0.1-commit.aada20e3 → 0.0.1-commit.b1c78909e
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 +11 -11
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +45 -15
- package/dest/client/interface.d.ts +46 -33
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +39 -51
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +151 -224
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +7 -8
- package/dest/config.d.ts +49 -18
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +92 -38
- package/dest/errors/tx-pool.error.d.ts +8 -0
- package/dest/errors/tx-pool.error.d.ts.map +1 -0
- package/dest/errors/tx-pool.error.js +9 -0
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +104 -88
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +445 -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 +353 -87
- 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/attestation_pool/mocks.d.ts +2 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +2 -2
- package/dest/mem_pools/index.d.ts +3 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/index.js +1 -1
- package/dest/mem_pools/instrumentation.d.ts +4 -2
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +16 -14
- package/dest/mem_pools/interface.d.ts +5 -5
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +3 -3
- package/dest/mem_pools/tx_pool/priority.d.ts +2 -2
- package/dest/mem_pools/tx_pool/priority.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/priority.js +4 -3
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +3 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +18 -9
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +5 -2
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +12 -4
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -2
- 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 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +54 -5
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +8 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +7 -5
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +7 -5
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +14 -6
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +4 -4
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +16 -4
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +3 -3
- package/dest/mem_pools/tx_pool_v2/index.d.ts +3 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/index.js +2 -1
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +28 -8
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +5 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +70 -14
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +132 -16
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +108 -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 +337 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +9 -4
- 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 +20 -6
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +14 -5
- 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 +467 -592
- 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/msg_validators/proposal_validator/block_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +10 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +16 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -8
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +48 -36
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +24 -20
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
- package/dest/msg_validators/tx_validator/factory.d.ts +125 -6
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +226 -58
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +1 -1
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/fee_payer_balance.js +6 -2
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +67 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +104 -37
- 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/metadata_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
- package/dest/msg_validators/tx_validator/phases_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +44 -23
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.js +6 -6
- package/dest/services/dummy_service.d.ts +13 -5
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +10 -4
- package/dest/services/encoding.d.ts +3 -3
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +11 -10
- package/dest/services/gossipsub/index.d.ts +3 -0
- package/dest/services/gossipsub/index.d.ts.map +1 -0
- package/dest/services/gossipsub/index.js +2 -0
- package/dest/services/gossipsub/scoring.d.ts +21 -3
- package/dest/services/gossipsub/scoring.d.ts.map +1 -1
- package/dest/services/gossipsub/scoring.js +24 -7
- package/dest/services/gossipsub/topic_score_params.d.ts +173 -0
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
- package/dest/services/gossipsub/topic_score_params.js +346 -0
- package/dest/services/libp2p/libp2p_service.d.ts +94 -42
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +463 -350
- package/dest/services/peer-manager/metrics.d.ts +3 -1
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +6 -0
- package/dest/services/peer-manager/peer_manager.d.ts +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +2 -1
- package/dest/services/peer-manager/peer_scoring.d.ts +1 -1
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +25 -2
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +6 -5
- 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 +26 -53
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +2 -6
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +10 -13
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.js +25 -46
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +17 -11
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +49 -15
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
- package/dest/services/reqresp/interface.d.ts +10 -1
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +15 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
- 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 +21 -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 +27 -11
- package/dest/services/reqresp/protocols/tx.d.ts +7 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +20 -0
- package/dest/services/reqresp/reqresp.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +13 -5
- package/dest/services/service.d.ts +39 -3
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +22 -4
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +49 -3
- package/dest/services/tx_collection/fast_tx_collection.d.ts +6 -5
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +64 -48
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_collection.js +167 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts +37 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_source.js +90 -0
- package/dest/services/tx_collection/index.d.ts +3 -2
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +1 -0
- 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 +2 -1
- package/dest/services/tx_collection/missing_txs_tracker.d.ts +32 -0
- package/dest/services/tx_collection/missing_txs_tracker.d.ts.map +1 -0
- package/dest/services/tx_collection/missing_txs_tracker.js +27 -0
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +15 -14
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +6 -6
- package/dest/services/tx_collection/slow_tx_collection.d.ts +7 -3
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +60 -26
- package/dest/services/tx_collection/tx_collection.d.ts +23 -10
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +75 -3
- package/dest/services/tx_collection/tx_collection_sink.d.ts +18 -8
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +26 -29
- package/dest/services/tx_collection/tx_source.d.ts +8 -3
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +19 -2
- package/dest/services/tx_file_store/config.d.ts +1 -3
- package/dest/services/tx_file_store/config.d.ts.map +1 -1
- package/dest/services/tx_file_store/config.js +0 -4
- package/dest/services/tx_file_store/tx_file_store.d.ts +4 -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 +9 -6
- package/dest/services/tx_provider.d.ts +4 -4
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +9 -8
- package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +1 -2
- package/dest/test-helpers/mock-pubsub.d.ts +30 -4
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +105 -4
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +4 -3
- package/dest/test-helpers/testbench-utils.d.ts +43 -38
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +129 -59
- package/dest/testbench/p2p_client_testbench_worker.d.ts +2 -2
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +17 -16
- package/dest/testbench/worker_client_manager.d.ts +3 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +6 -2
- package/dest/util.d.ts +3 -3
- package/dest/util.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/client/factory.ts +82 -28
- package/src/client/interface.ts +56 -34
- package/src/client/p2p_client.ts +181 -269
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +21 -12
- package/src/config.ts +141 -44
- package/src/errors/tx-pool.error.ts +12 -0
- package/src/index.ts +1 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +497 -91
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
- package/src/mem_pools/attestation_pool/index.ts +9 -2
- package/src/mem_pools/attestation_pool/mocks.ts +2 -1
- package/src/mem_pools/index.ts +4 -1
- package/src/mem_pools/instrumentation.ts +17 -13
- package/src/mem_pools/interface.ts +4 -4
- package/src/mem_pools/tx_pool/README.md +1 -1
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +3 -3
- package/src/mem_pools/tx_pool/priority.ts +6 -3
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +3 -1
- package/src/mem_pools/tx_pool_v2/README.md +112 -17
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
- package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +21 -8
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +5 -2
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +18 -4
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +4 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +59 -4
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +5 -5
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +5 -5
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +14 -9
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +33 -6
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +4 -3
- package/src/mem_pools/tx_pool_v2/index.ts +2 -1
- package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +28 -8
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +191 -24
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +430 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +24 -7
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +508 -676
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +14 -4
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +20 -7
- package/src/msg_validators/proposal_validator/proposal_validator.ts +63 -40
- package/src/msg_validators/tx_validator/README.md +115 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +5 -5
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
- package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
- package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
- package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
- package/src/msg_validators/tx_validator/factory.ts +366 -77
- package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
- package/src/msg_validators/tx_validator/gas_validator.ts +123 -27
- package/src/msg_validators/tx_validator/index.ts +2 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +51 -26
- package/src/msg_validators/tx_validator/timestamp_validator.ts +23 -18
- package/src/services/dummy_service.ts +18 -6
- package/src/services/encoding.ts +9 -9
- package/src/services/gossipsub/README.md +641 -0
- package/src/services/gossipsub/index.ts +2 -0
- package/src/services/gossipsub/scoring.ts +29 -5
- package/src/services/gossipsub/topic_score_params.ts +487 -0
- package/src/services/libp2p/libp2p_service.ts +488 -372
- package/src/services/peer-manager/metrics.ts +7 -0
- package/src/services/peer-manager/peer_manager.ts +2 -1
- package/src/services/peer-manager/peer_scoring.ts +25 -0
- package/src/services/reqresp/batch-tx-requester/README.md +7 -7
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +31 -59
- package/src/services/reqresp/batch-tx-requester/interface.ts +1 -5
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +23 -71
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +63 -24
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
- package/src/services/reqresp/interface.ts +26 -1
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +23 -14
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +38 -15
- package/src/services/reqresp/protocols/tx.ts +22 -0
- package/src/services/reqresp/reqresp.ts +16 -4
- package/src/services/service.ts +51 -2
- package/src/services/tx_collection/config.ts +74 -6
- package/src/services/tx_collection/fast_tx_collection.ts +74 -51
- package/src/services/tx_collection/file_store_tx_collection.ts +202 -0
- package/src/services/tx_collection/file_store_tx_source.ts +117 -0
- package/src/services/tx_collection/index.ts +2 -1
- package/src/services/tx_collection/instrumentation.ts +7 -1
- package/src/services/tx_collection/missing_txs_tracker.ts +52 -0
- package/src/services/tx_collection/proposal_tx_collector.ts +20 -21
- package/src/services/tx_collection/slow_tx_collection.ts +66 -33
- package/src/services/tx_collection/tx_collection.ts +113 -16
- package/src/services/tx_collection/tx_collection_sink.ts +30 -34
- package/src/services/tx_collection/tx_source.ts +22 -3
- package/src/services/tx_file_store/config.ts +0 -6
- package/src/services/tx_file_store/tx_file_store.ts +10 -8
- package/src/services/tx_provider.ts +10 -9
- package/src/test-helpers/make-test-p2p-clients.ts +4 -6
- package/src/test-helpers/mock-pubsub.ts +146 -9
- package/src/test-helpers/reqresp-nodes.ts +5 -7
- package/src/test-helpers/testbench-utils.ts +128 -71
- package/src/testbench/p2p_client_testbench_worker.ts +26 -22
- package/src/testbench/worker_client_manager.ts +13 -5
- package/src/util.ts +8 -2
- 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/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
1
|
+
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { Logger } from '@aztec/foundation/log';
|
|
3
|
+
import type { DateProvider } from '@aztec/foundation/timer';
|
|
3
4
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
4
5
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
5
6
|
import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
@@ -8,8 +9,10 @@ import type { L2Block, L2BlockId, L2BlockSource } from '@aztec/stdlib/block';
|
|
|
8
9
|
import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
9
10
|
import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
10
11
|
import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
|
|
12
|
+
import type { TelemetryClient } from '@aztec/telemetry-client';
|
|
11
13
|
|
|
12
14
|
import { TxArchive } from './archive/index.js';
|
|
15
|
+
import { DeletedPool } from './deleted_pool.js';
|
|
13
16
|
import {
|
|
14
17
|
EvictionManager,
|
|
15
18
|
FeePayerBalanceEvictionRule,
|
|
@@ -20,8 +23,12 @@ import {
|
|
|
20
23
|
LowPriorityPreAddRule,
|
|
21
24
|
NullifierConflictRule,
|
|
22
25
|
type PoolOperations,
|
|
26
|
+
type PreAddContext,
|
|
23
27
|
type PreAddPoolAccess,
|
|
28
|
+
TxPoolRejectionCode,
|
|
29
|
+
type TxPoolRejectionError,
|
|
24
30
|
} from './eviction/index.js';
|
|
31
|
+
import { TxPoolV2Instrumentation } from './instrumentation.js';
|
|
25
32
|
import {
|
|
26
33
|
type AddTxsResult,
|
|
27
34
|
DEFAULT_TX_POOL_V2_CONFIG,
|
|
@@ -29,14 +36,8 @@ import {
|
|
|
29
36
|
type TxPoolV2Config,
|
|
30
37
|
type TxPoolV2Dependencies,
|
|
31
38
|
} from './interfaces.js';
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
type TxState,
|
|
35
|
-
buildTxMetaData,
|
|
36
|
-
checkNullifierConflict,
|
|
37
|
-
compareFee,
|
|
38
|
-
compareTxHash,
|
|
39
|
-
} from './tx_metadata.js';
|
|
39
|
+
import { type TxMetaData, type TxState, buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
|
|
40
|
+
import { TxPoolIndices } from './tx_pool_indices.js';
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Callbacks for the implementation to notify the outer class about events and metrics.
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
export interface TxPoolV2Callbacks {
|
|
45
46
|
onTxsAdded: (txs: Tx[], opts: { source?: string }) => void;
|
|
46
47
|
onTxsRemoved: (txHashes: string[] | bigint[]) => void;
|
|
48
|
+
onTxsMined: (txHashes: string[]) => void;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/**
|
|
@@ -59,27 +61,19 @@ export class TxPoolV2Impl {
|
|
|
59
61
|
// === Dependencies ===
|
|
60
62
|
#l2BlockSource: L2BlockSource;
|
|
61
63
|
#worldStateSynchronizer: WorldStateSynchronizer;
|
|
62
|
-
#
|
|
64
|
+
#createTxValidator: TxPoolV2Dependencies['createTxValidator'];
|
|
63
65
|
|
|
64
66
|
// === In-Memory Indices ===
|
|
65
|
-
|
|
66
|
-
#metadata: Map<string, TxMetaData> = new Map();
|
|
67
|
-
/** Nullifier to txHash index (pending txs only) */
|
|
68
|
-
#nullifierToTxHash: Map<string, string> = new Map();
|
|
69
|
-
/** Fee payer to txHashes index (pending txs only) */
|
|
70
|
-
#feePayerToTxHashes: Map<string, Set<string>> = new Map();
|
|
71
|
-
/**
|
|
72
|
-
* Pending txHashes grouped by priority fee.
|
|
73
|
-
* Outer map: priorityFee -> Set of txHashes at that fee level.
|
|
74
|
-
*/
|
|
75
|
-
#pendingByPriority: Map<bigint, Set<string>> = new Map();
|
|
76
|
-
/** Protected transactions: txHash -> slotNumber. Includes txs we have and txs we expect to receive. */
|
|
77
|
-
#protectedTransactions: Map<string, SlotNumber> = new Map();
|
|
67
|
+
#indices: TxPoolIndices = new TxPoolIndices();
|
|
78
68
|
|
|
79
69
|
// === Config & Services ===
|
|
80
70
|
#config: TxPoolV2Config;
|
|
81
71
|
#archive: TxArchive;
|
|
72
|
+
#deletedPool: DeletedPool;
|
|
82
73
|
#evictionManager: EvictionManager;
|
|
74
|
+
#dateProvider: DateProvider;
|
|
75
|
+
#instrumentation: TxPoolV2Instrumentation;
|
|
76
|
+
#evictedTxHashes: Set<string> = new Set();
|
|
83
77
|
#log: Logger;
|
|
84
78
|
#callbacks: TxPoolV2Callbacks;
|
|
85
79
|
|
|
@@ -88,7 +82,9 @@ export class TxPoolV2Impl {
|
|
|
88
82
|
archiveStore: AztecAsyncKVStore,
|
|
89
83
|
deps: TxPoolV2Dependencies,
|
|
90
84
|
callbacks: TxPoolV2Callbacks,
|
|
85
|
+
telemetry: TelemetryClient,
|
|
91
86
|
config: Partial<TxPoolV2Config> = {},
|
|
87
|
+
dateProvider: DateProvider,
|
|
92
88
|
log: Logger,
|
|
93
89
|
) {
|
|
94
90
|
this.#store = store;
|
|
@@ -96,10 +92,13 @@ export class TxPoolV2Impl {
|
|
|
96
92
|
|
|
97
93
|
this.#l2BlockSource = deps.l2BlockSource;
|
|
98
94
|
this.#worldStateSynchronizer = deps.worldStateSynchronizer;
|
|
99
|
-
this.#
|
|
95
|
+
this.#createTxValidator = deps.createTxValidator;
|
|
100
96
|
|
|
101
97
|
this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
|
|
102
98
|
this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
|
|
99
|
+
this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
|
|
100
|
+
this.#dateProvider = dateProvider;
|
|
101
|
+
this.#instrumentation = new TxPoolV2Instrumentation(telemetry, () => this.#indices.getTotalMetadataBytes());
|
|
103
102
|
this.#log = log;
|
|
104
103
|
this.#callbacks = callbacks;
|
|
105
104
|
|
|
@@ -134,26 +133,42 @@ export class TxPoolV2Impl {
|
|
|
134
133
|
* by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
|
|
135
134
|
*/
|
|
136
135
|
async hydrateFromDatabase(): Promise<void> {
|
|
137
|
-
// Step
|
|
136
|
+
// Step 0: Hydrate deleted pool state
|
|
137
|
+
await this.#deletedPool.hydrateFromDatabase();
|
|
138
|
+
|
|
139
|
+
// Step 1: Load all transactions from DB (excluding soft-deleted)
|
|
138
140
|
const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
|
|
139
141
|
|
|
140
142
|
// Step 2: Check mined status for each tx
|
|
141
143
|
await this.#markMinedStatusBatch(loaded.map(l => l.meta));
|
|
142
144
|
|
|
143
145
|
// Step 3: Partition by mined status
|
|
144
|
-
const
|
|
146
|
+
const mined: TxMetaData[] = [];
|
|
147
|
+
const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
|
|
148
|
+
for (const entry of loaded) {
|
|
149
|
+
if (entry.meta.minedL2BlockId !== undefined) {
|
|
150
|
+
mined.push(entry.meta);
|
|
151
|
+
} else {
|
|
152
|
+
nonMined.push(entry);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
145
155
|
|
|
146
156
|
// Step 4: Validate non-mined transactions
|
|
147
|
-
const { valid, invalid } = await this.#
|
|
157
|
+
const { valid, invalid } = await this.#revalidateMetadata(
|
|
158
|
+
nonMined.map(e => e.meta),
|
|
159
|
+
'on startup',
|
|
160
|
+
);
|
|
148
161
|
|
|
149
162
|
// Step 5: Populate mined indices (these don't need conflict resolution)
|
|
150
|
-
|
|
163
|
+
for (const meta of mined) {
|
|
164
|
+
this.#indices.addMined(meta);
|
|
165
|
+
}
|
|
151
166
|
|
|
152
167
|
// Step 6: Rebuild pending pool by running pre-add rules for each tx
|
|
153
168
|
// This resolves nullifier conflicts, fee payer balance issues, and pool size limits
|
|
154
169
|
const { rejected } = await this.#rebuildPendingPool(valid);
|
|
155
170
|
|
|
156
|
-
// Step 7: Delete invalid and rejected txs from DB
|
|
171
|
+
// Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
|
|
157
172
|
const toDelete = [...deserializationErrors, ...invalid, ...rejected];
|
|
158
173
|
if (toDelete.length === 0) {
|
|
159
174
|
return;
|
|
@@ -163,17 +178,45 @@ export class TxPoolV2Impl {
|
|
|
163
178
|
await this.#txsDB.delete(txHashStr);
|
|
164
179
|
}
|
|
165
180
|
});
|
|
166
|
-
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup
|
|
181
|
+
this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, { txHashes: toDelete });
|
|
167
182
|
}
|
|
168
183
|
|
|
169
|
-
async addPendingTxs(txs: Tx[], opts: { source?: string }): Promise<AddTxsResult> {
|
|
184
|
+
async addPendingTxs(txs: Tx[], opts: { source?: string; feeComparisonOnly?: boolean }): Promise<AddTxsResult> {
|
|
170
185
|
const accepted: TxHash[] = [];
|
|
171
186
|
const ignored: TxHash[] = [];
|
|
172
187
|
const rejected: TxHash[] = [];
|
|
173
|
-
const
|
|
188
|
+
const errors = new Map<string, TxPoolRejectionError>();
|
|
174
189
|
const acceptedPending = new Set<string>();
|
|
175
190
|
|
|
191
|
+
// Phase 1: Pre-compute all throwable I/O outside the transaction.
|
|
192
|
+
// If any pre-computation throws, the entire call fails before mutations happen.
|
|
193
|
+
const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
|
|
194
|
+
|
|
195
|
+
const validator = await this.#createTxValidator();
|
|
196
|
+
|
|
197
|
+
for (const tx of txs) {
|
|
198
|
+
const txHash = tx.getTxHash();
|
|
199
|
+
const txHashStr = txHash.toString();
|
|
200
|
+
|
|
201
|
+
const meta = await buildTxMetaData(tx);
|
|
202
|
+
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
203
|
+
|
|
204
|
+
// Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
|
|
205
|
+
let isValid = true;
|
|
206
|
+
if (!minedBlockId) {
|
|
207
|
+
isValid = await this.#validateMeta(meta, validator);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
precomputed.set(txHashStr, { meta, minedBlockId, isValid });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Phase 2: Apply mutations inside the transaction using only pre-computed results,
|
|
214
|
+
// in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
|
|
176
215
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
216
|
+
const preAddContext: PreAddContext | undefined =
|
|
217
|
+
opts.feeComparisonOnly !== undefined
|
|
218
|
+
? { feeComparisonOnly: opts.feeComparisonOnly, priceBumpPercentage: this.#config.priceBumpPercentage }
|
|
219
|
+
: undefined;
|
|
177
220
|
|
|
178
221
|
await this.#store.transactionAsync(async () => {
|
|
179
222
|
for (const tx of txs) {
|
|
@@ -181,38 +224,51 @@ export class TxPoolV2Impl {
|
|
|
181
224
|
const txHashStr = txHash.toString();
|
|
182
225
|
|
|
183
226
|
// Skip duplicates
|
|
184
|
-
if (this.#
|
|
227
|
+
if (this.#indices.has(txHashStr)) {
|
|
185
228
|
ignored.push(txHash);
|
|
186
229
|
continue;
|
|
187
230
|
}
|
|
188
231
|
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
const preProtectedSlot = this.#protectedTransactions.get(txHashStr);
|
|
232
|
+
const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
|
|
233
|
+
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
192
234
|
|
|
193
235
|
if (minedBlockId) {
|
|
194
236
|
// Already mined - add directly (protection already set if pre-protected)
|
|
195
|
-
await this.#
|
|
237
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
196
238
|
accepted.push(txHash);
|
|
197
|
-
newlyAdded.push(tx);
|
|
198
239
|
} else if (preProtectedSlot !== undefined) {
|
|
199
240
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
200
|
-
await this.#
|
|
241
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
|
|
201
242
|
accepted.push(txHash);
|
|
202
|
-
|
|
243
|
+
} else if (!isValid) {
|
|
244
|
+
// Failed pre-computed validation
|
|
245
|
+
rejected.push(txHash);
|
|
203
246
|
} else {
|
|
204
|
-
// Regular pending tx -
|
|
205
|
-
const result = await this.#tryAddRegularPendingTx(
|
|
247
|
+
// Regular pending tx - run pre-add rules using pre-computed metadata
|
|
248
|
+
const result = await this.#tryAddRegularPendingTx(
|
|
249
|
+
tx,
|
|
250
|
+
meta,
|
|
251
|
+
opts,
|
|
252
|
+
poolAccess,
|
|
253
|
+
acceptedPending,
|
|
254
|
+
ignored,
|
|
255
|
+
errors,
|
|
256
|
+
preAddContext,
|
|
257
|
+
);
|
|
206
258
|
if (result.status === 'accepted') {
|
|
207
259
|
acceptedPending.add(txHashStr);
|
|
208
|
-
newlyAdded.push(tx);
|
|
209
|
-
} else if (result.status === 'rejected') {
|
|
210
|
-
rejected.push(txHash);
|
|
211
260
|
} else {
|
|
212
261
|
ignored.push(txHash);
|
|
213
262
|
}
|
|
214
263
|
}
|
|
215
264
|
}
|
|
265
|
+
|
|
266
|
+
// Run post-add eviction rules for pending txs (inside transaction for atomicity)
|
|
267
|
+
if (acceptedPending.size > 0) {
|
|
268
|
+
const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
|
|
269
|
+
const uniqueFeePayers = new Set<string>(feePayers);
|
|
270
|
+
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
|
|
271
|
+
}
|
|
216
272
|
});
|
|
217
273
|
|
|
218
274
|
// Build final accepted list for pending txs (excludes intra-batch evictions)
|
|
@@ -220,135 +276,183 @@ export class TxPoolV2Impl {
|
|
|
220
276
|
accepted.push(TxHash.fromString(txHashStr));
|
|
221
277
|
}
|
|
222
278
|
|
|
223
|
-
//
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
const uniqueFeePayers = new Set<string>(feePayers);
|
|
227
|
-
await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
|
|
279
|
+
// Record metrics
|
|
280
|
+
if (ignored.length > 0) {
|
|
281
|
+
this.#instrumentation.recordIgnored(ignored.length);
|
|
228
282
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (newlyAdded.length > 0) {
|
|
232
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
283
|
+
if (rejected.length > 0) {
|
|
284
|
+
this.#instrumentation.recordRejected(rejected.length);
|
|
233
285
|
}
|
|
234
286
|
|
|
235
|
-
return { accepted, ignored, rejected };
|
|
287
|
+
return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
|
|
236
288
|
}
|
|
237
289
|
|
|
238
|
-
/**
|
|
290
|
+
/** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
|
|
239
291
|
async #tryAddRegularPendingTx(
|
|
240
292
|
tx: Tx,
|
|
293
|
+
precomputedMeta: TxMetaData,
|
|
294
|
+
opts: { source?: string },
|
|
241
295
|
poolAccess: PreAddPoolAccess,
|
|
242
296
|
acceptedPending: Set<string>,
|
|
243
297
|
ignored: TxHash[],
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
298
|
+
errors: Map<string, TxPoolRejectionError>,
|
|
299
|
+
preAddContext?: PreAddContext,
|
|
300
|
+
): Promise<{ status: 'accepted' | 'ignored' }> {
|
|
301
|
+
const txHashStr = tx.getTxHash().toString();
|
|
247
302
|
|
|
248
|
-
//
|
|
249
|
-
const
|
|
250
|
-
if (validationResult.result !== 'valid') {
|
|
251
|
-
this.#log.info(`Rejecting tx ${txHashStr}: ${validationResult.reason?.join(', ')}`);
|
|
252
|
-
return { status: 'rejected' };
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Build metadata and run pre-add rules
|
|
256
|
-
const meta = await buildTxMetaData(tx);
|
|
257
|
-
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
303
|
+
// Run pre-add rules
|
|
304
|
+
const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
|
|
258
305
|
|
|
259
306
|
if (preAddResult.shouldIgnore) {
|
|
260
|
-
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason}`);
|
|
307
|
+
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
|
|
308
|
+
if (preAddResult.reason && preAddResult.reason.code !== TxPoolRejectionCode.INTERNAL_ERROR) {
|
|
309
|
+
errors.set(txHashStr, preAddResult.reason);
|
|
310
|
+
}
|
|
261
311
|
return { status: 'ignored' };
|
|
262
312
|
}
|
|
263
313
|
|
|
264
|
-
// Evict conflicts
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
314
|
+
// Evict conflicts, grouped by rule name for metrics
|
|
315
|
+
if (preAddResult.evictions && preAddResult.evictions.length > 0) {
|
|
316
|
+
const byReason = new Map<string, string[]>();
|
|
317
|
+
for (const { txHash: evictHash, reason } of preAddResult.evictions) {
|
|
318
|
+
const group = byReason.get(reason);
|
|
319
|
+
if (group) {
|
|
320
|
+
group.push(evictHash);
|
|
321
|
+
} else {
|
|
322
|
+
byReason.set(reason, [evictHash]);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
for (const [reason, hashes] of byReason) {
|
|
326
|
+
await this.#evictTxs(hashes, reason);
|
|
327
|
+
}
|
|
328
|
+
for (const evictHashStr of preAddResult.txHashesToEvict) {
|
|
329
|
+
this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`, {
|
|
330
|
+
evictedTxHash: evictHashStr,
|
|
331
|
+
replacementTxHash: txHashStr,
|
|
332
|
+
});
|
|
333
|
+
if (acceptedPending.has(evictHashStr)) {
|
|
334
|
+
// Evicted tx was from this batch - mark as ignored in result
|
|
335
|
+
acceptedPending.delete(evictHashStr);
|
|
336
|
+
ignored.push(TxHash.fromString(evictHashStr));
|
|
337
|
+
}
|
|
271
338
|
}
|
|
272
339
|
}
|
|
273
340
|
|
|
341
|
+
// Randomly drop the transaction for testing purposes (report as accepted so it propagates)
|
|
342
|
+
if (this.#config.dropTransactionsProbability > 0 && Math.random() < this.#config.dropTransactionsProbability) {
|
|
343
|
+
this.#log.debug(`Dropping tx ${txHashStr} (simulated drop for testing)`);
|
|
344
|
+
return { status: 'accepted' };
|
|
345
|
+
}
|
|
346
|
+
|
|
274
347
|
// Add the transaction
|
|
275
|
-
await this.#
|
|
348
|
+
await this.#addTx(tx, 'pending', opts, precomputedMeta);
|
|
276
349
|
return { status: 'accepted' };
|
|
277
350
|
}
|
|
278
351
|
|
|
279
|
-
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'
|
|
352
|
+
async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
|
|
280
353
|
const txHashStr = tx.getTxHash().toString();
|
|
281
354
|
|
|
282
355
|
// Check if already in pool
|
|
283
|
-
if (this.#
|
|
356
|
+
if (this.#indices.has(txHashStr)) {
|
|
357
|
+
this.#log.verbose(`canAddPendingTx: tx ${txHashStr} already in pool`);
|
|
284
358
|
return 'ignored';
|
|
285
359
|
}
|
|
286
360
|
|
|
287
|
-
//
|
|
288
|
-
const validationResult = await this.#pendingTxValidator.validateTx(tx);
|
|
289
|
-
if (validationResult.result !== 'valid') {
|
|
290
|
-
return 'rejected';
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Build metadata and use pre-add rules
|
|
361
|
+
// Build metadata and check pre-add rules
|
|
294
362
|
const meta = await buildTxMetaData(tx);
|
|
295
363
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
296
364
|
const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
|
|
297
365
|
|
|
298
|
-
|
|
366
|
+
if (preAddResult.shouldIgnore) {
|
|
367
|
+
this.#log.verbose(`canAddPendingTx: tx ${txHashStr} ignored by pre-add rule`, {
|
|
368
|
+
reason: preAddResult.reason?.message ?? 'no reason provided',
|
|
369
|
+
});
|
|
370
|
+
return 'ignored';
|
|
371
|
+
}
|
|
372
|
+
return 'accepted';
|
|
299
373
|
}
|
|
300
374
|
|
|
301
375
|
async addProtectedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
302
376
|
const slotNumber = block.globalVariables.slotNumber;
|
|
303
|
-
const newlyAdded: Tx[] = [];
|
|
304
377
|
|
|
305
378
|
await this.#store.transactionAsync(async () => {
|
|
306
379
|
for (const tx of txs) {
|
|
307
380
|
const txHash = tx.getTxHash();
|
|
308
381
|
const txHashStr = txHash.toString();
|
|
309
|
-
const isNew = !this.#
|
|
382
|
+
const isNew = !this.#indices.has(txHashStr);
|
|
310
383
|
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
311
384
|
|
|
312
385
|
if (isNew) {
|
|
313
|
-
// New tx - add as mined or protected
|
|
386
|
+
// New tx - add as mined or protected (callback emitted by #addTx)
|
|
314
387
|
if (minedBlockId) {
|
|
315
|
-
await this.#
|
|
316
|
-
this.#
|
|
388
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
389
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
317
390
|
} else {
|
|
318
|
-
await this.#
|
|
391
|
+
await this.#addTx(tx, { protected: slotNumber }, opts);
|
|
319
392
|
}
|
|
320
|
-
newlyAdded.push(tx);
|
|
321
393
|
} else {
|
|
322
394
|
// Existing tx - update protection and mined status
|
|
323
|
-
this.#updateProtection(txHashStr, slotNumber);
|
|
395
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
324
396
|
if (minedBlockId) {
|
|
325
|
-
this.#
|
|
397
|
+
const meta = this.#indices.getMetadata(txHashStr)!;
|
|
398
|
+
this.#indices.markAsMined(meta, minedBlockId);
|
|
326
399
|
}
|
|
327
400
|
}
|
|
328
401
|
}
|
|
329
402
|
});
|
|
330
|
-
|
|
331
|
-
if (newlyAdded.length > 0) {
|
|
332
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
333
|
-
}
|
|
334
403
|
}
|
|
335
404
|
|
|
336
|
-
protectTxs(txHashes: TxHash[], block: BlockHeader): TxHash[] {
|
|
405
|
+
async protectTxs(txHashes: TxHash[], block: BlockHeader): Promise<TxHash[]> {
|
|
337
406
|
const slotNumber = block.globalVariables.slotNumber;
|
|
338
407
|
const missing: TxHash[] = [];
|
|
408
|
+
let softDeletedHits = 0;
|
|
409
|
+
let missingPreviouslyEvicted = 0;
|
|
339
410
|
|
|
340
|
-
|
|
341
|
-
const
|
|
411
|
+
await this.#store.transactionAsync(async () => {
|
|
412
|
+
for (const txHash of txHashes) {
|
|
413
|
+
const txHashStr = txHash.toString();
|
|
342
414
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
415
|
+
if (this.#indices.has(txHashStr)) {
|
|
416
|
+
// Update protection for existing tx
|
|
417
|
+
this.#indices.updateProtection(txHashStr, slotNumber);
|
|
418
|
+
} else if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
419
|
+
// Resurrect soft-deleted tx as protected
|
|
420
|
+
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
421
|
+
if (buffer) {
|
|
422
|
+
const tx = Tx.fromBuffer(buffer);
|
|
423
|
+
await this.#addTx(tx, { protected: slotNumber });
|
|
424
|
+
softDeletedHits++;
|
|
425
|
+
} else {
|
|
426
|
+
// Data missing despite soft-delete flag — treat as truly missing
|
|
427
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
428
|
+
missing.push(txHash);
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
// Truly missing — pre-record protection for tx we don't have yet
|
|
432
|
+
this.#indices.setProtection(txHashStr, slotNumber);
|
|
433
|
+
missing.push(txHash);
|
|
434
|
+
if (this.#evictedTxHashes.has(txHashStr)) {
|
|
435
|
+
missingPreviouslyEvicted++;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
350
438
|
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Record metrics
|
|
442
|
+
if (softDeletedHits > 0) {
|
|
443
|
+
this.#instrumentation.recordSoftDeletedHits(softDeletedHits);
|
|
444
|
+
}
|
|
445
|
+
if (missing.length > 0) {
|
|
446
|
+
this.#log.debug(`protectTxs missing tx hashes: ${missing.map(h => h.toString()).join(', ')}`);
|
|
447
|
+
this.#instrumentation.recordMissingOnProtect(missing.length);
|
|
351
448
|
}
|
|
449
|
+
if (missingPreviouslyEvicted > 0) {
|
|
450
|
+
this.#instrumentation.recordMissingPreviouslyEvicted(missingPreviouslyEvicted);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
this.#log.info(
|
|
454
|
+
`Protected ${txHashes.length} txs, missing: ${missing.length}, soft-deleted hits: ${softDeletedHits}`,
|
|
455
|
+
);
|
|
352
456
|
|
|
353
457
|
return missing;
|
|
354
458
|
}
|
|
@@ -356,28 +460,22 @@ export class TxPoolV2Impl {
|
|
|
356
460
|
async addMinedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
|
|
357
461
|
// Step 1: Build block ID
|
|
358
462
|
const blockId = await this.#buildBlockId(block);
|
|
359
|
-
const newlyAdded: Tx[] = [];
|
|
360
463
|
|
|
361
464
|
await this.#store.transactionAsync(async () => {
|
|
362
465
|
for (const tx of txs) {
|
|
363
466
|
const txHashStr = tx.getTxHash().toString();
|
|
364
|
-
const existingMeta = this.#
|
|
467
|
+
const existingMeta = this.#indices.getMetadata(txHashStr);
|
|
365
468
|
|
|
366
469
|
if (existingMeta) {
|
|
367
|
-
//
|
|
368
|
-
this.#markAsMined(existingMeta, blockId);
|
|
470
|
+
// Mark existing tx as mined
|
|
471
|
+
this.#indices.markAsMined(existingMeta, blockId);
|
|
369
472
|
} else {
|
|
370
|
-
//
|
|
371
|
-
await this.#
|
|
372
|
-
newlyAdded.push(tx);
|
|
473
|
+
// Add new mined tx (callback emitted by #addTx)
|
|
474
|
+
await this.#addTx(tx, { mined: blockId }, opts);
|
|
373
475
|
}
|
|
476
|
+
await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
|
|
374
477
|
}
|
|
375
478
|
});
|
|
376
|
-
|
|
377
|
-
// Step 3: Emit events for newly added txs
|
|
378
|
-
if (newlyAdded.length > 0) {
|
|
379
|
-
this.#callbacks.onTxsAdded(newlyAdded, opts);
|
|
380
|
-
}
|
|
381
479
|
}
|
|
382
480
|
|
|
383
481
|
async handleMinedBlock(block: L2Block): Promise<void> {
|
|
@@ -392,61 +490,76 @@ export class TxPoolV2Impl {
|
|
|
392
490
|
const feePayers: string[] = [];
|
|
393
491
|
const found: TxMetaData[] = [];
|
|
394
492
|
for (const txHash of txHashes) {
|
|
395
|
-
const meta = this.#
|
|
493
|
+
const meta = this.#indices.getMetadata(txHash.toString());
|
|
396
494
|
if (meta) {
|
|
397
495
|
feePayers.push(meta.feePayer);
|
|
398
496
|
found.push(meta);
|
|
399
497
|
}
|
|
400
498
|
}
|
|
401
499
|
|
|
402
|
-
|
|
403
|
-
|
|
500
|
+
await this.#store.transactionAsync(async () => {
|
|
501
|
+
// Step 4: Mark txs as mined (only those we have in the pool)
|
|
502
|
+
for (const meta of found) {
|
|
503
|
+
this.#indices.markAsMined(meta, blockId);
|
|
504
|
+
await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
|
|
505
|
+
}
|
|
404
506
|
|
|
405
|
-
|
|
406
|
-
|
|
507
|
+
// Step 5: Run post-event eviction rules (inside transaction for atomicity)
|
|
508
|
+
await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
if (found.length > 0) {
|
|
512
|
+
this.#callbacks.onTxsMined(found.map(m => m.txHash));
|
|
513
|
+
}
|
|
407
514
|
|
|
408
|
-
this.#callbacks.onTxsRemoved(txHashes.map(h => h.toBigInt()));
|
|
409
515
|
this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
|
|
410
516
|
}
|
|
411
517
|
|
|
412
518
|
async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
|
|
413
|
-
|
|
414
|
-
|
|
519
|
+
await this.#store.transactionAsync(async () => {
|
|
520
|
+
// Step 0: Clean up slot-deleted txs from previous slots
|
|
521
|
+
await this.#deletedPool.cleanupSlotDeleted(slotNumber);
|
|
415
522
|
|
|
416
|
-
|
|
417
|
-
|
|
523
|
+
// Step 1: Find expired protected txs
|
|
524
|
+
const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
|
|
418
525
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (txsToRestore.length === 0) {
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
526
|
+
// Step 2: Clear protection for all expired entries (including those without metadata)
|
|
527
|
+
this.#indices.clearProtection(expiredProtected);
|
|
424
528
|
|
|
425
|
-
|
|
529
|
+
// Step 3: Filter to only txs that have metadata and are not mined
|
|
530
|
+
const txsToRestore = this.#indices.filterRestorable(expiredProtected);
|
|
531
|
+
if (txsToRestore.length === 0) {
|
|
532
|
+
this.#log.debug(`Preparing for slot ${slotNumber}, no txs to unprotect`);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
426
535
|
|
|
427
|
-
|
|
428
|
-
const { valid, invalid } = await this.#validateForPending(txsToRestore);
|
|
536
|
+
this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
|
|
429
537
|
|
|
430
|
-
|
|
431
|
-
|
|
538
|
+
// Step 4: Validate for pending pool
|
|
539
|
+
const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
|
|
432
540
|
|
|
433
|
-
|
|
434
|
-
|
|
541
|
+
// Step 5: Resolve nullifier conflicts and add winners to pending indices
|
|
542
|
+
const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
435
543
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
544
|
+
// Step 6: Delete invalid txs and evict conflict losers
|
|
545
|
+
await this.#deleteTxsBatch(invalid);
|
|
546
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
547
|
+
|
|
548
|
+
// Step 7: Run eviction rules (enforce pool size limit)
|
|
549
|
+
if (added.length > 0) {
|
|
550
|
+
const feePayers = added.map(meta => meta.feePayer);
|
|
551
|
+
const uniqueFeePayers = new Set<string>(feePayers);
|
|
552
|
+
await this.#evictionManager.evictAfterNewTxs(
|
|
553
|
+
added.map(m => m.txHash),
|
|
554
|
+
[...uniqueFeePayers],
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
});
|
|
445
558
|
}
|
|
446
559
|
|
|
447
|
-
async handlePrunedBlocks(latestBlock: L2BlockId): Promise<void> {
|
|
560
|
+
async handlePrunedBlocks(latestBlock: L2BlockId, options?: { deleteAllTxs?: boolean }): Promise<void> {
|
|
448
561
|
// Step 1: Find transactions mined after the prune point
|
|
449
|
-
const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
|
|
562
|
+
const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
|
|
450
563
|
if (txsToUnmine.length === 0) {
|
|
451
564
|
this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
|
|
452
565
|
return;
|
|
@@ -454,62 +567,99 @@ export class TxPoolV2Impl {
|
|
|
454
567
|
|
|
455
568
|
this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
|
|
456
569
|
|
|
457
|
-
|
|
458
|
-
|
|
570
|
+
await this.#store.transactionAsync(async () => {
|
|
571
|
+
// Step 2: Mark ALL un-mined txs with their original mined block number
|
|
572
|
+
// This ensures they get soft-deleted if removed later, and only hard-deleted
|
|
573
|
+
// when their original mined block is finalized
|
|
574
|
+
await this.#deletedPool.markFromPrunedBlock(
|
|
575
|
+
txsToUnmine.map(m => ({
|
|
576
|
+
txHash: m.txHash,
|
|
577
|
+
minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
|
|
578
|
+
})),
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
// Step 3: Unmine - clear mined status from metadata
|
|
582
|
+
for (const meta of txsToUnmine) {
|
|
583
|
+
this.#indices.markAsUnmined(meta);
|
|
584
|
+
}
|
|
459
585
|
|
|
460
|
-
|
|
461
|
-
|
|
586
|
+
// If deleteAllTxs is set (epoch prune), delete all un-mined txs and return early
|
|
587
|
+
if (options?.deleteAllTxs) {
|
|
588
|
+
const allTxHashes = txsToUnmine.map(m => m.txHash);
|
|
589
|
+
await this.#deleteTxsBatch(allTxHashes);
|
|
590
|
+
this.#log.info(
|
|
591
|
+
`Handled prune to block ${latestBlock.number} with deleteAllTxs: deleted ${allTxHashes.length} txs`,
|
|
592
|
+
);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
462
595
|
|
|
463
|
-
|
|
464
|
-
|
|
596
|
+
// Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
|
|
597
|
+
const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
|
|
465
598
|
|
|
466
|
-
|
|
467
|
-
|
|
599
|
+
// Step 5: Validate for pending pool
|
|
600
|
+
const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
|
|
468
601
|
|
|
469
|
-
|
|
470
|
-
|
|
602
|
+
// Step 6: Resolve nullifier conflicts and add winners to pending indices
|
|
603
|
+
const { toEvict } = this.#applyNullifierConflictResolution(valid);
|
|
471
604
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
605
|
+
// Step 7: Delete invalid txs and evict conflict losers
|
|
606
|
+
await this.#deleteTxsBatch(invalid);
|
|
607
|
+
await this.#evictTxs(toEvict, 'NullifierConflict');
|
|
608
|
+
|
|
609
|
+
this.#log.info(
|
|
610
|
+
`Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`,
|
|
611
|
+
{ txHashesRestored: valid.map(m => m.txHash), txHashesInvalid: invalid, txHashesEvicted: toEvict },
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
// Step 8: Run eviction rules for ALL pending txs (not just restored ones)
|
|
615
|
+
// This handles cases like existing pending txs with invalid fee payer balances
|
|
616
|
+
await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
|
|
617
|
+
});
|
|
475
618
|
}
|
|
476
619
|
|
|
477
620
|
async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
|
|
478
|
-
|
|
479
|
-
|
|
621
|
+
await this.#store.transactionAsync(async () => {
|
|
622
|
+
await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
|
|
623
|
+
});
|
|
480
624
|
|
|
481
|
-
this.#log.info(`Deleted ${txHashes.length} failed txs
|
|
625
|
+
this.#log.info(`Deleted ${txHashes.length} failed txs`, { txHashes: txHashes.map(h => h.toString()) });
|
|
482
626
|
}
|
|
483
627
|
|
|
484
628
|
async handleFinalizedBlock(block: BlockHeader): Promise<void> {
|
|
485
629
|
const blockNumber = block.globalVariables.blockNumber;
|
|
486
630
|
|
|
487
|
-
// Step 1: Find txs
|
|
488
|
-
const
|
|
489
|
-
if (txsToFinalize.length === 0) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
631
|
+
// Step 1: Find mined txs at or before finalized block
|
|
632
|
+
const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
|
|
492
633
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
634
|
+
await this.#store.transactionAsync(async () => {
|
|
635
|
+
// Step 2: Collect mined txs for archiving (before deletion)
|
|
636
|
+
const txsToArchive: Tx[] = [];
|
|
637
|
+
if (this.#archive.isEnabled()) {
|
|
638
|
+
for (const txHashStr of minedTxsToFinalize) {
|
|
639
|
+
const buffer = await this.#txsDB.getAsync(txHashStr);
|
|
640
|
+
if (buffer) {
|
|
641
|
+
txsToArchive.push(Tx.fromBuffer(buffer));
|
|
642
|
+
}
|
|
500
643
|
}
|
|
501
644
|
}
|
|
502
|
-
}
|
|
503
645
|
|
|
504
|
-
|
|
505
|
-
|
|
646
|
+
// Step 3: Delete mined txs from active pool
|
|
647
|
+
await this.#deleteTxsBatch(minedTxsToFinalize);
|
|
506
648
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
649
|
+
// Step 4: Finalize soft-deleted txs
|
|
650
|
+
await this.#deletedPool.finalizeBlock(blockNumber);
|
|
651
|
+
|
|
652
|
+
// Step 5: Archive mined txs
|
|
653
|
+
if (txsToArchive.length > 0) {
|
|
654
|
+
await this.#archive.archiveTxs(txsToArchive);
|
|
655
|
+
}
|
|
656
|
+
});
|
|
511
657
|
|
|
512
|
-
|
|
658
|
+
if (minedTxsToFinalize.length > 0) {
|
|
659
|
+
this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
|
|
660
|
+
txHashes: minedTxsToFinalize,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
513
663
|
}
|
|
514
664
|
|
|
515
665
|
// === Query Methods ===
|
|
@@ -529,42 +679,47 @@ export class TxPoolV2Impl {
|
|
|
529
679
|
}
|
|
530
680
|
|
|
531
681
|
hasTxs(txHashes: TxHash[]): boolean[] {
|
|
532
|
-
return txHashes.map(h =>
|
|
682
|
+
return txHashes.map(h => {
|
|
683
|
+
const hashStr = h.toString();
|
|
684
|
+
return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
|
|
685
|
+
});
|
|
533
686
|
}
|
|
534
687
|
|
|
535
688
|
getTxStatus(txHash: TxHash): TxState | undefined {
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
689
|
+
const txHashStr = txHash.toString();
|
|
690
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
691
|
+
if (meta) {
|
|
692
|
+
return this.#indices.getTxState(meta);
|
|
693
|
+
}
|
|
694
|
+
// Check if soft-deleted
|
|
695
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
696
|
+
return 'deleted';
|
|
539
697
|
}
|
|
540
|
-
return
|
|
698
|
+
return undefined;
|
|
541
699
|
}
|
|
542
700
|
|
|
543
701
|
getPendingTxHashes(): TxHash[] {
|
|
544
|
-
return [...this.#iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
|
|
702
|
+
return [...this.#indices.iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
getEligiblePendingTxHashes(): TxHash[] {
|
|
706
|
+
const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
|
|
707
|
+
return [...this.#indices.iterateEligiblePendingByPriority('desc', maxReceivedAt)].map(hash =>
|
|
708
|
+
TxHash.fromString(hash),
|
|
709
|
+
);
|
|
545
710
|
}
|
|
546
711
|
|
|
547
712
|
getPendingTxCount(): number {
|
|
548
|
-
|
|
549
|
-
for (const hashes of this.#pendingByPriority.values()) {
|
|
550
|
-
count += hashes.size;
|
|
551
|
-
}
|
|
552
|
-
return count;
|
|
713
|
+
return this.#indices.getPendingTxCount();
|
|
553
714
|
}
|
|
554
715
|
|
|
555
716
|
getMinedTxHashes(): [TxHash, L2BlockId][] {
|
|
556
|
-
|
|
557
|
-
for (const [txHash, meta] of this.#metadata) {
|
|
558
|
-
if (meta.minedL2BlockId !== undefined) {
|
|
559
|
-
result.push([TxHash.fromString(txHash), meta.minedL2BlockId]);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
return result;
|
|
717
|
+
return this.#indices.getMinedTxs().map(([hash, blockId]) => [TxHash.fromString(hash), blockId]);
|
|
563
718
|
}
|
|
564
719
|
|
|
565
720
|
getMinedTxCount(): number {
|
|
566
721
|
let count = 0;
|
|
567
|
-
for (const meta of this.#
|
|
722
|
+
for (const [, meta] of this.#indices.iterateMetadata()) {
|
|
568
723
|
if (meta.minedL2BlockId !== undefined) {
|
|
569
724
|
count++;
|
|
570
725
|
}
|
|
@@ -573,11 +728,11 @@ export class TxPoolV2Impl {
|
|
|
573
728
|
}
|
|
574
729
|
|
|
575
730
|
isEmpty(): boolean {
|
|
576
|
-
return this.#
|
|
731
|
+
return this.#indices.isEmpty();
|
|
577
732
|
}
|
|
578
733
|
|
|
579
734
|
getTxCount(): number {
|
|
580
|
-
return this.#
|
|
735
|
+
return this.#indices.getTxCount();
|
|
581
736
|
}
|
|
582
737
|
|
|
583
738
|
getArchivedTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
@@ -585,18 +740,7 @@ export class TxPoolV2Impl {
|
|
|
585
740
|
}
|
|
586
741
|
|
|
587
742
|
getLowestPriorityPending(limit: number): TxHash[] {
|
|
588
|
-
|
|
589
|
-
return [];
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
const result: TxHash[] = [];
|
|
593
|
-
for (const hash of this.#iteratePendingByPriority('asc')) {
|
|
594
|
-
result.push(TxHash.fromString(hash));
|
|
595
|
-
if (result.length >= limit) {
|
|
596
|
-
break;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return result;
|
|
743
|
+
return this.#indices.getLowestPriorityPending(limit).map(h => TxHash.fromString(h));
|
|
600
744
|
}
|
|
601
745
|
|
|
602
746
|
// === Configuration ===
|
|
@@ -609,6 +753,9 @@ export class TxPoolV2Impl {
|
|
|
609
753
|
this.#config.archivedTxLimit = config.archivedTxLimit;
|
|
610
754
|
this.#archive.updateLimit(config.archivedTxLimit);
|
|
611
755
|
}
|
|
756
|
+
if (config.minTxPoolAgeMs !== undefined) {
|
|
757
|
+
this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
|
|
758
|
+
}
|
|
612
759
|
// Update eviction rules with new config
|
|
613
760
|
this.#evictionManager.updateConfig(config);
|
|
614
761
|
}
|
|
@@ -617,159 +764,146 @@ export class TxPoolV2Impl {
|
|
|
617
764
|
|
|
618
765
|
getPoolReadAccess(): PoolReadAccess {
|
|
619
766
|
return {
|
|
620
|
-
getMetadata: (txHash: string) => this.#
|
|
621
|
-
getTxHashByNullifier: (nullifier: string) => this.#
|
|
622
|
-
getTxHashesByFeePayer: (feePayer: string) => this.#
|
|
623
|
-
getPendingTxCount: () => this.getPendingTxCount(),
|
|
767
|
+
getMetadata: (txHash: string) => this.#indices.getMetadata(txHash),
|
|
768
|
+
getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
|
|
769
|
+
getTxHashesByFeePayer: (feePayer: string) => this.#indices.getTxHashesByFeePayer(feePayer),
|
|
770
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
624
771
|
};
|
|
625
772
|
}
|
|
626
773
|
|
|
627
774
|
// === Metrics ===
|
|
628
775
|
|
|
629
|
-
countTxs(): {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
} else if (state === 'mined') {
|
|
641
|
-
mined++;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
return { pending, protected: protected_, mined };
|
|
776
|
+
countTxs(): {
|
|
777
|
+
pending: number;
|
|
778
|
+
protected: number;
|
|
779
|
+
mined: number;
|
|
780
|
+
softDeleted: number;
|
|
781
|
+
totalMetadataBytes: number;
|
|
782
|
+
} {
|
|
783
|
+
return {
|
|
784
|
+
...this.#indices.countTxs(),
|
|
785
|
+
softDeleted: this.#deletedPool.getSoftDeletedCount(),
|
|
786
|
+
};
|
|
646
787
|
}
|
|
647
788
|
|
|
648
789
|
// ============================================================================
|
|
649
|
-
// PRIVATE
|
|
790
|
+
// PRIVATE HELPERS - Transaction Management
|
|
650
791
|
// ============================================================================
|
|
651
792
|
|
|
652
793
|
/**
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
* - 'mined' if it has a minedL2BlockId
|
|
656
|
-
* - 'protected' if it's in the protectedTransactions map (but not mined)
|
|
657
|
-
* - 'pending' otherwise
|
|
794
|
+
* Adds a new transaction to the pool with the specified state.
|
|
795
|
+
* Emits onTxsAdded callback immediately after DB write.
|
|
658
796
|
*/
|
|
659
|
-
#
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
797
|
+
async #addTx(
|
|
798
|
+
tx: Tx,
|
|
799
|
+
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
800
|
+
opts: { source?: string } = {},
|
|
801
|
+
precomputedMeta?: TxMetaData,
|
|
802
|
+
): Promise<TxMetaData> {
|
|
803
|
+
const txHashStr = tx.getTxHash().toString();
|
|
804
|
+
const meta = precomputedMeta ?? (await buildTxMetaData(tx));
|
|
805
|
+
meta.receivedAt = this.#dateProvider.now();
|
|
806
|
+
|
|
807
|
+
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
808
|
+
await this.#deletedPool.clearSoftDeleted(txHashStr);
|
|
809
|
+
this.#callbacks.onTxsAdded([tx], opts);
|
|
810
|
+
|
|
811
|
+
if (state === 'pending') {
|
|
812
|
+
this.#indices.addPending(meta);
|
|
813
|
+
} else if ('protected' in state) {
|
|
814
|
+
this.#indices.addProtected(meta, state.protected);
|
|
664
815
|
} else {
|
|
665
|
-
|
|
816
|
+
meta.minedL2BlockId = state.mined;
|
|
817
|
+
this.#indices.addMined(meta);
|
|
666
818
|
}
|
|
819
|
+
|
|
820
|
+
const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
|
|
821
|
+
this.#log.debug(`Added tx ${txHashStr} as ${stateStr}`, {
|
|
822
|
+
eventName: 'tx-added-to-pool',
|
|
823
|
+
txHash: txHashStr,
|
|
824
|
+
state: stateStr,
|
|
825
|
+
source: opts.source,
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
return meta;
|
|
667
829
|
}
|
|
668
830
|
|
|
669
831
|
/**
|
|
670
|
-
*
|
|
671
|
-
*
|
|
832
|
+
* Deletes a transaction from both indices and DB.
|
|
833
|
+
* Emits onTxsRemoved callback immediately after DB delete.
|
|
672
834
|
*/
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
for (const fee of sortedFees) {
|
|
683
|
-
const hashesAtFee = this.#pendingByPriority.get(fee)!;
|
|
684
|
-
const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
|
|
685
|
-
for (const hash of sortedHashes) {
|
|
686
|
-
yield hash;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
835
|
+
/**
|
|
836
|
+
* Deletes a transaction from the pool.
|
|
837
|
+
* Delegates to DeletedPool which decides soft vs hard delete based on whether
|
|
838
|
+
* the tx is from a pruned block.
|
|
839
|
+
*/
|
|
840
|
+
async #deleteTx(txHashStr: string): Promise<void> {
|
|
841
|
+
this.#indices.remove(txHashStr);
|
|
842
|
+
this.#callbacks.onTxsRemoved([txHashStr]);
|
|
843
|
+
await this.#deletedPool.deleteTx(txHashStr);
|
|
689
844
|
}
|
|
690
845
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
// --- Finding & Filtering Steps ---
|
|
696
|
-
|
|
697
|
-
/** Finds all transactions mined in blocks after the given block number */
|
|
698
|
-
#findTxsMinedAfter(blockNumber: number): TxMetaData[] {
|
|
699
|
-
const result: TxMetaData[] = [];
|
|
700
|
-
for (const meta of this.#metadata.values()) {
|
|
701
|
-
if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number > blockNumber) {
|
|
702
|
-
result.push(meta);
|
|
703
|
-
}
|
|
846
|
+
/** Deletes a batch of transactions, emitting callbacks individually for each. */
|
|
847
|
+
async #deleteTxsBatch(txHashes: string[]): Promise<void> {
|
|
848
|
+
for (const txHashStr of txHashes) {
|
|
849
|
+
await this.#deleteTx(txHashStr);
|
|
704
850
|
}
|
|
705
|
-
return result;
|
|
706
851
|
}
|
|
707
852
|
|
|
708
|
-
/**
|
|
709
|
-
#
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
853
|
+
/** Evicts transactions: records eviction metric with reason, caches hashes, then deletes. */
|
|
854
|
+
async #evictTxs(txHashes: string[], reason: string): Promise<void> {
|
|
855
|
+
if (txHashes.length === 0) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
this.#instrumentation.recordEvictions(txHashes.length, reason);
|
|
859
|
+
for (const txHashStr of txHashes) {
|
|
860
|
+
this.#log.debug(`Evicting tx ${txHashStr}`, { txHash: txHashStr, reason });
|
|
861
|
+
this.#addToEvictedCache(txHashStr);
|
|
715
862
|
}
|
|
716
|
-
|
|
863
|
+
await this.#deleteTxsBatch(txHashes);
|
|
717
864
|
}
|
|
718
865
|
|
|
719
|
-
/**
|
|
720
|
-
#
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
866
|
+
/** Adds a tx hash to the bounded evicted cache, evicting the oldest entry if at capacity. */
|
|
867
|
+
#addToEvictedCache(txHashStr: string): void {
|
|
868
|
+
if (this.#evictedTxHashes.size >= this.#config.evictedTxCacheSize) {
|
|
869
|
+
// FIFO eviction: remove the first (oldest) entry
|
|
870
|
+
const oldest = this.#evictedTxHashes.values().next().value!;
|
|
871
|
+
this.#evictedTxHashes.delete(oldest);
|
|
726
872
|
}
|
|
727
|
-
|
|
873
|
+
this.#evictedTxHashes.add(txHashStr);
|
|
728
874
|
}
|
|
729
875
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
876
|
+
// ============================================================================
|
|
877
|
+
// PRIVATE HELPERS - Validation & Conflict Resolution
|
|
878
|
+
// ============================================================================
|
|
734
879
|
|
|
735
|
-
/**
|
|
736
|
-
#
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
880
|
+
/** Validates transaction metadata, returning true if valid */
|
|
881
|
+
async #validateMeta(meta: TxMetaData, validator?: TxValidator<TxMetaData>, context?: string): Promise<boolean> {
|
|
882
|
+
const txValidator = validator ?? (await this.#createTxValidator());
|
|
883
|
+
const result = await txValidator.validateTx(meta);
|
|
884
|
+
if (result.result !== 'valid') {
|
|
885
|
+
const contextStr = context ? ` ${context}` : '';
|
|
886
|
+
this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
|
|
887
|
+
return false;
|
|
743
888
|
}
|
|
744
|
-
return
|
|
889
|
+
return true;
|
|
745
890
|
}
|
|
746
891
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
892
|
+
/** Validates metadata directly */
|
|
893
|
+
async #revalidateMetadata(
|
|
894
|
+
metas: TxMetaData[],
|
|
895
|
+
context?: string,
|
|
896
|
+
): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
|
|
751
897
|
const valid: TxMetaData[] = [];
|
|
752
898
|
const invalid: string[] = [];
|
|
753
|
-
|
|
754
|
-
for (const meta of
|
|
755
|
-
|
|
756
|
-
if (!buffer) {
|
|
757
|
-
this.#log.warn(`Tx ${meta.txHash} not found in DB during validation`);
|
|
758
|
-
invalid.push(meta.txHash);
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
const tx = Tx.fromBuffer(buffer);
|
|
763
|
-
const result = await this.#pendingTxValidator.validateTx(tx);
|
|
764
|
-
|
|
765
|
-
if (result.result === 'valid') {
|
|
899
|
+
const validator = await this.#createTxValidator();
|
|
900
|
+
for (const meta of metas) {
|
|
901
|
+
if (await this.#validateMeta(meta, validator, context)) {
|
|
766
902
|
valid.push(meta);
|
|
767
903
|
} else {
|
|
768
|
-
this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
|
|
769
904
|
invalid.push(meta.txHash);
|
|
770
905
|
}
|
|
771
906
|
}
|
|
772
|
-
|
|
773
907
|
return { valid, invalid };
|
|
774
908
|
}
|
|
775
909
|
|
|
@@ -785,8 +919,8 @@ export class TxPoolV2Impl {
|
|
|
785
919
|
for (const meta of txs) {
|
|
786
920
|
const conflict = checkNullifierConflict(
|
|
787
921
|
meta,
|
|
788
|
-
nullifier => this.#
|
|
789
|
-
txHash => this.#
|
|
922
|
+
nullifier => this.#indices.getTxHashByNullifier(nullifier),
|
|
923
|
+
txHash => this.#indices.getMetadata(txHash),
|
|
790
924
|
);
|
|
791
925
|
if (conflict.shouldIgnore) {
|
|
792
926
|
// Lower priority than existing - don't add, mark for deletion
|
|
@@ -796,13 +930,13 @@ export class TxPoolV2Impl {
|
|
|
796
930
|
toEvict.push(...conflict.txHashesToEvict);
|
|
797
931
|
// Remove evicted from indices immediately for subsequent checks
|
|
798
932
|
for (const evictHash of conflict.txHashesToEvict) {
|
|
799
|
-
const evictMeta = this.#
|
|
933
|
+
const evictMeta = this.#indices.getMetadata(evictHash);
|
|
800
934
|
if (evictMeta) {
|
|
801
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
935
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
802
936
|
}
|
|
803
937
|
}
|
|
804
938
|
// Add to pending indices immediately so subsequent txs in the batch see this tx
|
|
805
|
-
this.#addToPendingIndices(meta);
|
|
939
|
+
this.#indices.addToPendingIndices(meta);
|
|
806
940
|
added.push(meta);
|
|
807
941
|
}
|
|
808
942
|
}
|
|
@@ -810,43 +944,10 @@ export class TxPoolV2Impl {
|
|
|
810
944
|
return { added, toEvict };
|
|
811
945
|
}
|
|
812
946
|
|
|
813
|
-
//
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
#unmineTxs(txs: TxMetaData[]): TxMetaData[] {
|
|
817
|
-
for (const meta of txs) {
|
|
818
|
-
meta.minedL2BlockId = undefined;
|
|
819
|
-
}
|
|
820
|
-
return txs;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
/** Removes protection from tx hashes and clears them from the protected map */
|
|
824
|
-
#clearProtection(txHashes: string[]): void {
|
|
825
|
-
for (const txHashStr of txHashes) {
|
|
826
|
-
this.#protectedTransactions.delete(txHashStr);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// --- Batch Operation Steps ---
|
|
831
|
-
|
|
832
|
-
/** Deletes a batch of transactions permanently */
|
|
833
|
-
async #deleteTxsBatch(txHashes: string[]): Promise<void> {
|
|
834
|
-
if (txHashes.length === 0) {
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
await this.#store.transactionAsync(async () => {
|
|
839
|
-
for (const txHashStr of txHashes) {
|
|
840
|
-
await this.#deleteTx(txHashStr);
|
|
841
|
-
}
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
this.#callbacks.onTxsRemoved(txHashes);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
// --- Block & Tx Info Steps ---
|
|
947
|
+
// ============================================================================
|
|
948
|
+
// PRIVATE HELPERS - Block & Hydration
|
|
949
|
+
// ============================================================================
|
|
848
950
|
|
|
849
|
-
/** Builds a block ID from a block header */
|
|
850
951
|
async #buildBlockId(block: BlockHeader): Promise<L2BlockId> {
|
|
851
952
|
return {
|
|
852
953
|
number: block.globalVariables.blockNumber,
|
|
@@ -866,50 +967,6 @@ export class TxPoolV2Impl {
|
|
|
866
967
|
};
|
|
867
968
|
}
|
|
868
969
|
|
|
869
|
-
/** Marks a batch of transactions as mined */
|
|
870
|
-
#markTxsAsMined(metas: TxMetaData[], blockId: L2BlockId): void {
|
|
871
|
-
for (const meta of metas) {
|
|
872
|
-
this.#markAsMined(meta, blockId);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// --- Add Transaction Steps ---
|
|
877
|
-
|
|
878
|
-
/** Persists a transaction to the database */
|
|
879
|
-
async #persistTx(txHashStr: string, tx: Tx): Promise<void> {
|
|
880
|
-
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/** Adds a new transaction as protected, returning its metadata */
|
|
884
|
-
async #addNewProtectedTx(tx: Tx, slotNumber: SlotNumber): Promise<TxMetaData> {
|
|
885
|
-
const txHashStr = tx.getTxHash().toString();
|
|
886
|
-
const meta = await buildTxMetaData(tx);
|
|
887
|
-
|
|
888
|
-
this.#protectedTransactions.set(txHashStr, slotNumber);
|
|
889
|
-
await this.#persistTx(txHashStr, tx);
|
|
890
|
-
this.#metadata.set(txHashStr, meta);
|
|
891
|
-
// Don't add to pending indices since it's protected
|
|
892
|
-
|
|
893
|
-
this.#log.verbose(`Added protected tx ${txHashStr} for slot ${slotNumber}`);
|
|
894
|
-
return meta;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
/** Adds a new transaction as mined, returning its metadata */
|
|
898
|
-
async #addNewMinedTx(tx: Tx, blockId: L2BlockId): Promise<TxMetaData> {
|
|
899
|
-
const txHashStr = tx.getTxHash().toString();
|
|
900
|
-
const meta = await buildTxMetaData(tx);
|
|
901
|
-
meta.minedL2BlockId = blockId;
|
|
902
|
-
|
|
903
|
-
await this.#persistTx(txHashStr, tx);
|
|
904
|
-
this.#metadata.set(txHashStr, meta);
|
|
905
|
-
// Don't add to pending indices since it's mined
|
|
906
|
-
|
|
907
|
-
this.#log.verbose(`Added mined tx ${txHashStr} from block ${blockId.number}`);
|
|
908
|
-
return meta;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// --- Hydration Steps ---
|
|
912
|
-
|
|
913
970
|
/** Loads all transactions from the database, returning loaded txs and deserialization errors */
|
|
914
971
|
async #loadAllTxsFromDb(): Promise<{
|
|
915
972
|
loaded: { tx: Tx; meta: TxMetaData }[];
|
|
@@ -919,6 +976,11 @@ export class TxPoolV2Impl {
|
|
|
919
976
|
const errors: string[] = [];
|
|
920
977
|
|
|
921
978
|
for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()) {
|
|
979
|
+
// Skip soft-deleted transactions - they stay in DB but not in indices
|
|
980
|
+
if (this.#deletedPool.isSoftDeleted(txHashStr)) {
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
|
|
922
984
|
try {
|
|
923
985
|
const tx = Tx.fromBuffer(buffer);
|
|
924
986
|
const meta = await buildTxMetaData(tx);
|
|
@@ -949,50 +1011,6 @@ export class TxPoolV2Impl {
|
|
|
949
1011
|
}
|
|
950
1012
|
}
|
|
951
1013
|
|
|
952
|
-
/** Partitions transactions by mined status */
|
|
953
|
-
#partitionByMinedStatus(txs: { tx: Tx; meta: TxMetaData }[]): {
|
|
954
|
-
mined: TxMetaData[];
|
|
955
|
-
nonMined: { tx: Tx; meta: TxMetaData }[];
|
|
956
|
-
} {
|
|
957
|
-
const mined: TxMetaData[] = [];
|
|
958
|
-
const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
|
|
959
|
-
|
|
960
|
-
for (const entry of txs) {
|
|
961
|
-
if (entry.meta.minedL2BlockId !== undefined) {
|
|
962
|
-
mined.push(entry.meta);
|
|
963
|
-
} else {
|
|
964
|
-
nonMined.push(entry);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
return { mined, nonMined };
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
/** Validates non-mined transactions, returning valid metadata and invalid hashes */
|
|
972
|
-
async #validateNonMinedTxs(txs: { tx: Tx; meta: TxMetaData }[]): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
|
|
973
|
-
const valid: TxMetaData[] = [];
|
|
974
|
-
const invalid: string[] = [];
|
|
975
|
-
|
|
976
|
-
for (const { tx, meta } of txs) {
|
|
977
|
-
const result = await this.#pendingTxValidator.validateTx(tx);
|
|
978
|
-
if (result.result === 'valid') {
|
|
979
|
-
valid.push(meta);
|
|
980
|
-
} else {
|
|
981
|
-
this.#log.info(`Removing invalid tx ${meta.txHash} on startup: ${result.reason?.join(', ')}`);
|
|
982
|
-
invalid.push(meta.txHash);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
return { valid, invalid };
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
/** Populates metadata index for mined transactions */
|
|
990
|
-
#populateMinedIndices(metas: TxMetaData[]): void {
|
|
991
|
-
for (const meta of metas) {
|
|
992
|
-
this.#metadata.set(meta.txHash, meta);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
1014
|
/**
|
|
997
1015
|
* Rebuilds the pending pool by processing each tx through pre-add rules.
|
|
998
1016
|
* Starts with an empty pending pool and adds txs one by one, resolving conflicts.
|
|
@@ -1010,24 +1028,26 @@ export class TxPoolV2Impl {
|
|
|
1010
1028
|
if (preAddResult.shouldIgnore) {
|
|
1011
1029
|
// Transaction rejected - mark for deletion from DB
|
|
1012
1030
|
rejected.push(meta.txHash);
|
|
1013
|
-
this.#log.debug(
|
|
1031
|
+
this.#log.debug(
|
|
1032
|
+
`Rejected tx ${meta.txHash} during rebuild: ${preAddResult.reason?.message ?? 'unknown reason'}`,
|
|
1033
|
+
);
|
|
1014
1034
|
continue;
|
|
1015
1035
|
}
|
|
1016
1036
|
|
|
1017
1037
|
// Evict any conflicting txs identified by pre-add rules
|
|
1018
1038
|
for (const evictHashStr of preAddResult.txHashesToEvict) {
|
|
1019
|
-
const evictMeta = this.#
|
|
1039
|
+
const evictMeta = this.#indices.getMetadata(evictHashStr);
|
|
1020
1040
|
if (evictMeta) {
|
|
1021
|
-
this.#removeFromPendingIndices(evictMeta);
|
|
1022
|
-
this.#
|
|
1041
|
+
this.#indices.removeFromPendingIndices(evictMeta);
|
|
1042
|
+
this.#indices.remove(evictHashStr);
|
|
1023
1043
|
rejected.push(evictHashStr);
|
|
1024
1044
|
accepted.delete(evictHashStr);
|
|
1025
1045
|
this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
|
|
1026
1046
|
}
|
|
1027
1047
|
}
|
|
1028
1048
|
|
|
1029
|
-
// Add to
|
|
1030
|
-
this.#
|
|
1049
|
+
// Add to indices
|
|
1050
|
+
this.#indices.addPending(meta);
|
|
1031
1051
|
accepted.add(meta.txHash);
|
|
1032
1052
|
}
|
|
1033
1053
|
|
|
@@ -1035,207 +1055,32 @@ export class TxPoolV2Impl {
|
|
|
1035
1055
|
return { accepted: [...accepted], rejected };
|
|
1036
1056
|
}
|
|
1037
1057
|
|
|
1038
|
-
// --- Add Pending Tx Steps ---
|
|
1039
|
-
|
|
1040
|
-
/** Checks if a tx is a duplicate (already in pool) */
|
|
1041
|
-
#isDuplicateTx(txHashStr: string): boolean {
|
|
1042
|
-
return this.#metadata.has(txHashStr);
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
/** Adds a new pending tx to the pool, returning its metadata */
|
|
1046
|
-
async #addNewPendingTx(tx: Tx): Promise<TxMetaData> {
|
|
1047
|
-
const txHashStr = tx.getTxHash().toString();
|
|
1048
|
-
const meta = await buildTxMetaData(tx);
|
|
1049
|
-
|
|
1050
|
-
await this.#persistTx(txHashStr, tx);
|
|
1051
|
-
this.#addToIndices(meta);
|
|
1052
|
-
|
|
1053
|
-
this.#log.verbose(`Added tx ${txHashStr} to pool`, {
|
|
1054
|
-
eventName: 'tx-added-to-pool',
|
|
1055
|
-
state: this.#getTxState(meta),
|
|
1056
|
-
});
|
|
1057
|
-
|
|
1058
|
-
return meta;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
1058
|
// ============================================================================
|
|
1062
|
-
//
|
|
1059
|
+
// PRIVATE HELPERS - Pool Access Adapters
|
|
1063
1060
|
// ============================================================================
|
|
1064
1061
|
|
|
1065
|
-
#addToIndices(meta: TxMetaData): void {
|
|
1066
|
-
this.#metadata.set(meta.txHash, meta);
|
|
1067
|
-
|
|
1068
|
-
if (this.#getTxState(meta) === 'pending') {
|
|
1069
|
-
this.#addToPendingIndices(meta);
|
|
1070
|
-
}
|
|
1071
|
-
// Protected and mined txs don't go into pending indices
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
#addToPendingIndices(meta: TxMetaData): void {
|
|
1075
|
-
// Add to nullifier index
|
|
1076
|
-
for (const nullifier of meta.nullifiers) {
|
|
1077
|
-
this.#nullifierToTxHash.set(nullifier, meta.txHash);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
// Add to fee payer index
|
|
1081
|
-
let feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
|
|
1082
|
-
if (!feePayerSet) {
|
|
1083
|
-
feePayerSet = new Set();
|
|
1084
|
-
this.#feePayerToTxHashes.set(meta.feePayer, feePayerSet);
|
|
1085
|
-
}
|
|
1086
|
-
feePayerSet.add(meta.txHash);
|
|
1087
|
-
|
|
1088
|
-
// Add to priority bucket
|
|
1089
|
-
let prioritySet = this.#pendingByPriority.get(meta.priorityFee);
|
|
1090
|
-
if (!prioritySet) {
|
|
1091
|
-
prioritySet = new Set();
|
|
1092
|
-
this.#pendingByPriority.set(meta.priorityFee, prioritySet);
|
|
1093
|
-
}
|
|
1094
|
-
prioritySet.add(meta.txHash);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
#removeFromPendingIndices(meta: TxMetaData): void {
|
|
1098
|
-
// Remove from nullifier index
|
|
1099
|
-
for (const nullifier of meta.nullifiers) {
|
|
1100
|
-
this.#nullifierToTxHash.delete(nullifier);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// Remove from fee payer index
|
|
1104
|
-
const feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
|
|
1105
|
-
if (feePayerSet) {
|
|
1106
|
-
feePayerSet.delete(meta.txHash);
|
|
1107
|
-
if (feePayerSet.size === 0) {
|
|
1108
|
-
this.#feePayerToTxHashes.delete(meta.feePayer);
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
// Remove from priority map
|
|
1113
|
-
const hashSet = this.#pendingByPriority.get(meta.priorityFee);
|
|
1114
|
-
if (hashSet) {
|
|
1115
|
-
hashSet.delete(meta.txHash);
|
|
1116
|
-
if (hashSet.size === 0) {
|
|
1117
|
-
this.#pendingByPriority.delete(meta.priorityFee);
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
#updateProtection(txHashStr: string, slotNumber: SlotNumber): void {
|
|
1123
|
-
const currentSlot = this.#protectedTransactions.get(txHashStr);
|
|
1124
|
-
|
|
1125
|
-
// Only update if not already protected at an equal or later slot
|
|
1126
|
-
if (currentSlot !== undefined && currentSlot >= slotNumber) {
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// Remove from pending indices if transitioning from pending to protected
|
|
1131
|
-
if (currentSlot === undefined) {
|
|
1132
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1133
|
-
if (meta) {
|
|
1134
|
-
this.#removeFromPendingIndices(meta);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
this.#protectedTransactions.set(txHashStr, slotNumber);
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
#markAsMined(meta: TxMetaData, blockId: L2BlockId): void {
|
|
1142
|
-
meta.minedL2BlockId = blockId;
|
|
1143
|
-
// Safe to call unconditionally - removeFromPendingIndices is idempotent
|
|
1144
|
-
this.#removeFromPendingIndices(meta);
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
async #deleteTx(txHashStr: string): Promise<void> {
|
|
1148
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1149
|
-
if (!meta) {
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
// Remove from all indices
|
|
1154
|
-
this.#metadata.delete(txHashStr);
|
|
1155
|
-
this.#protectedTransactions.delete(txHashStr);
|
|
1156
|
-
this.#removeFromPendingIndices(meta);
|
|
1157
|
-
|
|
1158
|
-
// Remove from persistence
|
|
1159
|
-
await this.#txsDB.delete(txHashStr);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
// ============================================================================
|
|
1163
|
-
// HELPER FUNCTIONS - Adapters
|
|
1164
|
-
// ============================================================================
|
|
1165
|
-
|
|
1166
|
-
/** Gets all pending transactions for a given fee payer. */
|
|
1167
|
-
#getFeePayerPendingTxs(feePayer: string): TxMetaData[] {
|
|
1168
|
-
const txHashes = this.#feePayerToTxHashes.get(feePayer);
|
|
1169
|
-
if (!txHashes) {
|
|
1170
|
-
return [];
|
|
1171
|
-
}
|
|
1172
|
-
const result: TxMetaData[] = [];
|
|
1173
|
-
for (const txHashStr of txHashes) {
|
|
1174
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1175
|
-
if (meta && this.#getTxState(meta) === 'pending') {
|
|
1176
|
-
result.push(meta);
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
return result;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
/**
|
|
1183
|
-
* Creates a PoolOperations adapter for use with the eviction manager.
|
|
1184
|
-
*/
|
|
1185
1062
|
#createPoolOperations(): PoolOperations {
|
|
1186
1063
|
return {
|
|
1187
|
-
getPendingTxs: ()
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
result.push(meta);
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
return result;
|
|
1198
|
-
},
|
|
1199
|
-
getPendingFeePayers: (): string[] => {
|
|
1200
|
-
return Array.from(this.#feePayerToTxHashes.keys());
|
|
1201
|
-
},
|
|
1202
|
-
getFeePayerPendingTxs: (feePayer: string): TxMetaData[] => {
|
|
1203
|
-
return this.#getFeePayerPendingTxs(feePayer);
|
|
1204
|
-
},
|
|
1205
|
-
getPendingTxCount: (): number => {
|
|
1206
|
-
return this.getPendingTxCount();
|
|
1207
|
-
},
|
|
1208
|
-
getLowestPriorityPending: (limit: number): string[] => {
|
|
1209
|
-
return this.getLowestPriorityPending(limit).map(h => h.toString());
|
|
1210
|
-
},
|
|
1211
|
-
deleteTxs: async (txHashes: string[]): Promise<void> => {
|
|
1212
|
-
await this.#store.transactionAsync(async () => {
|
|
1213
|
-
for (const txHashStr of txHashes) {
|
|
1214
|
-
await this.#deleteTx(txHashStr);
|
|
1215
|
-
}
|
|
1216
|
-
});
|
|
1217
|
-
this.#callbacks.onTxsRemoved(txHashes);
|
|
1218
|
-
},
|
|
1064
|
+
getPendingTxs: () => this.#indices.getPendingTxs(),
|
|
1065
|
+
getPendingFeePayers: () => this.#indices.getPendingFeePayers(),
|
|
1066
|
+
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
1067
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
1068
|
+
getLowestPriorityPending: (limit: number) => this.#indices.getLowestPriorityPending(limit),
|
|
1069
|
+
deleteTxs: (txHashes: string[], reason?: string) => this.#evictTxs(txHashes, reason ?? 'unknown'),
|
|
1219
1070
|
};
|
|
1220
1071
|
}
|
|
1221
1072
|
|
|
1222
|
-
/**
|
|
1223
|
-
* Creates a PreAddPoolAccess adapter for use with pre-add eviction rules.
|
|
1224
|
-
* All methods work with strings and TxMetaData for efficiency.
|
|
1225
|
-
*/
|
|
1226
1073
|
#createPreAddPoolAccess(): PreAddPoolAccess {
|
|
1227
1074
|
return {
|
|
1228
|
-
getMetadata: (txHashStr: string)
|
|
1229
|
-
const meta = this.#
|
|
1230
|
-
if (!meta || this.#getTxState(meta) !== 'pending') {
|
|
1075
|
+
getMetadata: (txHashStr: string) => {
|
|
1076
|
+
const meta = this.#indices.getMetadata(txHashStr);
|
|
1077
|
+
if (!meta || this.#indices.getTxState(meta) !== 'pending') {
|
|
1231
1078
|
return undefined;
|
|
1232
1079
|
}
|
|
1233
1080
|
return meta;
|
|
1234
1081
|
},
|
|
1235
|
-
getTxHashByNullifier: (nullifier: string)
|
|
1236
|
-
|
|
1237
|
-
},
|
|
1238
|
-
getFeePayerBalance: async (feePayer: string): Promise<bigint> => {
|
|
1082
|
+
getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
|
|
1083
|
+
getFeePayerBalance: async (feePayer: string) => {
|
|
1239
1084
|
const db = this.#worldStateSynchronizer.getCommitted();
|
|
1240
1085
|
const publicStateSource = new DatabasePublicStateSource(db);
|
|
1241
1086
|
const balance = await publicStateSource.storageRead(
|
|
@@ -1244,22 +1089,9 @@ export class TxPoolV2Impl {
|
|
|
1244
1089
|
);
|
|
1245
1090
|
return balance.toBigInt();
|
|
1246
1091
|
},
|
|
1247
|
-
getFeePayerPendingTxs: (feePayer: string)
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
getPendingTxCount: (): number => {
|
|
1251
|
-
return this.getPendingTxCount();
|
|
1252
|
-
},
|
|
1253
|
-
getLowestPriorityPendingTx: (): TxMetaData | undefined => {
|
|
1254
|
-
// Iterate in ascending order to find the lowest priority
|
|
1255
|
-
for (const txHashStr of this.#iteratePendingByPriority('asc')) {
|
|
1256
|
-
const meta = this.#metadata.get(txHashStr);
|
|
1257
|
-
if (meta) {
|
|
1258
|
-
return meta;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
return undefined;
|
|
1262
|
-
},
|
|
1092
|
+
getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
|
|
1093
|
+
getPendingTxCount: () => this.#indices.getPendingTxCount(),
|
|
1094
|
+
getLowestPriorityPendingTx: () => this.#indices.getLowestPriorityPendingTx(),
|
|
1263
1095
|
};
|
|
1264
1096
|
}
|
|
1265
1097
|
}
|