Convert txmempool exists to GenTxidVariant

This commit is contained in:
marcofleon 2025-03-31 16:28:50 +01:00
parent c5da76b54b
commit a3b6eda613
18 changed files with 61 additions and 59 deletions

View file

@ -4238,7 +4238,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Always relay transactions received from peers with forcerelay
// permission, even if they were already in the mempool, allowing
// the node to function as a gateway for nodes hidden behind it.
if (!m_mempool.exists(GenTxid::Txid(tx.GetHash()))) {
if (!m_mempool.exists(tx.GetHash())) {
LogPrintf("Not relaying non-mempool transaction %s (wtxid=%s) from forcerelay peer=%d\n",
tx.GetHash().ToString(), tx.GetWitnessHash().ToString(), pfrom.GetId());
} else {

View file

@ -673,7 +673,7 @@ public:
{
if (!m_node.mempool) return false;
LOCK(m_node.mempool->cs);
return m_node.mempool->exists(GenTxid::Txid(txid));
return m_node.mempool->exists(Txid::FromUint256(txid));
}
bool hasDescendantsInMempool(const uint256& txid) override
{

View file

@ -106,7 +106,7 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
// wallet(s) having loaded it while we were processing
// mempool transactions; consider these as valid, instead of
// failed, but mark them as 'already there'
if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
if (pool.exists(tx->GetHash())) {
++already_there;
} else {
++failed;

View file

@ -27,7 +27,7 @@ MiniMiner::MiniMiner(const CTxMemPool& mempool, const std::vector<COutPoint>& ou
// Anything that's spent by the mempool is to-be-replaced
// Anything otherwise unavailable just has a bump fee of 0
for (const auto& outpoint : outpoints) {
if (!mempool.exists(GenTxid::Txid(outpoint.hash))) {
if (!mempool.exists(outpoint.hash)) {
// This UTXO is either confirmed or not yet submitted to mempool.
// If it's confirmed, no bump fee is required.
// If it's not yet submitted, we have no information, so return 0.

View file

@ -148,7 +148,7 @@ bool TxDownloadManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_rec
if (RecentConfirmedTransactionsFilter().contains(hash)) return true;
return RecentRejectsFilter().contains(hash) || m_opts.m_mempool.exists(gtxid);
return RecentRejectsFilter().contains(hash) || m_opts.m_mempool.exists(gtxid.ToVariant());
}
void TxDownloadManagerImpl::ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info)
@ -383,7 +383,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
fRejectedParents = true;
break;
} else if (RecentRejectsReconsiderableFilter().contains(parent_txid) &&
!m_opts.m_mempool.exists(GenTxid::Txid(parent_txid))) {
!m_opts.m_mempool.exists(Txid::FromUint256(parent_txid))) {
// More than 1 parent in m_lazy_recent_rejects_reconsiderable: 1p1c will not be
// sufficient to accept this package, so just give up here.
if (rejected_parent_reconsiderable.has_value()) {

View file

@ -32,7 +32,7 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
// If this transaction is not in our mempool, then we can't be sure
// we will know about all its inputs.
if (!pool.exists(GenTxid::Txid(tx.GetHash()))) {
if (!pool.exists(tx.GetHash())) {
return RBFTransactionState::UNKNOWN;
}
@ -107,7 +107,7 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
// Rather than check the UTXO set - potentially expensive - it's cheaper to just check
// if the new input refers to a tx that's in the mempool.
if (pool.exists(GenTxid::Txid(tx.vin[j].prevout.hash))) {
if (pool.exists(tx.vin[j].prevout.hash)) {
return strprintf("replacement %s adds unconfirmed input, idx %d",
tx.GetHash().ToString(), j);
}

View file

@ -309,7 +309,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
std::set<std::string> setDepends;
for (const CTxIn& txin : tx.vin)
{
if (pool.exists(GenTxid::Txid(txin.prevout.hash)))
if (pool.exists(txin.prevout.hash))
setDepends.insert(txin.prevout.hash.ToString());
}
@ -1030,7 +1030,7 @@ static RPCHelpMan submitpackage()
// Belt-and-suspenders check; everything should be successful here
CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size());
for (const auto& tx : txns) {
CHECK_NONFATAL(mempool.exists(GenTxid::Txid(tx->GetHash())));
CHECK_NONFATAL(mempool.exists(tx->GetHash()));
}
break;
}
@ -1054,7 +1054,7 @@ static RPCHelpMan submitpackage()
size_t num_broadcast{0};
for (const auto& tx : txns) {
// We don't want to re-submit the txn for validation in BroadcastTransaction
if (!mempool.exists(GenTxid::Txid(tx->GetHash()))) {
if (!mempool.exists(tx->GetHash())) {
continue;
}

View file

@ -173,7 +173,7 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
if (!pool.GetConflictTx(coin)) outpoints.push_back(coin);
}
for (const auto& tx : transactions) {
assert(pool.exists(GenTxid::Txid(tx->GetHash())));
assert(pool.exists(tx->GetHash()));
for (uint32_t n{0}; n < tx->vout.size(); ++n) {
COutPoint coin{tx->GetHash(), n};
if (!pool.GetConflictTx(coin)) outpoints.push_back(coin);

View file

@ -310,7 +310,7 @@ FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)
const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
// We only prioritise out of mempool transactions since PrioritiseTransaction doesn't
// filter for ephemeral dust
if (tx_pool.exists(GenTxid::Txid(txid))) {
if (tx_pool.exists(txid)) {
const auto tx_info{tx_pool.info(GenTxid::Txid(txid))};
if (GetDust(*tx_info.tx, tx_pool.m_opts.dust_relay_feerate).empty()) {
tx_pool.PrioritiseTransaction(txid.ToUint256(), delta);

View file

@ -79,7 +79,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
available.insert(i);
}
if (add_to_mempool && !pool.exists(GenTxid::Txid(tx->GetHash()))) {
if (add_to_mempool && !pool.exists(tx->GetHash())) {
LOCK2(cs_main, pool.cs);
AddToMempool(pool, ConsumeTxMemPoolEntry(fuzzed_data_provider, *tx));
available.insert(i);

View file

@ -315,8 +315,8 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
node.validation_signals->SyncWithValidationInterfaceQueue();
node.validation_signals->UnregisterSharedValidationInterface(txr);
bool txid_in_mempool = tx_pool.exists(GenTxid::Txid(tx->GetHash()));
bool wtxid_in_mempool = tx_pool.exists(GenTxid::Wtxid(tx->GetWitnessHash()));
bool txid_in_mempool = tx_pool.exists(tx->GetHash());
bool wtxid_in_mempool = tx_pool.exists(tx->GetWitnessHash());
CheckATMPInvariants(res, txid_in_mempool, wtxid_in_mempool);
Assert(accepted != added.empty());

View file

@ -454,12 +454,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
AddToMempool(pool, entry.Fee(5000LL).FromTx(tx2));
pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
BOOST_CHECK(pool.exists(GenTxid::Txid(tx2.GetHash())));
BOOST_CHECK(pool.exists(tx1.GetHash()));
BOOST_CHECK(pool.exists(tx2.GetHash()));
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash())));
BOOST_CHECK(pool.exists(tx1.GetHash()));
BOOST_CHECK(!pool.exists(tx2.GetHash()));
AddToMempool(pool, entry.FromTx(tx2));
CMutableTransaction tx3 = CMutableTransaction();
@ -472,14 +472,14 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
AddToMempool(pool, entry.Fee(20000LL).FromTx(tx3));
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
BOOST_CHECK(pool.exists(GenTxid::Txid(tx2.GetHash())));
BOOST_CHECK(pool.exists(GenTxid::Txid(tx3.GetHash())));
BOOST_CHECK(!pool.exists(tx1.GetHash()));
BOOST_CHECK(pool.exists(tx2.GetHash()));
BOOST_CHECK(pool.exists(tx3.GetHash()));
pool.TrimToSize(GetVirtualTransactionSize(CTransaction(tx1))); // mempool is limited to tx1's size in memory usage, so nothing fits
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx3.GetHash())));
BOOST_CHECK(!pool.exists(tx1.GetHash()));
BOOST_CHECK(!pool.exists(tx2.GetHash()));
BOOST_CHECK(!pool.exists(tx3.GetHash()));
CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
@ -539,19 +539,19 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
// we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that
pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
BOOST_CHECK(pool.exists(tx4.GetHash()));
BOOST_CHECK(pool.exists(tx6.GetHash()));
BOOST_CHECK(!pool.exists(tx7.GetHash()));
if (!pool.exists(GenTxid::Txid(tx5.GetHash())))
if (!pool.exists(tx5.GetHash()))
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx5.GetHash())));
BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash())));
BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
BOOST_CHECK(pool.exists(tx4.GetHash()));
BOOST_CHECK(!pool.exists(tx5.GetHash()));
BOOST_CHECK(pool.exists(tx6.GetHash()));
BOOST_CHECK(!pool.exists(tx7.GetHash()));
AddToMempool(pool, entry.Fee(1000LL).FromTx(tx5));
AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));

View file

@ -293,7 +293,7 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
const auto spends_unconfirmed = make_tx({tx1}, {36 * CENT});
for (const auto& input : spends_unconfirmed->vin) {
// Spends unconfirmed inputs.
BOOST_CHECK(pool.exists(GenTxid::Txid(input.prevout.hash)));
BOOST_CHECK(pool.exists(input.prevout.hash));
}
BOOST_CHECK(HasNoNewUnconfirmed(/*tx=*/ *spends_unconfirmed.get(),
/*pool=*/ pool,

View file

@ -1090,7 +1090,7 @@ BOOST_AUTO_TEST_CASE(package_rbf_tests)
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
// child1 has been replaced
BOOST_CHECK(!m_node.mempool->exists(GenTxid::Txid(tx_child_1->GetHash())));
BOOST_CHECK(!m_node.mempool->exists(tx_child_1->GetHash()));
}
// Test package rbf.

View file

@ -122,17 +122,17 @@ std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns,
if (mempool) {
// The tx by txid should be in the mempool iff the result was not INVALID.
const bool txid_in_mempool{atmp_result.m_result_type != MempoolAcceptResult::ResultType::INVALID};
if (mempool->exists(GenTxid::Txid(tx->GetHash())) != txid_in_mempool) {
if (mempool->exists(tx->GetHash()) != txid_in_mempool) {
return strprintf("tx %s should %sbe in mempool", wtxid.ToString(), txid_in_mempool ? "" : "not ");
}
// Additionally, if the result was DIFFERENT_WITNESS, we shouldn't be able to find the tx in mempool by wtxid.
if (tx->HasWitness() && atmp_result.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS) {
if (mempool->exists(GenTxid::Wtxid(wtxid))) {
if (mempool->exists(wtxid)) {
return strprintf("wtxid %s should not be in mempool", wtxid.ToString());
}
}
for (const auto& tx_ref : atmp_result.m_replaced_transactions) {
if (mempool->exists(GenTxid::Txid(tx_ref->GetHash()))) {
if (mempool->exists(tx_ref->GetHash())) {
return strprintf("tx %s should not be in mempool as it was replaced", tx_ref->GetWitnessHash().ToString());
}
}

View file

@ -1017,7 +1017,7 @@ std::vector<CTxMemPool::txiter> CTxMemPool::GetIterVec(const std::vector<uint256
bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
{
for (unsigned int i = 0; i < tx.vin.size(); i++)
if (exists(GenTxid::Txid(tx.vin[i].prevout.hash)))
if (exists(tx.vin[i].prevout.hash))
return false;
return true;
}
@ -1187,7 +1187,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends
if (pvNoSpendsRemaining) {
for (const CTransaction& tx : txn) {
for (const CTxIn& txin : tx.vin) {
if (exists(GenTxid::Txid(txin.prevout.hash))) continue;
if (exists(txin.prevout.hash)) continue;
pvNoSpendsRemaining->push_back(txin.prevout);
}
}

View file

@ -20,6 +20,7 @@
#include <sync.h>
#include <util/epochguard.h>
#include <util/hasher.h>
#include <util/overloaded.h>
#include <util/result.h>
#include <util/feefrac.h>
@ -38,6 +39,7 @@
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
class CChain;
@ -644,13 +646,13 @@ public:
return m_total_fee;
}
bool exists(const GenTxid& gtxid) const
bool exists(const GenTxidVariant& gtxid) const
{
LOCK(cs);
if (gtxid.IsWtxid()) {
return (mapTx.get<index_by_wtxid>().count(gtxid.GetHash()) != 0);
}
return (mapTx.count(gtxid.GetHash()) != 0);
return std::visit(util::Overloaded{
[this](const Wtxid& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs) { return (mapTx.get<index_by_wtxid>().count(wtxid) != 0); },
[this](const Txid& txid) EXCLUSIVE_LOCKS_REQUIRED(cs) { return (mapTx.count(txid) != 0); }
}, gtxid);
}
const CTxMemPoolEntry* GetEntry(const Txid& txid) const LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(cs);
@ -677,7 +679,7 @@ public:
LOCK(cs);
// Sanity check the transaction is in the mempool & insert into
// unbroadcast set.
if (exists(GenTxid::Txid(txid))) m_unbroadcast_txids.insert(txid);
if (exists(Txid::FromUint256(txid))) m_unbroadcast_txids.insert(txid);
};
/** Removes a transaction from the unbroadcast set */

View file

@ -319,7 +319,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
} else if (m_mempool->exists(GenTxid::Txid((*it)->GetHash()))) {
} else if (m_mempool->exists((*it)->GetHash())) {
vHashUpdate.push_back((*it)->GetHash());
}
++it;
@ -370,7 +370,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
// If the transaction spends any coinbase outputs, it must be mature.
if (it->GetSpendsCoinbase()) {
for (const CTxIn& txin : tx.vin) {
if (m_mempool->exists(GenTxid::Txid(txin.prevout.hash))) continue;
if (m_mempool->exists(txin.prevout.hash)) continue;
const Coin& coin{CoinsTip().AccessCoin(txin.prevout)};
assert(!coin.IsSpent());
const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
@ -805,10 +805,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
}
if (m_pool.exists(GenTxid::Wtxid(tx.GetWitnessHash()))) {
if (m_pool.exists(tx.GetWitnessHash())) {
// Exact transaction already exists in the mempool.
return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-already-in-mempool");
} else if (m_pool.exists(GenTxid::Txid(tx.GetHash()))) {
} else if (m_pool.exists(tx.GetHash())) {
// Transaction with the same non-witness data but different witness (same txid, different
// wtxid) already exists in the mempool.
return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-same-nonwitness-data-in-mempool");
@ -1134,7 +1134,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
// CheckPackageLimits expects the package transactions to not already be in the mempool.
assert(std::all_of(txns.cbegin(), txns.cend(), [this](const auto& tx)
{ return !m_pool.exists(GenTxid::Txid(tx->GetHash()));}));
{ return !m_pool.exists(tx->GetHash());}));
assert(txns.size() == workspaces.size());
@ -1345,7 +1345,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
// Sanity check: none of the transactions should be in the mempool, and none of the transactions
// should have a same-txid-different-witness equivalent in the mempool.
assert(std::all_of(workspaces.cbegin(), workspaces.cend(), [this](const auto& ws){
return !m_pool.exists(GenTxid::Txid(ws.m_ptx->GetHash())); }));
return !m_pool.exists(ws.m_ptx->GetHash()); }));
bool all_submitted = true;
FinalizeSubpackage(args);
@ -1470,7 +1470,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
// Limit the mempool, if appropriate.
if (!args.m_package_submission && !args.m_bypass_limits) {
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());
if (!m_pool.exists(GenTxid::Txid(ws.m_hash))) {
if (!m_pool.exists(ws.m_hash)) {
// The tx no longer meets our (new) mempool minimum feerate but could be reconsidered in a package.
ws.m_state.Invalid(TxValidationResult::TX_RECONSIDERABLE, "mempool full");
return MempoolAcceptResult::FeeFailure(ws.m_state, CFeeRate(ws.m_modified_fees, ws.m_vsize), {ws.m_ptx->GetWitnessHash()});
@ -1765,7 +1765,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// There are 3 possibilities: already in mempool, same-txid-diff-wtxid already in mempool,
// or not in mempool. An already confirmed tx is treated as one not in mempool, because all
// we know is that the inputs aren't available.
if (m_pool.exists(GenTxid::Wtxid(wtxid))) {
if (m_pool.exists(wtxid)) {
// Exact transaction already exists in the mempool.
// Node operators are free to set their mempool policies however they please, nodes may receive
// transactions in different orders, and malicious counterparties may try to take advantage of
@ -1777,7 +1777,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
const auto& entry{*Assert(m_pool.GetEntry(txid))};
results_final.emplace(wtxid, MempoolAcceptResult::MempoolTx(entry.GetTxSize(), entry.GetFee()));
} else if (m_pool.exists(GenTxid::Txid(txid))) {
} else if (m_pool.exists(txid)) {
// Transaction with the same non-witness data but different witness (same txid,
// different wtxid) already exists in the mempool.
//
@ -1796,7 +1796,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
if (single_res.m_result_type == MempoolAcceptResult::ResultType::VALID) {
// The transaction succeeded on its own and is now in the mempool. Don't include it
// in package validation, because its fees should only be "used" once.
assert(m_pool.exists(GenTxid::Wtxid(wtxid)));
assert(m_pool.exists(wtxid));
results_final.emplace(wtxid, single_res);
} else if (package.size() == 1 || // If there is only one transaction, no need to retry it "as a package"
(single_res.m_state.GetResult() != TxValidationResult::TX_RECONSIDERABLE &&
@ -1841,7 +1841,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// If it was submitted, check to see if the tx is still in the mempool. It could have
// been evicted due to LimitMempoolSize() above.
const auto& txresult = multi_submission_result.m_tx_results.at(wtxid);
if (txresult.m_result_type == MempoolAcceptResult::ResultType::VALID && !m_pool.exists(GenTxid::Wtxid(wtxid))) {
if (txresult.m_result_type == MempoolAcceptResult::ResultType::VALID && !m_pool.exists(wtxid)) {
package_state_final.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
TxValidationState mempool_full_state;
mempool_full_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
@ -1855,7 +1855,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
Assume(it->second.m_result_type != MempoolAcceptResult::ResultType::INVALID);
Assume(individual_results_nonfinal.count(wtxid) == 0);
// Query by txid to include the same-txid-different-witness ones.
if (!m_pool.exists(GenTxid::Txid(tx->GetHash()))) {
if (!m_pool.exists(tx->GetHash())) {
package_state_final.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
TxValidationState mempool_full_state;
mempool_full_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");