|
|
|
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
#include "base58.h"
|
|
|
|
#include "clientversion.h"
|
|
|
|
#include "coins.h"
|
|
|
|
#include "consensus/consensus.h"
|
|
|
|
#include "core_io.h"
|
|
|
|
#include "keystore.h"
|
|
|
|
#include "primitives/transaction.h"
|
|
|
|
#include "script/script.h"
|
|
|
|
#include "script/sign.h"
|
|
|
|
#include "univalue/univalue.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "utilmoneystr.h"
|
|
|
|
#include "utilstrencodings.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <boost/assign/list_of.hpp>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
static bool fCreateBlank;
|
|
|
|
static map<string,UniValue> registers;
|
|
|
|
|
|
|
|
static bool AppInitRawTx(int argc, char* argv[])
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Parameters
|
|
|
|
//
|
|
|
|
ParseParameters(argc, argv);
|
|
|
|
|
|
|
|
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
|
|
|
|
if (!SelectParamsFromCommandLine()) {
|
|
|
|
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fCreateBlank = GetBoolArg("-create", false);
|
|
|
|
|
|
|
|
if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help"))
|
|
|
|
{
|
|
|
|
// First part of help message is specific to this utility
|
|
|
|
std::string strUsage = _("Bitcoin Core bitcoin-tx utility version") + " " + FormatFullVersion() + "\n\n" +
|
|
|
|
_("Usage:") + "\n" +
|
|
|
|
" bitcoin-tx [options] <hex-tx> [commands] " + _("Update hex-encoded bitcoin transaction") + "\n" +
|
|
|
|
" bitcoin-tx [options] -create [commands] " + _("Create hex-encoded bitcoin transaction") + "\n" +
|
|
|
|
"\n";
|
|
|
|
|
|
|
|
fprintf(stdout, "%s", strUsage.c_str());
|
|
|
|
|
|
|
|
strUsage = HelpMessageGroup(_("Options:"));
|
|
|
|
strUsage += HelpMessageOpt("-?", _("This help message"));
|
|
|
|
strUsage += HelpMessageOpt("-create", _("Create new, empty TX."));
|
|
|
|
strUsage += HelpMessageOpt("-json", _("Select JSON output"));
|
|
|
|
strUsage += HelpMessageOpt("-txid", _("Output only the hex-encoded transaction id of the resultant transaction."));
|
|
|
|
strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be solved instantly."));
|
|
|
|
strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
|
|
|
|
|
|
|
|
fprintf(stdout, "%s", strUsage.c_str());
|
|
|
|
|
|
|
|
strUsage = HelpMessageGroup(_("Commands:"));
|
|
|
|
strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX"));
|
|
|
|
strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX"));
|
|
|
|
strUsage += HelpMessageOpt("in=TXID:VOUT", _("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("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
|
|
|
|
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX"));
|
|
|
|
strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
|
|
|
|
_("This command requires JSON registers:") +
|
|
|
|
_("prevtxs=JSON object") + ", " +
|
|
|
|
_("privatekeys=JSON object") + ". " +
|
|
|
|
_("See signrawtransaction docs for format of sighash flags, JSON objects."));
|
|
|
|
fprintf(stdout, "%s", strUsage.c_str());
|
|
|
|
|
|
|
|
strUsage = HelpMessageGroup(_("Register Commands:"));
|
|
|
|
strUsage += HelpMessageOpt("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME"));
|
|
|
|
strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
|
|
|
|
fprintf(stdout, "%s", strUsage.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RegisterSetJson(const string& key, const string& rawJson)
|
|
|
|
{
|
|
|
|
UniValue val;
|
|
|
|
if (!val.read(rawJson)) {
|
|
|
|
string strErr = "Cannot parse JSON for key " + key;
|
|
|
|
throw runtime_error(strErr);
|
|
|
|
}
|
|
|
|
|
|
|
|
registers[key] = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RegisterSet(const string& strInput)
|
|
|
|
{
|
|
|
|
// separate NAME:VALUE in string
|
|
|
|
size_t pos = strInput.find(':');
|
|
|
|
if ((pos == string::npos) ||
|
|
|
|
(pos == 0) ||
|
|
|
|
(pos == (strInput.size() - 1)))
|
|
|
|
throw runtime_error("Register input requires NAME:VALUE");
|
|
|
|
|
|
|
|
string key = strInput.substr(0, pos);
|
|
|
|
string valStr = strInput.substr(pos + 1, string::npos);
|
|
|
|
|
|
|
|
RegisterSetJson(key, valStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RegisterLoad(const string& strInput)
|
|
|
|
{
|
|
|
|
// separate NAME:FILENAME in string
|
|
|
|
size_t pos = strInput.find(':');
|
|
|
|
if ((pos == string::npos) ||
|
|
|
|
(pos == 0) ||
|
|
|
|
(pos == (strInput.size() - 1)))
|
|
|
|
throw runtime_error("Register load requires NAME:FILENAME");
|
|
|
|
|
|
|
|
string key = strInput.substr(0, pos);
|
|
|
|
string filename = strInput.substr(pos + 1, string::npos);
|
|
|
|
|
|
|
|
FILE *f = fopen(filename.c_str(), "r");
|
|
|
|
if (!f) {
|
|
|
|
string strErr = "Cannot open file " + filename;
|
|
|
|
throw runtime_error(strErr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// load file chunks into one big buffer
|
|
|
|
string valStr;
|
|
|
|
while ((!feof(f)) && (!ferror(f))) {
|
|
|
|
char buf[4096];
|
|
|
|
int bread = fread(buf, 1, sizeof(buf), f);
|
|
|
|
if (bread <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
valStr.insert(valStr.size(), buf, bread);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ferror(f)) {
|
|
|
|
string strErr = "Error reading file " + filename;
|
|
|
|
throw runtime_error(strErr);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
// evaluate as JSON buffer register
|
|
|
|
RegisterSetJson(key, valStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
|
|
|
|
{
|
|
|
|
int64_t newVersion = atoi64(cmdVal);
|
|
|
|
if (newVersion < 1 || newVersion > CTransaction::CURRENT_VERSION)
|
|
|
|
throw runtime_error("Invalid TX version requested");
|
|
|
|
|
|
|
|
tx.nVersion = (int) newVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
|
|
|
|
{
|
|
|
|
int64_t newLocktime = atoi64(cmdVal);
|
|
|
|
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
|
|
|
|
throw runtime_error("Invalid TX locktime requested");
|
|
|
|
|
|
|
|
tx.nLockTime = (unsigned int) newLocktime;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
|
|
|
|
{
|
|
|
|
// separate TXID:VOUT in string
|
|
|
|
size_t pos = strInput.find(':');
|
|
|
|
if ((pos == string::npos) ||
|
|
|
|
(pos == 0) ||
|
|
|
|
(pos == (strInput.size() - 1)))
|
|
|
|
throw runtime_error("TX input missing separator");
|
|
|
|
|
|
|
|
// extract and validate TXID
|
|
|
|
string strTxid = strInput.substr(0, pos);
|
|
|
|
if ((strTxid.size() != 64) || !IsHex(strTxid))
|
|
|
|
throw runtime_error("invalid TX input txid");
|
|
|
|
uint256 txid(uint256S(strTxid));
|
|
|
|
|
|
|
|
static const unsigned int minTxOutSz = 9;
|
|
|
|
static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz;
|
|
|
|
|
|
|
|
// extract and validate vout
|
|
|
|
string strVout = strInput.substr(pos + 1, string::npos);
|
|
|
|
int vout = atoi(strVout);
|
|
|
|
if ((vout < 0) || (vout > (int)maxVout))
|
|
|
|
throw runtime_error("invalid TX input vout");
|
|
|
|
|
|
|
|
// append to transaction input list
|
|
|
|
CTxIn txin(txid, vout);
|
|
|
|
tx.vin.push_back(txin);
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
// extract and validate VALUE
|
|
|
|
string strValue = strInput.substr(0, pos);
|
|
|
|
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);
|
|
|
|
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 MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
|
|
|
|
{
|
|
|
|
// separate VALUE:SCRIPT in string
|
|
|
|
size_t pos = strInput.find(':');
|
|
|
|
if ((pos == string::npos) ||
|
|
|
|
(pos == 0))
|
|
|
|
throw runtime_error("TX output missing separator");
|
|
|
|
|
|
|
|
// extract and validate VALUE
|
|
|
|
string strValue = strInput.substr(0, pos);
|
|
|
|
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
|
|
|
|
|
|
|
|
// construct TxOut, append to transaction output list
|
|
|
|
CTxOut txout(value, scriptPubKey);
|
|
|
|
tx.vout.push_back(txout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
|
|
|
|
{
|
|
|
|
// parse requested deletion index
|
|
|
|
int inIdx = atoi(strInIdx);
|
|
|
|
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
|
|
|
|
string strErr = "Invalid TX input index '" + strInIdx + "'";
|
|
|
|
throw runtime_error(strErr.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete input from transaction
|
|
|
|
tx.vin.erase(tx.vin.begin() + inIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx)
|
|
|
|
{
|
|
|
|
// parse requested deletion index
|
|
|
|
int outIdx = atoi(strOutIdx);
|
|
|
|
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
|
|
|
|
string strErr = "Invalid TX output index '" + strOutIdx + "'";
|
|
|
|
throw runtime_error(strErr.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete output from transaction
|
|
|
|
tx.vout.erase(tx.vout.begin() + outIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const unsigned int N_SIGHASH_OPTS = 6;
|
|
|
|
static const struct {
|
|
|
|
const char *flagStr;
|
|
|
|
int flags;
|
|
|
|
} sighashOptions[N_SIGHASH_OPTS] = {
|
|
|
|
{"ALL", SIGHASH_ALL},
|
|
|
|
{"NONE", SIGHASH_NONE},
|
|
|
|
{"SINGLE", SIGHASH_SINGLE},
|
|
|
|
{"ALL|ANYONECANPAY", SIGHASH_ALL|SIGHASH_ANYONECANPAY},
|
|
|
|
{"NONE|ANYONECANPAY", SIGHASH_NONE|SIGHASH_ANYONECANPAY},
|
|
|
|
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool findSighashFlags(int& flags, const string& flagStr)
|
|
|
|
{
|
|
|
|
flags = 0;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
|
|
|
|
if (flagStr == sighashOptions[i].flagStr) {
|
|
|
|
flags = sighashOptions[i].flags;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint256 ParseHashUO(map<string,UniValue>& o, string strKey)
|
|
|
|
{
|
|
|
|
if (!o.count(strKey))
|
|
|
|
return uint256();
|
|
|
|
return ParseHashUV(o[strKey], strKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
|
|
|
|
{
|
|
|
|
if (!o.count(strKey)) {
|
|
|
|
vector<unsigned char> emptyVec;
|
|
|
|
return emptyVec;
|
|
|
|
}
|
|
|
|
return ParseHexUV(o[strKey], strKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
|
|
|
|
{
|
|
|
|
int nHashType = SIGHASH_ALL;
|
|
|
|
|
|
|
|
if (flagStr.size() > 0)
|
|
|
|
if (!findSighashFlags(nHashType, flagStr))
|
|
|
|
throw runtime_error("unknown sighash flag/sign option");
|
|
|
|
|
|
|
|
vector<CTransaction> txVariants;
|
|
|
|
txVariants.push_back(tx);
|
|
|
|
|
|
|
|
// mergedTx will end up with all the signatures; it
|
|
|
|
// starts as a clone of the raw tx:
|
|
|
|
CMutableTransaction mergedTx(txVariants[0]);
|
|
|
|
bool fComplete = true;
|
|
|
|
CCoinsView viewDummy;
|
|
|
|
CCoinsViewCache view(&viewDummy);
|
|
|
|
|
|
|
|
if (!registers.count("privatekeys"))
|
|
|
|
throw runtime_error("privatekeys register variable must be set.");
|
|
|
|
bool fGivenKeys = false;
|
|
|
|
CBasicKeyStore tempKeystore;
|
|
|
|
UniValue keysObj = registers["privatekeys"];
|
|
|
|
fGivenKeys = true;
|
|
|
|
|
|
|
|
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
|
|
|
|
if (!keysObj[kidx].isStr())
|
|
|
|
throw runtime_error("privatekey not a string");
|
|
|
|
CBitcoinSecret vchSecret;
|
|
|
|
bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
|
|
|
|
if (!fGood)
|
|
|
|
throw runtime_error("privatekey not valid");
|
|
|
|
|
|
|
|
CKey key = vchSecret.GetKey();
|
|
|
|
tempKeystore.AddKey(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add previous txouts given in the RPC call:
|
|
|
|
if (!registers.count("prevtxs"))
|
|
|
|
throw runtime_error("prevtxs register variable must be set.");
|
|
|
|
UniValue prevtxsObj = registers["prevtxs"];
|
|
|
|
{
|
|
|
|
for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
|
|
|
|
UniValue prevOut = prevtxsObj[previdx];
|
|
|
|
if (!prevOut.isObject())
|
|
|
|
throw runtime_error("expected prevtxs internal object");
|
|
|
|
|
|
|
|
map<string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
|
|
|
|
if (!prevOut.checkObject(types))
|
|
|
|
throw runtime_error("prevtxs internal object typecheck fail");
|
|
|
|
|
|
|
|
uint256 txid = ParseHashUV(prevOut["txid"], "txid");
|
|
|
|
|
|
|
|
int nOut = atoi(prevOut["vout"].getValStr());
|
|
|
|
if (nOut < 0)
|
|
|
|
throw runtime_error("vout must be positive");
|
|
|
|
|
|
|
|
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) {
|
|
|
|
string err("Previous output scriptPubKey mismatch:\n");
|
|
|
|
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
|
|
|
scriptPubKey.ToString();
|
|
|
|
throw 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; // we don't know the actual output value
|
|
|
|
}
|
|
|
|
|
|
|
|
// if redeemScript given and private keys given,
|
|
|
|
// add redeemScript to the tempKeystore so it can be signed:
|
|
|
|
if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
|
|
|
|
prevOut.exists("redeemScript")) {
|
|
|
|
UniValue v = prevOut["redeemScript"];
|
|
|
|
vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
|
|
|
|
CScript redeemScript(rsData.begin(), rsData.end());
|
|
|
|
tempKeystore.AddCScript(redeemScript);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const CKeyStore& keystore = tempKeystore;
|
|
|
|
|
|
|
|
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
|
|
|
|
|
|
|
// 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)) {
|
|
|
|
fComplete = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
|
|
|
|
|
|
|
|
txin.scriptSig.clear();
|
|
|
|
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
|
|
|
if (!fHashSingle || (i < mergedTx.vout.size()))
|
|
|
|
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
|
|
|
|
|
|
|
|
// ... and merge in other signatures:
|
|
|
|
BOOST_FOREACH(const CTransaction& txv, txVariants) {
|
|
|
|
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
|
|
|
|
}
|
|
|
|
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i)))
|
|
|
|
fComplete = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fComplete) {
|
|
|
|
// do nothing... for now
|
|
|
|
// perhaps store this for later optional JSON output
|
|
|
|
}
|
|
|
|
|
|
|
|
tx = mergedTx;
|
|
|
|
}
|
|
|
|
|
Update key.cpp to use new libsecp256k1
libsecp256k1's API changed, so update key.cpp to use it.
Libsecp256k1 now has explicit context objects, which makes it completely thread-safe.
In turn, keep an explicit context object in key.cpp, which is explicitly initialized
destroyed. This is not really pretty now, but it's more efficient than the static
initialized object in key.cpp (which made for example bitcoin-tx slow, as for most of
its calls, libsecp256k1 wasn't actually needed).
This also brings in the new blinding support in libsecp256k1. By passing in a random
seed, temporary variables during the elliptic curve computations are altered, in such
a way that if an attacker does not know the blind, observing the internal operations
leaks less information about the keys used. This was implemented by Greg Maxwell.
10 years ago
|
|
|
class Secp256k1Init
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Secp256k1Init() { ECC_Start(); }
|
|
|
|
~Secp256k1Init() { ECC_Stop(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
static void MutateTx(CMutableTransaction& tx, const string& command,
|
|
|
|
const string& commandVal)
|
|
|
|
{
|
Update key.cpp to use new libsecp256k1
libsecp256k1's API changed, so update key.cpp to use it.
Libsecp256k1 now has explicit context objects, which makes it completely thread-safe.
In turn, keep an explicit context object in key.cpp, which is explicitly initialized
destroyed. This is not really pretty now, but it's more efficient than the static
initialized object in key.cpp (which made for example bitcoin-tx slow, as for most of
its calls, libsecp256k1 wasn't actually needed).
This also brings in the new blinding support in libsecp256k1. By passing in a random
seed, temporary variables during the elliptic curve computations are altered, in such
a way that if an attacker does not know the blind, observing the internal operations
leaks less information about the keys used. This was implemented by Greg Maxwell.
10 years ago
|
|
|
boost::scoped_ptr<Secp256k1Init> ecc;
|
|
|
|
|
|
|
|
if (command == "nversion")
|
|
|
|
MutateTxVersion(tx, commandVal);
|
|
|
|
else if (command == "locktime")
|
|
|
|
MutateTxLocktime(tx, commandVal);
|
|
|
|
|
|
|
|
else if (command == "delin")
|
|
|
|
MutateTxDelInput(tx, commandVal);
|
|
|
|
else if (command == "in")
|
|
|
|
MutateTxAddInput(tx, commandVal);
|
|
|
|
|
|
|
|
else if (command == "delout")
|
|
|
|
MutateTxDelOutput(tx, commandVal);
|
|
|
|
else if (command == "outaddr")
|
|
|
|
MutateTxAddOutAddr(tx, commandVal);
|
|
|
|
else if (command == "outscript")
|
|
|
|
MutateTxAddOutScript(tx, commandVal);
|
|
|
|
|
Update key.cpp to use new libsecp256k1
libsecp256k1's API changed, so update key.cpp to use it.
Libsecp256k1 now has explicit context objects, which makes it completely thread-safe.
In turn, keep an explicit context object in key.cpp, which is explicitly initialized
destroyed. This is not really pretty now, but it's more efficient than the static
initialized object in key.cpp (which made for example bitcoin-tx slow, as for most of
its calls, libsecp256k1 wasn't actually needed).
This also brings in the new blinding support in libsecp256k1. By passing in a random
seed, temporary variables during the elliptic curve computations are altered, in such
a way that if an attacker does not know the blind, observing the internal operations
leaks less information about the keys used. This was implemented by Greg Maxwell.
10 years ago
|
|
|
else if (command == "sign") {
|
|
|
|
if (!ecc) { ecc.reset(new Secp256k1Init()); }
|
|
|
|
MutateTxSign(tx, commandVal);
|
Update key.cpp to use new libsecp256k1
libsecp256k1's API changed, so update key.cpp to use it.
Libsecp256k1 now has explicit context objects, which makes it completely thread-safe.
In turn, keep an explicit context object in key.cpp, which is explicitly initialized
destroyed. This is not really pretty now, but it's more efficient than the static
initialized object in key.cpp (which made for example bitcoin-tx slow, as for most of
its calls, libsecp256k1 wasn't actually needed).
This also brings in the new blinding support in libsecp256k1. By passing in a random
seed, temporary variables during the elliptic curve computations are altered, in such
a way that if an attacker does not know the blind, observing the internal operations
leaks less information about the keys used. This was implemented by Greg Maxwell.
10 years ago
|
|
|
}
|
|
|
|
|
|
|
|
else if (command == "load")
|
|
|
|
RegisterLoad(commandVal);
|
|
|
|
|
|
|
|
else if (command == "set")
|
|
|
|
RegisterSet(commandVal);
|
|
|
|
|
|
|
|
else
|
|
|
|
throw runtime_error("unknown command");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OutputTxJSON(const CTransaction& tx)
|
|
|
|
{
|
|
|
|
UniValue entry(UniValue::VOBJ);
|
|
|
|
TxToUniv(tx, uint256(), entry);
|
|
|
|
|
|
|
|
string jsonOutput = entry.write(4);
|
|
|
|
fprintf(stdout, "%s\n", jsonOutput.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OutputTxHash(const CTransaction& tx)
|
|
|
|
{
|
|
|
|
string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
|
|
|
|
|
|
|
|
fprintf(stdout, "%s\n", strHexHash.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OutputTxHex(const CTransaction& tx)
|
|
|
|
{
|
|
|
|
string strHex = EncodeHexTx(tx);
|
|
|
|
|
|
|
|
fprintf(stdout, "%s\n", strHex.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OutputTx(const CTransaction& tx)
|
|
|
|
{
|
|
|
|
if (GetBoolArg("-json", false))
|
|
|
|
OutputTxJSON(tx);
|
|
|
|
else if (GetBoolArg("-txid", false))
|
|
|
|
OutputTxHash(tx);
|
|
|
|
else
|
|
|
|
OutputTxHex(tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static string readStdin()
|
|
|
|
{
|
|
|
|
char buf[4096];
|
|
|
|
string ret;
|
|
|
|
|
|
|
|
while (!feof(stdin)) {
|
|
|
|
size_t bread = fread(buf, 1, sizeof(buf), stdin);
|
|
|
|
ret.append(buf, bread);
|
|
|
|
if (bread < sizeof(buf))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ferror(stdin))
|
|
|
|
throw runtime_error("error reading stdin");
|
|
|
|
|
|
|
|
boost::algorithm::trim_right(ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CommandLineRawTx(int argc, char* argv[])
|
|
|
|
{
|
|
|
|
string strPrint;
|
|
|
|
int nRet = 0;
|
|
|
|
try {
|
|
|
|
// Skip switches; Permit common stdin convention "-"
|
|
|
|
while (argc > 1 && IsSwitchChar(argv[1][0]) &&
|
|
|
|
(argv[1][1] != 0)) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTransaction txDecodeTmp;
|
|
|
|
int startArg;
|
|
|
|
|
|
|
|
if (!fCreateBlank) {
|
|
|
|
// require at least one param
|
|
|
|
if (argc < 2)
|
|
|
|
throw runtime_error("too few parameters");
|
|
|
|
|
|
|
|
// param: hex-encoded bitcoin transaction
|
|
|
|
string strHexTx(argv[1]);
|
|
|
|
if (strHexTx == "-") // "-" implies standard input
|
|
|
|
strHexTx = readStdin();
|
|
|
|
|
|
|
|
if (!DecodeHexTx(txDecodeTmp, strHexTx))
|
|
|
|
throw runtime_error("invalid transaction encoding");
|
|
|
|
|
|
|
|
startArg = 2;
|
|
|
|
} else
|
|
|
|
startArg = 1;
|
|
|
|
|
|
|
|
CMutableTransaction tx(txDecodeTmp);
|
|
|
|
|
|
|
|
for (int i = startArg; i < argc; i++) {
|
|
|
|
string arg = argv[i];
|
|
|
|
string key, value;
|
|
|
|
size_t eqpos = arg.find('=');
|
|
|
|
if (eqpos == string::npos)
|
|
|
|
key = arg;
|
|
|
|
else {
|
|
|
|
key = arg.substr(0, eqpos);
|
|
|
|
value = arg.substr(eqpos + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
MutateTx(tx, key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
OutputTx(tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (const boost::thread_interrupted&) {
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
strPrint = string("error: ") + e.what();
|
|
|
|
nRet = EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
PrintExceptionContinue(NULL, "CommandLineRawTx()");
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strPrint != "") {
|
|
|
|
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
|
|
|
}
|
|
|
|
return nRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
|
|
|
SetupEnvironment();
|
|
|
|
|
|
|
|
try {
|
|
|
|
if(!AppInitRawTx(argc, argv))
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
PrintExceptionContinue(&e, "AppInitRawTx()");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
} catch (...) {
|
|
|
|
PrintExceptionContinue(NULL, "AppInitRawTx()");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = EXIT_FAILURE;
|
|
|
|
try {
|
|
|
|
ret = CommandLineRawTx(argc, argv);
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
PrintExceptionContinue(&e, "CommandLineRawTx()");
|
|
|
|
} catch (...) {
|
|
|
|
PrintExceptionContinue(NULL, "CommandLineRawTx()");
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|