aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/Makefile.am23
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/bitcoin-tx.cpp597
-rw-r--r--src/core_io.h7
-rw-r--r--src/core_read.cpp25
-rw-r--r--src/core_write.cpp74
-rw-r--r--src/univalue/univalue.cpp233
-rw-r--r--src/univalue/univalue.h157
-rw-r--r--src/univalue/univalue_read.cpp390
-rw-r--r--src/univalue/univalue_write.cpp145
13 files changed, 1655 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 4169a2d96c..e21ea92552 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
src/bitcoin
src/bitcoind
src/bitcoin-cli
+src/bitcoin-tx
src/test/test_bitcoin
src/qt/test/test_bitcoin-qt
diff --git a/src/Makefile.am b/src/Makefile.am
index 4e59146424..2d84eeba1c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 = \
@@ -178,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 = \
@@ -229,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) \
@@ -267,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
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 2772bc753a..2052264dc9 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -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
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 51ce006fc1..d49b2240ea 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -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
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 12b90adca3..72451fba9a 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -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)
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
new file mode 100644
index 0000000000..299315424b
--- /dev/null
+++ b/src/bitcoin-tx.cpp
@@ -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;
+}
+
diff --git a/src/core_io.h b/src/core_io.h
index 528c472803..8a7d580570 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -3,14 +3,21 @@
#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__
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 1ecd6db324..0f06bb6952 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -4,12 +4,14 @@
#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;
@@ -100,3 +102,26 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx)
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);
+}
+
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 960974df89..2eb220779f 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -1,8 +1,12 @@
+#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;
@@ -13,3 +17,73 @@ string EncodeHexTx(const CTransaction& 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());
+}
+
diff --git a/src/univalue/univalue.cpp b/src/univalue/univalue.cpp
new file mode 100644
index 0000000000..e577aa8ee4
--- /dev/null
+++ b/src/univalue/univalue.cpp
@@ -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;
+}
+
diff --git a/src/univalue/univalue.h b/src/univalue/univalue.h
new file mode 100644
index 0000000000..5e94b6ba23
--- /dev/null
+++ b/src/univalue/univalue.h
@@ -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__
diff --git a/src/univalue/univalue_read.cpp b/src/univalue/univalue_read.cpp
new file mode 100644
index 0000000000..405be3e81f
--- /dev/null
+++ b/src/univalue/univalue_read.cpp
@@ -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;
+}
+
diff --git a/src/univalue/univalue_write.cpp b/src/univalue/univalue_write.cpp
new file mode 100644
index 0000000000..1818f5c6f9
--- /dev/null
+++ b/src/univalue/univalue_write.cpp
@@ -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 += "}";
+}
+