Merge branch 'rawtx' into merge-rawtx
This commit is contained in:
commit
a715a643bc
19 changed files with 1846 additions and 182 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@
|
|||
src/bitcoin
|
||||
src/bitcoind
|
||||
src/bitcoin-cli
|
||||
src/bitcoin-tx
|
||||
src/test/test_bitcoin
|
||||
src/qt/test/test_bitcoin-qt
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ LIBBITCOIN_COMMON=libbitcoin_common.a
|
|||
LIBBITCOIN_CLI=libbitcoin_cli.a
|
||||
LIBBITCOIN_UTIL=libbitcoin_util.a
|
||||
LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a
|
||||
LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a
|
||||
LIBBITCOINQT=qt/libbitcoinqt.a
|
||||
|
||||
noinst_LIBRARIES = \
|
||||
|
@ -41,6 +42,7 @@ noinst_LIBRARIES = \
|
|||
libbitcoin_common.a \
|
||||
libbitcoin_cli.a \
|
||||
libbitcoin_util.a \
|
||||
univalue/libbitcoin_univalue.a \
|
||||
crypto/libbitcoin_crypto.a
|
||||
if ENABLE_WALLET
|
||||
BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
|
||||
|
@ -58,6 +60,8 @@ if BUILD_BITCOIN_CLI
|
|||
bin_PROGRAMS += bitcoin-cli
|
||||
endif
|
||||
|
||||
bin_PROGRAMS += bitcoin-tx
|
||||
|
||||
.PHONY: FORCE
|
||||
# bitcoin core #
|
||||
BITCOIN_CORE_H = \
|
||||
|
@ -75,6 +79,7 @@ BITCOIN_CORE_H = \
|
|||
coins.h \
|
||||
compat.h \
|
||||
core.h \
|
||||
core_io.h \
|
||||
crypter.h \
|
||||
db.h \
|
||||
hash.h \
|
||||
|
@ -177,6 +182,13 @@ crypto_libbitcoin_crypto_a_SOURCES = \
|
|||
crypto/sha1.h \
|
||||
crypto/ripemd160.h
|
||||
|
||||
# univalue JSON library
|
||||
univalue_libbitcoin_univalue_a_SOURCES = \
|
||||
univalue/univalue.cpp \
|
||||
univalue/univalue_read.cpp \
|
||||
univalue/univalue_write.cpp \
|
||||
univalue/univalue.h
|
||||
|
||||
# common: shared between bitcoind, and bitcoin-qt and non-server tools
|
||||
libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
libbitcoin_common_a_SOURCES = \
|
||||
|
@ -185,6 +197,8 @@ libbitcoin_common_a_SOURCES = \
|
|||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
core.cpp \
|
||||
core_read.cpp \
|
||||
core_write.cpp \
|
||||
hash.cpp \
|
||||
key.cpp \
|
||||
keystore.cpp \
|
||||
|
@ -226,6 +240,7 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
|
|||
bitcoind_LDADD = \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_UNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBLEVELDB) \
|
||||
|
@ -264,6 +279,17 @@ endif
|
|||
bitcoin_cli_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
#
|
||||
|
||||
# bitcoin-tx binary #
|
||||
bitcoin_tx_LDADD = \
|
||||
$(LIBBITCOIN_UNIVALUE) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(BOOST_LIBS)
|
||||
bitcoin_tx_SOURCES = bitcoin-tx.cpp
|
||||
bitcoin_tx_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
#
|
||||
|
||||
if TARGET_WINDOWS
|
||||
bitcoin_cli_SOURCES += bitcoin-cli-res.rc
|
||||
endif
|
||||
|
|
|
@ -359,7 +359,7 @@ qt_bitcoin_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
|
|||
if ENABLE_WALLET
|
||||
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS)
|
||||
if USE_LIBSECP256K1
|
||||
qt_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la
|
||||
|
|
|
@ -30,7 +30,7 @@ qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER)
|
|||
if ENABLE_WALLET
|
||||
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBLEVELDB) \
|
||||
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) \
|
||||
$(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
|
||||
$(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS)
|
||||
if USE_LIBSECP256K1
|
||||
|
|
|
@ -64,7 +64,7 @@ endif
|
|||
|
||||
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
|
||||
test_test_bitcoin_CPPFLAGS = $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS)
|
||||
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB)
|
||||
if ENABLE_WALLET
|
||||
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
|
||||
|
|
|
@ -50,8 +50,7 @@ static bool AppInitRPC(int argc, char* argv[])
|
|||
// Parameters
|
||||
//
|
||||
ParseParameters(argc, argv);
|
||||
if (!boost::filesystem::is_directory(GetDataDir(false)))
|
||||
{
|
||||
if (!boost::filesystem::is_directory(GetDataDir(false))) {
|
||||
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
|
||||
return false;
|
||||
}
|
||||
|
@ -66,11 +65,9 @@ static bool AppInitRPC(int argc, char* argv[])
|
|||
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
|
||||
return false;
|
||||
}
|
||||
if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version"))
|
||||
{
|
||||
if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
|
||||
std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n";
|
||||
if (!mapArgs.count("-version"))
|
||||
{
|
||||
if (!mapArgs.count("-version")) {
|
||||
strUsage += "\n" + _("Usage:") + "\n" +
|
||||
" bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" +
|
||||
" bitcoin-cli [options] help " + _("List commands") + "\n" +
|
||||
|
@ -153,11 +150,9 @@ int CommandLineRPC(int argc, char *argv[])
|
|||
{
|
||||
string strPrint;
|
||||
int nRet = 0;
|
||||
try
|
||||
{
|
||||
try {
|
||||
// Skip switches
|
||||
while (argc > 1 && IsSwitchChar(argv[1][0]))
|
||||
{
|
||||
while (argc > 1 && IsSwitchChar(argv[1][0])) {
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
@ -178,15 +173,12 @@ int CommandLineRPC(int argc, char *argv[])
|
|||
const Value& result = find_value(reply, "result");
|
||||
const Value& error = find_value(reply, "error");
|
||||
|
||||
if (error.type() != null_type)
|
||||
{
|
||||
if (error.type() != null_type) {
|
||||
// Error
|
||||
strPrint = "error: " + write_string(error, false);
|
||||
int code = find_value(error.get_obj(), "code").get_int();
|
||||
nRet = abs(code);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Result
|
||||
if (result.type() == null_type)
|
||||
strPrint = "";
|
||||
|
@ -208,8 +200,7 @@ int CommandLineRPC(int argc, char *argv[])
|
|||
throw;
|
||||
}
|
||||
|
||||
if (strPrint != "")
|
||||
{
|
||||
if (strPrint != "") {
|
||||
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
||||
}
|
||||
return nRet;
|
||||
|
@ -219,8 +210,7 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
SetupEnvironment();
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
if(!AppInitRPC(argc, argv))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
@ -233,8 +223,7 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
int ret = EXIT_FAILURE;
|
||||
try
|
||||
{
|
||||
try {
|
||||
ret = CommandLineRPC(argc, argv);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
|
|
597
src/bitcoin-tx.cpp
Normal file
597
src/bitcoin-tx.cpp
Normal file
|
@ -0,0 +1,597 @@
|
|||
// Copyright (c) 2009-2014 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "base58.h"
|
||||
#include "util.h"
|
||||
#include "core.h"
|
||||
#include "main.h" // for MAX_BLOCK_SIZE
|
||||
#include "keystore.h"
|
||||
#include "ui_interface.h" // for _(...)
|
||||
#include "univalue/univalue.h"
|
||||
#include "core_io.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace boost::assign;
|
||||
|
||||
static bool fCreateBlank;
|
||||
static map<string,UniValue> registers;
|
||||
CClientUIInterface uiInterface;
|
||||
|
||||
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 = _("Options:") + "\n";
|
||||
strUsage += " -? " + _("This help message") + "\n";
|
||||
strUsage += " -create " + _("Create new, empty TX.") + "\n";
|
||||
strUsage += " -json " + _("Select JSON output") + "\n";
|
||||
strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be solved instantly.") + "\n";
|
||||
strUsage += " -testnet " + _("Use the test network") + "\n";
|
||||
strUsage += "\n";
|
||||
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
|
||||
|
||||
strUsage = _("Commands:") + "\n";
|
||||
strUsage += " delin=N " + _("Delete input N from TX") + "\n";
|
||||
strUsage += " delout=N " + _("Delete output N from TX") + "\n";
|
||||
strUsage += " in=TXID:VOUT " + _("Add input to TX") + "\n";
|
||||
strUsage += " locktime=N " + _("Set TX lock time to N") + "\n";
|
||||
strUsage += " nversion=N " + _("Set TX version to N") + "\n";
|
||||
strUsage += " outaddr=VALUE:ADDRESS " + _("Add address-based output to TX") + "\n";
|
||||
strUsage += " outscript=VALUE:SCRIPT " + _("Add raw script output to TX") + "\n";
|
||||
strUsage += " sign=SIGHASH-FLAGS " + _("Add zero or more signatures to transaction") + "\n";
|
||||
strUsage += " This command requires JSON registers:\n";
|
||||
strUsage += " prevtxs=JSON object\n";
|
||||
strUsage += " privatekeys=JSON object\n";
|
||||
strUsage += " See signrawtransaction docs for format of sighash flags, JSON objects.\n";
|
||||
strUsage += "\n";
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
|
||||
strUsage = _("Register Commands:") + "\n";
|
||||
strUsage += " load=NAME:FILENAME " + _("Load JSON file FILENAME into register NAME") + "\n";
|
||||
strUsage += " set=NAME:JSON-STRING " + _("Set register NAME to given JSON-STRING") + "\n";
|
||||
strUsage += "\n";
|
||||
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(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);
|
||||
int64_t 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 SetDestination()
|
||||
CScript scriptPubKey;
|
||||
scriptPubKey.SetDestination(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) ||
|
||||
(pos == (strInput.size() - 1)))
|
||||
throw runtime_error("TX output missing separator");
|
||||
|
||||
// extract and validate VALUE
|
||||
string strValue = strInput.substr(0, pos);
|
||||
int64_t 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 0;
|
||||
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.count(); 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["privatekeys"];
|
||||
{
|
||||
for (unsigned int previdx = 0; previdx < prevtxsObj.count(); previdx++) {
|
||||
UniValue prevOut = prevtxsObj[previdx];
|
||||
if (!prevOut.isObject())
|
||||
throw runtime_error("expected prevtxs internal object");
|
||||
|
||||
map<string,UniValue::VType> types = 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");
|
||||
|
||||
int nOut = atoi(prevOut["vout"].getValStr());
|
||||
if (nOut < 0)
|
||||
throw runtime_error("vout must be positive");
|
||||
|
||||
vector<unsigned char> pkData(ParseHexUV(prevOut, "scriptPubKey"));
|
||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||
|
||||
CCoins coins;
|
||||
if (view.GetCoins(txid, coins)) {
|
||||
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);
|
||||
}
|
||||
// what todo if txid is known, but the actual output isn't?
|
||||
}
|
||||
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
|
||||
view.SetCoins(txid, coins);
|
||||
|
||||
// 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];
|
||||
CCoins coins;
|
||||
if (!view.GetCoins(txin.prevout.hash, 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, mergedTx, i, STANDARD_SCRIPT_VERIFY_FLAGS, 0))
|
||||
fComplete = false;
|
||||
}
|
||||
|
||||
if (fComplete) {
|
||||
// do nothing... for now
|
||||
// perhaps store this for later optional JSON output
|
||||
}
|
||||
|
||||
tx = mergedTx;
|
||||
}
|
||||
|
||||
static void MutateTx(CMutableTransaction& tx, const string& command,
|
||||
const string& commandVal)
|
||||
{
|
||||
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);
|
||||
|
||||
else if (command == "sign")
|
||||
MutateTxSign(tx, commandVal);
|
||||
|
||||
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, 0, entry);
|
||||
|
||||
string jsonOutput = entry.write(4);
|
||||
fprintf(stdout, "%s\n", jsonOutput.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
|
||||
OutputTxHex(tx);
|
||||
}
|
||||
|
||||
static int CommandLineRawTx(int argc, char* argv[])
|
||||
{
|
||||
string strPrint;
|
||||
int nRet = 0;
|
||||
try {
|
||||
// Skip switches
|
||||
while (argc > 1 && IsSwitchChar(argv[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 (!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 (boost::thread_interrupted) {
|
||||
throw;
|
||||
}
|
||||
catch (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 (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 (std::exception& e) {
|
||||
PrintExceptionContinue(&e, "CommandLineRawTx()");
|
||||
} catch (...) {
|
||||
PrintExceptionContinue(NULL, "CommandLineRawTx()");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
23
src/core_io.h
Normal file
23
src/core_io.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef __BITCOIN_CORE_IO_H__
|
||||
#define __BITCOIN_CORE_IO_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
class uint256;
|
||||
class CScript;
|
||||
class CTransaction;
|
||||
class UniValue;
|
||||
|
||||
// core_read.cpp
|
||||
extern CScript ParseScript(std::string s);
|
||||
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
|
||||
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
|
||||
extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
|
||||
|
||||
// core_write.cpp
|
||||
extern std::string EncodeHexTx(const CTransaction& tx);
|
||||
extern void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||
UniValue& out, bool fIncludeHex);
|
||||
extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry);
|
||||
|
||||
#endif // __BITCOIN_CORE_IO_H__
|
127
src/core_read.cpp
Normal file
127
src/core_read.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
|
||||
#include <vector>
|
||||
#include "core_io.h"
|
||||
#include "core.h"
|
||||
#include "serialize.h"
|
||||
#include "script.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include "univalue/univalue.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace boost::algorithm;
|
||||
|
||||
CScript ParseScript(std::string s)
|
||||
{
|
||||
CScript result;
|
||||
|
||||
static map<string, opcodetype> mapOpNames;
|
||||
|
||||
if (mapOpNames.empty())
|
||||
{
|
||||
for (int op = 0; op <= OP_NOP10; op++)
|
||||
{
|
||||
// Allow OP_RESERVED to get into mapOpNames
|
||||
if (op < OP_NOP && op != OP_RESERVED)
|
||||
continue;
|
||||
|
||||
const char* name = GetOpName((opcodetype)op);
|
||||
if (strcmp(name, "OP_UNKNOWN") == 0)
|
||||
continue;
|
||||
string strName(name);
|
||||
mapOpNames[strName] = (opcodetype)op;
|
||||
// Convenience: OP_ADD and just ADD are both recognized:
|
||||
replace_first(strName, "OP_", "");
|
||||
mapOpNames[strName] = (opcodetype)op;
|
||||
}
|
||||
}
|
||||
|
||||
vector<string> words;
|
||||
split(words, s, is_any_of(" \t\n"), token_compress_on);
|
||||
|
||||
for (std::vector<std::string>::const_iterator w = words.begin(); w != words.end(); ++w)
|
||||
{
|
||||
if (w->empty())
|
||||
{
|
||||
// Empty string, ignore. (boost::split given '' will return one word)
|
||||
}
|
||||
else if (all(*w, is_digit()) ||
|
||||
(starts_with(*w, "-") && all(string(w->begin()+1, w->end()), is_digit())))
|
||||
{
|
||||
// Number
|
||||
int64_t n = atoi64(*w);
|
||||
result << n;
|
||||
}
|
||||
else if (starts_with(*w, "0x") && (w->begin()+2 != w->end()) && IsHex(string(w->begin()+2, w->end())))
|
||||
{
|
||||
// Raw hex data, inserted NOT pushed onto stack:
|
||||
std::vector<unsigned char> raw = ParseHex(string(w->begin()+2, w->end()));
|
||||
result.insert(result.end(), raw.begin(), raw.end());
|
||||
}
|
||||
else if (w->size() >= 2 && starts_with(*w, "'") && ends_with(*w, "'"))
|
||||
{
|
||||
// Single-quoted string, pushed as data. NOTE: this is poor-man's
|
||||
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
|
||||
std::vector<unsigned char> value(w->begin()+1, w->end()-1);
|
||||
result << value;
|
||||
}
|
||||
else if (mapOpNames.count(*w))
|
||||
{
|
||||
// opcode, e.g. OP_ADD or ADD:
|
||||
result << mapOpNames[*w];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw runtime_error("script parse error");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx)
|
||||
{
|
||||
if (!IsHex(strHexTx))
|
||||
return false;
|
||||
|
||||
vector<unsigned char> txData(ParseHex(strHexTx));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
try {
|
||||
ssData >> tx;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint256 ParseHashUV(const UniValue& v, const string& strName)
|
||||
{
|
||||
string strHex;
|
||||
if (v.isStr())
|
||||
strHex = v.getValStr();
|
||||
if (!IsHex(strHex)) // Note: IsHex("") is false
|
||||
throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
|
||||
uint256 result;
|
||||
result.SetHex(strHex);
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<unsigned char> ParseHexUV(const UniValue& v, const string& strName)
|
||||
{
|
||||
string strHex;
|
||||
if (v.isStr())
|
||||
strHex = v.getValStr();
|
||||
if (!IsHex(strHex))
|
||||
throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
return ParseHex(strHex);
|
||||
}
|
||||
|
89
src/core_write.cpp
Normal file
89
src/core_write.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
|
||||
#include <vector>
|
||||
#include "core_io.h"
|
||||
#include "univalue/univalue.h"
|
||||
#include "script.h"
|
||||
#include "core.h"
|
||||
#include "serialize.h"
|
||||
#include "util.h"
|
||||
#include "base58.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
string EncodeHexTx(const CTransaction& tx)
|
||||
{
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << tx;
|
||||
return HexStr(ssTx.begin(), ssTx.end());
|
||||
}
|
||||
|
||||
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||
UniValue& out, bool fIncludeHex)
|
||||
{
|
||||
txnouttype type;
|
||||
vector<CTxDestination> addresses;
|
||||
int nRequired;
|
||||
|
||||
out.pushKV("asm", scriptPubKey.ToString());
|
||||
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);
|
||||
BOOST_FOREACH(const CTxDestination& addr, addresses)
|
||||
a.push_back(CBitcoinAddress(addr).ToString());
|
||||
out.pushKV("addresses", a);
|
||||
}
|
||||
|
||||
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
|
||||
{
|
||||
entry.pushKV("txid", tx.GetHash().GetHex());
|
||||
entry.pushKV("version", tx.nVersion);
|
||||
entry.pushKV("locktime", (int64_t)tx.nLockTime);
|
||||
|
||||
UniValue vin(UniValue::VARR);
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||
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", txin.scriptSig.ToString());
|
||||
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
|
||||
in.pushKV("scriptSig", o);
|
||||
}
|
||||
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);
|
||||
|
||||
UniValue outValue(UniValue::VNUM, FormatMoney(txout.nValue));
|
||||
out.pushKV("value", outValue);
|
||||
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 != 0)
|
||||
entry.pushKV("blockhash", hashBlock.GetHex());
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#include "main.h"
|
||||
#include "miner.h"
|
||||
#include "pow.h"
|
||||
#include "core_io.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "db.h"
|
||||
#include "wallet.h"
|
||||
|
@ -472,9 +473,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
|
|||
|
||||
Object entry;
|
||||
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << tx;
|
||||
entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end())));
|
||||
entry.push_back(Pair("data", EncodeHexTx(tx)));
|
||||
|
||||
entry.push_back(Pair("hash", txHash.GetHex()));
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "base58.h"
|
||||
#include "core.h"
|
||||
#include "core_io.h"
|
||||
#include "init.h"
|
||||
#include "keystore.h"
|
||||
#include "main.h"
|
||||
|
@ -36,8 +37,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH
|
|||
if (fIncludeHex)
|
||||
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
||||
|
||||
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
|
||||
{
|
||||
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
|
||||
out.push_back(Pair("type", GetTxnOutputType(type)));
|
||||
return;
|
||||
}
|
||||
|
@ -57,13 +57,11 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
|
|||
entry.push_back(Pair("version", tx.nVersion));
|
||||
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
|
||||
Array vin;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.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
|
||||
{
|
||||
else {
|
||||
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
|
||||
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
|
||||
Object o;
|
||||
|
@ -76,8 +74,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
|
|||
}
|
||||
entry.push_back(Pair("vin", vin));
|
||||
Array vout;
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
const CTxOut& txout = tx.vout[i];
|
||||
Object out;
|
||||
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
|
||||
|
@ -89,15 +86,12 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
|
|||
}
|
||||
entry.push_back(Pair("vout", vout));
|
||||
|
||||
if (hashBlock != 0)
|
||||
{
|
||||
if (hashBlock != 0) {
|
||||
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
|
||||
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
|
||||
if (mi != mapBlockIndex.end() && (*mi).second)
|
||||
{
|
||||
if (mi != mapBlockIndex.end() && (*mi).second) {
|
||||
CBlockIndex* pindex = (*mi).second;
|
||||
if (chainActive.Contains(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()));
|
||||
|
@ -182,9 +176,7 @@ Value getrawtransaction(const Array& params, bool fHelp)
|
|||
if (!GetTransaction(hash, tx, hashBlock, true))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
|
||||
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << tx;
|
||||
string strHex = HexStr(ssTx.begin(), ssTx.end());
|
||||
string strHex = EncodeHexTx(tx);
|
||||
|
||||
if (!fVerbose)
|
||||
return strHex;
|
||||
|
@ -245,11 +237,9 @@ Value listunspent(const Array& params, bool fHelp)
|
|||
nMaxDepth = params[1].get_int();
|
||||
|
||||
set<CBitcoinAddress> setAddress;
|
||||
if (params.size() > 2)
|
||||
{
|
||||
if (params.size() > 2) {
|
||||
Array inputs = params[2].get_array();
|
||||
BOOST_FOREACH(Value& input, inputs)
|
||||
{
|
||||
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());
|
||||
|
@ -263,13 +253,11 @@ Value listunspent(const Array& params, bool fHelp)
|
|||
vector<COutput> vecOutputs;
|
||||
assert(pwalletMain != NULL);
|
||||
pwalletMain->AvailableCoins(vecOutputs, false);
|
||||
BOOST_FOREACH(const COutput& out, vecOutputs)
|
||||
{
|
||||
BOOST_FOREACH(const COutput& out, vecOutputs) {
|
||||
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
|
||||
continue;
|
||||
|
||||
if (setAddress.size())
|
||||
{
|
||||
if (setAddress.size()) {
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
|
||||
continue;
|
||||
|
@ -284,18 +272,15 @@ Value listunspent(const Array& params, bool fHelp)
|
|||
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))
|
||||
{
|
||||
if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
|
||||
entry.push_back(Pair("address", CBitcoinAddress(address).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())
|
||||
{
|
||||
if (pk.IsPayToScriptHash()) {
|
||||
CTxDestination address;
|
||||
if (ExtractDestination(pk, address))
|
||||
{
|
||||
if (ExtractDestination(pk, address)) {
|
||||
const CScriptID& hash = boost::get<const CScriptID&>(address);
|
||||
CScript redeemScript;
|
||||
if (pwalletMain->GetCScript(hash, redeemScript))
|
||||
|
@ -352,8 +337,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
|||
|
||||
CMutableTransaction rawTx;
|
||||
|
||||
BOOST_FOREACH(const Value& input, inputs)
|
||||
{
|
||||
BOOST_FOREACH(const Value& input, inputs) {
|
||||
const Object& o = input.get_obj();
|
||||
|
||||
uint256 txid = ParseHashO(o, "txid");
|
||||
|
@ -370,8 +354,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
|||
}
|
||||
|
||||
set<CBitcoinAddress> setAddress;
|
||||
BOOST_FOREACH(const Pair& s, sendTo)
|
||||
{
|
||||
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_);
|
||||
|
@ -388,9 +371,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
|||
rawTx.vout.push_back(out);
|
||||
}
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << rawTx;
|
||||
return HexStr(ss.begin(), ss.end());
|
||||
return EncodeHexTx(rawTx);
|
||||
}
|
||||
|
||||
Value decoderawtransaction(const Array& params, bool fHelp)
|
||||
|
@ -444,15 +425,12 @@ Value decoderawtransaction(const Array& params, bool fHelp)
|
|||
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
|
||||
);
|
||||
|
||||
vector<unsigned char> txData(ParseHexV(params[0], "argument"));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
RPCTypeCheck(params, list_of(str_type));
|
||||
|
||||
CTransaction tx;
|
||||
try {
|
||||
ssData >> tx;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
|
||||
if (!DecodeHexTx(tx, params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
|
||||
Object result;
|
||||
TxToJSON(tx, 0, result);
|
||||
|
@ -556,8 +534,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
vector<CMutableTransaction> txVariants;
|
||||
while (!ssData.empty())
|
||||
{
|
||||
while (!ssData.empty()) {
|
||||
try {
|
||||
CMutableTransaction tx;
|
||||
ssData >> tx;
|
||||
|
@ -596,12 +573,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
|
||||
bool fGivenKeys = false;
|
||||
CBasicKeyStore tempKeystore;
|
||||
if (params.size() > 2 && params[2].type() != null_type)
|
||||
{
|
||||
if (params.size() > 2 && params[2].type() != null_type) {
|
||||
fGivenKeys = true;
|
||||
Array keys = params[2].get_array();
|
||||
BOOST_FOREACH(Value k, keys)
|
||||
{
|
||||
BOOST_FOREACH(Value k, keys) {
|
||||
CBitcoinSecret vchSecret;
|
||||
bool fGood = vchSecret.SetString(k.get_str());
|
||||
if (!fGood)
|
||||
|
@ -616,11 +591,9 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
#endif
|
||||
|
||||
// Add previous txouts given in the RPC call:
|
||||
if (params.size() > 1 && params[1].type() != null_type)
|
||||
{
|
||||
if (params.size() > 1 && params[1].type() != null_type) {
|
||||
Array prevTxs = params[1].get_array();
|
||||
BOOST_FOREACH(Value& p, prevTxs)
|
||||
{
|
||||
BOOST_FOREACH(Value& p, prevTxs) {
|
||||
if (p.type() != obj_type)
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
|
||||
|
||||
|
@ -655,12 +628,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
|
||||
// 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())
|
||||
{
|
||||
if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) {
|
||||
RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
|
||||
Value v = find_value(prevOut, "redeemScript");
|
||||
if (!(v == Value::null))
|
||||
{
|
||||
if (!(v == Value::null)) {
|
||||
vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
|
||||
CScript redeemScript(rsData.begin(), rsData.end());
|
||||
tempKeystore.AddCScript(redeemScript);
|
||||
|
@ -676,8 +647,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
#endif
|
||||
|
||||
int nHashType = SIGHASH_ALL;
|
||||
if (params.size() > 3 && params[3].type() != null_type)
|
||||
{
|
||||
if (params.size() > 3 && params[3].type() != null_type) {
|
||||
static map<string, int> mapSigHashValues =
|
||||
boost::assign::map_list_of
|
||||
(string("ALL"), int(SIGHASH_ALL))
|
||||
|
@ -697,12 +667,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
||||
|
||||
// Sign what we can:
|
||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
||||
CTxIn& txin = mergedTx.vin[i];
|
||||
CCoins coins;
|
||||
if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
|
||||
{
|
||||
if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) {
|
||||
fComplete = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -714,8 +682,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
|
||||
|
||||
// ... and merge in other signatures:
|
||||
BOOST_FOREACH(const CMutableTransaction& txv, txVariants)
|
||||
{
|
||||
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
|
||||
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
|
||||
}
|
||||
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STANDARD_SCRIPT_VERIFY_FLAGS, 0))
|
||||
|
@ -723,9 +690,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
|||
}
|
||||
|
||||
Object result;
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << mergedTx;
|
||||
result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
|
||||
result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
|
||||
result.push_back(Pair("complete", fComplete));
|
||||
|
||||
return result;
|
||||
|
@ -754,25 +719,18 @@ Value sendrawtransaction(const Array& params, bool fHelp)
|
|||
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
|
||||
);
|
||||
|
||||
RPCTypeCheck(params, list_of(str_type)(bool_type));
|
||||
|
||||
// parse hex string from parameter
|
||||
vector<unsigned char> txData(ParseHexV(params[0], "parameter"));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
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();
|
||||
|
||||
// deserialize binary data stream
|
||||
try {
|
||||
ssData >> tx;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
const uint256 &hashTx = tx.GetHash();
|
||||
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
CCoins existingCoins;
|
||||
bool fHaveMempool = mempool.exists(hashTx);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "base58.h"
|
||||
#include "core_io.h"
|
||||
#include "rpcserver.h"
|
||||
#include "init.h"
|
||||
#include "net.h"
|
||||
|
@ -1550,9 +1551,7 @@ Value gettransaction(const Array& params, bool fHelp)
|
|||
ListTransactions(wtx, "*", 0, false, details, filter);
|
||||
entry.push_back(Pair("details", details));
|
||||
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << static_cast<CTransaction>(wtx);
|
||||
string strHex = HexStr(ssTx.begin(), ssTx.end());
|
||||
string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
|
||||
entry.push_back(Pair("hex", strHex));
|
||||
|
||||
return entry;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "key.h"
|
||||
#include "keystore.h"
|
||||
#include "main.h"
|
||||
#include "core_io.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <stdint.h>
|
||||
|
@ -36,76 +37,6 @@ extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo
|
|||
|
||||
static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
|
||||
CScript
|
||||
ParseScript(string s)
|
||||
{
|
||||
CScript result;
|
||||
|
||||
static map<string, opcodetype> mapOpNames;
|
||||
|
||||
if (mapOpNames.size() == 0)
|
||||
{
|
||||
for (int op = 0; op <= OP_NOP10; op++)
|
||||
{
|
||||
// Allow OP_RESERVED to get into mapOpNames
|
||||
if (op < OP_NOP && op != OP_RESERVED)
|
||||
continue;
|
||||
|
||||
const char* name = GetOpName((opcodetype)op);
|
||||
if (strcmp(name, "OP_UNKNOWN") == 0)
|
||||
continue;
|
||||
string strName(name);
|
||||
mapOpNames[strName] = (opcodetype)op;
|
||||
// Convenience: OP_ADD and just ADD are both recognized:
|
||||
replace_first(strName, "OP_", "");
|
||||
mapOpNames[strName] = (opcodetype)op;
|
||||
}
|
||||
}
|
||||
|
||||
vector<string> words;
|
||||
split(words, s, is_any_of(" \t\n"), token_compress_on);
|
||||
|
||||
BOOST_FOREACH(string w, words)
|
||||
{
|
||||
if (w.size() == 0)
|
||||
{
|
||||
// Empty string, ignore. (boost::split given '' will return one word)
|
||||
}
|
||||
else if (all(w, is_digit()) ||
|
||||
(starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit())))
|
||||
{
|
||||
// Number
|
||||
int64_t n = atoi64(w);
|
||||
result << n;
|
||||
}
|
||||
else if (starts_with(w, "0x") && IsHex(string(w.begin()+2, w.end())))
|
||||
{
|
||||
// Raw hex data, inserted NOT pushed onto stack:
|
||||
std::vector<unsigned char> raw = ParseHex(string(w.begin()+2, w.end()));
|
||||
result.insert(result.end(), raw.begin(), raw.end());
|
||||
}
|
||||
else if (w.size() >= 2 && starts_with(w, "'") && ends_with(w, "'"))
|
||||
{
|
||||
// Single-quoted string, pushed as data. NOTE: this is poor-man's
|
||||
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
|
||||
std::vector<unsigned char> value(w.begin()+1, w.end()-1);
|
||||
result << value;
|
||||
}
|
||||
else if (mapOpNames.count(w))
|
||||
{
|
||||
// opcode, e.g. OP_ADD or ADD:
|
||||
result << mapOpNames[w];
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ERROR("Parse error: " << s);
|
||||
return CScript();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Array
|
||||
read_json(const std::string& jsondata)
|
||||
{
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "keystore.h"
|
||||
#include "main.h"
|
||||
#include "script.h"
|
||||
#include "core_io.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
@ -24,7 +25,6 @@ using namespace boost::algorithm;
|
|||
|
||||
// In script_tests.cpp
|
||||
extern Array read_json(const std::string& jsondata);
|
||||
extern CScript ParseScript(string s);
|
||||
|
||||
unsigned int ParseFlags(string strFlags){
|
||||
unsigned int flags = 0;
|
||||
|
|
233
src/univalue/univalue.cpp
Normal file
233
src/univalue/univalue.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
// Copyright 2014 BitPay Inc.
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <sstream>
|
||||
#include "univalue.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const UniValue nullValue;
|
||||
|
||||
void UniValue::clear()
|
||||
{
|
||||
typ = VNULL;
|
||||
val.clear();
|
||||
keys.clear();
|
||||
values.clear();
|
||||
}
|
||||
|
||||
bool UniValue::setNull()
|
||||
{
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::setBool(bool val_)
|
||||
{
|
||||
clear();
|
||||
typ = VBOOL;
|
||||
if (val_)
|
||||
val = "1";
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool validNumStr(const string& s)
|
||||
{
|
||||
string tokenVal;
|
||||
unsigned int consumed;
|
||||
enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str());
|
||||
return (tt == JTOK_NUMBER);
|
||||
}
|
||||
|
||||
bool UniValue::setNumStr(const string& val_)
|
||||
{
|
||||
if (!validNumStr(val))
|
||||
return false;
|
||||
|
||||
clear();
|
||||
typ = VNUM;
|
||||
val = val_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::setInt(uint64_t val)
|
||||
{
|
||||
string s;
|
||||
ostringstream oss;
|
||||
|
||||
oss << val;
|
||||
|
||||
return setNumStr(oss.str());
|
||||
}
|
||||
|
||||
bool UniValue::setInt(int64_t val)
|
||||
{
|
||||
string s;
|
||||
ostringstream oss;
|
||||
|
||||
oss << val;
|
||||
|
||||
return setNumStr(oss.str());
|
||||
}
|
||||
|
||||
bool UniValue::setFloat(double val)
|
||||
{
|
||||
string s;
|
||||
ostringstream oss;
|
||||
|
||||
oss << val;
|
||||
|
||||
return setNumStr(oss.str());
|
||||
}
|
||||
|
||||
bool UniValue::setStr(const string& val_)
|
||||
{
|
||||
clear();
|
||||
typ = VSTR;
|
||||
val = val_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::setArray()
|
||||
{
|
||||
clear();
|
||||
typ = VARR;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::setObject()
|
||||
{
|
||||
clear();
|
||||
typ = VOBJ;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::push_back(const UniValue& val)
|
||||
{
|
||||
if (typ != VARR)
|
||||
return false;
|
||||
|
||||
values.push_back(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::push_backV(const std::vector<UniValue>& vec)
|
||||
{
|
||||
if (typ != VARR)
|
||||
return false;
|
||||
|
||||
values.insert(values.end(), vec.begin(), vec.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::pushKV(const std::string& key, const UniValue& val)
|
||||
{
|
||||
if (typ != VOBJ)
|
||||
return false;
|
||||
|
||||
keys.push_back(key);
|
||||
values.push_back(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::pushKVs(const UniValue& obj)
|
||||
{
|
||||
if (typ != VOBJ || obj.typ != VOBJ)
|
||||
return false;
|
||||
|
||||
for (unsigned int i = 0; i < obj.keys.size(); i++) {
|
||||
keys.push_back(obj.keys[i]);
|
||||
values.push_back(obj.values[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::getArray(std::vector<UniValue>& arr)
|
||||
{
|
||||
if (typ != VARR)
|
||||
return false;
|
||||
|
||||
arr = values;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniValue::getObject(std::map<std::string,UniValue>& obj)
|
||||
{
|
||||
if (typ != VOBJ)
|
||||
return false;
|
||||
|
||||
obj.clear();
|
||||
for (unsigned int i = 0; i < keys.size(); i++) {
|
||||
obj[keys[i]] = values[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int UniValue::findKey(const std::string& key) const
|
||||
{
|
||||
for (unsigned int i = 0; i < keys.size(); i++) {
|
||||
if (keys[i] == key)
|
||||
return (int) i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t)
|
||||
{
|
||||
for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin();
|
||||
it != t.end(); it++) {
|
||||
int idx = findKey(it->first);
|
||||
if (idx < 0)
|
||||
return false;
|
||||
|
||||
if (values[idx].getType() != it->second)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const UniValue& UniValue::operator[](const std::string& key) const
|
||||
{
|
||||
if (typ != VOBJ)
|
||||
return nullValue;
|
||||
|
||||
int index = findKey(key);
|
||||
if (index < 0)
|
||||
return nullValue;
|
||||
|
||||
return values[index];
|
||||
}
|
||||
|
||||
const UniValue& UniValue::operator[](unsigned int index) const
|
||||
{
|
||||
if (typ != VOBJ && typ != VARR)
|
||||
return nullValue;
|
||||
if (index >= values.size())
|
||||
return nullValue;
|
||||
|
||||
return values[index];
|
||||
}
|
||||
|
||||
const char *uvTypeName(UniValue::VType t)
|
||||
{
|
||||
switch (t) {
|
||||
case UniValue::VNULL: return "null";
|
||||
case UniValue::VBOOL: return "bool";
|
||||
case UniValue::VOBJ: return "object";
|
||||
case UniValue::VARR: return "array";
|
||||
case UniValue::VSTR: return "string";
|
||||
case UniValue::VNUM: return "number";
|
||||
}
|
||||
|
||||
// not reached
|
||||
return NULL;
|
||||
}
|
||||
|
157
src/univalue/univalue.h
Normal file
157
src/univalue/univalue.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2014 BitPay Inc.
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef __UNIVALUE_H__
|
||||
#define __UNIVALUE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
|
||||
class UniValue {
|
||||
public:
|
||||
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
|
||||
|
||||
UniValue() { typ = VNULL; }
|
||||
UniValue(UniValue::VType initialType, const std::string& initialStr = "") {
|
||||
typ = initialType;
|
||||
val = initialStr;
|
||||
}
|
||||
UniValue(uint64_t val_) {
|
||||
setInt(val_);
|
||||
}
|
||||
UniValue(int64_t val_) {
|
||||
setInt(val_);
|
||||
}
|
||||
UniValue(int val_) {
|
||||
setInt(val_);
|
||||
}
|
||||
UniValue(double val_) {
|
||||
setFloat(val_);
|
||||
}
|
||||
UniValue(const std::string& val_) {
|
||||
setStr(val_);
|
||||
}
|
||||
UniValue(const char *val_) {
|
||||
std::string s(val_);
|
||||
setStr(s);
|
||||
}
|
||||
~UniValue() {}
|
||||
|
||||
void clear();
|
||||
|
||||
bool setNull();
|
||||
bool setBool(bool val);
|
||||
bool setNumStr(const std::string& val);
|
||||
bool setInt(uint64_t val);
|
||||
bool setInt(int64_t val);
|
||||
bool setInt(int val) { return setInt((int64_t)val); }
|
||||
bool setFloat(double val);
|
||||
bool setStr(const std::string& val);
|
||||
bool setArray();
|
||||
bool setObject();
|
||||
|
||||
enum VType getType() const { return typ; }
|
||||
std::string getValStr() const { return val; }
|
||||
bool empty() const { return (values.size() == 0); }
|
||||
|
||||
size_t count() const { return values.size(); }
|
||||
|
||||
bool getBool() const { return isTrue(); }
|
||||
bool getArray(std::vector<UniValue>& arr);
|
||||
bool getObject(std::map<std::string,UniValue>& obj);
|
||||
bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes);
|
||||
const UniValue& operator[](const std::string& key) const;
|
||||
const UniValue& operator[](unsigned int index) const;
|
||||
bool exists(const std::string& key) const { return (findKey(key) >= 0); }
|
||||
|
||||
bool isNull() const { return (typ == VNULL); }
|
||||
bool isTrue() const { return (typ == VBOOL) && (val == "1"); }
|
||||
bool isFalse() const { return (!isTrue()); }
|
||||
bool isBool() const { return (typ == VBOOL); }
|
||||
bool isStr() const { return (typ == VSTR); }
|
||||
bool isNum() const { return (typ == VNUM); }
|
||||
bool isArray() const { return (typ == VARR); }
|
||||
bool isObject() const { return (typ == VOBJ); }
|
||||
|
||||
bool push_back(const UniValue& val);
|
||||
bool push_back(const std::string& val_) {
|
||||
UniValue tmpVal(VSTR, val_);
|
||||
return push_back(tmpVal);
|
||||
}
|
||||
bool push_back(const char *val_) {
|
||||
std::string s(val_);
|
||||
return push_back(s);
|
||||
}
|
||||
bool push_backV(const std::vector<UniValue>& vec);
|
||||
|
||||
bool pushKV(const std::string& key, const UniValue& val);
|
||||
bool pushKV(const std::string& key, const std::string& val) {
|
||||
UniValue tmpVal(VSTR, val);
|
||||
return pushKV(key, tmpVal);
|
||||
}
|
||||
bool pushKV(const std::string& key, const char *val_) {
|
||||
std::string val(val_);
|
||||
return pushKV(key, val);
|
||||
}
|
||||
bool pushKV(const std::string& key, int64_t val) {
|
||||
UniValue tmpVal(val);
|
||||
return pushKV(key, tmpVal);
|
||||
}
|
||||
bool pushKV(const std::string& key, uint64_t val) {
|
||||
UniValue tmpVal(val);
|
||||
return pushKV(key, tmpVal);
|
||||
}
|
||||
bool pushKV(const std::string& key, int val) {
|
||||
UniValue tmpVal((int64_t)val);
|
||||
return pushKV(key, tmpVal);
|
||||
}
|
||||
bool pushKV(const std::string& key, double val) {
|
||||
UniValue tmpVal(val);
|
||||
return pushKV(key, tmpVal);
|
||||
}
|
||||
bool pushKVs(const UniValue& obj);
|
||||
|
||||
std::string write(unsigned int prettyIndent = 0,
|
||||
unsigned int indentLevel = 0) const;
|
||||
|
||||
bool read(const char *raw);
|
||||
bool read(const std::string& rawStr) {
|
||||
return read(rawStr.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
UniValue::VType typ;
|
||||
std::string val; // numbers are stored as C++ strings
|
||||
std::vector<std::string> keys;
|
||||
std::vector<UniValue> values;
|
||||
|
||||
int findKey(const std::string& key) const;
|
||||
void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
|
||||
void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
|
||||
};
|
||||
|
||||
enum jtokentype {
|
||||
JTOK_ERR = -1,
|
||||
JTOK_NONE = 0, // eof
|
||||
JTOK_OBJ_OPEN,
|
||||
JTOK_OBJ_CLOSE,
|
||||
JTOK_ARR_OPEN,
|
||||
JTOK_ARR_CLOSE,
|
||||
JTOK_COLON,
|
||||
JTOK_COMMA,
|
||||
JTOK_KW_NULL,
|
||||
JTOK_KW_TRUE,
|
||||
JTOK_KW_FALSE,
|
||||
JTOK_NUMBER,
|
||||
JTOK_STRING,
|
||||
};
|
||||
|
||||
extern enum jtokentype getJsonToken(std::string& tokenVal,
|
||||
unsigned int& consumed, const char *raw);
|
||||
extern const char *uvTypeName(UniValue::VType t);
|
||||
|
||||
#endif // __UNIVALUE_H__
|
390
src/univalue/univalue_read.cpp
Normal file
390
src/univalue/univalue_read.cpp
Normal file
|
@ -0,0 +1,390 @@
|
|||
// Copyright 2014 BitPay Inc.
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include "univalue.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// convert hexadecimal string to unsigned integer
|
||||
static const char *hatoui(const char *first, const char *last,
|
||||
unsigned int& out)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
int digit;
|
||||
if (isdigit(*first))
|
||||
digit = *first - '0';
|
||||
|
||||
else if (*first >= 'a' && *first <= 'f')
|
||||
digit = *first - 'a' + 10;
|
||||
|
||||
else if (*first >= 'A' && *first <= 'F')
|
||||
digit = *first - 'A' + 10;
|
||||
|
||||
else
|
||||
break;
|
||||
|
||||
result = 16 * result + digit;
|
||||
}
|
||||
out = result;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
|
||||
const char *raw)
|
||||
{
|
||||
tokenVal.clear();
|
||||
consumed = 0;
|
||||
|
||||
const char *rawStart = raw;
|
||||
|
||||
while ((*raw) && (isspace(*raw))) // skip whitespace
|
||||
raw++;
|
||||
|
||||
switch (*raw) {
|
||||
|
||||
case 0:
|
||||
return JTOK_NONE;
|
||||
|
||||
case '{':
|
||||
raw++;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_OBJ_OPEN;
|
||||
case '}':
|
||||
raw++;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_OBJ_CLOSE;
|
||||
case '[':
|
||||
raw++;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_ARR_OPEN;
|
||||
case ']':
|
||||
raw++;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_ARR_CLOSE;
|
||||
|
||||
case ':':
|
||||
raw++;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_COLON;
|
||||
case ',':
|
||||
raw++;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_COMMA;
|
||||
|
||||
case 'n':
|
||||
case 't':
|
||||
case 'f':
|
||||
if (!strncmp(raw, "null", 4)) {
|
||||
raw += 4;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_KW_NULL;
|
||||
} else if (!strncmp(raw, "true", 4)) {
|
||||
raw += 4;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_KW_TRUE;
|
||||
} else if (!strncmp(raw, "false", 5)) {
|
||||
raw += 5;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_KW_FALSE;
|
||||
} else
|
||||
return JTOK_ERR;
|
||||
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': {
|
||||
// part 1: int
|
||||
string numStr;
|
||||
|
||||
const char *first = raw;
|
||||
|
||||
const char *firstDigit = first;
|
||||
if (!isdigit(*firstDigit))
|
||||
firstDigit++;
|
||||
if ((*firstDigit == '0') && isdigit(firstDigit[1]))
|
||||
return JTOK_ERR;
|
||||
|
||||
numStr += *raw; // copy first char
|
||||
raw++;
|
||||
|
||||
if ((*first == '-') && (!isdigit(*raw)))
|
||||
return JTOK_ERR;
|
||||
|
||||
while ((*raw) && isdigit(*raw)) { // copy digits
|
||||
numStr += *raw;
|
||||
raw++;
|
||||
}
|
||||
|
||||
// part 2: frac
|
||||
if (*raw == '.') {
|
||||
numStr += *raw; // copy .
|
||||
raw++;
|
||||
|
||||
if (!isdigit(*raw))
|
||||
return JTOK_ERR;
|
||||
while ((*raw) && isdigit(*raw)) { // copy digits
|
||||
numStr += *raw;
|
||||
raw++;
|
||||
}
|
||||
}
|
||||
|
||||
// part 3: exp
|
||||
if (*raw == 'e' || *raw == 'E') {
|
||||
numStr += *raw; // copy E
|
||||
raw++;
|
||||
|
||||
if (*raw == '-' || *raw == '+') { // copy +/-
|
||||
numStr += *raw;
|
||||
raw++;
|
||||
}
|
||||
|
||||
if (!isdigit(*raw))
|
||||
return JTOK_ERR;
|
||||
while ((*raw) && isdigit(*raw)) { // copy digits
|
||||
numStr += *raw;
|
||||
raw++;
|
||||
}
|
||||
}
|
||||
|
||||
tokenVal = numStr;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_NUMBER;
|
||||
}
|
||||
|
||||
case '"': {
|
||||
raw++; // skip "
|
||||
|
||||
string valStr;
|
||||
|
||||
while (*raw) {
|
||||
if (*raw < 0x20)
|
||||
return JTOK_ERR;
|
||||
|
||||
else if (*raw == '\\') {
|
||||
raw++; // skip backslash
|
||||
|
||||
switch (*raw) {
|
||||
case '"': valStr += "\""; break;
|
||||
case '\\': valStr += "\\"; break;
|
||||
case '/': valStr += "/"; break;
|
||||
case 'b': valStr += "\b"; break;
|
||||
case 'f': valStr += "\f"; break;
|
||||
case 'n': valStr += "\n"; break;
|
||||
case 'r': valStr += "\r"; break;
|
||||
case 't': valStr += "\t"; break;
|
||||
|
||||
case 'u': {
|
||||
char buf[4] = {0,0,0,0};
|
||||
char *last = &buf[0];
|
||||
unsigned int codepoint;
|
||||
if (hatoui(raw + 1, raw + 1 + 4, codepoint) !=
|
||||
raw + 1 + 4)
|
||||
return JTOK_ERR;
|
||||
|
||||
if (codepoint <= 0x7f)
|
||||
*last = (char)codepoint;
|
||||
else if (codepoint <= 0x7FF) {
|
||||
*last++ = (char)(0xC0 | (codepoint >> 6));
|
||||
*last = (char)(0x80 | (codepoint & 0x3F));
|
||||
} else if (codepoint <= 0xFFFF) {
|
||||
*last++ = (char)(0xE0 | (codepoint >> 12));
|
||||
*last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F));
|
||||
*last = (char)(0x80 | (codepoint & 0x3F));
|
||||
}
|
||||
|
||||
valStr += buf;
|
||||
raw += 4;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return JTOK_ERR;
|
||||
|
||||
}
|
||||
|
||||
raw++; // skip esc'd char
|
||||
}
|
||||
|
||||
else if (*raw == '"') {
|
||||
raw++; // skip "
|
||||
break; // stop scanning
|
||||
}
|
||||
|
||||
else {
|
||||
valStr += *raw;
|
||||
raw++;
|
||||
}
|
||||
}
|
||||
|
||||
tokenVal = valStr;
|
||||
consumed = (raw - rawStart);
|
||||
return JTOK_STRING;
|
||||
}
|
||||
|
||||
default:
|
||||
return JTOK_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
bool UniValue::read(const char *raw)
|
||||
{
|
||||
clear();
|
||||
|
||||
bool expectName = false;
|
||||
bool expectColon = false;
|
||||
vector<UniValue*> stack;
|
||||
|
||||
enum jtokentype tok = JTOK_NONE;
|
||||
enum jtokentype last_tok = JTOK_NONE;
|
||||
while (1) {
|
||||
last_tok = tok;
|
||||
|
||||
string tokenVal;
|
||||
unsigned int consumed;
|
||||
tok = getJsonToken(tokenVal, consumed, raw);
|
||||
if (tok == JTOK_NONE || tok == JTOK_ERR)
|
||||
break;
|
||||
raw += consumed;
|
||||
|
||||
switch (tok) {
|
||||
|
||||
case JTOK_OBJ_OPEN:
|
||||
case JTOK_ARR_OPEN: {
|
||||
VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
|
||||
if (!stack.size()) {
|
||||
if (utyp == VOBJ)
|
||||
setObject();
|
||||
else
|
||||
setArray();
|
||||
stack.push_back(this);
|
||||
} else {
|
||||
UniValue tmpVal(utyp);
|
||||
UniValue *top = stack.back();
|
||||
top->values.push_back(tmpVal);
|
||||
|
||||
UniValue *newTop = &(top->values.back());
|
||||
stack.push_back(newTop);
|
||||
}
|
||||
|
||||
if (utyp == VOBJ)
|
||||
expectName = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case JTOK_OBJ_CLOSE:
|
||||
case JTOK_ARR_CLOSE: {
|
||||
if (!stack.size() || expectColon || (last_tok == JTOK_COMMA))
|
||||
return false;
|
||||
|
||||
VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
|
||||
UniValue *top = stack.back();
|
||||
if (utyp != top->getType())
|
||||
return false;
|
||||
|
||||
stack.pop_back();
|
||||
expectName = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case JTOK_COLON: {
|
||||
if (!stack.size() || expectName || !expectColon)
|
||||
return false;
|
||||
|
||||
UniValue *top = stack.back();
|
||||
if (top->getType() != VOBJ)
|
||||
return false;
|
||||
|
||||
expectColon = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case JTOK_COMMA: {
|
||||
if (!stack.size() || expectName || expectColon ||
|
||||
(last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
|
||||
return false;
|
||||
|
||||
UniValue *top = stack.back();
|
||||
if (top->getType() == VOBJ)
|
||||
expectName = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case JTOK_KW_NULL:
|
||||
case JTOK_KW_TRUE:
|
||||
case JTOK_KW_FALSE: {
|
||||
if (!stack.size() || expectName || expectColon)
|
||||
return false;
|
||||
|
||||
UniValue tmpVal;
|
||||
switch (tok) {
|
||||
case JTOK_KW_NULL:
|
||||
// do nothing more
|
||||
break;
|
||||
case JTOK_KW_TRUE:
|
||||
tmpVal.setBool(true);
|
||||
break;
|
||||
case JTOK_KW_FALSE:
|
||||
tmpVal.setBool(false);
|
||||
break;
|
||||
default: /* impossible */ break;
|
||||
}
|
||||
|
||||
UniValue *top = stack.back();
|
||||
top->values.push_back(tmpVal);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case JTOK_NUMBER: {
|
||||
if (!stack.size() || expectName || expectColon)
|
||||
return false;
|
||||
|
||||
UniValue tmpVal(VNUM, tokenVal);
|
||||
UniValue *top = stack.back();
|
||||
top->values.push_back(tmpVal);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case JTOK_STRING: {
|
||||
if (!stack.size())
|
||||
return false;
|
||||
|
||||
UniValue *top = stack.back();
|
||||
|
||||
if (expectName) {
|
||||
top->keys.push_back(tokenVal);
|
||||
expectName = false;
|
||||
expectColon = true;
|
||||
} else {
|
||||
UniValue tmpVal(VSTR, tokenVal);
|
||||
top->values.push_back(tmpVal);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.size() != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
145
src/univalue/univalue_write.cpp
Normal file
145
src/univalue/univalue_write.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2014 BitPay Inc.
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "univalue.h"
|
||||
|
||||
// TODO: Using UTF8
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool initEscapes;
|
||||
static const char *escapes[256];
|
||||
|
||||
static void initJsonEscape()
|
||||
{
|
||||
escapes['"'] = "\\\"";
|
||||
escapes['\\'] = "\\\\";
|
||||
escapes['/'] = "\\/";
|
||||
escapes['\b'] = "\\b";
|
||||
escapes['\f'] = "\\f";
|
||||
escapes['\n'] = "\\n";
|
||||
escapes['\r'] = "\\r";
|
||||
escapes['\t'] = "\\t";
|
||||
|
||||
initEscapes = true;
|
||||
}
|
||||
|
||||
static string json_escape(const string& inS)
|
||||
{
|
||||
if (!initEscapes)
|
||||
initJsonEscape();
|
||||
|
||||
string outS;
|
||||
outS.reserve(inS.size() * 2);
|
||||
|
||||
for (unsigned int i = 0; i < inS.size(); i++) {
|
||||
unsigned char ch = inS[i];
|
||||
const char *escStr = escapes[ch];
|
||||
|
||||
if (escStr)
|
||||
outS += escStr;
|
||||
|
||||
else if (isprint(ch))
|
||||
outS += ch;
|
||||
|
||||
else {
|
||||
char tmpesc[16];
|
||||
sprintf(tmpesc, "\\u%04x", ch);
|
||||
outS += tmpesc;
|
||||
}
|
||||
}
|
||||
|
||||
return outS;
|
||||
}
|
||||
|
||||
string UniValue::write(unsigned int prettyIndent,
|
||||
unsigned int indentLevel) const
|
||||
{
|
||||
string s;
|
||||
s.reserve(1024);
|
||||
|
||||
unsigned int modIndent = indentLevel;
|
||||
if (modIndent == 0)
|
||||
modIndent = 1;
|
||||
|
||||
switch (typ) {
|
||||
case VNULL:
|
||||
s += "null";
|
||||
break;
|
||||
case VOBJ:
|
||||
writeObject(prettyIndent, modIndent, s);
|
||||
break;
|
||||
case VARR:
|
||||
writeArray(prettyIndent, modIndent, s);
|
||||
break;
|
||||
case VSTR:
|
||||
s += "\"" + json_escape(val) + "\"";
|
||||
break;
|
||||
case VNUM:
|
||||
s += val;
|
||||
break;
|
||||
case VBOOL:
|
||||
s += (val == "1" ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static string spaceStr;
|
||||
|
||||
static string indentStr(unsigned int prettyIndent, unsigned int indentLevel)
|
||||
{
|
||||
unsigned int spaces = prettyIndent * indentLevel;
|
||||
while (spaceStr.size() < spaces)
|
||||
spaceStr += " ";
|
||||
|
||||
return spaceStr.substr(0, spaces);
|
||||
}
|
||||
|
||||
void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
|
||||
{
|
||||
s += "[";
|
||||
if (prettyIndent)
|
||||
s += "\n";
|
||||
|
||||
for (unsigned int i = 0; i < values.size(); i++) {
|
||||
if (prettyIndent)
|
||||
s += indentStr(prettyIndent, indentLevel);
|
||||
s += values[i].write(prettyIndent, indentLevel + 1);
|
||||
if (i != (values.size() - 1))
|
||||
s += ", ";
|
||||
if (prettyIndent)
|
||||
s += "\n";
|
||||
}
|
||||
|
||||
if (prettyIndent)
|
||||
s += indentStr(prettyIndent, indentLevel - 1);
|
||||
s += "]";
|
||||
}
|
||||
|
||||
void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
|
||||
{
|
||||
s += "{";
|
||||
if (prettyIndent)
|
||||
s += "\n";
|
||||
|
||||
for (unsigned int i = 0; i < keys.size(); i++) {
|
||||
if (prettyIndent)
|
||||
s += indentStr(prettyIndent, indentLevel);
|
||||
s += "\"" + json_escape(keys[i]) + "\": ";
|
||||
s += values[i].write(prettyIndent, indentLevel + 1);
|
||||
if (i != (values.size() - 1))
|
||||
s += ",";
|
||||
if (prettyIndent)
|
||||
s += "\n";
|
||||
}
|
||||
|
||||
if (prettyIndent)
|
||||
s += indentStr(prettyIndent, indentLevel - 1);
|
||||
s += "}";
|
||||
}
|
||||
|
Loading…
Reference in a new issue