@ -50,6 +50,7 @@
# include <validationinterface.h>
# include <warnings.h>
# include <numeric>
# include <optional>
# include <string>
@ -477,6 +478,13 @@ public:
// Single transaction acceptance
MempoolAcceptResult AcceptSingleTransaction ( const CTransactionRef & ptx , ATMPArgs & args ) EXCLUSIVE_LOCKS_REQUIRED ( cs_main ) ;
/**
* Multiple transaction acceptance . Transactions may or may not be interdependent ,
* but must not conflict with each other . Parents must come before children if any
* dependencies exist , otherwise a TX_MISSING_INPUTS error will be returned .
*/
PackageMempoolAcceptResult AcceptMultipleTransactions ( const std : : vector < CTransactionRef > & txns , ATMPArgs & args ) EXCLUSIVE_LOCKS_REQUIRED ( cs_main ) ;
private :
// All the intermediate state that gets passed between the various levels
// of checking a given transaction.
@ -1064,6 +1072,76 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
return MempoolAcceptResult : : Success ( std : : move ( ws . m_replaced_transactions ) , ws . m_base_fees ) ;
}
PackageMempoolAcceptResult MemPoolAccept : : AcceptMultipleTransactions ( const std : : vector < CTransactionRef > & txns , ATMPArgs & args )
{
AssertLockHeld ( cs_main ) ;
PackageValidationState package_state ;
const unsigned int package_count = txns . size ( ) ;
std : : vector < Workspace > workspaces { } ;
workspaces . reserve ( package_count ) ;
std : : transform ( txns . cbegin ( ) , txns . cend ( ) , std : : back_inserter ( workspaces ) , [ ] ( const auto & tx ) {
return Workspace ( tx ) ;
} ) ;
std : : map < const uint256 , const MempoolAcceptResult > results ;
{
// Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
std : : unordered_set < COutPoint , SaltedOutpointHasher > inputs_seen ;
for ( const auto & tx : txns ) {
for ( const auto & input : tx - > vin ) {
if ( inputs_seen . find ( input . prevout ) ! = inputs_seen . end ( ) ) {
// This input is also present in another tx in the package.
package_state . Invalid ( PackageValidationResult : : PCKG_POLICY , " conflict-in-package " ) ;
return PackageMempoolAcceptResult ( package_state , { } ) ;
}
}
// Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
// catch duplicate inputs within a single tx. This is a more severe, consensus error,
// and we want to report that from CheckTransaction instead.
std : : transform ( tx - > vin . cbegin ( ) , tx - > vin . cend ( ) , std : : inserter ( inputs_seen , inputs_seen . end ( ) ) ,
[ ] ( const auto & input ) { return input . prevout ; } ) ;
}
}
LOCK ( m_pool . cs ) ;
// Do all PreChecks first and fail fast to avoid running expensive script checks when unnecessary.
for ( Workspace & ws : workspaces ) {
if ( ! PreChecks ( args , ws ) ) {
package_state . Invalid ( PackageValidationResult : : PCKG_TX , " transaction failed " ) ;
// Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
results . emplace ( ws . m_ptx - > GetWitnessHash ( ) , MempoolAcceptResult : : Failure ( ws . m_state ) ) ;
return PackageMempoolAcceptResult ( package_state , std : : move ( results ) ) ;
}
// Make the coins created by this transaction available for subsequent transactions in the
// package to spend. Since we already checked conflicts in the package and RBFs are
// impossible, we don't need to track the coins spent. Note that this logic will need to be
// updated if RBFs in packages are allowed in the future.
assert ( args . disallow_mempool_conflicts ) ;
m_viewmempool . PackageAddTransaction ( ws . m_ptx ) ;
}
for ( Workspace & ws : workspaces ) {
PrecomputedTransactionData txdata ;
if ( ! PolicyScriptChecks ( args , ws , txdata ) ) {
// Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
package_state . Invalid ( PackageValidationResult : : PCKG_TX , " transaction failed " ) ;
results . emplace ( ws . m_ptx - > GetWitnessHash ( ) , MempoolAcceptResult : : Failure ( ws . m_state ) ) ;
return PackageMempoolAcceptResult ( package_state , std : : move ( results ) ) ;
}
if ( args . m_test_accept ) {
// When test_accept=true, transactions that pass PolicyScriptChecks are valid because there are
// no further mempool checks (passing PolicyScriptChecks implies passing ConsensusScriptChecks).
results . emplace ( ws . m_ptx - > GetWitnessHash ( ) ,
MempoolAcceptResult : : Success ( std : : move ( ws . m_replaced_transactions ) , ws . m_base_fees ) ) ;
}
}
return PackageMempoolAcceptResult ( package_state , std : : move ( results ) ) ;
}
} // anon namespace
/** (try to) add transaction to memory pool with a specified acceptance time **/
@ -1101,6 +1179,29 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
return AcceptToMemoryPoolWithTime ( Params ( ) , pool , active_chainstate , tx , GetTime ( ) , bypass_limits , test_accept ) ;
}
PackageMempoolAcceptResult ProcessNewPackage ( CChainState & active_chainstate , CTxMemPool & pool ,
const Package & package , bool test_accept )
{
AssertLockHeld ( cs_main ) ;
assert ( test_accept ) ; // Only allow package accept dry-runs (testmempoolaccept RPC).
assert ( ! package . empty ( ) ) ;
assert ( std : : all_of ( package . cbegin ( ) , package . cend ( ) , [ ] ( const auto & tx ) { return tx ! = nullptr ; } ) ) ;
std : : vector < COutPoint > coins_to_uncache ;
const CChainParams & chainparams = Params ( ) ;
MemPoolAccept : : ATMPArgs args { chainparams , GetTime ( ) , /* bypass_limits */ false , coins_to_uncache ,
test_accept , /* disallow_mempool_conflicts */ true } ;
assert ( std : : addressof ( : : ChainstateActive ( ) ) = = std : : addressof ( active_chainstate ) ) ;
const PackageMempoolAcceptResult result = MemPoolAccept ( pool , active_chainstate ) . AcceptMultipleTransactions ( package , args ) ;
// Uncache coins pertaining to transactions that were not submitted to the mempool.
// Ensure the cache is still within its size limits.
for ( const COutPoint & hashTx : coins_to_uncache ) {
active_chainstate . CoinsTip ( ) . Uncache ( hashTx ) ;
}
return result ;
}
CTransactionRef GetTransaction ( const CBlockIndex * const block_index , const CTxMemPool * const mempool , const uint256 & hash , const Consensus : : Params & consensusParams , uint256 & hashBlock )
{
LOCK ( cs_main ) ;