aboutsummaryrefslogtreecommitdiff
path: root/src/bitcoin-tx.cpp
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2017-01-12 12:24:25 +0100
committerWladimir J. van der Laan <laanwj@gmail.com>2017-01-12 12:24:35 +0100
commitd5d4ad87afbf6c09a936c910826a75568d42d7ea (patch)
treee7c79fc9616b2d4ef0a605be888a2bea0acfb0df /src/bitcoin-tx.cpp
parentfac0f30482d56c7a38e9bdd7a4090b896965f030 (diff)
parent0c50909347c4def009e22da224339bc82e171e42 (diff)
downloadbitcoin-d5d4ad87afbf6c09a936c910826a75568d42d7ea.tar.xz
Merge #8883: Add all standard TXO types to bitcoin-tx
0c50909 testcases: explicitly specify transaction version 1 (John Newbery) b7e144b Add test cases to test new bitcoin-tx functionality (jnewbery) 61a1534 Add all transaction output types to bitcoin-tx. (jnewbery) 1814b08 add p2sh and segwit options to bitcoin-tx outscript command (Stanislas Marion)
Diffstat (limited to 'src/bitcoin-tx.cpp')
-rw-r--r--src/bitcoin-tx.cpp195
1 files changed, 164 insertions, 31 deletions
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 24a53b65a0..3c3646523a 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -78,8 +78,16 @@ static int AppInitRawTx(int argc, char* argv[])
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
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") + ". " +
+ _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX"));
- strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX"));
+ strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " +
+ _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
+ _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
+ strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " +
+ _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
+ _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
_("This command requires JSON registers:") +
_("prevtxs=JSON object") + ", " +
@@ -168,6 +176,14 @@ static void RegisterLoad(const std::string& strInput)
RegisterSetJson(key, valStr);
}
+static CAmount ExtractAndValidateValue(const std::string& strValue)
+{
+ CAmount value;
+ if (!ParseMoney(strValue, value))
+ throw std::runtime_error("invalid TX output value");
+ return value;
+}
+
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newVersion = atoi64(cmdVal);
@@ -222,25 +238,18 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
{
- // separate VALUE:ADDRESS in string
- size_t pos = strInput.find(':');
- if ((pos == std::string::npos) ||
- (pos == 0) ||
- (pos == (strInput.size() - 1)))
- throw std::runtime_error("TX output missing separator");
+ // Separate into VALUE:ADDRESS
+ std::vector<std::string> vStrInputParts;
+ boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
- // extract and validate VALUE
- std::string strValue = strInput.substr(0, pos);
- CAmount value;
- if (!ParseMoney(strValue, value))
- throw std::runtime_error("invalid TX output value");
+ // Extract and validate VALUE
+ CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate ADDRESS
- std::string strAddr = strInput.substr(pos + 1, std::string::npos);
+ std::string strAddr = vStrInputParts[1];
CBitcoinAddress addr(strAddr);
if (!addr.IsValid())
throw std::runtime_error("invalid TX output address");
-
// build standard output script via GetScriptForDestination()
CScript scriptPubKey = GetScriptForDestination(addr.Get());
@@ -249,6 +258,114 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn
tx.vout.push_back(txout);
}
+static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput)
+{
+ // Separate into VALUE:PUBKEY[:FLAGS]
+ std::vector<std::string> vStrInputParts;
+ boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
+
+ // Extract and validate VALUE
+ CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
+
+ // Extract and validate PUBKEY
+ CPubKey pubkey(ParseHex(vStrInputParts[1]));
+ if (!pubkey.IsFullyValid())
+ throw std::runtime_error("invalid TX output pubkey");
+ CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
+ CBitcoinAddress addr(scriptPubKey);
+
+ // Extract and validate FLAGS
+ bool bSegWit = false;
+ bool bScriptHash = false;
+ if (vStrInputParts.size() == 3) {
+ std::string flags = vStrInputParts[2];
+ bSegWit = (flags.find("W") != std::string::npos);
+ bScriptHash = (flags.find("S") != std::string::npos);
+ }
+
+ if (bSegWit) {
+ // Call GetScriptForWitness() to build a P2WSH scriptPubKey
+ scriptPubKey = GetScriptForWitness(scriptPubKey);
+ }
+ if (bScriptHash) {
+ // Get the address for the redeem script, then call
+ // GetScriptForDestination() to construct a P2SH scriptPubKey.
+ CBitcoinAddress redeemScriptAddr(scriptPubKey);
+ scriptPubKey = GetScriptForDestination(redeemScriptAddr.Get());
+ }
+
+ // construct TxOut, append to transaction output list
+ CTxOut txout(value, scriptPubKey);
+ tx.vout.push_back(txout);
+}
+
+static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput)
+{
+ // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
+ std::vector<std::string> vStrInputParts;
+ boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
+
+ // Check that there are enough parameters
+ if (vStrInputParts.size()<3)
+ throw std::runtime_error("Not enough multisig parameters");
+
+ // Extract and validate VALUE
+ CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
+
+ // Extract REQUIRED
+ uint32_t required = stoul(vStrInputParts[1]);
+
+ // Extract NUMKEYS
+ uint32_t numkeys = stoul(vStrInputParts[2]);
+
+ // Validate there are the correct number of pubkeys
+ if (vStrInputParts.size() < numkeys + 3)
+ throw std::runtime_error("incorrect number of multisig pubkeys");
+
+ if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required)
+ throw std::runtime_error("multisig parameter mismatch. Required " \
+ + std::to_string(required) + " of " + std::to_string(numkeys) + "signatures.");
+
+ // extract and validate PUBKEYs
+ std::vector<CPubKey> pubkeys;
+ for(int pos = 1; pos <= int(numkeys); pos++) {
+ CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
+ if (!pubkey.IsFullyValid())
+ throw std::runtime_error("invalid TX output pubkey");
+ pubkeys.push_back(pubkey);
+ }
+
+ // Extract FLAGS
+ bool bSegWit = false;
+ bool bScriptHash = false;
+ if (vStrInputParts.size() == numkeys + 4) {
+ std::string flags = vStrInputParts.back();
+ bSegWit = (flags.find("W") != std::string::npos);
+ bScriptHash = (flags.find("S") != std::string::npos);
+ }
+ else if (vStrInputParts.size() > numkeys + 4) {
+ // Validate that there were no more parameters passed
+ throw std::runtime_error("Too many parameters");
+ }
+
+ CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
+
+ if (bSegWit) {
+ // Call GetScriptForWitness() to build a P2WSH scriptPubKey
+ scriptPubKey = GetScriptForWitness(scriptPubKey);
+ }
+ if (bScriptHash) {
+ // Get the address for the redeem script, then call
+ // GetScriptForDestination() to construct a P2SH scriptPubKey.
+ CBitcoinAddress addr(scriptPubKey);
+ scriptPubKey = GetScriptForDestination(addr.Get());
+ }
+
+ // construct TxOut, append to transaction output list
+ CTxOut txout(value, scriptPubKey);
+ tx.vout.push_back(txout);
+}
+
static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput)
{
CAmount value = 0;
@@ -260,10 +377,8 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn
throw std::runtime_error("TX output value not specified");
if (pos != std::string::npos) {
- // extract and validate VALUE
- std::string strValue = strInput.substr(0, pos);
- if (!ParseMoney(strValue, value))
- throw std::runtime_error("invalid TX output value");
+ // Extract and validate VALUE
+ value = ExtractAndValidateValue(strInput.substr(0, pos));
}
// extract and validate DATA
@@ -280,21 +395,35 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
{
- // separate VALUE:SCRIPT in string
- size_t pos = strInput.find(':');
- if ((pos == std::string::npos) ||
- (pos == 0))
+ // separate VALUE:SCRIPT[:FLAGS]
+ std::vector<std::string> vStrInputParts;
+ boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
+ if (vStrInputParts.size() < 2)
throw std::runtime_error("TX output missing separator");
- // extract and validate VALUE
- std::string strValue = strInput.substr(0, pos);
- CAmount value;
- if (!ParseMoney(strValue, value))
- throw std::runtime_error("invalid TX output value");
+ // Extract and validate VALUE
+ CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate script
- std::string strScript = strInput.substr(pos + 1, std::string::npos);
- CScript scriptPubKey = ParseScript(strScript); // throws on err
+ std::string strScript = vStrInputParts[1];
+ CScript scriptPubKey = ParseScript(strScript);
+
+ // Extract FLAGS
+ bool bSegWit = false;
+ bool bScriptHash = false;
+ if (vStrInputParts.size() == 3) {
+ std::string flags = vStrInputParts.back();
+ bSegWit = (flags.find("W") != std::string::npos);
+ bScriptHash = (flags.find("S") != std::string::npos);
+ }
+
+ if (bSegWit) {
+ scriptPubKey = GetScriptForWitness(scriptPubKey);
+ }
+ if (bScriptHash) {
+ CBitcoinAddress addr(scriptPubKey);
+ scriptPubKey = GetScriptForDestination(addr.Get());
+ }
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
@@ -538,10 +667,14 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
MutateTxDelOutput(tx, commandVal);
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
- else if (command == "outdata")
- MutateTxAddOutData(tx, commandVal);
+ else if (command == "outpubkey")
+ MutateTxAddOutPubKey(tx, commandVal);
+ else if (command == "outmultisig")
+ MutateTxAddOutMultiSig(tx, commandVal);
else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
+ else if (command == "outdata")
+ MutateTxAddOutData(tx, commandVal);
else if (command == "sign") {
if (!ecc) { ecc.reset(new Secp256k1Init()); }