From e25b158ab8812abaad2a83b04db6b821154a6a0f Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 16 Dec 2015 14:57:54 -0500 Subject: RPC: indicate which transactions are replaceable Add "bip125-replaceable" output field to listtransactions and gettransaction which indicates if an unconfirmed transaction, or any unconfirmed parent, is signaling opt-in RBF according to BIP 125. Github-Pull: #7286 Rebased-From: eaa8d2754b48b62cdd07255fc3028feecad0c095 --- src/Makefile.am | 2 ++ src/policy/rbf.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/policy/rbf.h | 20 ++++++++++++++++++++ src/wallet/rpcwallet.cpp | 22 ++++++++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 src/policy/rbf.cpp create mode 100644 src/policy/rbf.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5da1a873de..5d7fbb13d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -122,6 +122,7 @@ BITCOIN_CORE_H = \ noui.h \ policy/fees.h \ policy/policy.h \ + policy/rbf.h \ pow.h \ prevector.h \ primitives/block.h \ @@ -239,6 +240,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/wallet.cpp \ wallet/wallet_ismine.cpp \ wallet/walletdb.cpp \ + policy/rbf.cpp \ $(BITCOIN_CORE_H) # crypto primitives library diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp new file mode 100644 index 0000000000..98b1a1ba4c --- /dev/null +++ b/src/policy/rbf.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2016 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "policy/rbf.h" + +bool SignalsOptInRBF(const CTransaction &tx) +{ + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + if (txin.nSequence < std::numeric_limits::max()-1) { + return true; + } + } + return false; +} + +bool IsRBFOptIn(const CTxMemPoolEntry &entry, CTxMemPool &pool) +{ + AssertLockHeld(pool.cs); + + CTxMemPool::setEntries setAncestors; + + // First check the transaction itself. + if (SignalsOptInRBF(entry.GetTx())) { + return true; + } + + // If this transaction is not in our mempool, then we can't be sure + // we will know about all its inputs. + if (!pool.exists(entry.GetTx().GetHash())) { + throw std::runtime_error("Cannot determine RBF opt-in signal for non-mempool transaction\n"); + } + + // If all the inputs have nSequence >= maxint-1, it still might be + // signaled for RBF if any unconfirmed parents have signaled. + uint64_t noLimit = std::numeric_limits::max(); + std::string dummy; + pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false); + + BOOST_FOREACH(CTxMemPool::txiter it, setAncestors) { + if (SignalsOptInRBF(it->GetTx())) { + return true; + } + } + return false; +} diff --git a/src/policy/rbf.h b/src/policy/rbf.h new file mode 100644 index 0000000000..925ce0d9bd --- /dev/null +++ b/src/policy/rbf.h @@ -0,0 +1,20 @@ +// Copyright (c) 2016 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_POLICY_RBF_H +#define BITCOIN_POLICY_RBF_H + +#include "txmempool.h" + +// Check whether the sequence numbers on this transaction are signaling +// opt-in to replace-by-fee, according to BIP 125 +bool SignalsOptInRBF(const CTransaction &tx); + +// Determine whether an in-mempool transaction is signaling opt-in to RBF +// according to BIP 125 +// This involves checking sequence numbers of the transaction, as well +// as the sequence numbers of all in-mempool ancestors. +bool IsRBFOptIn(const CTxMemPoolEntry &entry, CTxMemPool &pool); + +#endif // BITCOIN_POLICY_RBF_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e68d646096..f7f1467631 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -11,6 +11,7 @@ #include "main.h" #include "net.h" #include "netbase.h" +#include "policy/rbf.h" #include "rpcserver.h" #include "timedata.h" #include "util.h" @@ -76,6 +77,23 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) entry.push_back(Pair("walletconflicts", conflicts)); entry.push_back(Pair("time", wtx.GetTxTime())); entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); + + // Add opt-in RBF status + std::string rbfStatus = "no"; + if (confirms <= 0) { + LOCK(mempool.cs); + if (!mempool.exists(hash)) { + if (SignalsOptInRBF(wtx)) { + rbfStatus = "yes"; + } else { + rbfStatus = "unknown"; + } + } else if (IsRBFOptIn(*mempool.mapTx.find(hash), mempool)) { + rbfStatus = "yes"; + } + } + entry.push_back(Pair("bip125-replaceable", rbfStatus)); + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) entry.push_back(Pair(item.first, item.second)); } @@ -1439,6 +1457,8 @@ UniValue listtransactions(const UniValue& params, bool fHelp) " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" " from (for receiving funds, positive amounts), or went to (for sending funds,\n" " negative amounts).\n" + " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" " }\n" "]\n" @@ -1707,6 +1727,8 @@ UniValue gettransaction(const UniValue& params, bool fHelp) " \"txid\" : \"transactionid\", (string) The transaction id.\n" " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" + " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" " \"details\" : [\n" " {\n" " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" -- cgit v1.2.3