Browse Source

Merge branch 'rawtx' into merge-rawtx

0.10
Jeff Garzik 10 years ago
parent
commit
a715a643bc
Failed to extract signature
  1. 1
      .gitignore
  2. 26
      src/Makefile.am
  3. 2
      src/Makefile.qt.include
  4. 2
      src/Makefile.qttest.include
  5. 2
      src/Makefile.test.include
  6. 31
      src/bitcoin-cli.cpp
  7. 597
      src/bitcoin-tx.cpp
  8. 23
      src/core_io.h
  9. 127
      src/core_read.cpp
  10. 89
      src/core_write.cpp
  11. 5
      src/rpcmining.cpp
  12. 120
      src/rpcrawtransaction.cpp
  13. 5
      src/rpcwallet.cpp
  14. 71
      src/test/script_tests.cpp
  15. 2
      src/test/transaction_tests.cpp
  16. 233
      src/univalue/univalue.cpp
  17. 157
      src/univalue/univalue.h
  18. 390
      src/univalue/univalue_read.cpp
  19. 145
      src/univalue/univalue_write.cpp

1
.gitignore vendored

@ -4,6 +4,7 @@
src/bitcoin src/bitcoin
src/bitcoind src/bitcoind
src/bitcoin-cli src/bitcoin-cli
src/bitcoin-tx
src/test/test_bitcoin src/test/test_bitcoin
src/qt/test/test_bitcoin-qt src/qt/test/test_bitcoin-qt

26
src/Makefile.am

@ -34,6 +34,7 @@ LIBBITCOIN_COMMON=libbitcoin_common.a
LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_CLI=libbitcoin_cli.a
LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_UTIL=libbitcoin_util.a
LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a
LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a
LIBBITCOINQT=qt/libbitcoinqt.a LIBBITCOINQT=qt/libbitcoinqt.a
noinst_LIBRARIES = \ noinst_LIBRARIES = \
@ -41,6 +42,7 @@ noinst_LIBRARIES = \
libbitcoin_common.a \ libbitcoin_common.a \
libbitcoin_cli.a \ libbitcoin_cli.a \
libbitcoin_util.a \ libbitcoin_util.a \
univalue/libbitcoin_univalue.a \
crypto/libbitcoin_crypto.a crypto/libbitcoin_crypto.a
if ENABLE_WALLET if ENABLE_WALLET
BITCOIN_INCLUDES += $(BDB_CPPFLAGS) BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
@ -58,6 +60,8 @@ if BUILD_BITCOIN_CLI
bin_PROGRAMS += bitcoin-cli bin_PROGRAMS += bitcoin-cli
endif endif
bin_PROGRAMS += bitcoin-tx
.PHONY: FORCE .PHONY: FORCE
# bitcoin core # # bitcoin core #
BITCOIN_CORE_H = \ BITCOIN_CORE_H = \
@ -75,6 +79,7 @@ BITCOIN_CORE_H = \
coins.h \ coins.h \
compat.h \ compat.h \
core.h \ core.h \
core_io.h \
crypter.h \ crypter.h \
db.h \ db.h \
hash.h \ hash.h \
@ -177,6 +182,13 @@ crypto_libbitcoin_crypto_a_SOURCES = \
crypto/sha1.h \ crypto/sha1.h \
crypto/ripemd160.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 # common: shared between bitcoind, and bitcoin-qt and non-server tools
libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_common_a_SOURCES = \ libbitcoin_common_a_SOURCES = \
@ -185,6 +197,8 @@ libbitcoin_common_a_SOURCES = \
chainparams.cpp \ chainparams.cpp \
coins.cpp \ coins.cpp \
core.cpp \ core.cpp \
core_read.cpp \
core_write.cpp \
hash.cpp \ hash.cpp \
key.cpp \ key.cpp \
keystore.cpp \ keystore.cpp \
@ -226,6 +240,7 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
bitcoind_LDADD = \ bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UNIVALUE) \
$(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \ $(LIBBITCOIN_CRYPTO) \
$(LIBLEVELDB) \ $(LIBLEVELDB) \
@ -264,6 +279,17 @@ endif
bitcoin_cli_CPPFLAGS = $(BITCOIN_INCLUDES) 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 if TARGET_WINDOWS
bitcoin_cli_SOURCES += bitcoin-cli-res.rc bitcoin_cli_SOURCES += bitcoin-cli-res.rc
endif endif

2
src/Makefile.qt.include

@ -359,7 +359,7 @@ qt_bitcoin_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
if ENABLE_WALLET if ENABLE_WALLET
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
endif 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) $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS)
if USE_LIBSECP256K1 if USE_LIBSECP256K1
qt_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la qt_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la

2
src/Makefile.qttest.include

@ -30,7 +30,7 @@ qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER)
if ENABLE_WALLET if ENABLE_WALLET
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
endif 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) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
$(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS)
if USE_LIBSECP256K1 if USE_LIBSECP256K1

2
src/Makefile.test.include

@ -64,7 +64,7 @@ endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) 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_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) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB)
if ENABLE_WALLET if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)

31
src/bitcoin-cli.cpp

@ -50,8 +50,7 @@ static bool AppInitRPC(int argc, char* argv[])
// Parameters // Parameters
// //
ParseParameters(argc, argv); 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()); fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
return false; return false;
} }
@ -66,11 +65,9 @@ static bool AppInitRPC(int argc, char* argv[])
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
return false; 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"; std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n";
if (!mapArgs.count("-version")) if (!mapArgs.count("-version")) {
{
strUsage += "\n" + _("Usage:") + "\n" + strUsage += "\n" + _("Usage:") + "\n" +
" bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" + " bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" +
" bitcoin-cli [options] help " + _("List commands") + "\n" + " bitcoin-cli [options] help " + _("List commands") + "\n" +
@ -153,11 +150,9 @@ int CommandLineRPC(int argc, char *argv[])
{ {
string strPrint; string strPrint;
int nRet = 0; int nRet = 0;
try try {
{
// Skip switches // Skip switches
while (argc > 1 && IsSwitchChar(argv[1][0])) while (argc > 1 && IsSwitchChar(argv[1][0])) {
{
argc--; argc--;
argv++; argv++;
} }
@ -178,15 +173,12 @@ int CommandLineRPC(int argc, char *argv[])
const Value& result = find_value(reply, "result"); const Value& result = find_value(reply, "result");
const Value& error = find_value(reply, "error"); const Value& error = find_value(reply, "error");
if (error.type() != null_type) if (error.type() != null_type) {
{
// Error // Error
strPrint = "error: " + write_string(error, false); strPrint = "error: " + write_string(error, false);
int code = find_value(error.get_obj(), "code").get_int(); int code = find_value(error.get_obj(), "code").get_int();
nRet = abs(code); nRet = abs(code);
} } else {
else
{
// Result // Result
if (result.type() == null_type) if (result.type() == null_type)
strPrint = ""; strPrint = "";
@ -208,8 +200,7 @@ int CommandLineRPC(int argc, char *argv[])
throw; throw;
} }
if (strPrint != "") if (strPrint != "") {
{
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
} }
return nRet; return nRet;
@ -219,8 +210,7 @@ int main(int argc, char* argv[])
{ {
SetupEnvironment(); SetupEnvironment();
try try {
{
if(!AppInitRPC(argc, argv)) if(!AppInitRPC(argc, argv))
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -233,8 +223,7 @@ int main(int argc, char* argv[])
} }
int ret = EXIT_FAILURE; int ret = EXIT_FAILURE;
try try {
{
ret = CommandLineRPC(argc, argv); ret = CommandLineRPC(argc, argv);
} }
catch (std::exception& e) { catch (std::exception& e) {

597
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;
}

23
src/core_io.h

@ -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

@ -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

@ -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());
}

5
src/rpcmining.cpp

@ -10,6 +10,7 @@
#include "main.h" #include "main.h"
#include "miner.h" #include "miner.h"
#include "pow.h" #include "pow.h"
#include "core_io.h"
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
#include "db.h" #include "db.h"
#include "wallet.h" #include "wallet.h"
@ -472,9 +473,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
Object entry; Object entry;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); entry.push_back(Pair("data", EncodeHexTx(tx)));
ssTx << tx;
entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end())));
entry.push_back(Pair("hash", txHash.GetHex())); entry.push_back(Pair("hash", txHash.GetHex()));

120
src/rpcrawtransaction.cpp

@ -5,6 +5,7 @@
#include "base58.h" #include "base58.h"
#include "core.h" #include "core.h"
#include "core_io.h"
#include "init.h" #include "init.h"
#include "keystore.h" #include "keystore.h"
#include "main.h" #include "main.h"
@ -36,8 +37,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH
if (fIncludeHex) if (fIncludeHex)
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); 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))); out.push_back(Pair("type", GetTxnOutputType(type)));
return; 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("version", tx.nVersion));
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
Array vin; Array vin;
BOOST_FOREACH(const CTxIn& txin, tx.vin) BOOST_FOREACH(const CTxIn& txin, tx.vin) {
{
Object in; Object in;
if (tx.IsCoinBase()) if (tx.IsCoinBase())
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); 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("txid", txin.prevout.hash.GetHex()));
in.push_back(Pair("vout", (int64_t)txin.prevout.n)); in.push_back(Pair("vout", (int64_t)txin.prevout.n));
Object o; Object o;
@ -76,8 +74,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
} }
entry.push_back(Pair("vin", vin)); entry.push_back(Pair("vin", vin));
Array vout; 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]; const CTxOut& txout = tx.vout[i];
Object out; Object out;
out.push_back(Pair("value", ValueFromAmount(txout.nValue))); 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)); entry.push_back(Pair("vout", vout));
if (hashBlock != 0) if (hashBlock != 0) {
{
entry.push_back(Pair("blockhash", hashBlock.GetHex())); entry.push_back(Pair("blockhash", hashBlock.GetHex()));
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
if (mi != mapBlockIndex.end() && (*mi).second) if (mi != mapBlockIndex.end() && (*mi).second) {
{
CBlockIndex* pindex = (*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("confirmations", 1 + chainActive.Height() - pindex->nHeight));
entry.push_back(Pair("time", pindex->GetBlockTime())); entry.push_back(Pair("time", pindex->GetBlockTime()));
entry.push_back(Pair("blocktime", 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)) if (!GetTransaction(hash, tx, hashBlock, true))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); string strHex = EncodeHexTx(tx);
ssTx << tx;
string strHex = HexStr(ssTx.begin(), ssTx.end());
if (!fVerbose) if (!fVerbose)
return strHex; return strHex;
@ -245,11 +237,9 @@ Value listunspent(const Array& params, bool fHelp)
nMaxDepth = params[1].get_int(); nMaxDepth = params[1].get_int();
set<CBitcoinAddress> setAddress; set<CBitcoinAddress> setAddress;
if (params.size() > 2) if (params.size() > 2) {
{
Array inputs = params[2].get_array(); Array inputs = params[2].get_array();
BOOST_FOREACH(Value& input, inputs) BOOST_FOREACH(Value& input, inputs) {
{
CBitcoinAddress address(input.get_str()); CBitcoinAddress address(input.get_str());
if (!address.IsValid()) if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str()); 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; vector<COutput> vecOutputs;
assert(pwalletMain != NULL); assert(pwalletMain != NULL);
pwalletMain->AvailableCoins(vecOutputs, false); pwalletMain->AvailableCoins(vecOutputs, false);
BOOST_FOREACH(const COutput& out, vecOutputs) BOOST_FOREACH(const COutput& out, vecOutputs) {
{
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue; continue;
if (setAddress.size()) if (setAddress.size()) {
{
CTxDestination address; CTxDestination address;
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
continue; 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("txid", out.tx->GetHash().GetHex()));
entry.push_back(Pair("vout", out.i)); entry.push_back(Pair("vout", out.i));
CTxDestination address; 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())); entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
if (pwalletMain->mapAddressBook.count(address)) if (pwalletMain->mapAddressBook.count(address))
entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name));
} }
entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
if (pk.IsPayToScriptHash()) if (pk.IsPayToScriptHash()) {
{
CTxDestination address; CTxDestination address;
if (ExtractDestination(pk, address)) if (ExtractDestination(pk, address)) {
{
const CScriptID& hash = boost::get<const CScriptID&>(address); const CScriptID& hash = boost::get<const CScriptID&>(address);
CScript redeemScript; CScript redeemScript;
if (pwalletMain->GetCScript(hash, redeemScript)) if (pwalletMain->GetCScript(hash, redeemScript))
@ -352,8 +337,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
CMutableTransaction rawTx; CMutableTransaction rawTx;
BOOST_FOREACH(const Value& input, inputs) BOOST_FOREACH(const Value& input, inputs) {
{
const Object& o = input.get_obj(); const Object& o = input.get_obj();
uint256 txid = ParseHashO(o, "txid"); uint256 txid = ParseHashO(o, "txid");
@ -370,8 +354,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
} }
set<CBitcoinAddress> setAddress; set<CBitcoinAddress> setAddress;
BOOST_FOREACH(const Pair& s, sendTo) BOOST_FOREACH(const Pair& s, sendTo) {
{
CBitcoinAddress address(s.name_); CBitcoinAddress address(s.name_);
if (!address.IsValid()) if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); 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); rawTx.vout.push_back(out);
} }
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); return EncodeHexTx(rawTx);
ss << rawTx;
return HexStr(ss.begin(), ss.end());
} }
Value decoderawtransaction(const Array& params, bool fHelp) Value decoderawtransaction(const Array& params, bool fHelp)
@ -444,15 +425,12 @@ Value decoderawtransaction(const Array& params, bool fHelp)
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"") + HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
); );
vector<unsigned char> txData(ParseHexV(params[0], "argument")); RPCTypeCheck(params, list_of(str_type));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx; CTransaction tx;
try {
ssData >> tx; if (!DecodeHexTx(tx, params[0].get_str()))
}
catch (std::exception &e) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
Object result; Object result;
TxToJSON(tx, 0, 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")); vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
vector<CMutableTransaction> txVariants; vector<CMutableTransaction> txVariants;
while (!ssData.empty()) while (!ssData.empty()) {
{
try { try {
CMutableTransaction tx; CMutableTransaction tx;
ssData >> tx; ssData >> tx;
@ -596,12 +573,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
bool fGivenKeys = false; bool fGivenKeys = false;
CBasicKeyStore tempKeystore; CBasicKeyStore tempKeystore;
if (params.size() > 2 && params[2].type() != null_type) if (params.size() > 2 && params[2].type() != null_type) {
{
fGivenKeys = true; fGivenKeys = true;
Array keys = params[2].get_array(); Array keys = params[2].get_array();
BOOST_FOREACH(Value k, keys) BOOST_FOREACH(Value k, keys) {
{
CBitcoinSecret vchSecret; CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(k.get_str()); bool fGood = vchSecret.SetString(k.get_str());
if (!fGood) if (!fGood)
@ -616,11 +591,9 @@ Value signrawtransaction(const Array& params, bool fHelp)
#endif #endif
// Add previous txouts given in the RPC call: // 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(); Array prevTxs = params[1].get_array();
BOOST_FOREACH(Value& p, prevTxs) BOOST_FOREACH(Value& p, prevTxs) {
{
if (p.type() != obj_type) if (p.type() != obj_type)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); 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 // if redeemScript given and not using the local wallet (private keys
// given), add redeemScript to the tempKeystore so it can be signed: // 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)); RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
Value v = find_value(prevOut, "redeemScript"); Value v = find_value(prevOut, "redeemScript");
if (!(v == Value::null)) if (!(v == Value::null)) {
{
vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end()); CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript); tempKeystore.AddCScript(redeemScript);
@ -676,8 +647,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
#endif #endif
int nHashType = SIGHASH_ALL; 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 = static map<string, int> mapSigHashValues =
boost::assign::map_list_of boost::assign::map_list_of
(string("ALL"), int(SIGHASH_ALL)) (string("ALL"), int(SIGHASH_ALL))
@ -697,12 +667,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Sign what we can: // 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]; CTxIn& txin = mergedTx.vin[i];
CCoins coins; 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; fComplete = false;
continue; continue;
} }
@ -714,8 +682,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
// ... and merge in other signatures: // ... 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); txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
} }
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STANDARD_SCRIPT_VERIFY_FLAGS, 0)) 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; Object result;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
ssTx << mergedTx;
result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
result.push_back(Pair("complete", fComplete)); result.push_back(Pair("complete", fComplete));
return result; return result;
@ -754,25 +719,18 @@ Value sendrawtransaction(const Array& params, bool fHelp)
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"") + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
); );
RPCTypeCheck(params, list_of(str_type)(bool_type));
// parse hex string from parameter // parse hex string from parameter
vector<unsigned char> txData(ParseHexV(params[0], "parameter"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx; CTransaction tx;
if (!DecodeHexTx(tx, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
uint256 hashTx = tx.GetHash();
bool fOverrideFees = false; bool fOverrideFees = false;
if (params.size() > 1) if (params.size() > 1)
fOverrideFees = params[1].get_bool(); 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; CCoinsViewCache &view = *pcoinsTip;
CCoins existingCoins; CCoins existingCoins;
bool fHaveMempool = mempool.exists(hashTx); bool fHaveMempool = mempool.exists(hashTx);

5
src/rpcwallet.cpp

@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h" #include "base58.h"
#include "core_io.h"
#include "rpcserver.h" #include "rpcserver.h"
#include "init.h" #include "init.h"
#include "net.h" #include "net.h"
@ -1550,9 +1551,7 @@ Value gettransaction(const Array& params, bool fHelp)
ListTransactions(wtx, "*", 0, false, details, filter); ListTransactions(wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details)); entry.push_back(Pair("details", details));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
ssTx << static_cast<CTransaction>(wtx);
string strHex = HexStr(ssTx.begin(), ssTx.end());
entry.push_back(Pair("hex", strHex)); entry.push_back(Pair("hex", strHex));
return entry; return entry;

71
src/test/script_tests.cpp

@ -10,6 +10,7 @@
#include "key.h" #include "key.h"
#include "keystore.h" #include "keystore.h"
#include "main.h" #include "main.h"
#include "core_io.h"
#include <fstream> #include <fstream>
#include <stdint.h> #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; 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 Array
read_json(const std::string& jsondata) read_json(const std::string& jsondata)
{ {

2
src/test/transaction_tests.cpp

@ -9,6 +9,7 @@
#include "keystore.h" #include "keystore.h"
#include "main.h" #include "main.h"
#include "script.h" #include "script.h"
#include "core_io.h"
#include <map> #include <map>
#include <string> #include <string>
@ -24,7 +25,6 @@ using namespace boost::algorithm;
// In script_tests.cpp // In script_tests.cpp
extern Array read_json(const std::string& jsondata); extern Array read_json(const std::string& jsondata);
extern CScript ParseScript(string s);
unsigned int ParseFlags(string strFlags){ unsigned int ParseFlags(string strFlags){
unsigned int flags = 0; unsigned int flags = 0;

233
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;
}

157
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__

390
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;
}

145
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 += "}";
}
Loading…
Cancel
Save