Skip to content

Commit dfa2ac6

Browse files
committed
Add all transaction output types to bitcoin-tx.
1 parent 7e19a32 commit dfa2ac6

File tree

1 file changed

+148
-20
lines changed

1 file changed

+148
-20
lines changed

src/bitcoin-tx.cpp

Lines changed: 148 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,16 @@ static bool AppInitRawTx(int argc, char* argv[])
7575
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
7676
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
7777
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
78+
strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " +
79+
_("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " +
80+
_("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
7881
strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX"));
79-
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT(:\"SEGWIT\")(:\"P2SH\")", _("Add raw script output to TX") + ". " +
80-
_("Optionally add the \"SEGWIT\" flag to produce a segwit output") + ". " +
81-
_("Optionally add the \"P2SH\" flag to wrap the script in a P2SH output."));
82+
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " +
83+
_("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
84+
_("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
85+
strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " +
86+
_("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
87+
_("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
8288
strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
8389
_("This command requires JSON registers:") +
8490
_("prevtxs=JSON object") + ", " +
@@ -217,33 +223,142 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
217223

218224
static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
219225
{
220-
// separate VALUE:ADDRESS in string
221-
size_t pos = strInput.find(':');
222-
if ((pos == string::npos) ||
223-
(pos == 0) ||
224-
(pos == (strInput.size() - 1)))
225-
throw runtime_error("TX output missing separator");
226+
// Seperate into VALUE:ADDRESS
227+
std::vector<std::string> vStrInputParts;
228+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
226229

227-
// extract and validate VALUE
228-
string strValue = strInput.substr(0, pos);
230+
// Extract and validate VALUE
231+
string strValue = vStrInputParts[0];
229232
CAmount value;
230233
if (!ParseMoney(strValue, value))
231234
throw runtime_error("invalid TX output value");
232235

233236
// extract and validate ADDRESS
234-
string strAddr = strInput.substr(pos + 1, string::npos);
237+
string strAddr = vStrInputParts[1];
235238
CBitcoinAddress addr(strAddr);
236239
if (!addr.IsValid())
237240
throw runtime_error("invalid TX output address");
238-
239-
// build standard output script via GetScriptForDestination()
240241
CScript scriptPubKey = GetScriptForDestination(addr.Get());
241242

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

248+
static void MutateTxAddOutPubKey(CMutableTransaction& tx, const string& strInput)
249+
{
250+
// Seperate into VALUE:PUBKEY[:FLAGS]
251+
std::vector<std::string> vStrInputParts;
252+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
253+
254+
// Extract and validate VALUE
255+
string strValue = vStrInputParts[0];
256+
CAmount value;
257+
if (!ParseMoney(strValue, value))
258+
throw runtime_error("invalid TX output value");
259+
260+
// Extract and validate PUBKEY
261+
CPubKey pubkey(ParseHex(vStrInputParts[1]));
262+
if (!pubkey.IsFullyValid())
263+
throw runtime_error("invalid TX output pubkey");
264+
CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
265+
CBitcoinAddress addr(scriptPubKey);
266+
267+
// Extract and validate FLAGS
268+
bool bSegWit = false;
269+
bool bScriptHash = false;
270+
if (vStrInputParts.size() == 3) {
271+
std::string flags = vStrInputParts[2];
272+
bSegWit = (flags.find("W") != std::string::npos);
273+
bScriptHash = (flags.find("S") != std::string::npos);
274+
}
275+
276+
if (bSegWit) {
277+
// Call GetScriptForWitness() to build a P2WSH scriptPubKey
278+
scriptPubKey = GetScriptForWitness(scriptPubKey);
279+
}
280+
if (bScriptHash) {
281+
// Get the address for the redeem script, then call
282+
// GetScriptForDestination() to construct a P2SH scriptPubKey.
283+
CBitcoinAddress addr(scriptPubKey);
284+
scriptPubKey = GetScriptForDestination(addr.Get());
285+
}
286+
287+
// construct TxOut, append to transaction output list
288+
CTxOut txout(value, scriptPubKey);
289+
tx.vout.push_back(txout);
290+
}
291+
292+
static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const string& strInput)
293+
{
294+
// Seperate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
295+
std::vector<std::string> vStrInputParts;
296+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
297+
298+
// Check that there are enough parameters
299+
if (vStrInputParts.size()<3)
300+
throw runtime_error("Not enough multisig parameters");
301+
302+
// Extract and validate VALUE
303+
string strValue = vStrInputParts[0];
304+
CAmount value;
305+
if (!ParseMoney(strValue, value))
306+
throw runtime_error("invalid TX output value");
307+
308+
// Extract REQUIRED
309+
uint required = std::stoul(vStrInputParts[1]);
310+
311+
// Extract NUMKEYS
312+
uint numkeys = std::stoul(vStrInputParts[2]);
313+
314+
// Validate there are the correct number of pubkeys
315+
if (vStrInputParts.size() < numkeys + 3)
316+
throw runtime_error("incorrect number of multisig pubkeys");
317+
318+
if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required)
319+
throw runtime_error("multisig parameter mismatch. Required " \
320+
+ to_string(required) + " of " + to_string(numkeys) + "signatures.");
321+
322+
// extract and validate PUBKEYs
323+
std::vector<CPubKey> pubkeys;
324+
for(uint pos = 1; pos <= numkeys; pos++) {
325+
CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
326+
if (!pubkey.IsFullyValid())
327+
throw runtime_error("invalid TX output pubkey");
328+
pubkeys.push_back(pubkey);
329+
}
330+
331+
// Extract FLAGS
332+
bool bSegWit = false;
333+
bool bScriptHash = false;
334+
if (vStrInputParts.size() == numkeys + 4) {
335+
std::string flags = vStrInputParts.back();
336+
bSegWit = (flags.find("W") != std::string::npos);
337+
bScriptHash = (flags.find("S") != std::string::npos);
338+
}
339+
else if (vStrInputParts.size() > numkeys + 4) {
340+
// Validate that there were no more parameters passed
341+
throw runtime_error("Too many parameters");
342+
}
343+
344+
CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
345+
346+
if (bSegWit) {
347+
// Call GetScriptForWitness() to build a P2WSH scriptPubKey
348+
scriptPubKey = GetScriptForWitness(scriptPubKey);
349+
}
350+
if (bScriptHash) {
351+
// Get the address for the redeem script, then call
352+
// GetScriptForDestination() to construct a P2SH scriptPubKey.
353+
CBitcoinAddress addr(scriptPubKey);
354+
scriptPubKey = GetScriptForDestination(addr.Get());
355+
}
356+
357+
// construct TxOut, append to transaction output list
358+
CTxOut txout(value, scriptPubKey);
359+
tx.vout.push_back(txout);
360+
}
361+
247362
static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)
248363
{
249364
CAmount value = 0;
@@ -275,7 +390,7 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)
275390

276391
static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
277392
{
278-
// separate VALUE:SCRIPT(:SEGWIT)(:P2SH)
393+
// separate VALUE:SCRIPT[:FLAGS]
279394
std::vector<std::string> vStrInput;
280395
boost::split(vStrInput, strInput, boost::is_any_of(":"));
281396
if (vStrInput.size() < 2)
@@ -289,12 +404,21 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput
289404

290405
// extract and validate script
291406
string strScript = vStrInput[1];
292-
CScript scriptPubKey = ParseScript(strScript); // throws on err
407+
CScript scriptPubKey = ParseScript(strScript);
408+
409+
// Extract FLAGS
410+
bool bSegWit = false;
411+
bool bScriptHash = false;
412+
if (vStrInput.size() == 3) {
413+
std::string flags = vStrInput.back();
414+
bSegWit = (flags.find("W") != std::string::npos);
415+
bScriptHash = (flags.find("S") != std::string::npos);
416+
}
293417

294-
if (std::find(vStrInput.begin(), vStrInput.end(), "SEGWIT") != vStrInput.end()) {
418+
if (bSegWit) {
295419
scriptPubKey = GetScriptForWitness(scriptPubKey);
296420
}
297-
if (std::find(vStrInput.begin(), vStrInput.end(), "P2SH") != vStrInput.end()) {
421+
if (bScriptHash) {
298422
CBitcoinAddress addr(scriptPubKey);
299423
scriptPubKey = GetScriptForDestination(addr.Get());
300424
}
@@ -541,10 +665,14 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
541665
MutateTxDelOutput(tx, commandVal);
542666
else if (command == "outaddr")
543667
MutateTxAddOutAddr(tx, commandVal);
544-
else if (command == "outdata")
545-
MutateTxAddOutData(tx, commandVal);
668+
else if (command == "outpubkey")
669+
MutateTxAddOutPubKey(tx, commandVal);
670+
else if (command == "outmultisig")
671+
MutateTxAddOutMultiSig(tx, commandVal);
546672
else if (command == "outscript")
547673
MutateTxAddOutScript(tx, commandVal);
674+
else if (command == "outdata")
675+
MutateTxAddOutData(tx, commandVal);
548676

549677
else if (command == "sign") {
550678
if (!ecc) { ecc.reset(new Secp256k1Init()); }

0 commit comments

Comments
 (0)