lbrycrd/src/core_write.cpp
Wladimir J. van der Laan 820ddd48a7
Merge #11027: [RPC] Only return hex field once in getrawtransaction
6bbdafc Pass serialization flags and whether to include hex to TxToUniv (Andrew Chow)
e029c6e Only return hex field once in getrawtransaction (Andrew Chow)

Pull request description:

  The hex is already returned in `TxToUniv()`, no need to give it out a second time in getrawtransaction itself.

Tree-SHA512: 270289f2d6dea37f51f5a42db3dae5debdbe83c6b504fccfd3391588da986ed474592c6655d522dc51022d4b08fa90ed1ebb249afe036309f95adfe3652cb262
2017-08-21 08:58:08 +02:00

213 lines
8.4 KiB
C++

// Copyright (c) 2009-2016 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 "core_io.h"
#include "base58.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "script/script.h"
#include "script/standard.h"
#include "serialize.h"
#include "streams.h"
#include <univalue.h>
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
UniValue ValueFromAmount(const CAmount& amount)
{
bool sign = amount < 0;
int64_t n_abs = (sign ? -amount : amount);
int64_t quotient = n_abs / COIN;
int64_t remainder = n_abs % COIN;
return UniValue(UniValue::VNUM,
strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
}
std::string FormatScript(const CScript& script)
{
std::string ret;
CScript::const_iterator it = script.begin();
opcodetype op;
while (it != script.end()) {
CScript::const_iterator it2 = it;
std::vector<unsigned char> vch;
if (script.GetOp2(it, op, &vch)) {
if (op == OP_0) {
ret += "0 ";
continue;
} else if ((op >= OP_1 && op <= OP_16) || op == OP_1NEGATE) {
ret += strprintf("%i ", op - OP_1NEGATE - 1);
continue;
} else if (op >= OP_NOP && op <= OP_NOP10) {
std::string str(GetOpName(op));
if (str.substr(0, 3) == std::string("OP_")) {
ret += str.substr(3, std::string::npos) + " ";
continue;
}
}
if (vch.size() > 0) {
ret += strprintf("0x%x 0x%x ", HexStr(it2, it - vch.size()), HexStr(it - vch.size(), it));
} else {
ret += strprintf("0x%x ", HexStr(it2, it));
}
continue;
}
ret += strprintf("0x%x ", HexStr(it2, script.end()));
break;
}
return ret.substr(0, ret.size() - 1);
}
const std::map<unsigned char, std::string> mapSigHashTypes = {
{static_cast<unsigned char>(SIGHASH_ALL), std::string("ALL")},
{static_cast<unsigned char>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), std::string("ALL|ANYONECANPAY")},
{static_cast<unsigned char>(SIGHASH_NONE), std::string("NONE")},
{static_cast<unsigned char>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), std::string("NONE|ANYONECANPAY")},
{static_cast<unsigned char>(SIGHASH_SINGLE), std::string("SINGLE")},
{static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")},
};
/**
* Create the assembly string representation of a CScript object.
* @param[in] script CScript object to convert into the asm string representation.
* @param[in] fAttemptSighashDecode Whether to attempt to decode sighash types on data within the script that matches the format
* of a signature. Only pass true for scripts you believe could contain signatures. For example,
* pass false, or omit the this argument (defaults to false), for scriptPubKeys.
*/
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode)
{
std::string str;
opcodetype opcode;
std::vector<unsigned char> vch;
CScript::const_iterator pc = script.begin();
while (pc < script.end()) {
if (!str.empty()) {
str += " ";
}
if (!script.GetOp(pc, opcode, vch)) {
str += "[error]";
return str;
}
if (0 <= opcode && opcode <= OP_PUSHDATA4) {
if (vch.size() <= static_cast<std::vector<unsigned char>::size_type>(4)) {
str += strprintf("%d", CScriptNum(vch, false).getint());
} else {
// the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature
if (fAttemptSighashDecode && !script.IsUnspendable()) {
std::string strSigHashDecode;
// goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig.
// this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to
// the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the
// checks in CheckSignatureEncoding.
if (CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC, nullptr)) {
const unsigned char chSigHashType = vch.back();
if (mapSigHashTypes.count(chSigHashType)) {
strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
}
}
str += HexStr(vch) + strSigHashDecode;
} else {
str += HexStr(vch);
}
}
} else {
str += GetOpName(opcode);
}
}
return str;
}
std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
{
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | serializeFlags);
ssTx << tx;
return HexStr(ssTx.begin(), ssTx.end());
}
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
UniValue& out, bool fIncludeHex)
{
txnouttype type;
std::vector<CTxDestination> addresses;
int nRequired;
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
if (fIncludeHex)
out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
out.pushKV("type", GetTxnOutputType(type));
return;
}
out.pushKV("reqSigs", nRequired);
out.pushKV("type", GetTxnOutputType(type));
UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses)
a.push_back(CBitcoinAddress(addr).ToString());
out.pushKV("addresses", a);
}
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags)
{
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
entry.pushKV("version", tx.nVersion);
entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION));
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
entry.pushKV("locktime", (int64_t)tx.nLockTime);
UniValue vin(UniValue::VARR);
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const CTxIn& txin = tx.vin[i];
UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase())
in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
else {
in.pushKV("txid", txin.prevout.hash.GetHex());
in.pushKV("vout", (int64_t)txin.prevout.n);
UniValue o(UniValue::VOBJ);
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
in.pushKV("scriptSig", o);
if (!tx.vin[i].scriptWitness.IsNull()) {
UniValue txinwitness(UniValue::VARR);
for (const auto& item : tx.vin[i].scriptWitness.stack) {
txinwitness.push_back(HexStr(item.begin(), item.end()));
}
in.pushKV("txinwitness", txinwitness);
}
}
in.pushKV("sequence", (int64_t)txin.nSequence);
vin.push_back(in);
}
entry.pushKV("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.pushKV("value", ValueFromAmount(txout.nValue));
out.pushKV("n", (int64_t)i);
UniValue o(UniValue::VOBJ);
ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
out.pushKV("scriptPubKey", o);
vout.push_back(out);
}
entry.pushKV("vout", vout);
if (!hashBlock.IsNull())
entry.pushKV("blockhash", hashBlock.GetHex());
if (include_hex) {
entry.pushKV("hex", EncodeHexTx(tx, serialize_flags)); // the hex-encoded transaction. used the name "hex" to be consistent with the verbose output of "getrawtransaction".
}
}