// Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin 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 "blind.h" #include "primitives/transaction.h" #include "core_io.h" #include "init.h" #include "keystore.h" #include "main.h" #include "merkleblock.h" #include "net.h" #include "rpcserver.h" #include "script/script.h" #include "script/sign.h" #include "script/standard.h" #include "uint256.h" #ifdef ENABLE_WALLET #include "wallet.h" #endif #include #include #include "json/json_spirit_utils.h" #include "json/json_spirit_value.h" #include using namespace boost; using namespace boost::assign; using namespace json_spirit; using namespace std; void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex) { txnouttype type; vector addresses; int nRequired; out.push_back(Pair("asm", scriptPubKey.ToString())); if (fIncludeHex) out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { out.push_back(Pair("type", GetTxnOutputType(type))); return; } out.push_back(Pair("reqSigs", nRequired)); out.push_back(Pair("type", GetTxnOutputType(type))); Array a; 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, Object& entry) { entry.push_back(Pair("txid", tx.GetHash().GetHex())); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); entry.push_back(Pair("fee", ValueFromAmount(tx.nTxFee))); Array vin; BOOST_FOREACH(const CTxIn& txin, tx.vin) { Object in; 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)); Object o; o.push_back(Pair("asm", txin.scriptSig.ToString())); 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)); Array vout; for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; Object out; if (txout.nValue.IsAmount()) { out.push_back(Pair("value", ValueFromAmount(txout.nValue.GetAmount()))); } else { int exp; int mantissa; uint64_t minv; uint64_t maxv; if (secp256k1_rangeproof_info(NULL, &exp, &mantissa, &minv, &maxv, &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size())) { if (exp == -1) { out.push_back(Pair("value", ValueFromAmount((CAmount)minv))); } else { out.push_back(Pair("value-minimum", ValueFromAmount((CAmount)minv))); out.push_back(Pair("value-maximum", ValueFromAmount((CAmount)maxv))); } out.push_back(Pair("ct-exponent", exp)); out.push_back(Pair("ct-bits", mantissa)); } } { CDataStream ssValue(SER_NETWORK, PROTOCOL_VERSION); ssValue << txout.nValue; out.push_back(Pair("serValue", HexStr(ssValue.begin(), ssValue.end()))); } out.push_back(Pair("n", (int64_t)i)); Object o; ScriptPubKeyToJSON(txout.scriptPubKey, o, true); out.push_back(Pair("scriptPubKey", o)); vout.push_back(out); } entry.push_back(Pair("vout", vout)); if (hashBlock != 0) { entry.push_back(Pair("blockhash", hashBlock.GetHex())); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; 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)); } } } Value getrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "getrawtransaction \"txid\" ( verbose )\n" "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" "or there is an unspent output in the utxo for this transaction. To make it always work,\n" "you need to maintain a transaction index, using the -txindex command line option.\n" "\nReturn the raw transaction data.\n" "\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n" "If verbose is non-zero, returns an Object with information about '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" "\nResult (if verbose is not set or set to 0):\n" "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" "\nResult (if verbose > 0):\n" "{\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as provided)\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 btc\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" " \"bitcoinaddress\" (string) bitcoin 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" "}\n" "\nExamples:\n" + HelpExampleCli("getrawtransaction", "\"mytxid\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" 1") + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") ); uint256 hash = ParseHashV(params[0], "parameter 1"); bool fVerbose = false; if (params.size() > 1) fVerbose = (params[1].get_int() != 0); CTransaction tx; uint256 hashBlock = 0; if (!GetTransaction(hash, tx, hashBlock, true)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); string strHex = EncodeHexTx(tx); if (!fVerbose) return strHex; Object result; result.push_back(Pair("hex", strHex)); TxToJSON(tx, hashBlock, result); return result; } Value gettxoutproof(const Array& params, bool fHelp) { if (fHelp || (params.size() != 1 && params.size() != 2)) throw runtime_error( "gettxoutproof [\"txid\",...] ( blockhash )\n" "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" "\nNOTE: By default this function only works sometimes. This is when there is an\n" "unspent output in the utxo for this transaction. To make it always work,\n" "you need to maintain a transaction index, using the -txindex command line option or\n" "specify the block in which the transaction is included in manually (by blockhash).\n" "\nReturn the raw transaction data.\n" "\nArguments:\n" "1. \"txids\" (string) A json array of txids to filter\n" " [\n" " \"txid\" (string) A transaction hash\n" " ,...\n" " ]\n" "2. \"block hash\" (string, optional) If specified, looks for txid in the block with this hash\n" "\nResult:\n" "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" ); set setTxids; uint256 oneTxid; Array txids = params[0].get_array(); BOOST_FOREACH(Value& txid, txids) { if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); uint256 hash(txid.get_str()); if (setTxids.count(hash)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); setTxids.insert(hash); oneTxid = hash; } CBlockIndex* pblockindex = NULL; uint256 hashBlock = 0; if (params.size() > 1) { hashBlock = uint256(params[1].get_str()); if (!mapBlockIndex.count(hashBlock)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); pblockindex = mapBlockIndex[hashBlock]; } else { CCoins coins; if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) pblockindex = chainActive[coins.nHeight]; } if (pblockindex == NULL) { CTransaction tx; if (!GetTransaction(oneTxid, tx, hashBlock, false) || hashBlock == 0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); if (!mapBlockIndex.count(hashBlock)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); pblockindex = mapBlockIndex[hashBlock]; } CBlock block; if(!ReadBlockFromDisk(block, pblockindex)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); unsigned int ntxFound = 0; BOOST_FOREACH(const CTransaction&tx, block.vtx) if (setTxids.count(tx.GetHash())) ntxFound++; if (ntxFound != setTxids.size()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock mb(block, setTxids); ssMB << mb; std::string strHex = HexStr(ssMB.begin(), ssMB.end()); return strHex; } Value verifytxoutproof(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw 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 generated by gettxoutproof\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; Array res; vector vMatch; if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot) return res; if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) 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; } #ifdef ENABLE_WALLET Value listunspent(const Array& params, bool fHelp) { if (fHelp || params.size() > 3) throw runtime_error( "listunspent ( minconf maxconf [\"address\",...] )\n" "\nReturns array of unspent transaction outputs\n" "with between minconf and maxconf (inclusive) confirmations.\n" "Optionally filter to only include txouts paid to specified addresses.\n" "Results are an array of Objects, each of which has:\n" "{txid, vout, scriptPubKey, amount, confirmations}\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" " [\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ]\n" "\nResult\n" "[ (array of json object)\n" " {\n" " \"txid\" : \"txid\", (string) the transaction id \n" " \"vout\" : n, (numeric) the vout value\n" " \"address\" : \"address\", (string) the bitcoin address\n" " \"account\" : \"account\", (string) The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction amount in btc\n" " \"serValue\" : \"hex\", (string) the output's value commitment\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"blinder\" : \"blind\" (string) The blinding factor used for a confidential output (or \"\")\n" " }\n" " ,...\n" "]\n" "\nExamples\n" + HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") ); RPCTypeCheck(params, list_of(int_type)(int_type)(array_type)); int nMinDepth = 1; if (params.size() > 0) nMinDepth = params[0].get_int(); int nMaxDepth = 9999999; if (params.size() > 1) nMaxDepth = params[1].get_int(); set setAddress; if (params.size() > 2) { Array inputs = params[2].get_array(); BOOST_FOREACH(Value& input, inputs) { CBitcoinAddress address(input.get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str()); if (setAddress.count(address)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); setAddress.insert(address); } } Array results; vector vecOutputs; assert(pwalletMain != NULL); pwalletMain->AvailableCoins(vecOutputs, false); BOOST_FOREACH(const COutput& out, vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; if (setAddress.size()) { CTxDestination address; if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) continue; if (!setAddress.count(address)) continue; } CAmount nValue = out.tx->GetValueOut(out.i); const CScript& pk = out.tx->vout[out.i].scriptPubKey; Object entry; entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("vout", out.i)); CTxDestination address; if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { CBitcoinAddress addr(address); if (out.tx->GetBlindingFactor(out.i).size() > 0) { addr.AddBlindingKey(pwalletMain->blinding_pubkey); } entry.push_back(Pair("address", addr.ToString())); if (pwalletMain->mapAddressBook.count(address)) entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); } entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); if (pk.IsPayToScriptHash()) { CTxDestination address; if (ExtractDestination(pk, address)) { const CScriptID& hash = boost::get(address); CScript redeemScript; if (pwalletMain->GetCScript(hash, redeemScript)) entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); } } CDataStream ssValue(SER_NETWORK, PROTOCOL_VERSION); ssValue << nValue; entry.push_back(Pair("serValue", HexStr(ssValue.begin(), ssValue.end()))); entry.push_back(Pair("amount",ValueFromAmount(nValue))); entry.push_back(Pair("blinder",HexStr(out.tx->GetBlindingFactor(out.i)))); entry.push_back(Pair("confirmations",out.nDepth)); entry.push_back(Pair("spendable", out.fSpendable)); results.push_back(entry); } return results; } #endif Value createrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() != 2) throw runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n" "\nCreate a transaction spending the given inputs and sending to the given addresses.\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" " \"nValue\":x.xxx, (numeric, required) The amount being spent\n" " }\n" " ,...\n" " ]\n" "2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n" " {\n" " \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the btc amount\n" " ,...\n" " }\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" "\nExamples\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") ); RPCTypeCheck(params, list_of(array_type)(obj_type)); Array inputs = params[0].get_array(); Object sendTo = params[1].get_obj(); CMutableTransaction rawTx; CAmount inputValue = 0; BOOST_FOREACH(const Value& input, inputs) { const Object& o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); const Value& vout_v = find_value(o, "vout"); if (vout_v.type() != int_type) 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"); const Value& vout_value = find_value(o, "nValue"); inputValue += AmountFromValue(vout_value); CTxIn in(COutPoint(txid, nOutput)); rawTx.vin.push_back(in); } CAmount outputValue = 0; set setAddress; BOOST_FOREACH(const Pair& s, sendTo) { CBitcoinAddress address(s.name_); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); if (setAddress.count(address)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); setAddress.insert(address); CScript scriptPubKey = GetScriptForDestination(address.Get()); CAmount nAmount = AmountFromValue(s.value_); outputValue += nAmount; CTxOut out(nAmount, scriptPubKey); if (address.IsBlinded()) { CPubKey confidentiality_pubkey = address.GetBlindingKey(); if (!confidentiality_pubkey.IsValid()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given")); out.nValue.vchNonceCommitment = std::vector(confidentiality_pubkey.begin(), confidentiality_pubkey.end()); } rawTx.vout.push_back(out); } rawTx.nTxFee = inputValue - outputValue; return EncodeHexTx(rawTx); } Value rawblindrawtransaction(const Array& params, bool fHelp) { if (fHelp || (params.size() != 2 && params.size() != 3)) throw runtime_error( "rawblindrawtransaction \"hexstring\" [\"inputblinder\",...] [\"totalblinder\"]\n" "\nConvert one or more outputs of a raw transaction into confidential ones.\n" "Returns the hex-encoded raw transaction.\n" "If at least one of the inputs is confidential, at least one of the outputs must be.\n" "The input raw transaction cannot have already-blinded outputs.\n" "The output keys used can be specified by using a confidential address in createrawtransaction.\n" "\nArguments:\n" "1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n" "2. [ (array, required) An array with one entry per transaction input.\n" " \"inputblinder\" (string, required) A hex-encoded blinding factor, one for each input.\n" " Blinding factors can be found in the \"blinder\" output of listunspent.\n" " ],\n" "3. \"totalblinder\" (string, optional) Ignored for now.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" ); if (params.size() == 2) { RPCTypeCheck(params, list_of(str_type)(array_type)); } else { RPCTypeCheck(params, list_of(str_type)(array_type)(str_type)); } CMutableTransaction rawTx; vector txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CMutableTransaction tx; try { ssData >> tx; } catch (const std::exception &) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } Array inputBlinds = params[1].get_array(); if (inputBlinds.size() != tx.vin.size()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: one (potentially empty) input blind for each input must be provided")); std::vector > input_blinds; std::vector > output_blinds; std::vector output_pubkeys; for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) { if (!tx.vout[nOut].nValue.IsAmount()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction outputs must be unblinded")); if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) { output_pubkeys.push_back(CPubKey()); } else { CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment); if (!pubkey.IsValid()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given")); } output_pubkeys.push_back(pubkey); } output_blinds.push_back(std::vector(0, 0)); } BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx); return EncodeHexTx(tx); } #ifdef ENABLE_WALLET Value blindrawtransaction(const Array& params, bool fHelp) { if (fHelp || (params.size() != 1 && params.size() != 2)) throw runtime_error( "blindrawtransaction \"hexstring\" [\"totalblinder\"]\n" "\nConvert one or more outputs of a raw transaction into confidential ones using only wallet inputs.\n" "Returns the hex-encoded raw transaction.\n" "If at least one of the inputs is confidential, at least one of the outputs must be.\n" "The output keys used can be specified by using a confidential address in createrawtransaction.\n" "\nArguments:\n" "1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n" "2. \"totalblinder\" (string, optional) Ignored for now.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" ); if (params.size() == 1) { RPCTypeCheck(params, list_of(str_type)); } else { RPCTypeCheck(params, list_of(str_type)(str_type)); } CMutableTransaction rawTx; vector txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CMutableTransaction tx; try { ssData >> tx; } catch (const std::exception &) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } LOCK(pwalletMain->cs_wallet); std::vector > input_blinds; std::vector > output_blinds; std::vector output_pubkeys; for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) { std::map::iterator it = pwalletMain->mapWallet.find(tx.vin[nIn].prevout.hash); if (it == pwalletMain->mapWallet.end()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction spends from non-wallet output")); } if (tx.vin[nIn].prevout.n >= it->second.vout.size()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction spends non-existing output")); } input_blinds.push_back(it->second.GetBlindingFactor(tx.vin[nIn].prevout.n)); } for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) { if (!tx.vout[nOut].nValue.IsAmount()) { std::vector blinding_factor; CAmount amount; if (UnblindOutput(pwalletMain->blinding_key, tx.vout[nOut], amount, blinding_factor) != 0) { output_blinds.push_back(blinding_factor); output_pubkeys.push_back(CPubKey()); } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction outputs must be unblinded or to wallet")); } } else if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) { output_pubkeys.push_back(CPubKey()); output_blinds.push_back(std::vector(0, 0)); } else { CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment); if (!pubkey.IsValid()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given")); } output_pubkeys.push_back(pubkey); output_blinds.push_back(std::vector(0, 0)); } } BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx); return EncodeHexTx(tx); } #endif Value decoderawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw 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" " \"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 btc\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\"") ); RPCTypeCheck(params, list_of(str_type)); CTransaction tx; if (!DecodeHexTx(tx, params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); Object result; TxToJSON(tx, 0, result); return result; } Value decodescript(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw 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, list_of(str_type)); Object r; CScript script; if (params[0].get_str().size() > 0){ vector 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; } Value signrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 4) throw 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" " \"nValue\": \"hex\", (string, required) The output's value commitment\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n" " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\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 raw transaction with signature(s) (hex-encoded string)\n" " \"complete\": n (numeric) if transaction has a complete set of signature (0 if not)\n" "}\n" "\nExamples:\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + HelpExampleRpc("signrawtransaction", "\"myhex\"") ); RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); vector txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); vector txVariants; while (!ssData.empty()) { try { CMutableTransaction tx; ssData >> tx; txVariants.push_back(tx); } catch (const std::exception &) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } } 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]); bool fComplete = true; // Fetch previous transactions (inputs): CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { LOCK(mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { const uint256& prevHash = txin.prevout.hash; CCoins coins; view.AccessCoins(prevHash); // this is certainly allowed to fail } view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } bool fGivenKeys = false; CBasicKeyStore tempKeystore; if (params.size() > 2 && params[2].type() != null_type) { fGivenKeys = true; Array keys = params[2].get_array(); BOOST_FOREACH(Value k, keys) { 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); } } #ifdef ENABLE_WALLET else EnsureWalletIsUnlocked(); #endif // Add previous txouts given in the RPC call: if (params.size() > 1 && params[1].type() != null_type) { Array prevTxs = params[1].get_array(); BOOST_FOREACH(Value& p, prevTxs) { if (p.type() != obj_type) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"nValue\",\"scriptPubKey\"}"); Object prevOut = p.get_obj(); RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("nValue", str_type)("scriptPubKey", str_type)); uint256 txid = ParseHashO(prevOut, "txid"); int nOut = find_value(prevOut, "vout").get_int(); if (nOut < 0) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); vector vchValue(ParseHexO(prevOut, "nValue")); CTxOutValue nValue; CDataStream ssValue(vchValue, SER_NETWORK, PROTOCOL_VERSION); ssValue >> nValue; vector pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { CCoinsModifier coins = view.ModifyCoins(txid); if (coins->IsAvailable(nOut) && coins->vout[nOut].nValue != nValue) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Previous output nValue mismatch"); 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 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 = nValue; } // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsWithdrawOutput())) { RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("nValue", str_type)("scriptPubKey", str_type)("redeemScript",str_type)); Value v = find_value(prevOut, "redeemScript"); if (!(v == Value::null)) { vector 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].type() != null_type) { static map mapSigHashValues = boost::assign::map_list_of (string("ALL"), int(SIGHASH_ALL)) (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) (string("NONE"), int(SIGHASH_NONE)) (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) (string("SINGLE"), int(SIGHASH_SINGLE)) (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) ; 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); // 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 == NULL || !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, coins->vout[txin.prevout.n].nValue, mergedTx, i, nHashType); // ... and merge in other signatures: BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, coins->vout[txin.prevout.n].nValue, txin.scriptSig, txv.vin[i].scriptSig); } if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionNoWithdrawsSignatureChecker(&mergedTx, i, coins->vout[txin.prevout.n].nValue))) fComplete = false; } Object result; result.push_back(Pair("hex", EncodeHexTx(mergedTx))); result.push_back(Pair("complete", fComplete)); return result; } Value sendrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw 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\"") ); RPCTypeCheck(params, list_of(str_type)(bool_type)); // parse hex string from parameter CTransaction tx; if (!DecodeHexTx(tx, params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); uint256 hashTx = tx.GetHash(); bool fOverrideFees = false; if (params.size() > 1) fOverrideFees = params[1].get_bool(); CCoinsViewCache &view = *pcoinsTip; const CCoins* existingCoins = view.AccessCoins(hashTx); bool fHaveMempool = mempool.exists(hashTx); bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) { if(state.IsInvalid()) throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); else throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); } } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } RelayTransaction(tx); return hashTx.GetHex(); } #ifdef ENABLE_WALLET Value fundrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "fundrawtransaction \"hexstring\"\n" "\nAdd vIns to a raw transaction.\n" "\nAlso see createrawtransaction and signrawtransaction calls.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" "2. includeWatching (boolean, optional, default=false) Use watchonly outputs\n" "\nResult:\n" "{\n" " \"hex\": \"value\", (string) The raw transaction with vIns (hex-encoded string)\n" " \"fee\": n calculated fee\n" "}\n" "\"hex\" \n" "\nExamples:\n" "\nCreate a transaction with empty vIns\n" + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + "\nFund the transaction, and get back the hex\n" + HelpExampleCli("fundrawtransaction", "\"myhex\"") + "\nSign 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\"") ); RPCTypeCheck(params, list_of(str_type)(bool_type)); // parse hex string from parameter CTransaction tx; if (!DecodeHexTx(tx, params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); bool includeWatching = false; if (params.size() > 1) includeWatching = params[1].get_bool(); CMutableTransaction txNew; CAmount nFeeRet; string strFailReason; if(!pwalletMain->FundTransaction(tx, txNew, nFeeRet, strFailReason, includeWatching)) throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); Object result; result.push_back(Pair("hex", EncodeHexTx(txNew))); result.push_back(Pair("fee", nFeeRet)); return result; } #endif