From 578ec80d4fcef6be48acf45185d1248bb62bfdbf Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 2 Feb 2017 22:15:55 +0000 Subject: [PATCH 1/8] RPC: rawtransaction: Add RBF support for createrawtransaction --- src/rpc/client.cpp | 1 + src/rpc/rawtransaction.cpp | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index a3a692c14..ec27e55f0 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -86,6 +86,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 2, "locktime" }, + { "createrawtransaction", 3, "optintorbf" }, { "signrawtransaction", 1, "prevtxs" }, { "signrawtransaction", 2, "privkeys" }, { "sendrawtransaction", 1, "allowhighfees" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index e27c2a77c..07a7ef34e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -289,9 +289,9 @@ UniValue verifytxoutproof(const JSONRPCRequest& request) UniValue createrawtransaction(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( - "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( optintorbf )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" @@ -315,6 +315,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) " ,...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" + "4. optintorbf (boolean, optional, default=false) Allow this transaction to be replaced by a transaction with higher fees\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" @@ -341,6 +342,8 @@ UniValue createrawtransaction(const JSONRPCRequest& request) rawTx.nLockTime = nLockTime; } + bool rbfOptIn = request.params.size() > 3 ? request.params[3].isTrue() : false; + for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); @@ -354,16 +357,26 @@ UniValue createrawtransaction(const JSONRPCRequest& request) if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); + uint32_t nSequence; + if (rbfOptIn) { + nSequence = std::numeric_limits::max() - 2; + } else if (rawTx.nLockTime) { + nSequence = std::numeric_limits::max() - 1; + } else { + nSequence = std::numeric_limits::max(); + } // set the sequence number if passed in the parameters object const UniValue& sequenceObj = find_value(o, "sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.get_int64(); - if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) + if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); - else + } else if (seqNr64 <= std::numeric_limits::max() - 2 && request.params.size() > 3 && request.params[3].isFalse()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number contradicts optintorbf option"); + } else { nSequence = (uint32_t)seqNr64; + } } CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); From 891c5eeec2dbf295d14e8f888de02637367dd930 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 2 Feb 2017 22:33:24 +0000 Subject: [PATCH 2/8] Wallet: Refactor FundTransaction to accept parameters via CCoinControl --- src/wallet/rpcwallet.cpp | 22 ++++++++++++---------- src/wallet/wallet.cpp | 7 +------ src/wallet/wallet.h | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3b6dfd42c..5e264dd1a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -21,6 +21,7 @@ #include "timedata.h" #include "util.h" #include "utilmoneystr.h" +#include "wallet/coincontrol.h" #include "wallet/feebumper.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" @@ -2678,20 +2679,21 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); - CTxDestination changeAddress = CNoDestination(); + CCoinControl coinControl; + coinControl.destChange = CNoDestination(); int changePosition = -1; - bool includeWatching = false; + coinControl.fAllowWatchOnly = false; // include watching bool lockUnspents = false; bool reserveChangeKey = true; - CFeeRate feeRate = CFeeRate(0); - bool overrideEstimatedFeerate = false; + coinControl.nFeeRate = CFeeRate(0); + coinControl.fOverrideFeeRate = false; UniValue subtractFeeFromOutputs; std::set setSubtractFeeFromOutputs; if (request.params.size() > 1) { if (request.params[1].type() == UniValue::VBOOL) { // backward compatibility bool only fallback - includeWatching = request.params[1].get_bool(); + coinControl.fAllowWatchOnly = request.params[1].get_bool(); } else { RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); @@ -2716,14 +2718,14 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address"); - changeAddress = address.Get(); + coinControl.destChange = address.Get(); } if (options.exists("changePosition")) changePosition = options["changePosition"].get_int(); if (options.exists("includeWatching")) - includeWatching = options["includeWatching"].get_bool(); + coinControl.fAllowWatchOnly = options["includeWatching"].get_bool(); if (options.exists("lockUnspents")) lockUnspents = options["lockUnspents"].get_bool(); @@ -2733,8 +2735,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) if (options.exists("feeRate")) { - feeRate = CFeeRate(AmountFromValue(options["feeRate"])); - overrideEstimatedFeerate = true; + coinControl.nFeeRate = CFeeRate(AmountFromValue(options["feeRate"])); + coinControl.fOverrideFeeRate = true; } if (options.exists("subtractFeeFromOutputs")) @@ -2767,7 +2769,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) CAmount nFeeOut; std::string strFailReason; - if (!pwallet->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, reserveChangeKey, changeAddress)) { + if (!pwallet->FundTransaction(tx, nFeeOut, changePosition, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl, reserveChangeKey)) { throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3e000d2a9..c00f52282 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2414,7 +2414,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx) return true; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, CCoinControl coinControl, bool keepReserveKey) { std::vector vecSend; @@ -2426,12 +2426,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov vecSend.push_back(recipient); } - CCoinControl coinControl; - coinControl.destChange = destChange; coinControl.fAllowOtherInputs = true; - coinControl.fAllowWatchOnly = includeWatching; - coinControl.fOverrideFeeRate = overrideEstimatedFeeRate; - coinControl.nFeeRate = specificFeeRate; BOOST_FOREACH(const CTxIn& txin, tx.vin) coinControl.Select(txin.prevout); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a3974bf00..6f69c1603 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -935,7 +935,7 @@ public: * Insert additional inputs into the transaction by * calling CreateTransaction(); */ - bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, bool keepReserveKey = true, const CTxDestination& destChange = CNoDestination()); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, CCoinControl, bool keepReserveKey = true); bool SignTransaction(CMutableTransaction& tx); /** From 36bcab2356c4ccbefd463035b3ed1576ddb493e5 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 2 Feb 2017 22:36:50 +0000 Subject: [PATCH 3/8] RPC/Wallet: Add RBF support for fundrawtransaction --- src/wallet/rpcwallet.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5e264dd1a..0841c23b8 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2629,7 +2629,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( "fundrawtransaction \"hexstring\" ( options )\n" "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" @@ -2658,6 +2658,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" " If no outputs are specified here, the sender pays the fee.\n" " [vout_index,...]\n" + " \"optIntoRbf\" (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees\n" " }\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" "\nResult:\n" @@ -2709,6 +2710,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, {"feeRate", UniValueType()}, // will be checked below {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, + {"optIntoRbf", UniValueType(UniValue::VBOOL)}, }, true, true); @@ -2741,6 +2743,10 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) if (options.exists("subtractFeeFromOutputs")) subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array(); + + if (options.exists("optIntoRbf")) { + coinControl.signalRbf = options["optIntoRbf"].get_bool(); + } } } From 5d26244148b636c1b81b9eb5ab6a45f33766bd08 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 3 Dec 2015 10:45:41 +0100 Subject: [PATCH 4/8] [Tests] extend the replace-by-fee test to cover RPC rawtx features --- test/functional/replace-by-fee.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index e940ce535..da19648d8 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -99,6 +99,9 @@ class ReplaceByFeeTest(BitcoinTestFramework): self.log.info("Running test opt-in...") self.test_opt_in() + self.log.info("Running test RPC...") + self.test_rpc() + self.log.info("Running test prioritised transactions...") self.test_prioritised_transactions() @@ -516,5 +519,25 @@ class ReplaceByFeeTest(BitcoinTestFramework): assert(tx2b_txid in self.nodes[0].getrawmempool()) + def test_rpc(self): + us0 = self.nodes[0].listunspent()[0] + ins = [us0]; + outs = {self.nodes[0].getnewaddress() : Decimal(1.0000000)} + rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True) + rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False) + json0 = self.nodes[0].decoderawtransaction(rawtx0) + json1 = self.nodes[0].decoderawtransaction(rawtx1) + assert_equal(json0["vin"][0]["sequence"], 4294967293) + assert_equal(json1["vin"][0]["sequence"], 4294967295) + + rawtx2 = self.nodes[0].createrawtransaction([], outs) + frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"optIntoRbf": True}) + frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"optIntoRbf": False}) + + json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex']) + json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex']) + assert_equal(json0["vin"][0]["sequence"], 4294967293) + assert_equal(json1["vin"][0]["sequence"], 4294967294) + if __name__ == '__main__': ReplaceByFeeTest().main() From 575cde4605c9a321ad591dbb7d632dd0ef946d40 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 3 Dec 2015 16:42:01 +0100 Subject: [PATCH 5/8] [bitcoin-tx] add rbfoptin command --- src/bitcoin-tx.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index cf280f485..87d847594 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -77,6 +77,7 @@ static int AppInitRawTx(int argc, char* argv[]) strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX")); strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); + strUsage += HelpMessageOpt("rbfoptin(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " + @@ -202,6 +203,24 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal) tx.nLockTime = (unsigned int) newLocktime; } +static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx) +{ + // parse requested index + int inIdx = atoi(strInIdx); + if (inIdx < 0 || inIdx >= (int)tx.vin.size()) { + throw std::runtime_error("Invalid TX input index '" + strInIdx + "'"); + } + + // set the nSequence to MAX_INT - 2 (= RBF opt in flag) + int cnt = 0; + for (CTxIn& txin : tx.vin) { + if (strInIdx == "" || cnt == inIdx) { + txin.nSequence = std::numeric_limits::max() - 2; + } + ++cnt; + } +} + static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput) { std::vector vStrInputParts; @@ -649,6 +668,9 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, MutateTxVersion(tx, commandVal); else if (command == "locktime") MutateTxLocktime(tx, commandVal); + else if (command == "rbfoptin") { + MutateTxRBFOptIn(tx, commandVal); + } else if (command == "delin") MutateTxDelInput(tx, commandVal); From b005bf21a7cabe827ef62f266c22adf2ee745b61 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 3 Feb 2017 19:13:28 +0000 Subject: [PATCH 6/8] Introduce MAX_BIP125_RBF_SEQUENCE constant --- src/bitcoin-tx.cpp | 3 ++- src/policy/rbf.h | 2 ++ src/rpc/rawtransaction.cpp | 5 +++-- src/wallet/wallet.cpp | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 87d847594..a67c42ec5 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -13,6 +13,7 @@ #include "core_io.h" #include "keystore.h" #include "policy/policy.h" +#include "policy/rbf.h" #include "primitives/transaction.h" #include "script/script.h" #include "script/sign.h" @@ -215,7 +216,7 @@ static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInId int cnt = 0; for (CTxIn& txin : tx.vin) { if (strInIdx == "" || cnt == inIdx) { - txin.nSequence = std::numeric_limits::max() - 2; + txin.nSequence = MAX_BIP125_RBF_SEQUENCE; } ++cnt; } diff --git a/src/policy/rbf.h b/src/policy/rbf.h index 139aec576..22c73f331 100644 --- a/src/policy/rbf.h +++ b/src/policy/rbf.h @@ -7,6 +7,8 @@ #include "txmempool.h" +static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd; + enum RBFTransactionState { RBF_TRANSACTIONSTATE_UNKNOWN, RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 07a7ef34e..332dbc08a 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -14,6 +14,7 @@ #include "merkleblock.h" #include "net.h" #include "policy/policy.h" +#include "policy/rbf.h" #include "primitives/transaction.h" #include "rpc/server.h" #include "script/script.h" @@ -359,7 +360,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) uint32_t nSequence; if (rbfOptIn) { - nSequence = std::numeric_limits::max() - 2; + nSequence = MAX_BIP125_RBF_SEQUENCE; } else if (rawTx.nLockTime) { nSequence = std::numeric_limits::max() - 1; } else { @@ -372,7 +373,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) int64_t seqNr64 = sequenceObj.get_int64(); if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); - } else if (seqNr64 <= std::numeric_limits::max() - 2 && request.params.size() > 3 && request.params[3].isFalse()) { + } else if (seqNr64 <= MAX_BIP125_RBF_SEQUENCE && request.params.size() > 3 && request.params[3].isFalse()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number contradicts optintorbf option"); } else { nSequence = (uint32_t)seqNr64; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c00f52282..b2706d09f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2685,9 +2685,10 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT // and in the spirit of "smallest possible change from prior // behavior." bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf; + const uint32_t nSequence = rbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits::max() - 1); for (const auto& coin : setCoins) txNew.vin.push_back(CTxIn(coin.outpoint,CScript(), - std::numeric_limits::max() - (rbf ? 2 : 1))); + nSequence)); // Fill in dummy signatures for fee calculation. if (!DummySignTx(txNew, setCoins)) { From 23b0fe34f526693985099d9160806d00d48a9ecd Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 3 Feb 2017 19:14:02 +0000 Subject: [PATCH 7/8] bitcoin-tx: rbfoptin: Avoid touching nSequence if the value is already opting in --- src/bitcoin-tx.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index a67c42ec5..367801dd7 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -216,7 +216,9 @@ static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInId int cnt = 0; for (CTxIn& txin : tx.vin) { if (strInIdx == "" || cnt == inIdx) { - txin.nSequence = MAX_BIP125_RBF_SEQUENCE; + if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) { + txin.nSequence = MAX_BIP125_RBF_SEQUENCE; + } } ++cnt; } From 9a5a1d7d452eff242be1bfe155a30c1f05b2d5a4 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 3 Feb 2017 19:23:22 +0000 Subject: [PATCH 8/8] RPC/rawtransaction: createrawtransaction: Check opt_into_rbf when provided with either value --- src/Makefile.am | 2 +- src/rpc/rawtransaction.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index ae2eb29c9..199d60b72 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -198,6 +198,7 @@ libbitcoin_server_a_SOURCES = \ noui.cpp \ policy/fees.cpp \ policy/policy.cpp \ + policy/rbf.cpp \ pow.cpp \ rest.cpp \ rpc/blockchain.cpp \ @@ -240,7 +241,6 @@ libbitcoin_wallet_a_SOURCES = \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ - policy/rbf.cpp \ $(BITCOIN_CORE_H) # crypto primitives library diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 332dbc08a..00ddd9d16 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -316,7 +316,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) " ,...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" - "4. optintorbf (boolean, optional, default=false) Allow this transaction to be replaced by a transaction with higher fees\n" + "4. optintorbf (boolean, optional, default=false) Allow this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" @@ -373,8 +373,6 @@ UniValue createrawtransaction(const JSONRPCRequest& request) int64_t seqNr64 = sequenceObj.get_int64(); if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); - } else if (seqNr64 <= MAX_BIP125_RBF_SEQUENCE && request.params.size() > 3 && request.params[3].isFalse()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number contradicts optintorbf option"); } else { nSequence = (uint32_t)seqNr64; } @@ -411,6 +409,10 @@ UniValue createrawtransaction(const JSONRPCRequest& request) } } + if (request.params.size() > 3 && rbfOptIn != SignalsOptInRBF(rawTx)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict optintorbf option"); + } + return EncodeHexTx(rawTx); }