Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 159 additions & 21 deletions src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,16 @@ static bool 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") + ", " +
Expand Down Expand Up @@ -215,33 +223,142 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)

static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
{
// separate VALUE:ADDRESS in string
size_t pos = strInput.find(':');
if ((pos == string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw runtime_error("TX output missing separator");
// Seperate into VALUE:ADDRESS
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));

// extract and validate VALUE
string strValue = strInput.substr(0, pos);
// Extract and validate VALUE
string strValue = vStrInputParts[0];
CAmount value;
if (!ParseMoney(strValue, value))
throw runtime_error("invalid TX output value");

// extract and validate ADDRESS
string strAddr = strInput.substr(pos + 1, string::npos);
string strAddr = vStrInputParts[1];
CBitcoinAddress addr(strAddr);
if (!addr.IsValid())
throw runtime_error("invalid TX output address");

// build standard output script via GetScriptForDestination()
CScript scriptPubKey = GetScriptForDestination(addr.Get());

// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}

static void MutateTxAddOutPubKey(CMutableTransaction& tx, const string& strInput)
{
// Seperate into VALUE:PUBKEY[:FLAGS]
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));

// Extract and validate VALUE
string strValue = vStrInputParts[0];
CAmount value;
if (!ParseMoney(strValue, value))
throw runtime_error("invalid TX output value");

// Extract and validate PUBKEY
CPubKey pubkey(ParseHex(vStrInputParts[1]));
if (!pubkey.IsFullyValid())
throw 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 addr(scriptPubKey);
scriptPubKey = GetScriptForDestination(addr.Get());
}

// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}

static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const string& strInput)
{
// Seperate 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 runtime_error("Not enough multisig parameters");

// Extract and validate VALUE
string strValue = vStrInputParts[0];
CAmount value;
if (!ParseMoney(strValue, value))
throw runtime_error("invalid TX output value");

// Extract REQUIRED
uint required = std::stoul(vStrInputParts[1]);

// Extract NUMKEYS
uint numkeys = std::stoul(vStrInputParts[2]);

// Validate there are the correct number of pubkeys
if (vStrInputParts.size() < numkeys + 3)
throw runtime_error("incorrect number of multisig pubkeys");

if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required)
throw runtime_error("multisig parameter mismatch. Required " \
+ to_string(required) + " of " + to_string(numkeys) + "signatures.");

// extract and validate PUBKEYs
std::vector<CPubKey> pubkeys;
for(uint pos = 1; pos <= numkeys; pos++) {
CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
if (!pubkey.IsFullyValid())
throw 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 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 string& strInput)
{
CAmount value = 0;
Expand Down Expand Up @@ -273,21 +390,38 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)

static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
{
// separate VALUE:SCRIPT in string
size_t pos = strInput.find(':');
if ((pos == string::npos) ||
(pos == 0))
// separate VALUE:SCRIPT[:FLAGS]
std::vector<std::string> vStrInput;
boost::split(vStrInput, strInput, boost::is_any_of(":"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be a general move away from Boost (https://github.com/bitcoin/bitcoin/projects/3). Sadly, I don't think there's anything in c++11 as simple as boost::split for splitting a string (see RegisterSet() for another way of splitting the string).

It's probably cleaner to keep this as is, but you may want to consider doing it without boost.

if (vStrInput.size() < 2)
throw runtime_error("TX output missing separator");

// extract and validate VALUE
string strValue = strInput.substr(0, pos);
string strValue = vStrInput[0];
CAmount value;
if (!ParseMoney(strValue, value))
throw runtime_error("invalid TX output value");

// extract and validate script
string strScript = strInput.substr(pos + 1, string::npos);
CScript scriptPubKey = ParseScript(strScript); // throws on err
string strScript = vStrInput[1];
CScript scriptPubKey = ParseScript(strScript);

// Extract FLAGS
bool bSegWit = false;
bool bScriptHash = false;
if (vStrInput.size() == 3) {
std::string flags = vStrInput.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);
Expand Down Expand Up @@ -531,10 +665,14 @@ static void MutateTx(CMutableTransaction& tx, const 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()); }
Expand Down
116 changes: 116 additions & 0 deletions src/test/data/bitcoin-util-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,46 @@
"args": ["-json", "-create", "outscript=0:"],
"output_cmp": "txcreate2.json"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outscript=0:OP_DROP"],
"output_cmp": "txcreatescript1.hex",
"description": "Create a new transaction with a single output script (OP_DROP)"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outscript=0:OP_DROP"],
"output_cmp": "txcreatescript1.json",
"description": "Create a new transaction with a single output script (OP_DROP) (output as json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outscript=0:OP_DROP:S"],
"output_cmp": "txcreatescript2.hex",
"description": "Create a new transaction with a single output script (OP_DROP) in a P2SH"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outscript=0:OP_DROP:S"],
"output_cmp": "txcreatescript2.json",
"description": "Create a new transaction with a single output script (OP_DROP) in a P2SH (output as json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outscript=0:OP_DROP:W"],
"output_cmp": "txcreatescript3.hex",
"description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outscript=0:OP_DROP:W"],
"output_cmp": "txcreatescript3.json",
"description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH (output as json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outscript=0:OP_DROP:WS"],
"output_cmp": "txcreatescript4.hex",
"description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH, wrapped in a P2SH"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outscript=0:OP_DROP:WS"],
"output_cmp": "txcreatescript4.json",
"description": "Create a new transaction with a single output script (OP_DROP) in a P2SH, wrapped in a P2SH (output as json)"
},
{ "exec": "./bitcoin-tx",
"args":
["-create",
Expand All @@ -107,6 +147,42 @@
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
"output_cmp": "txcreatesign.json"
},
{ "exec": "./bitcoin-tx",
"args":
["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397"],
"output_cmp": "txcreateoutpubkey1.hex",
"description": "Creates a new transaction with a single pay-to-pubkey output"
},
{ "exec": "./bitcoin-tx",
"args":
["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397"],
"output_cmp": "txcreateoutpubkey1.json",
"description": "Creates a new transaction with a single pay-to-pubkey output (output as json)"
},
{ "exec": "./bitcoin-tx",
"args":
["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:W"],
"output_cmp": "txcreateoutpubkey2.hex",
"description": "Creates a new transaction with a single pay-to-witness-pubkey output"
},
{ "exec": "./bitcoin-tx",
"args":
["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:W"],
"output_cmp": "txcreateoutpubkey2.json",
"description": "Creates a new transaction with a single pay-to-witness-pubkey output (output as json)"
},
{ "exec": "./bitcoin-tx",
"args":
["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:WS"],
"output_cmp": "txcreateoutpubkey3.hex",
"description": "Creates a new transaction with a single pay-to-witness-pubkey, wrapped in P2SH output"
},
{ "exec": "./bitcoin-tx",
"args":
["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:WS"],
"output_cmp": "txcreateoutpubkey3.json",
"description": "Creates a new transaction with a single pay-to-pub-key output, wrapped in P2SH (output as json)"
},
{ "exec": "./bitcoin-tx",
"args":
["-create",
Expand Down Expand Up @@ -182,5 +258,45 @@
"01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:1"],
"output_cmp": "txcreatedata_seq1.json"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485"],
"output_cmp": "txcreatemultisig1.hex",
"description": "Creates a new transaction with a single 2-of-3 multisig output"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485"],
"output_cmp": "txcreatemultisig1.json",
"description": "Creates a new transaction with a single 2-of-3 multisig output (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:S"],
"output_cmp": "txcreatemultisig2.hex",
"description": "Creates a new transaction with a single 2-of-3 multisig in a P2SH output"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:S"],
"output_cmp": "txcreatemultisig2.json",
"description": "Creates a new transaction with a single 2-of-3 multisig in a P2SH output (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:W"],
"output_cmp": "txcreatemultisig3.hex",
"description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:W"],
"output_cmp": "txcreatemultisig3.json",
"description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS"],
"output_cmp": "txcreatemultisig4.hex",
"description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH"
},
{ "exec": "./bitcoin-tx",
"args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS"],
"output_cmp": "txcreatemultisig4.json",
"description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH (output in json)"
}
]
1 change: 1 addition & 0 deletions src/test/data/txcreatemultisig1.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
01000000000100e1f5050000000069522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae00000000
25 changes: 25 additions & 0 deletions src/test/data/txcreatemultisig1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"txid": "0d1d4edfc217d9db3ab6a9298f26a52eae3c52f55a6cb8ccbc14f7c727572894",
"version": 1,
"locktime": 0,
"vin": [
],
"vout": [
{
"value": 1.00,
"n": 0,
"scriptPubKey": {
"asm": "2 02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397 021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d 02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485 3 OP_CHECKMULTISIG",
"hex": "522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae",
"reqSigs": 2,
"type": "multisig",
"addresses": [
"1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz",
"1FXtz9KU8JNmQDyHdiEm5HDiALuP3zdHvV",
"14LuavcBbXZYJ6Tsz3cAUQj9SuQoL2xCQX"
]
}
}
],
"hex": "01000000000100e1f5050000000069522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae00000000"
}
Loading