aboutsummaryrefslogtreecommitdiff
path: root/src/bitcoin-tx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitcoin-tx.cpp')
-rw-r--r--src/bitcoin-tx.cpp60
1 files changed, 45 insertions, 15 deletions
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 45738b5df8..714ee555ec 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"
@@ -24,7 +25,6 @@
#include <stdio.h>
#include <boost/algorithm/string.hpp>
-#include <boost/assign/list_of.hpp>
static bool fCreateBlank;
static std::map<std::string,UniValue> registers;
@@ -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,26 @@ 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) {
+ if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) {
+ txin.nSequence = MAX_BIP125_RBF_SEQUENCE;
+ }
+ }
+ ++cnt;
+ }
+}
+
static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
{
std::vector<std::string> vStrInputParts;
@@ -546,7 +567,11 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (!prevOut.isObject())
throw std::runtime_error("expected prevtxs internal object");
- std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
+ std::map<std::string, UniValue::VType> types = {
+ {"txid", UniValue::VSTR},
+ {"vout", UniValue::VNUM},
+ {"scriptPubKey", UniValue::VSTR},
+ };
if (!prevOut.checkObject(types))
throw std::runtime_error("prevtxs internal object typecheck fail");
@@ -556,24 +581,26 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (nOut < 0)
throw std::runtime_error("vout must be positive");
+ COutPoint out(txid, nOut);
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
- CCoinsModifier coins = view.ModifyCoins(txid);
- if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
+ const Coin& coin = view.AccessCoin(out);
+ if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
std::string err("Previous output scriptPubKey mismatch:\n");
- err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
+ err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
ScriptToAsmStr(scriptPubKey);
throw std::runtime_error(err);
}
- if ((unsigned int)nOut >= coins->vout.size())
- coins->vout.resize(nOut+1);
- coins->vout[nOut].scriptPubKey = scriptPubKey;
- coins->vout[nOut].nValue = 0;
+ Coin newcoin;
+ newcoin.out.scriptPubKey = scriptPubKey;
+ newcoin.out.nValue = 0;
if (prevOut.exists("amount")) {
- coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]);
+ newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
}
+ newcoin.nHeight = 1;
+ view.AddCoin(out, std::move(newcoin), true);
}
// if redeemScript given and private keys given,
@@ -595,13 +622,13 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
- const CCoins* coins = view.AccessCoins(txin.prevout.hash);
- if (!coins || !coins->IsAvailable(txin.prevout.n)) {
+ const Coin& coin = view.AccessCoin(txin.prevout);
+ if (coin.IsSpent()) {
fComplete = false;
continue;
}
- const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
- const CAmount& amount = coins->vout[txin.prevout.n].nValue;
+ const CScript& prevPubKey = coin.out.scriptPubKey;
+ const CAmount& amount = coin.out.nValue;
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
@@ -609,7 +636,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
// ... and merge in other signatures:
- BOOST_FOREACH(const CTransaction& txv, txVariants)
+ for (const CTransaction& txv : txVariants)
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
UpdateTransaction(mergedTx, i, sigdata);
@@ -647,6 +674,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);