Add all transaction output types to bitcoin-tx.
This commit enhances bitcoin-tx so all remaining standard TXO types can be created: - Pay to Pub Key - Multi-sig - bare multi-sig - multi-sig in Pay To Script Hash - multi-sig in Pay to Witness Script Hash - multi-sig in Pay to Witness Script Hash, wrapped in P2SH - Pay to Witness Pub Key Hash - Pay to Witness Pub Key Hash, wrapped in P2SH - Pay to Witness Script Hash - Pay to Witness Script Hash, wrapped in P2SH
This commit is contained in:
parent
1814b089fb
commit
61a153443e
1 changed files with 159 additions and 36 deletions
|
@ -78,10 +78,16 @@ static int AppInitRawTx(int argc, char* argv[])
|
||||||
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
|
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
|
||||||
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
|
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
|
||||||
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
|
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("outdata=[VALUE:]DATA", _("Add data-based output to TX"));
|
||||||
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT(:\"SEGWIT\")(:\"P2SH\")", _("Add raw script output to TX") + ". " +
|
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " +
|
||||||
_("Optionally add the \"SEGWIT\" flag to produce a segwit output") + ". " +
|
_("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
|
||||||
_("Optionally add the \"P2SH\" flag to wrap the script in a P2SH 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") + ". " +
|
strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
|
||||||
_("This command requires JSON registers:") +
|
_("This command requires JSON registers:") +
|
||||||
_("prevtxs=JSON object") + ", " +
|
_("prevtxs=JSON object") + ", " +
|
||||||
|
@ -170,6 +176,14 @@ static void RegisterLoad(const std::string& strInput)
|
||||||
RegisterSetJson(key, valStr);
|
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)
|
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
|
||||||
{
|
{
|
||||||
int64_t newVersion = atoi64(cmdVal);
|
int64_t newVersion = atoi64(cmdVal);
|
||||||
|
@ -224,25 +238,18 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
|
||||||
|
|
||||||
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
|
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
|
||||||
{
|
{
|
||||||
// separate VALUE:ADDRESS in string
|
// Separate into VALUE:ADDRESS
|
||||||
size_t pos = strInput.find(':');
|
std::vector<std::string> vStrInputParts;
|
||||||
if ((pos == std::string::npos) ||
|
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
|
||||||
(pos == 0) ||
|
|
||||||
(pos == (strInput.size() - 1)))
|
|
||||||
throw std::runtime_error("TX output missing separator");
|
|
||||||
|
|
||||||
// extract and validate VALUE
|
// Extract and validate VALUE
|
||||||
std::string strValue = strInput.substr(0, pos);
|
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
|
||||||
CAmount value;
|
|
||||||
if (!ParseMoney(strValue, value))
|
|
||||||
throw std::runtime_error("invalid TX output value");
|
|
||||||
|
|
||||||
// extract and validate ADDRESS
|
// extract and validate ADDRESS
|
||||||
std::string strAddr = strInput.substr(pos + 1, std::string::npos);
|
std::string strAddr = vStrInputParts[1];
|
||||||
CBitcoinAddress addr(strAddr);
|
CBitcoinAddress addr(strAddr);
|
||||||
if (!addr.IsValid())
|
if (!addr.IsValid())
|
||||||
throw std::runtime_error("invalid TX output address");
|
throw std::runtime_error("invalid TX output address");
|
||||||
|
|
||||||
// build standard output script via GetScriptForDestination()
|
// build standard output script via GetScriptForDestination()
|
||||||
CScript scriptPubKey = GetScriptForDestination(addr.Get());
|
CScript scriptPubKey = GetScriptForDestination(addr.Get());
|
||||||
|
|
||||||
|
@ -251,6 +258,114 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn
|
||||||
tx.vout.push_back(txout);
|
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)
|
static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput)
|
||||||
{
|
{
|
||||||
CAmount value = 0;
|
CAmount value = 0;
|
||||||
|
@ -262,10 +377,8 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn
|
||||||
throw std::runtime_error("TX output value not specified");
|
throw std::runtime_error("TX output value not specified");
|
||||||
|
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
// extract and validate VALUE
|
// Extract and validate VALUE
|
||||||
std::string strValue = strInput.substr(0, pos);
|
value = ExtractAndValidateValue(strInput.substr(0, pos));
|
||||||
if (!ParseMoney(strValue, value))
|
|
||||||
throw std::runtime_error("invalid TX output value");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract and validate DATA
|
// extract and validate DATA
|
||||||
|
@ -282,26 +395,32 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn
|
||||||
|
|
||||||
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
|
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
|
||||||
{
|
{
|
||||||
// separate VALUE:SCRIPT(:SEGWIT)(:P2SH)
|
// separate VALUE:SCRIPT[:FLAGS]
|
||||||
std::vector<std::string> vStrInput;
|
std::vector<std::string> vStrInputParts;
|
||||||
boost::split(vStrInput, strInput, boost::is_any_of(":"));
|
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
|
||||||
if (vStrInput.size() < 2)
|
if (vStrInputParts.size() < 2)
|
||||||
throw srd::runtime_error("TX output missing separator");
|
throw std::runtime_error("TX output missing separator");
|
||||||
|
|
||||||
// extract and validate VALUE
|
// Extract and validate VALUE
|
||||||
std::string strValue = vStrInput[0];
|
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
|
||||||
CAmount value;
|
|
||||||
if (!ParseMoney(strValue, value))
|
|
||||||
throw std::runtime_error("invalid TX output value");
|
|
||||||
|
|
||||||
// extract and validate script
|
// extract and validate script
|
||||||
std::string strScript = vStrInput[1];
|
std::string strScript = vStrInputParts[1];
|
||||||
CScript scriptPubKey = ParseScript(strScript); // throws on err
|
CScript scriptPubKey = ParseScript(strScript);
|
||||||
|
|
||||||
if (std::find(vStrInput.begin(), vStrInput.end(), "SEGWIT") != vStrInput.end()) {
|
// 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);
|
scriptPubKey = GetScriptForWitness(scriptPubKey);
|
||||||
}
|
}
|
||||||
if (std::find(vStrInput.begin(), vStrInput.end(), "P2SH") != vStrInput.end()) {
|
if (bScriptHash) {
|
||||||
CBitcoinAddress addr(scriptPubKey);
|
CBitcoinAddress addr(scriptPubKey);
|
||||||
scriptPubKey = GetScriptForDestination(addr.Get());
|
scriptPubKey = GetScriptForDestination(addr.Get());
|
||||||
}
|
}
|
||||||
|
@ -548,10 +667,14 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
|
||||||
MutateTxDelOutput(tx, commandVal);
|
MutateTxDelOutput(tx, commandVal);
|
||||||
else if (command == "outaddr")
|
else if (command == "outaddr")
|
||||||
MutateTxAddOutAddr(tx, commandVal);
|
MutateTxAddOutAddr(tx, commandVal);
|
||||||
else if (command == "outdata")
|
else if (command == "outpubkey")
|
||||||
MutateTxAddOutData(tx, commandVal);
|
MutateTxAddOutPubKey(tx, commandVal);
|
||||||
|
else if (command == "outmultisig")
|
||||||
|
MutateTxAddOutMultiSig(tx, commandVal);
|
||||||
else if (command == "outscript")
|
else if (command == "outscript")
|
||||||
MutateTxAddOutScript(tx, commandVal);
|
MutateTxAddOutScript(tx, commandVal);
|
||||||
|
else if (command == "outdata")
|
||||||
|
MutateTxAddOutData(tx, commandVal);
|
||||||
|
|
||||||
else if (command == "sign") {
|
else if (command == "sign") {
|
||||||
if (!ecc) { ecc.reset(new Secp256k1Init()); }
|
if (!ecc) { ecc.reset(new Secp256k1Init()); }
|
||||||
|
|
Loading…
Reference in a new issue