Files
thehub/libs/server/rpcrawtransaction.cpp
T

929 lines
40 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2010 Satoshi Nakamoto
* Copyright (C) 2009-2015 The Bitcoin Core developers
* Copyright (C) 2016-2021 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Application.h>
#include "encodings_legacy.h"
#include "chain.h"
#include <validation/Engine.h>
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
#include "keystore.h"
#include "main.h"
#include "BlockMetaData.h"
#include "merkleblock.h"
#include "net.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpcserver.h"
#include "primitives/script.h"
#include "script/script_error.h"
#include "script/sign.h"
#include "script/standard.h"
#include "txmempool.h"
#include "BlocksDB.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include <utxo/UnspentOutputDatabase.h>
#include "UnspentOutputData.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
#include <primitives/Block.h>
#include <univalue.h>
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
{
Script::TxnOutType type;
std::vector<CTxDestination> addresses;
int nRequired;
out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey)));
if (fIncludeHex)
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
out.push_back(Pair("type", Script::getTxnOutputType(type)));
return;
}
out.push_back(Pair("reqSigs", nRequired));
out.push_back(Pair("type", Script::getTxnOutputType(type)));
UniValue a(UniValue::VARR);
BOOST_FOREACH(const CTxDestination& addr, addresses)
a.push_back(CBitcoinAddress(addr).ToString());
out.push_back(Pair("addresses", a));
}
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
entry.push_back(Pair("version", tx.nVersion));
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
UniValue vin(UniValue::VARR);
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase())
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
else {
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
UniValue o(UniValue::VOBJ);
o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
in.push_back(Pair("scriptSig", o));
}
in.push_back(Pair("sequence", (int64_t)txin.nSequence));
vin.push_back(in);
}
entry.push_back(Pair("vin", vin));
UniValue vout(UniValue::VARR);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
const CTxOut& txout = tx.vout[i];
UniValue out(UniValue::VOBJ);
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
out.push_back(Pair("n", (int64_t)i));
UniValue o(UniValue::VOBJ);
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
out.push_back(Pair("scriptPubKey", o));
vout.push_back(out);
}
entry.push_back(Pair("vout", vout));
if (!hashBlock.IsNull()) {
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
CBlockIndex* pindex = Blocks::Index::get(hashBlock);
if (pindex) {
if (chainActive.Contains(pindex)) {
entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
entry.push_back(Pair("time", pindex->GetBlockTime()));
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
}
else
entry.push_back(Pair("confirmations", 0));
}
}
}
UniValue getrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 3)
throw std::runtime_error("getrawtransaction"
"\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
"enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
"is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
"provided, only that block will be searched and if the transaction is in the mempool or other\n"
"blocks, or if this node does not have the given block available, the transaction will not be found.\n"
"DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
"\n"
"Return the raw transaction data.\n"
"\n"
"If verbose is 'true', returns an Object with information about 'txid'.\n"
"If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n"
"\narguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n"
"3. blockhash (string, required) The block in which to look for the transaction\n"
"\nResult (if verbose is not set or set to false):\n"
"\"data\" (string) The serialized, hex-encoded data for "
"'txid'\n"
"\nResult (if verbose is set to true):\n"
"{\n"
" \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
" \"hash\" : \"id\", (string) The transaction hash\n"
" \"size\" : n, (numeric) The serialized transaction size\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric)\n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in BCH\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"address\" (string) Bitcoin Cash address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
" \"blockhash\" : \"hash\", (string) the block hash\n"
" \"confirmations\" : n, (numeric) The confirmations\n"
" \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"blocktime\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"in_active_chain\": b (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getrawtransaction", "\"mytxid\"") +
HelpExampleCli("getrawtransaction", "\"mytxid\" true") +
HelpExampleRpc("getrawtransaction", "\"mytxid\", true") +
HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"") +
HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\""));
LOCK(cs_main);
bool in_active_chain = true;
uint256 hash = ParseHashV(params[0], "parameter 1");
bool fVerbose = false;
if (!params[1].isNull()) {
fVerbose = params[1].isNum() ? (params[1].get_int() != 0) : params[1].get_bool();
}
CTransaction tx;
bool success = false;
bool fromBlock = false;
if (!params[2].isNull()) { // we got a block hash, look up in that block.
fromBlock = true;
uint256 blockId = ParseHashV(params[2], "parameter 2");
auto index = Blocks::Index::get(blockId);
if (!index)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
in_active_chain = Blocks::DB::instance()->headerChain().Contains(index);
if (index->nMetaDataPos > 0) {
logDebug() << "Searching for txid in the blockmetadata object";
BlockMetaData meta = Blocks::DB::instance()->loadBlockMetaData(index->GetMetaDataPos());
auto mtx = meta.findTransaction(hash);
if (mtx) {
success = true;
Block block = Blocks::DB::instance()->loadBlock(index->GetBlockPos());
Tx::Iterator iter(block, mtx->offsetInBlock);
iter.next(Tx::End);
tx = iter.prevTx().createOldTransaction();
}
}
else { // fall back to slow version.
Block block;
try {
block = Blocks::DB::instance()->loadBlock(index->GetBlockPos());
} catch (...) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not available");
}
const int ctorHeight = Params().GetConsensus().hf201811Height;
auto ftx = block.findTransaction(hash, index->nHeight >= ctorHeight);
if (ftx.size() > 0) {
success = true;
tx = ftx.createOldTransaction();
}
}
if (!success)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No such transaction found in the provided block");
}
else {
// we just get it from the mempool.
success = flApp->mempool()->lookup(hash, tx);
}
if (!success) {
const int ctorHeight = Params().GetConsensus().hf201811Height;
auto index = chainActive.Tip();
for (int i = 0; !success && index && i < 6; ++i) {
if (index->nMetaDataPos > 0) {
logDebug() << "Searching for txid in the blockmetadata object";
BlockMetaData meta = Blocks::DB::instance()->loadBlockMetaData(index->GetMetaDataPos());
auto mtx = meta.findTransaction(hash);
if (mtx) {
Block block = Blocks::DB::instance()->loadBlock(index->GetBlockPos());
Tx::Iterator iter(block, mtx->offsetInBlock);
iter.next(Tx::End);
tx = iter.prevTx().createOldTransaction();
success = true;
break;
}
}
else { // fall back to slow version.
Block block = Blocks::DB::instance()->loadBlock(index->GetBlockPos());
auto ftx = block.findTransaction(hash, index->nHeight >= ctorHeight);
if (ftx.size() > 0) {
tx = ftx.createOldTransaction();
success = true;
break;
}
}
index = index->pprev;
}
if (!success)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No such mempool transaction.");
}
std::string strHex = EncodeHexTx(tx);
if (!fVerbose)
return strHex;
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", strHex));
uint256 hashBlock;
TxToJSON(tx, hashBlock, result);
if (fromBlock) {
result.push_back(Pair("in_active_chain", in_active_chain));
}
return result;
}
UniValue verifytxoutproof(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw std::runtime_error(
"verifytxoutproof \"proof\"\n"
"\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
"and throwing an RPC error if the block is not in our best chain\n"
"\nArguments:\n"
"1. \"proof\" (string, required) The hex-encoded proof\n"
"\nResult:\n"
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n"
);
CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
UniValue res(UniValue::VARR);
std::vector<uint256> vMatch;
if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot)
return res;
auto item = Blocks::Index::get(merkleBlock.header.createHash());
if (!item || !chainActive.Contains(item))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
BOOST_FOREACH(const uint256& hash, vMatch)
res.push_back(hash.GetHex());
return res;
}
UniValue createrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 4)
throw std::runtime_error(
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime, txversion )\n"
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n"
"Note that the transaction's inputs are not signed, and\n"
"it is not stored in the wallet or transmitted to the network.\n"
"\nArguments:\n"
"1. \"transactions\" (string, required) A json array of json objects\n"
" [\n"
" {\n"
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n (numeric, required) The output number\n"
" }\n"
" ,...\n"
" ]\n"
"2. \"outputs\" (string, required) a json object with outputs\n"
" {\n"
" \"address\": x.xxx (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the BCH amount\n"
" \"data\": \"hex\", (string, required) The key is \"data\", the value is hex encoded data\n"
" ...\n"
" }\n"
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
"4. txversion (numeric, optional, default=1) Specifies the transaction format version\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
"\nExamples\n"
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"")
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"")
);
LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true);
if (params[0].isNull() || params[1].isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
UniValue inputs = params[0].get_array();
UniValue sendTo = params[1].get_obj();
CMutableTransaction rawTx;
if (params.size() > 2 && !params[2].isNull()) {
int64_t nLockTime = params[2].get_int64();
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
rawTx.nLockTime = nLockTime;
}
if (params.size() > 3 && !params[3].isNull()) {
int version = params[3].get_int();
if (version != 1 && version != 4)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txversion can only be 1 or 4");
rawTx.nVersion = version;
}
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
const UniValue& o = input.get_obj();
uint256 txid = ParseHashO(o, "txid");
const UniValue& vout_v = find_value(o, "vout");
if (!vout_v.isNum())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
int nOutput = vout_v.get_int();
if (nOutput < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max());
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
rawTx.vin.push_back(in);
}
std::set<CBitcoinAddress> setAddress;
std::vector<std::string> addrList = sendTo.getKeys();
BOOST_FOREACH(const std::string& name_, addrList) {
if (name_ == "data") {
std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data");
CTxOut out(0, CScript() << OP_RETURN << data);
rawTx.vout.push_back(out);
} else {
CBitcoinAddress address(name_);
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_);
if (setAddress.count(address))
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_);
setAddress.insert(address);
CScript scriptPubKey = GetScriptForDestination(address.Get());
int64_t nAmount = AmountFromValue(sendTo[name_]);
CTxOut out(nAmount, scriptPubKey);
rawTx.vout.push_back(out);
}
}
return EncodeHexTx(rawTx);
}
UniValue decoderawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw std::runtime_error(
"decoderawtransaction \"hexstring\"\n"
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
"\nArguments:\n"
"1. \"hex\" (string, required) The transaction hex string\n"
"\nResult:\n"
"{\n"
" \"txid\" : \"id\", (string) The transaction id\n"
" \"size\" : n, (numeric) The transaction size\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric) The output number\n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in BCH\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
);
LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
CTransaction tx;
if (!DecodeHexTx(tx, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
UniValue result(UniValue::VOBJ);
TxToJSON(tx, uint256(), result);
return result;
}
UniValue decodescript(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw std::runtime_error(
"decodescript \"hex\"\n"
"\nDecode a hex-encoded script.\n"
"\nArguments:\n"
"1. \"hex\" (string) the hex encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
" \"hex\":\"hex\", (string) hex encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
" \"p2sh\",\"address\" (string) script address\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
UniValue r(UniValue::VOBJ);
CScript script;
if (params[0].get_str().size() > 0){
std::vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
script = CScript(scriptData.begin(), scriptData.end());
} else {
// Empty scripts are valid
}
ScriptPubKeyToJSON(script, r, false);
r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString()));
return r;
}
/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
{
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("txid", txin.prevout.hash.ToString()));
entry.push_back(Pair("vout", (uint64_t)txin.prevout.n));
entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
entry.push_back(Pair("sequence", (uint64_t)txin.nSequence));
entry.push_back(Pair("error", strMessage));
vErrorsRet.push_back(entry);
}
UniValue signrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 4)
throw std::runtime_error(
"signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain.\n"
"The third optional argument (may be null) is an array of base58-encoded private\n"
"keys that, if given, will be the only keys used to sign the transaction.\n"
#ifdef ENABLE_WALLET
+ HelpRequiringPassphrase() + "\n"
#endif
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex string\n"
"2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
" [ (json array of json objects, or 'null' if none provided)\n"
" {\n"
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
" \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n"
" \"amount\": \"value\" (numeric, required) amount spent\n"
" }\n"
" ,...\n"
" ]\n"
"3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n"
" [ (json array of strings, or 'null' if none provided)\n"
" \"privatekey\" (string) private key in base58-encoding\n"
" ,...\n"
" ]\n"
"4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\"\n"
"\nResult:\n"
"{\n"
" \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
" \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
" \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
" {\n"
" \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
" \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
" \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
" \"sequence\" : n, (numeric) Script sequence number\n"
" \"error\" : \"text\" (string) Verification or signing error related to the input\n"
" }\n"
" ,...\n"
" ]\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("signrawtransaction", "\"myhex\"")
+ HelpExampleRpc("signrawtransaction", "\"myhex\"")
);
#ifdef ENABLE_WALLET
LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
#else
LOCK(cs_main);
#endif
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true);
std::vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
std::vector<CMutableTransaction> txVariants;
while (!ssData.empty()) {
try {
CMutableTransaction tx;
ssData >> tx;
txVariants.push_back(tx);
}
catch (const std::exception &e) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, std::string("TX decode failed with ") + e.what());
}
}
if (txVariants.empty())
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
// mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx:
CMutableTransaction mergedTx(txVariants[0]);
// First, fish required transactions out of the mempool
typedef boost::unordered_map<uint256, Tx, HashShortener> TXMap;
TXMap txMap;
{
CTxMemPool *mempool = flApp->mempool();
for (const CTxIn &txin : mergedTx.vin) {
auto ti = txMap.find(txin.prevout.hash);
if (ti != txMap.end()) // Spending multiple outputs from one tx
continue;
Tx prevTx;
if (mempool->lookup(txin.prevout.hash, prevTx)) {
txMap.insert(std::make_pair(txin.prevout.hash, prevTx));
}
}
}
// Fetch previous transactions (inputs) given in the RPC call:
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
if (params.size() > 2 && !params[2].isNull()) {
UniValue keys = params[2].get_array();
for (unsigned int idx = 0; idx < keys.size(); idx++) {
UniValue k = keys[idx];
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(k.get_str());
if (!fGood)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
CKey key = vchSecret.GetKey();
if (!key.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
tempKeystore.AddKey(key);
fGivenKeys = true;
}
}
#ifdef ENABLE_WALLET
else if (pwalletMain)
EnsureWalletIsUnlocked();
#endif
typedef boost::unordered_map<Tx::Input, Tx::Output> InputMap;
InputMap inputMap;
// Add previous txouts given in the RPC call:
if (params.size() > 1 && !params[1].isNull()) {
UniValue prevTxs = params[1].get_array();
for (unsigned int idx = 0; idx < prevTxs.size(); idx++) {
const UniValue& p = prevTxs[idx];
if (!p.isObject())
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
UniValue prevOut = p.get_obj();
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR));
Tx::Input input;
input.txid = ParseHashO(prevOut, "txid");
input.index = find_value(prevOut, "vout").get_int();
if (input.index < 0)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
Tx::Output output;
output.outputValue = 0;
const UniValue &amountObj = find_value(prevOut, "amount");
if (amountObj.isNum())
output.outputValue = AmountFromValue(amountObj);
if (output.outputValue <= 0 || (!amountObj.isNull() && !amountObj.isNum()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "amount must be a positive number");
std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
output.outputScript = Streaming::ConstBuffer::create(pkData);
inputMap.insert(std::make_pair(input, output));
// {
// CCoinsModifier coins = view.ModifyCoins(txid);
// if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
// std::string err("Previous output scriptPubKey mismatch:\n");
// err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
// ScriptToAsmStr(scriptPubKey);
// throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
// }
// if ((unsigned int)nOut >= coins->vout.size())
// coins->vout.resize(nOut+1);
// coins->vout[nOut].scriptPubKey = scriptPubKey;
// coins->vout[nOut].nValue = amount;
// }
// if redeemScript given and not using the local wallet (private keys
// given), add redeemScript to the tempKeystore so it can be signed:
CScript outScript = output.outputScript;
if (fGivenKeys && outScript.IsPayToScriptHash()) {
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR));
UniValue v = find_value(prevOut, "redeemScript");
if (!v.isNull()) {
std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
}
}
#ifdef ENABLE_WALLET
const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain);
#else
const CKeyStore& keystore = tempKeystore;
#endif
int nHashType = SIGHASH_ALL;
if (params.size() > 3 && !params[3].isNull()) {
static std::map<std::string, int> mapSigHashValues =
boost::assign::map_list_of
(std::string("ALL"), int(SIGHASH_ALL))
(std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
(std::string("NONE"), int(SIGHASH_NONE))
(std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
(std::string("SINGLE"), int(SIGHASH_SINGLE))
(std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
;
std::string strHashType = params[3].get_str();
if (mapSigHashValues.count(strHashType))
nHashType = mapSigHashValues[strHashType];
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
}
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
uint32_t validationFlags = STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_SIGHASH_FORKID;
nHashType |= SIGHASH_FORKID;
// Script verification errors
UniValue vErrors(UniValue::VARR);
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mergedTx);
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
CScript outputScript;
int64_t amount = 0;
bool found = false;
Tx::Input input;
input.index = txin.prevout.n;
input.txid = txin.prevout.hash;
auto inIter = inputMap.find(input);
if (inIter != inputMap.end()) {
outputScript = inIter->second.outputScript;
amount = inIter->second.outputValue;
found = true;
}
if (!found) {
auto txIter = txMap.find(input.txid);
if (txIter != txMap.end()) {
Tx::Output out = txIter->second.output(input.index);
if (out.outputValue >= 0) {
outputScript = out.outputScript;
amount = out.outputValue;
found = true;
}
}
}
if (!found) {
UnspentOutputData uo(g_utxo->find(input.txid, input.index));
if (uo.isValid()) {
outputScript = uo.outputScript();
amount = uo.outputValue();
found = true;
}
}
if (!found) {
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
continue;
}
txin.scriptSig.clear();
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || i < mergedTx.vout.size())
SignSignature(keystore, outputScript, mergedTx, i, amount, nHashType);
// ... and merge in other signatures:
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
txin.scriptSig = CombineSignatures(outputScript, TransactionSignatureChecker(&txConst, i, amount), txin.scriptSig, txv.vin[i].scriptSig);
}
Script::State state(validationFlags);
if (!Script::verify(txin.scriptSig, outputScript, TransactionSignatureChecker(&txConst, i, amount), state)) {
TxInErrorToJSON(txin, vErrors, state.errorString());
}
}
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
result.push_back(Pair("complete", fComplete));
if (!vErrors.empty()) {
result.push_back(Pair("errors", vErrors));
}
return result;
}
UniValue sendrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw std::runtime_error(
"sendrawtransaction \"hexstring\" ( allowhighfees )\n"
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nAlso see createrawtransaction and signrawtransaction calls.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw transaction)\n"
"2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
"\nResult:\n"
"\"hex\" (string) The transaction hash in hex\n"
"\nExamples:\n"
"\nCreate a transaction\n"
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
"Sign the transaction, and get back the hex\n"
+ HelpExampleCli("signrawtransaction", "\"myhex\"") +
"\nSend the transaction (signed hex)\n"
+ HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
std::future<std::string> future;
uint256 hashTx;
{
LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
// parse hex string from parameter
CTransaction tx;
if (!DecodeHexTx(tx, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
hashTx = tx.GetHash();
bool fOverrideFees = false;
if (params.size() > 1)
fOverrideFees = params[1].get_bool();
auto utxo = g_utxo->find(hashTx, 0);
bool fHaveMempool = mempool.exists(hashTx);
bool fHaveChain = utxo.isValid() && utxo.blockHeight() < 1000000000;
if (!fHaveMempool && !fHaveChain) {
// push to local node and sync with wallets
uint32_t flags = 0;
if (!fOverrideFees)
flags |= Validation::RejectAbsurdFeeTx;
future = Application::instance()->validation()->addTransaction(Tx::fromOldTransaction(tx), flags);
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
}
}
if (future.valid()) {
const std::string result = future.get();
if (!result.empty()) {
if (result == "16: missing-inputs")
throw JSONRPCError(RPC_TRANSACTION_ERROR, result);
throw JSONRPCError(RPC_TRANSACTION_REJECTED, result);
}
}
return hashTx.GetHex();
}