-
Notifications
You must be signed in to change notification settings - Fork 2.5k
mempool: start of new in-place Mempoolv2 #2439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Roasbeef
wants to merge
11
commits into
btcsuite:truc-mempool-v2-base
Choose a base branch
from
Roasbeef:truc-mempool-v2-new
base: truc-mempool-v2-base
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
mempool: start of new in-place Mempoolv2 #2439
Roasbeef
wants to merge
11
commits into
btcsuite:truc-mempool-v2-base
from
Roasbeef:truc-mempool-v2-new
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In this commit, we create the foundational TxMempoolV2 structure that integrates TxGraph, OrphanManager, and PolicyEnforcer into a cohesive mempool implementation. This establishes the architecture for the new graph-based mempool that will eventually replace the flat-map TxPool. Architecture and Design Philosophy: The TxMempoolV2 separates three critical concerns that were previously intertwined in the old TxPool: data structure operations (graph), lifecycle management (orphans), and policy enforcement (validation). This separation provides significant benefits for testing, maintainability, and future protocol upgrades. The graph component handles the pure data structure: transaction nodes, parent-child relationships, and efficient traversal for ancestor/descendant queries. It provides O(1) lookups and maintains cluster information needed for RBF validation, where replacement transactions must improve the fee rate of the entire cluster they affect. The OrphanManager uses a completely separate graph instance for orphan transactions. This isolation prevents potentially invalid or spam transactions from polluting the main graph used for mining and fee estimation. Orphans have different lifecycle requirements including TTL-based expiration and peer-based tagging for removal when peers disconnect or misbehave. The PolicyEnforcer interface abstracts policy decisions including BIP 125 RBF validation, ancestor/descendant limit enforcement, and fee rate validation. This abstraction enables different policy configurations for testing and future protocol changes without modifying the core mempool structure. Component Integration: The NewTxMempoolV2 constructor applies sensible defaults when configuration is not provided. For the graph, we use 100k node capacity and 101 transaction max package size matching Bitcoin Core. For orphan management, we default to 100 orphan limit, 100KB max size, 20 minute TTL, and 5 minute expiration scan interval. For policy enforcement, we use Bitcoin Core compatible defaults: 25 ancestor/descendant limits, 101 KB size limits, and BIP 125 RBF with 0xfffffffd sequence threshold. Implementation Status: This commit implements the structure and constructor but stubs out most methods with "not implemented" panics. The simple query methods are implemented: Count (delegates to graph), HaveTransaction (checks both main and orphan), IsTransactionInPool (main only), IsOrphanInPool (orphan only), and LastUpdated (atomic timestamp load). The stubbed methods will be implemented in subsequent tasks: - Transaction operations (MaybeAcceptTransaction, ProcessTransaction, etc) in implement-tx-operations task - Orphan operations (RemoveOrphan, ProcessOrphans, etc) in implement-orphan-ops task - Query operations (FetchTransaction, TxDescs, etc) in implement-query-ops task - RBF validation (CheckMempoolAcceptance) in implement-rbf-validation task Concurrency Model: The mempool uses an RWMutex for concurrency control, allowing multiple concurrent readers (mining, RPC handlers) while serializing writers (transaction add/remove). The LastUpdated field uses atomic operations for lock-free reads, which is critical for frequent polling by mining code and RPC handlers that need to detect mempool changes. This structure runs in parallel with the old TxPool during development and testing. The _v2 suffix is temporary and will be removed once the implementation is validated and the old TxPool is retired.
In this commit, we add a complete test suite for the TxMempoolV2 core structure and constructor. These tests verify that all components are properly initialized and that the basic query methods work correctly. Test Coverage: TestNewTxMempoolV2 verifies the constructor creates a properly initialized mempool with all three components (graph, orphan manager, policy enforcer) and sets the initial LastUpdated timestamp to a recent time. TestMempoolConfigDefaults ensures that when nil configuration is provided, appropriate defaults are applied for all components. This is critical because most production code will rely on defaults rather than specifying every configuration parameter. TestMempoolCount, TestMempoolHaveTransaction, TestMempoolIsTransactionInPool, and TestMempoolIsOrphanInPool test the basic query methods. These tests currently verify behavior on empty mempools. Once transaction operations are implemented in subsequent tasks, these tests will be expanded to cover actual transaction addition and lookup. TestMempoolLastUpdated verifies that the LastUpdated method returns a valid timestamp that's recent (within the last second). This tests the atomic timestamp initialization in the constructor. TestMempoolConcurrentAccess is a smoke test for the RWMutex concurrency model. It launches 10 goroutines that each perform 100 concurrent reads, verifying that the read lock allows true concurrent access without races or deadlocks. TestMempoolStubbedMethodsPanic verifies that all unimplemented methods panic as expected with "not implemented" messages. This documents which methods are not yet functional and ensures they fail fast rather than returning incorrect results. TestMempoolInterfaceCompliance is an explicit compile-time check that TxMempoolV2 implements the TxMempool interface. While this is also checked by the var declaration in mempool_v2.go, the explicit test serves as documentation. TestMempoolConfigCustomization verifies that custom configuration is properly applied when provided, ensuring that operators can override defaults for specific deployment scenarios. Future Expansion: These tests establish the foundation for more comprehensive integration testing. As transaction operations, orphan management, and query operations are implemented in subsequent tasks, these test files will be expanded to cover the full functionality. The tests follow the project's testing conventions including parallel execution (t.Parallel) for improved test performance and use of the testify/require package for clear assertion messages. All tests pass with 100% success rate.
In this commit, we extract three validation helper functions from TxPool's internal methods into standalone functions in policy.go. This allows both the existing TxPool and the new TxMempoolV2 to share the same validation logic without duplication. The extracted functions are: CheckSegWitDeployment validates that witness transactions can only be accepted when SegWit is active. This prevents accepting transactions into the mempool that cannot yet be mined. The function includes simnet-specific messaging to help developers understand activation thresholds during testing. CheckTransactionSigCost validates that a transaction's signature operation cost does not exceed the maximum allowed per transaction. This check is critical for preventing denial-of-service attacks that attempt to fill blocks with expensive signature verification operations. CheckRelayFee validates minimum relay fee requirements, including both absolute fee checks and optional priority-based free transaction acceptance. The priority calculation uses the mining package's CalcPriority to determine if zero-fee transactions have sufficient coin-age to qualify for relay. CheckSequenceLocks validates BIP 68 relative timelocks, ensuring transactions can be included in the next block with respect to their input sequence numbers and median time past. These functions maintain identical behavior to their previous inline implementations, with the addition of accepting configuration parameters that were previously accessed via the TxPool receiver. This makes them testable in isolation and reusable across different mempool implementations. The extraction follows the principle of single responsibility, where each function handles one specific aspect of transaction validation. Error handling preserves the existing chainRuleError and txRuleError wrapping to maintain compatibility with the RPC layer's error reporting.
In this commit, we update TxPool's internal validation methods to call the newly extracted standalone validation functions in policy.go. This removes code duplication and ensures both TxPool and TxMempoolV2 use identical validation logic. The validateSegWitDeployment method now delegates to CheckSegWitDeployment, passing the necessary deployment checker, chain parameters, and best height. This reduces the method from 30 lines to a simple delegation call while maintaining identical behavior. The validateSigCost method now calls CheckTransactionSigCost, passing the configured maximum signature operation cost. The standalone function handles all the blockchain.GetSigOpCost logic and error wrapping that was previously inline. The validateRelayFeeMet method has been simplified to call CheckRelayFee for the core fee and priority validation, then handle rate limiting separately. This separation of concerns makes the rate limiting logic more obvious, as it only applies after the minimum fee checks pass. The rate limiting code remains in TxPool since it maintains per-pool state (mp.pennyTotal) that is specific to the legacy implementation. These changes maintain full backward compatibility with existing behavior while enabling TxMempoolV2 to reuse the same validation logic without reimplementing it. All error messages and rejection codes remain identical to preserve RPC compatibility.
In this commit, we introduce the TxValidator interface to separate low-level transaction validation (scripts, timelocks, inputs) from high-level policy enforcement (fees, limits, RBF rules). This abstraction allows different mempool implementations to share validation logic while enabling easy testing through mock implementations. The TxValidator interface defines five core validation operations: ValidateSanity performs preliminary checks without blockchain context, validating invariant rules like non-negative outputs and valid script format. This is always the first validation step. ValidateUtxoAvailability checks if a transaction's outputs already exist in the blockchain (duplicate detection) and identifies missing parent transactions (orphan detection). The function modifies the UTXO view by removing the transaction's own outputs, ensuring subsequent checks only examine inputs. It returns a list of missing parent hashes, with a non-empty list indicating an orphan transaction. ValidateInputs performs blockchain context-aware validation, checking that inputs exist and are unspent while calculating the transaction fee. This must be called after UTXO availability checks to ensure the view contains valid entries. ValidateScripts verifies cryptographic signatures for all inputs. This is the most expensive validation operation and should be performed last, after all policy checks pass, to avoid wasting CPU on transactions that will be rejected for other reasons. ValidateSequenceLocks validates BIP 68 relative timelocks, ensuring the transaction can be included in the next block. StandardTxValidator implements this interface using Bitcoin Core's validation rules, delegating to the blockchain package for consensus checks. It wraps blockchain errors with appropriate RuleError types for RPC compatibility. The TxValidatorConfig allows injection of signature caches, hash caches, sequence lock calculation functions, and chain parameters. This makes the validator flexible enough to work in both production and testing environments. This design enables TxMempoolV2 to use the same validation logic as TxPool while allowing tests to inject mock validators that return predetermined results, significantly simplifying test setup.
In this commit, we implement checkMempoolAcceptance for TxMempoolV2, which validates transactions for mempool admission using the TxValidator and PolicyEnforcer abstractions. This replaces the monolithic validation approach in TxPool with a composable design that cleanly separates concerns. The validation flow proceeds in a carefully ordered sequence to fail fast and avoid expensive operations on transactions that will be rejected: First, we check SegWit deployment status, which is cheap and eliminates witness transactions before SegWit activation. Next, we verify the transaction isn't already in the mempool or orphan pool through a quick hash lookup in the transaction graph. We then validate minimum transaction size (65 bytes non-witness) to prevent tiny spam transactions. This check happens early since it requires no blockchain context. The TxValidator.ValidateSanity performs consensus-level sanity checks on the transaction structure, validating things like non-negative outputs and proper input/output counts. We reject coinbase transactions at this stage since they can never be in the mempool. After fetching the UTXO view, we use TxValidator.ValidateUtxoAvailability to check if the transaction already exists in the blockchain and identify any missing parents. If missing parents are found, we return early with the orphan list, avoiding all remaining validation work. For non-orphan transactions, we use the transaction graph's GetConflicts to detect double-spends. This replaces the old checkPoolDoubleSpend with a more efficient graph-based approach that returns comprehensive conflict information for RBF validation. TxValidator.ValidateInputs performs blockchain context-aware checks and calculates the transaction fee, which is needed for subsequent policy checks. The PolicyEnforcer then validates standardness, sequence locks, signature operation cost, and relay fees. For replacement transactions, we check RBF signaling (explicit or inherited through ancestors) and validate BIP 125 rules using PolicyEnforcer.ValidateReplacement. This includes checking that the replacement pays higher fees and doesn't evict too many transactions. Finally, TxValidator.ValidateScripts performs expensive signature verification. By placing this last, we avoid wasting CPU on transactions that fail cheaper policy checks. The function returns a MempoolAcceptResult containing the transaction fee, size, conflicts, UTXO view, and best height. For orphans, it returns the missing parent hashes, allowing the caller to decide whether to store the orphan or reject it. This implementation demonstrates the value of the TxValidator and PolicyEnforcer abstractions, as the validation logic is clean, testable, and reusable across different mempool implementations.
In this commit, we update TxMempoolV2 to use the TxValidator interface and implement MaybeAcceptTransaction and ProcessTransaction using the new validation pipeline. This represents a major shift from the stub-based implementation to a functional mempool that can validate and accept transactions. The MempoolConfig structure has been redesigned to require explicit dependency injection. Instead of accepting raw configuration values and creating dependencies internally, the config now requires callers to provide fully constructed PolicyEnforcer, TxValidator, and OrphanManager instances. This makes dependencies explicit and enables easier testing with mock implementations. We introduce three new interface types (OrphanTxManager, TxFeeEstimator, TxAddrIndexer) that define only the methods TxMempoolV2 actually uses. This follows the Interface Segregation Principle, making dependencies minimal and allowing tests to provide simple mocks without implementing unused methods. The constructor NewTxMempoolV2 now validates that all required dependencies are provided, returning an error if any are nil. This fails fast at initialization rather than panicking at runtime, making configuration errors immediately obvious. The MaybeAcceptTransaction implementation delegates to checkMempoolAcceptance for validation, then handles the result based on whether the transaction is an orphan or conflicts with existing mempool transactions. For replacements, we remove conflicts from the graph before adding the new transaction, leveraging the graph's automatic cascade to descendants. The function creates a TxDesc and adds the transaction to the graph, updates the address index and fee estimator if enabled, and returns the descriptor for the caller. ProcessTransaction wraps MaybeAcceptTransaction with orphan handling. If the transaction is an orphan and orphans are allowed, it stores the orphan and returns an empty list. If the transaction is accepted, it processes any previously orphaned transactions that may now be acceptable, accumulating all accepted transactions into a result list. This enables batch processing of orphan chains when their parent arrives. This implementation completes the core transaction acceptance pipeline for TxMempoolV2, making it functionally equivalent to TxPool for adding individual transactions. The separation of validation (TxValidator), policy (PolicyEnforcer), and data structure (TxGraph) makes the code significantly more testable than the monolithic TxPool design.
In this commit, we add a comprehensive test suite for TxMempoolV2's MaybeAcceptTransaction functionality. These tests validate the entire transaction acceptance pipeline using mock dependencies, ensuring proper interaction between TxValidator, PolicyEnforcer, and the transaction graph. The test harness uses testify/mock to create mockPolicyEnforcer, mockTxValidator, and mockUtxoViewFetcher implementations. This allows tests to control exactly what validation results are returned, making it easy to test specific code paths without complex blockchain setup. We implement several helper functions that encapsulate common mock setup patterns: expectTxAccepted sets up all mocks for a successful transaction acceptance, including validation passes for sanity, UTXO availability, inputs, standardness, sequence locks, signature cost, relay fee, and scripts. This reduces test boilerplate for the happy path. expectTxOrphan configures mocks to return missing parents from ValidateUtxoAvailability, simulating orphan detection without requiring actual blockchain state. expectValidationFailure allows tests to inject failures at specific validation stages (sanity, UTXO availability, inputs, standardness, sequence locks, signature cost, relay fee, or scripts). This ensures each validation point is properly checked. expectRBFRejection sets up mocks for testing RBF rejection scenarios, including conflicts without proper signaling. expectEarlyRejection handles pre-validation failures like duplicate transactions in the blockchain. The test suite covers: Successful transaction acceptance, verifying the transaction is added to the graph and returned with correct fee and size information. Duplicate transaction rejection, both for mempool and blockchain duplicates. Orphan transaction detection, returning missing parent hashes without adding to the main pool. Validation failures at each stage of the pipeline, ensuring errors propagate correctly. RBF scenarios, including conflicts without signaling and successful replacements. The mock validator includes ValidateUtxoAvailability, which is the new method we added to the TxValidator interface. This test demonstrates the value of the interface abstraction, as we can test complex UTXO availability scenarios (duplicates, orphans) without blockchain state. These tests provide confidence that the validation pipeline works correctly and that TxMempoolV2 properly coordinates between its dependencies.
In this commit, we refactor the TxMempoolV2 tests to eliminate significant code duplication through the introduction of helper functions. This makes tests more maintainable and easier to understand. We introduce two helper functions: newTestMempoolConfig creates a standard test configuration with all required dependencies properly initialized. It provides sensible defaults for FetchUtxoView (returning empty viewpoint), BestHeight (100), MedianTimePast (current time), and creates standard instances of PolicyEnforcer, TxValidator, and OrphanManager. This eliminates the need to repeat 15+ lines of config setup in every test. newTestMempoolV2 wraps NewTxMempoolV2 with proper error handling and test failure. It calls t.Helper() to ensure test failures point to the correct line in the calling test rather than inside the helper. This reduces each test's setup to a single line. The refactoring updates all tests that were manually creating configs: TestNewTxMempoolV2 now uses newTestMempoolConfig instead of inline construction, reducing the test from 40+ lines to 15 lines while maintaining identical behavior. TestMempoolMissingDependenciesError continues to use newTestMempoolConfig as the base for creating invalid configurations. Each test case modifies the valid config to remove one dependency, ensuring we test all validation paths. TestMempoolCount, TestMempoolHaveTransaction, TestMempoolIsTransactionInPool, TestMempoolIsOrphanInPool, TestMempoolLastUpdated, TestMempoolConcurrentAccess, and TestMempoolStubbedMethodsPanic all now use newTestMempoolV2 for one-line setup. TestMempoolConfigCustomization remains unchanged, as it specifically tests custom dependency injection and needs explicit config construction. This refactoring eliminates approximately 150 lines of duplicated configuration code while making the tests more focused on what they're actually testing rather than setup boilerplate. The helpers also make it obvious when a test deviates from the standard configuration, improving readability.
In this commit, we expand the PolicyEnforcer interface to include methods for validating standardness, signature costs, and SegWit deployment. This makes PolicyEnforcer a complete abstraction for all policy-level validation, complementing the TxValidator's consensus-level checks. We add four new methods to the PolicyEnforcer interface: ValidateStandardness checks transaction standardness requirements including version, finalization, size limits, script types, and dust outputs. This delegates to the existing CheckTransactionStandard and checkInputsStandard helper functions. ValidateSigCost validates that signature operation cost doesn't exceed relay limits, using the CheckTransactionSigCost helper function we extracted earlier. ValidateSegWitDeployment ensures SegWit transactions are only accepted when SegWit is active, using CheckSegWitDeployment. ValidateRelayFee signature is updated to accept utxoView and nextBlockHeight, enabling priority calculation for low-fee transactions. The implementation now calls CheckRelayFee for the core validation, then applies rate limiting separately. The PolicyConfig structure is expanded to include MaxTxVersion, MaxSigOpCostPerTx, IsDeploymentActive, ChainParams, and BestHeight. These fields enable the policy enforcer to perform validation without requiring external context from the mempool. DefaultPolicyConfig provides sensible defaults for all new fields, assuming SegWit is active and using mainnet parameters. StandardPolicyEnforcer implements all new methods by delegating to the standalone helper functions in policy.go. This keeps the implementation thin while maintaining full test coverage through the existing helper function tests. The ValidateRelayFee tests are updated to pass the new parameters. We fix a test case that was incorrectly expecting non-new transactions with low fees to be rejected - per the CheckRelayFee implementation, non-new transactions (from reorgs) are exempted from priority checks for transactions under the size threshold. We add a new test case showing that large non-new transactions are still correctly rejected if their fees are insufficient. This expanded interface allows TxMempoolV2's validation pipeline to use PolicyEnforcer for all policy checks, creating a clean separation between consensus validation (TxValidator) and policy validation (PolicyEnforcer).
…imator In this commit, we add compile-time interface compliance checks for OrphanManager and FeeEstimator, ensuring they correctly implement the OrphanTxManager and TxFeeEstimator interfaces respectively. These var _ InterfaceType = (*ConcreteType)(nil) declarations are a Go idiom for compile-time interface verification. If OrphanManager doesn't implement all methods of OrphanTxManager, or FeeEstimator doesn't implement TxFeeEstimator, the code will fail to compile with a clear error message. This is particularly valuable during refactoring, as it ensures that if we change an interface method signature, all implementations are updated accordingly. The checks have zero runtime cost and serve as living documentation of the implementation relationships.
kmk142789
approved these changes
Nov 5, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
TxMempoolV2: Validation Pipeline Refactoring
Overview
This PR refactors transaction validation to support the new TxMempoolV2 implementation while maintaining full backward compatibility with the existing TxPool. The core change introduces a
TxValidatorinterface that separates consensus-level validation (scripts, timelocks, inputs) from policy-level validation (fees, limits, RBF), enabling both mempool implementations to share the same validation logic.Motivation and Design
The existing TxPool intertwines validation logic with mempool operations, making it difficult to test validation in isolation or reuse validation logic across different mempool implementations. TxMempoolV2 takes a different approach by separating concerns into three distinct components: TxValidator handles consensus validation, PolicyEnforcer handles policy decisions, and TxGraph manages the transaction relationship data structure.
This separation emerged from the need to support future protocol features like TRUC transactions (BIP 431) and ephemeral dust (P2A outputs). These features require package-level validation that examines multiple transactions together, which is difficult to implement cleanly in TxPool's monolithic design. By extracting validation into composable interfaces, we can layer package validation on top of the existing single-transaction validation without code duplication.
Implementation Details
Extracted Validation Helpers
We extracted four validation functions from TxPool's internal methods into standalone functions in
policy.go:CheckSegWitDeployment,CheckTransactionSigCost,CheckRelayFee, andCheckSequenceLocks. These functions encapsulate specific validation rules and can be called by both TxPool and TxMempoolV2. The existing TxPool was updated to use these helpers, reducing its validation methods from hundreds of lines to simple delegation calls while preserving identical behavior.TxValidator Interface
The new
TxValidatorinterface defines five core validation operations that must be performed on every transaction.ValidateSanitychecks transaction structure without blockchain context.ValidateUtxoAvailabilitychecks whether the transaction already exists in the blockchain and identifies missing parents for orphan detection, returning a list of missing parent hashes when inputs reference unknown transactions.ValidateInputsverifies that inputs exist and are unspent while calculating the transaction fee.ValidateScriptsperforms expensive cryptographic signature verification.ValidateSequenceLocksvalidates BIP 68 relative timelocks.The
StandardTxValidatorimplementation delegates to the blockchain package for consensus checks and wraps errors appropriately for RPC compatibility. This implementation is used by both TxPool (indirectly, through the extracted helpers) and TxMempoolV2 (directly, through the interface).Validation Pipeline
The
checkMempoolAcceptancefunction invalidation_v2.goorchestrates the complete validation flow for TxMempoolV2. The validation proceeds in a carefully ordered sequence designed to fail fast on cheap checks before performing expensive operations. Early checks include SegWit deployment status, duplicate detection in the mempool and orphan pools, and minimum transaction size. After fetching the UTXO view, we check whether the transaction already exists in the blockchain and identify any missing parents. For orphan transactions, we return immediately with the missing parent hashes, avoiding all subsequent validation work.For non-orphan transactions, we use the transaction graph's
GetConflictsmethod to detect double-spends. If conflicts exist, we later validate that the transaction properly signals replacement (BIP 125) and satisfies RBF rules. The validation continues with input checks, standardness validation, sequence lock validation, signature cost validation, and relay fee validation. Script validation happens last because it's the most expensive operation and there's no point wasting CPU on transactions that will be rejected for cheaper-to-check policy violations.MempoolConfig and Dependency Injection
TxMempoolV2's configuration was redesigned to require explicit dependency injection rather than accepting raw configuration values and creating dependencies internally. The
MempoolConfignow requires callers to provide fully constructedPolicyEnforcer,TxValidator, andOrphanManagerinstances. This makes dependencies explicit in the type system and enables testing with mock implementations. The constructor validates that all required dependencies are provided, failing fast at initialization rather than panicking at runtime when a nil dependency is accessed.We introduced three minimal interface types (
OrphanTxManager,TxFeeEstimator,TxAddrIndexer) that define only the methods TxMempoolV2 actually uses. This follows the Interface Segregation Principle and allows tests to provide simple mocks without implementing dozens of unused methods from the concrete types.Transaction Acceptance
The
MaybeAcceptTransactionimplementation uses the validation pipeline to check whether a transaction can be accepted, then handles the result based on whether it's an orphan or creates conflicts. For replacement transactions, we remove the conflicting transactions from the graph before adding the new transaction, leveraging the graph's automatic cascade to descendants. The function creates a TxDesc and adds the transaction to the graph, updates the address index and fee estimator if enabled, and returns the descriptor.ProcessTransactionwrapsMaybeAcceptTransactionwith orphan handling. When a transaction is accepted, it processes any previously orphaned transactions that may now be acceptable because their parent arrived, accumulating all accepted transactions into a result list for the caller.Testing Strategy
The test suite in
mempool_v2_ops_test.gouses mock implementations ofPolicyEnforcer,TxValidator, and the UTXO view fetcher. This allows testing specific validation paths without complex blockchain setup. For example, to test orphan detection, we configure the mock validator to return missing parent hashes fromValidateUtxoAvailabilitywithout needing actual blockchain state. The test harness includes helper functions likeexpectTxAccepted,expectTxOrphan, andexpectValidationFailurethat encapsulate common mock setup patterns, reducing test boilerplate.We also refactored the basic TxMempoolV2 tests in
mempool_v2_test.goto eliminate approximately 150 lines of duplicated configuration setup through the introduction ofnewTestMempoolConfigandnewTestMempoolV2helper functions.Next Steps
This PR completes the core transaction validation pipeline for TxMempoolV2, making it functionally capable of validating and accepting individual transactions. However, several pieces remain before TxMempoolV2 can replace TxPool in production.
The next immediate work involves implementing the remaining
TxMempoolinterface methods. This includes transaction removal with proper cascade logic, fetching transactions and descriptors for RPC responses, and generating mining descriptors for block template creation. Most of these operations will be straightforward delegations to the transaction graph.Following that, we'll implement package validation support for TRUC transactions (BIP 431) and ephemeral dust detection. This requires creating a
PackageAnalyzerinterface and implementing concrete analyzers that validate v3 transaction topology restrictions and identify P2A outputs that must be spent in the same package. The validation pipeline will be extended to call package analyzers when appropriate.Performance optimization and production integration come next. We'll benchmark TxMempoolV2 against TxPool to ensure there are no regressions, optimize graph operations if needed, and wire up TxMempoolV2 in the server with a feature flag for gradual rollout. Once validated in production, we can remove the legacy TxPool implementation.
Reviewer Guide
Start by reading the interface definitions in
tx_validator.goand the expandedPolicyEnforcerinterface inpolicy_enforcer.goto understand the contract between components. Then examine the extracted helpers inpolicy.goand compare them with the original TxPool implementations to verify behavior preservation.The validation pipeline in
validation_v2.goshows how the components compose together. Pay attention to the ordering of validation steps and the fail-fast design. The integration inmempool_v2.godemonstrates how dependency injection works and howMaybeAcceptTransactionorchestrates the validation flow.The tests in
mempool_v2_ops_test.goshow how mock-based testing enables comprehensive coverage of edge cases without complex blockchain setup. Look for test cases covering orphan detection, RBF scenarios, and validation failures at each stage of the pipeline.This refactoring maintains full backward compatibility. The existing TxPool continues to work identically, all tests pass, and the RPC interface is unchanged. The validation logic is functionally identical to the original implementation, just reorganized into reusable, testable components.