aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
authorglozow <gzhao408@berkeley.edu>2021-04-05 10:12:58 -0700
committerglozow <gzhao408@berkeley.edu>2021-05-24 14:42:10 +0100
commit2ef187941db439c5b3e529f08b6ab153ff061fc5 (patch)
treee0b09e18c6e411566b0a4db3e210bde2b7a73133 /src/validation.cpp
parent578148ded62828a9820398165c41670f4dbb523d (diff)
downloadbitcoin-2ef187941db439c5b3e529f08b6ab153ff061fc5.tar.xz
[validation] package validation for test accepts
Only allow test accepts for now. Use the CoinsViewTemporary to keep track of coins created by each transaction so that subsequent transactions can spend them. Uncache all coins since we only ever do test accepts (Note this is different from ATMP which doesn't uncache for valid test_accepts) to minimize impact on the coins cache. Require that the input txns have no conflicts and be ordered topologically. This commit isn't able to detect unsorted packages.
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp101
1 files changed, 101 insertions, 0 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 9165a1f1b3..ad0f636554 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -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);