Skip to content

Conversation

@Roasbeef
Copy link
Member

@Roasbeef Roasbeef commented Oct 3, 2025

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 TxValidator interface 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, and CheckSequenceLocks. 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 TxValidator interface defines five core validation operations that must be performed on every transaction. ValidateSanity checks transaction structure without blockchain context. ValidateUtxoAvailability checks 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. ValidateInputs verifies that inputs exist and are unspent while calculating the transaction fee. ValidateScripts performs expensive cryptographic signature verification. ValidateSequenceLocks validates BIP 68 relative timelocks.

The StandardTxValidator implementation 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 checkMempoolAcceptance function in validation_v2.go orchestrates 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 GetConflicts method 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 MempoolConfig now requires callers to provide fully constructed PolicyEnforcer, TxValidator, and OrphanManager instances. 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 MaybeAcceptTransaction implementation 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.

ProcessTransaction wraps MaybeAcceptTransaction with 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.go uses mock implementations of PolicyEnforcer, 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 from ValidateUtxoAvailability without needing actual blockchain state. The test harness includes helper functions like expectTxAccepted, expectTxOrphan, and expectValidationFailure that encapsulate common mock setup patterns, reducing test boilerplate.

We also refactored the basic TxMempoolV2 tests in mempool_v2_test.go to eliminate approximately 150 lines of duplicated configuration setup through the introduction of newTestMempoolConfig and newTestMempoolV2 helper 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 TxMempool interface 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 PackageAnalyzer interface 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.go and the expanded PolicyEnforcer interface in policy_enforcer.go to understand the contract between components. Then examine the extracted helpers in policy.go and compare them with the original TxPool implementations to verify behavior preservation.

The validation pipeline in validation_v2.go shows how the components compose together. Pay attention to the ordering of validation steps and the fail-fast design. The integration in mempool_v2.go demonstrates how dependency injection works and how MaybeAcceptTransaction orchestrates the validation flow.

The tests in mempool_v2_ops_test.go show 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.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants