diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/init.cpp | 3 | ||||
-rw-r--r-- | src/main.cpp | 12 | ||||
-rw-r--r-- | src/main.h | 3 | ||||
-rw-r--r-- | src/policy/policy.cpp | 37 | ||||
-rw-r--r-- | src/policy/rbf.cpp | 46 | ||||
-rw-r--r-- | src/policy/rbf.h | 20 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 4 | ||||
-rw-r--r-- | src/script/standard.cpp | 21 | ||||
-rw-r--r-- | src/script/standard.h | 1 | ||||
-rw-r--r-- | src/test/script_P2SH_tests.cpp | 9 | ||||
-rw-r--r-- | src/test/transaction_tests.cpp | 8 | ||||
-rw-r--r-- | src/wallet/db.cpp | 2 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 22 |
14 files changed, 115 insertions, 75 deletions
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/init.cpp b/src/init.cpp index 53b77775ac..363575f236 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -367,6 +367,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy")); strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)")); strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG)); + strUsage += HelpMessageOpt("-permitrbf", strprintf(_("Permit transaction replacement (default: %u)"), DEFAULT_PERMIT_REPLACEMENT)); strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), 1)); if (showDebug) strUsage += HelpMessageOpt("-enforcenodebloom", strprintf("Enforce minimum protocol version to limit use of bloom filters (default: %u)", 0)); @@ -1015,6 +1016,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (GetBoolArg("-peerbloomfilters", true)) nLocalServices |= NODE_BLOOM; + fPermitReplacement = GetBoolArg("-permitrbf", DEFAULT_PERMIT_REPLACEMENT); + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Initialize elliptic curve code diff --git a/src/main.cpp b/src/main.cpp index ee657d997b..d1b0fbac2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,6 +75,7 @@ bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; +bool fPermitReplacement = DEFAULT_PERMIT_REPLACEMENT; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); @@ -865,12 +866,15 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // unconfirmed ancestors anyway; doing otherwise is hopelessly // insecure. bool fReplacementOptOut = true; - BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin) + if (fPermitReplacement) { - if (txin.nSequence < std::numeric_limits<unsigned int>::max()-1) + BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin) { - fReplacementOptOut = false; - break; + if (txin.nSequence < std::numeric_limits<unsigned int>::max()-1) + { + fReplacementOptOut = false; + break; + } } } if (fReplacementOptOut) diff --git a/src/main.h b/src/main.h index cefaedabf5..d3137c371d 100644 --- a/src/main.h +++ b/src/main.h @@ -106,6 +106,8 @@ static const bool DEFAULT_TXINDEX = false; static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100; static const bool DEFAULT_TESTSAFEMODE = false; +/** Default for -permitrbf */ +static const bool DEFAULT_PERMIT_REPLACEMENT = true; /** Maximum number of headers to announce when relaying blocks with headers message.*/ static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8; @@ -137,6 +139,7 @@ extern bool fCheckpointsEnabled; extern size_t nCoinCacheUsage; extern CFeeRate minRelayTxFee; extern bool fAlerts; +extern bool fPermitReplacement; /** Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 273a482fa1..c92a249c17 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -135,45 +135,20 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) const CScript& prevScript = prev.scriptPubKey; if (!Solver(prevScript, whichType, vSolutions)) return false; - int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); - if (nArgsExpected < 0) - return false; - - // Transactions with extra stuff in their scriptSigs are - // non-standard. Note that this EvalScript() call will - // be quick, because if there are any operations - // beside "push data" in the scriptSig - // IsStandardTx() will have already returned false - // and this method isn't called. - std::vector<std::vector<unsigned char> > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker())) - return false; if (whichType == TX_SCRIPTHASH) { + std::vector<std::vector<unsigned char> > stack; + // convert the scriptSig into a stack, so we can inspect the redeemScript + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), 0)) + return false; if (stack.empty()) return false; CScript subscript(stack.back().begin(), stack.back().end()); - std::vector<std::vector<unsigned char> > vSolutions2; - txnouttype whichType2; - if (Solver(subscript, whichType2, vSolutions2)) - { - int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; - } - else - { - // Any other Script with less than 15 sigops OK: - unsigned int sigops = subscript.GetSigOpCount(true); - // ... extra data left on the stack after execution is OK, too: - return (sigops <= MAX_P2SH_SIGOPS); + if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) { + return false; } } - - if (stack.size() != (unsigned int)nArgsExpected) - return false; } return true; 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<unsigned int>::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<uint64_t>::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/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 4c869b9ac5..be589c815c 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -472,7 +472,11 @@ void RPCConsole::clear() // Set default style sheet QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont()); // Try to make fixed font adequately large on different OS +#ifdef WIN32 + QString ptSize = QString("%1pt").arg(QFontInfo(QFont()).pointSize() * 10 / 8); +#else QString ptSize = QString("%1pt").arg(QFontInfo(QFont()).pointSize() * 8.5 / 9); +#endif ui->messagesWidget->document()->setDefaultStyleSheet( QString( "table { }" diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 30935768ac..67b6af327a 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -161,27 +161,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi return false; } -int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions) -{ - switch (t) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - return -1; - case TX_PUBKEY: - return 1; - case TX_PUBKEYHASH: - return 2; - case TX_MULTISIG: - if (vSolutions.size() < 1 || vSolutions[0].size() < 1) - return -1; - return vSolutions[0][0] + 1; - case TX_SCRIPTHASH: - return 1; // doesn't include args needed by the script - } - return -1; -} - bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { vector<valtype> vSolutions; diff --git a/src/script/standard.h b/src/script/standard.h index 6bac6e4097..64bf010ec1 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -71,7 +71,6 @@ typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination; const char* GetTxnOutputType(txnouttype t); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); -int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 7bd4b8441b..28b85e8d29 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -346,15 +346,6 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4] BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 22U); - // Make sure adding crap to the scriptSigs makes them non-standard: - for (int i = 0; i < 3; i++) - { - CScript t = txTo.vin[i].scriptSig; - txTo.vin[i].scriptSig = (CScript() << 11) + t; - BOOST_CHECK(!::AreInputsStandard(txTo, coins)); - txTo.vin[i].scriptSig = t; - } - CMutableTransaction txToNonStd1; txToNonStd1.vout.resize(1); txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID()); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 3dca7ea0f7..c27f194b55 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -310,14 +310,6 @@ BOOST_AUTO_TEST_CASE(test_Get) BOOST_CHECK(AreInputsStandard(t1, coins)); BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT); - - // Adding extra junk to the scriptSig should make it non-standard: - t1.vin[0].scriptSig << OP_11; - BOOST_CHECK(!AreInputsStandard(t1, coins)); - - // ... as should not having enough: - t1.vin[0].scriptSig = CScript(); - BOOST_CHECK(!AreInputsStandard(t1, coins)); } BOOST_AUTO_TEST_CASE(test_IsStandard) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index d18250b76f..50b0f40a6a 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -205,7 +205,7 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<C std::string keyHex, valueHex; while (!strDump.eof() && keyHex != "DATA=END") { getline(strDump, keyHex); - if (keyHex != "DATA_END") { + if (keyHex != "DATA=END") { getline(strDump, valueHex); vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex))); } 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" |