aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am26
-rw-r--r--src/Makefile.qt.include3
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/allocators.h2
-rw-r--r--src/bitcoin-cli.cpp31
-rw-r--r--src/bitcoin-tx.cpp597
-rw-r--r--src/checkpoints.cpp5
-rw-r--r--src/core_io.h23
-rw-r--r--src/core_read.cpp127
-rw-r--r--src/core_write.cpp89
-rw-r--r--src/init.cpp11
-rw-r--r--src/main.cpp66
-rw-r--r--src/main.h1
-rw-r--r--src/net.cpp35
-rw-r--r--src/net.h1
-rw-r--r--src/netbase.cpp70
-rw-r--r--src/netbase.h2
-rw-r--r--src/qt/bitcoinamountfield.cpp272
-rw-r--r--src/qt/bitcoinamountfield.h14
-rw-r--r--src/qt/bitcoinunits.cpp29
-rw-r--r--src/qt/bitcoinunits.h7
-rw-r--r--src/qt/optionsdialog.cpp4
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/qt/sendcoinsentry.cpp18
-rw-r--r--src/qt/transactiondesc.cpp2
-rw-r--r--src/qt/transactiontablemodel.cpp2
-rw-r--r--src/qt/transactiontablemodel.h4
-rw-r--r--src/rpcclient.cpp2
-rw-r--r--src/rpcdump.cpp26
-rw-r--r--src/rpcmining.cpp28
-rw-r--r--src/rpcmisc.cpp13
-rw-r--r--src/rpcrawtransaction.cpp120
-rw-r--r--src/rpcwallet.cpp5
-rw-r--r--src/test/script_tests.cpp71
-rw-r--r--src/test/transaction_tests.cpp2
-rw-r--r--src/timedata.cpp18
-rw-r--r--src/txmempool.cpp7
-rw-r--r--src/univalue/univalue.cpp211
-rw-r--r--src/univalue/univalue.h155
-rw-r--r--src/univalue/univalue_read.cpp390
-rw-r--r--src/univalue/univalue_write.cpp145
-rw-r--r--src/version.h4
43 files changed, 2226 insertions, 417 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ff23747592..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 = \
@@ -75,6 +79,7 @@ BITCOIN_CORE_H = \
coins.h \
compat.h \
core.h \
+ core_io.h \
crypter.h \
db.h \
hash.h \
@@ -177,6 +182,13 @@ crypto_libbitcoin_crypto_a_SOURCES = \
crypto/sha1.h \
crypto/ripemd160.h
+# univalue JSON library
+univalue_libbitcoin_univalue_a_SOURCES = \
+ univalue/univalue.cpp \
+ univalue/univalue_read.cpp \
+ univalue/univalue_write.cpp \
+ univalue/univalue.h
+
# common: shared between bitcoind, and bitcoin-qt and non-server tools
libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_common_a_SOURCES = \
@@ -185,6 +197,8 @@ libbitcoin_common_a_SOURCES = \
chainparams.cpp \
coins.cpp \
core.cpp \
+ core_read.cpp \
+ core_write.cpp \
hash.cpp \
key.cpp \
keystore.cpp \
@@ -226,6 +240,7 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_UNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBLEVELDB) \
@@ -264,6 +279,17 @@ endif
bitcoin_cli_CPPFLAGS = $(BITCOIN_INCLUDES)
#
+# bitcoin-tx binary #
+bitcoin_tx_LDADD = \
+ $(LIBBITCOIN_UNIVALUE) \
+ $(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_CRYPTO) \
+ $(BOOST_LIBS)
+bitcoin_tx_SOURCES = bitcoin-tx.cpp
+bitcoin_tx_CPPFLAGS = $(BITCOIN_INCLUDES)
+#
+
if TARGET_WINDOWS
bitcoin_cli_SOURCES += bitcoin-cli-res.rc
endif
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 75b7b683dd..2052264dc9 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -145,6 +145,7 @@ BITCOIN_MM = \
QT_MOC = \
qt/bitcoin.moc \
+ qt/bitcoinamountfield.moc \
qt/intro.moc \
qt/overviewpage.moc \
qt/rpcconsole.moc
@@ -358,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/allocators.h b/src/allocators.h
index 7012ef7e2a..be0be7ab96 100644
--- a/src/allocators.h
+++ b/src/allocators.h
@@ -131,7 +131,7 @@ public:
* Due to the unpredictable order of static initializers, we have to make sure the
* LockedPageManager instance exists before any other STL-based objects that use
* secure_allocator are created. So instead of having LockedPageManager also be
- * static-intialized, it is created on demand.
+ * static-initialized, it is created on demand.
*/
class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker>
{
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 016b2f50f5..3b991f9276 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -50,8 +50,7 @@ static bool AppInitRPC(int argc, char* argv[])
// Parameters
//
ParseParameters(argc, argv);
- if (!boost::filesystem::is_directory(GetDataDir(false)))
- {
+ if (!boost::filesystem::is_directory(GetDataDir(false))) {
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
return false;
}
@@ -66,11 +65,9 @@ static bool AppInitRPC(int argc, char* argv[])
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
return false;
}
- if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version"))
- {
+ if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n";
- if (!mapArgs.count("-version"))
- {
+ if (!mapArgs.count("-version")) {
strUsage += "\n" + _("Usage:") + "\n" +
" bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" +
" bitcoin-cli [options] help " + _("List commands") + "\n" +
@@ -153,11 +150,9 @@ int CommandLineRPC(int argc, char *argv[])
{
string strPrint;
int nRet = 0;
- try
- {
+ try {
// Skip switches
- while (argc > 1 && IsSwitchChar(argv[1][0]))
- {
+ while (argc > 1 && IsSwitchChar(argv[1][0])) {
argc--;
argv++;
}
@@ -178,15 +173,12 @@ int CommandLineRPC(int argc, char *argv[])
const Value& result = find_value(reply, "result");
const Value& error = find_value(reply, "error");
- if (error.type() != null_type)
- {
+ if (error.type() != null_type) {
// Error
strPrint = "error: " + write_string(error, false);
int code = find_value(error.get_obj(), "code").get_int();
nRet = abs(code);
- }
- else
- {
+ } else {
// Result
if (result.type() == null_type)
strPrint = "";
@@ -208,8 +200,7 @@ int CommandLineRPC(int argc, char *argv[])
throw;
}
- if (strPrint != "")
- {
+ if (strPrint != "") {
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
}
return nRet;
@@ -219,8 +210,7 @@ int main(int argc, char* argv[])
{
SetupEnvironment();
- try
- {
+ try {
if(!AppInitRPC(argc, argv))
return EXIT_FAILURE;
}
@@ -233,8 +223,7 @@ int main(int argc, char* argv[])
}
int ret = EXIT_FAILURE;
- try
- {
+ try {
ret = CommandLineRPC(argc, argv);
}
catch (std::exception& e) {
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/checkpoints.cpp b/src/checkpoints.cpp
index 4cab11db3d..717f0b90fe 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -51,11 +51,12 @@ namespace Checkpoints {
(225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"))
(250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"))
(279000, uint256("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"))
+ (295000, uint256("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983"))
;
static const CCheckpointData data = {
&mapCheckpoints,
- 1389047471, // * UNIX timestamp of last checkpoint block
- 30549816, // * total number of transactions between genesis and last checkpoint
+ 1397080064, // * UNIX timestamp of last checkpoint block
+ 36544669, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the SetBestChain debug.log lines)
60000.0 // * estimated number of transactions per day after checkpoint
};
diff --git a/src/core_io.h b/src/core_io.h
new file mode 100644
index 0000000000..8a7d580570
--- /dev/null
+++ b/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__
diff --git a/src/core_read.cpp b/src/core_read.cpp
new file mode 100644
index 0000000000..0f06bb6952
--- /dev/null
+++ b/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);
+}
+
diff --git a/src/core_write.cpp b/src/core_write.cpp
new file mode 100644
index 0000000000..2eb220779f
--- /dev/null
+++ b/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());
+}
+
diff --git a/src/init.cpp b/src/init.cpp
index 3488a8bedf..b84c233b99 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -240,7 +240,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n";
strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n";
strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + _("(default: 1)") + "\n";
- strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n";
+ strUsage += " -dnsseed " + _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)") + "\n";
+ strUsage += " -forcednsseed " + _("Always query for peer addresses via DNS lookup (default: 0)") + "\n";
strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n";
strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n";
strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n";
@@ -262,6 +263,7 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n";
strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n";
+ strUsage += " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway") + "\n";
#ifdef ENABLE_WALLET
strUsage += "\n" + _("Wallet options:") + "\n";
@@ -285,7 +287,6 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += "\n" + _("Debugging/Testing options:") + "\n";
if (GetBoolArg("-help-debug", false))
{
- strUsage += " -benchmark " + _("Show benchmark information (default: 0)") + "\n";
strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n";
strUsage += " -dblogsize=<n> " + _("Flush database activity from memory pool to disk log every <n> megabytes (default: 100)") + "\n";
strUsage += " -disablesafemode " + _("Disable safemode, override a real safe mode event (default: 0)") + "\n";
@@ -298,7 +299,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -debug=<category> " + _("Output debugging information (default: 0, supplying <category> is optional)") + "\n";
strUsage += " " + _("If <category> is not supplied, output all debugging information.") + "\n";
strUsage += " " + _("<category> can be:");
- strUsage += " addrman, alert, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below
+ strUsage += " addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT)
strUsage += ", qt";
strUsage += ".\n";
@@ -599,7 +600,9 @@ bool AppInit2(boost::thread_group& threadGroup)
if (GetBoolArg("-tor", false))
return InitError(_("Error: Unsupported argument -tor found, use -onion."));
- fBenchmark = GetBoolArg("-benchmark", false);
+ if (GetBoolArg("-benchmark", false))
+ InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench."));
+
// Checkmempool defaults to true in regtest mode
mempool.setSanityCheck(GetBoolArg("-checkmempool", Params().DefaultCheckMemPool()));
Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
diff --git a/src/main.cpp b/src/main.cpp
index 84178b16e2..353cde0bd7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -45,7 +45,6 @@ CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
-bool fBenchmark = false;
bool fTxIndex = false;
bool fIsBareMultisigStd = true;
unsigned int nCoinCacheSize = 5000;
@@ -96,7 +95,9 @@ namespace {
};
CBlockIndex *pindexBestInvalid;
- // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed
+
+ // The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS or better that are at least
+ // as good as our current tip. Entries may be failed, though.
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid;
CCriticalSection cs_LastBlockFile;
@@ -1680,6 +1681,12 @@ void ThreadScriptCheck() {
scriptcheckqueue.Thread();
}
+static int64_t nTimeVerify = 0;
+static int64_t nTimeConnect = 0;
+static int64_t nTimeIndex = 0;
+static int64_t nTimeCallbacks = 0;
+static int64_t nTimeTotal = 0;
+
bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
{
AssertLockHeld(cs_main);
@@ -1735,7 +1742,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
- int64_t nStart = GetTimeMicros();
+ int64_t nTimeStart = GetTimeMicros();
int64_t nFees = 0;
int nInputs = 0;
unsigned int nSigOps = 0;
@@ -1785,9 +1792,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
vPos.push_back(std::make_pair(tx.GetHash(), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
- int64_t nTime = GetTimeMicros() - nStart;
- if (fBenchmark)
- LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1));
+ int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
+ LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
return state.DoS(100,
@@ -1797,9 +1803,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
if (!control.Wait())
return state.DoS(100, false);
- int64_t nTime2 = GetTimeMicros() - nStart;
- if (fBenchmark)
- LogPrintf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
+ int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart;
+ LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001);
if (fJustCheck)
return true;
@@ -1840,6 +1845,9 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
ret = view.SetBestBlock(pindex->GetBlockHash());
assert(ret);
+ int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2;
+ LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001);
+
// Watch for transactions paying to me
BOOST_FOREACH(const CTransaction& tx, block.vtx)
g_signals.SyncTransaction(tx, &block);
@@ -1849,6 +1857,9 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
g_signals.UpdatedTransaction(hashPrevBestCoinBase);
hashPrevBestCoinBase = block.vtx[0].GetHash();
+ int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3;
+ LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001);
+
return true;
}
@@ -1928,8 +1939,7 @@ bool static DisconnectTip(CValidationState &state) {
return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
assert(view.Flush());
}
- if (fBenchmark)
- LogPrintf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
+ LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.
if (!WriteChainState(state))
return false;
@@ -1953,16 +1963,25 @@ bool static DisconnectTip(CValidationState &state) {
return true;
}
+static int64_t nTimeReadFromDisk = 0;
+static int64_t nTimeConnectTotal = 0;
+static int64_t nTimeFlush = 0;
+static int64_t nTimeChainState = 0;
+static int64_t nTimePostConnect = 0;
+
// Connect a new block to chainActive.
bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) {
assert(pindexNew->pprev == chainActive.Tip());
mempool.check(pcoinsTip);
// Read block from disk.
+ int64_t nTime1 = GetTimeMicros();
CBlock block;
if (!ReadBlockFromDisk(block, pindexNew))
return state.Abort(_("Failed to read block"));
// Apply the block atomically to the chain state.
- int64_t nStart = GetTimeMicros();
+ int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
+ int64_t nTime3;
+ LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
{
CCoinsViewCache view(*pcoinsTip, true);
CInv inv(MSG_BLOCK, pindexNew->GetBlockHash());
@@ -1972,13 +1991,17 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) {
return error("ConnectTip() : ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}
mapBlockSource.erase(inv.hash);
+ nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
+ LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
assert(view.Flush());
}
- if (fBenchmark)
- LogPrintf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
+ int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
+ LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
// Write the chain state to disk, if necessary.
if (!WriteChainState(state))
return false;
+ int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
+ LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool.
list<CTransaction> txConflicted;
mempool.removeForBlock(block.vtx, pindexNew->nHeight, txConflicted);
@@ -1994,6 +2017,9 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) {
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
SyncWithWallets(tx, &block);
}
+ int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
+ LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
+ LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
return true;
}
@@ -2016,7 +2042,7 @@ static CBlockIndex* FindMostWorkChain() {
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !chainActive.Contains(pindexTest)) {
- if (!pindexTest->IsValid(BLOCK_VALID_TRANSACTIONS) || !(pindexTest->nStatus & BLOCK_HAVE_DATA)) {
+ if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
// Candidate has an invalid ancestor, remove entire chain from the set.
if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
@@ -2026,6 +2052,7 @@ static CBlockIndex* FindMostWorkChain() {
setBlockIndexValid.erase(pindexFailed);
pindexFailed = pindexFailed->pprev;
}
+ setBlockIndexValid.erase(pindexTest);
fInvalidAncestor = true;
break;
}
@@ -2072,6 +2099,15 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
return false;
}
} else {
+ // Delete all entries in setBlockIndexValid that are worse than our new current block.
+ // Note that we can't delete the current block itself, as we may need to return to it later in case a
+ // reorganization to a better block fails.
+ std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexValid.begin();
+ while (setBlockIndexValid.value_comp()(*it, chainActive.Tip())) {
+ setBlockIndexValid.erase(it++);
+ }
+ // Either the current tip or a successor of it we're working towards is left in setBlockIndexValid.
+ assert(!setBlockIndexValid.empty());
if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
// We're in a better position than we were. Return temporarily to release the lock.
break;
diff --git a/src/main.h b/src/main.h
index 5f231fa45b..48ec86f6a8 100644
--- a/src/main.h
+++ b/src/main.h
@@ -91,7 +91,6 @@ extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange;
extern bool fImporting;
extern bool fReindex;
-extern bool fBenchmark;
extern int nScriptCheckThreads;
extern bool fTxIndex;
extern bool fIsBareMultisigStd;
diff --git a/src/net.cpp b/src/net.cpp
index b55cd72e86..62124514c8 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -456,7 +456,7 @@ CNode* FindNode(const CNetAddr& ip)
return NULL;
}
-CNode* FindNode(std::string addrName)
+CNode* FindNode(const std::string& addrName)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -501,14 +501,8 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
addrman.Attempt(addrConnect);
// Set to non-blocking
-#ifdef WIN32
- u_long nOne = 1;
- if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR)
- LogPrintf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %s\n", NetworkErrorString(WSAGetLastError()));
-#else
- if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
- LogPrintf("ConnectSocket() : fcntl non-blocking setting failed, error %s\n", NetworkErrorString(errno));
-#endif
+ if (!SetSocketNonBlocking(hSocket, true))
+ LogPrintf("ConnectNode: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
// Add node
CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
@@ -1227,6 +1221,18 @@ void MapPort(bool)
void ThreadDNSAddressSeed()
{
+ // goal: only query DNS seeds if address need is acute
+ if ((addrman.size() > 0) &&
+ (!GetBoolArg("-forcednsseed", false))) {
+ MilliSleep(11 * 1000);
+
+ LOCK(cs_vNodes);
+ if (vNodes.size() >= 2) {
+ LogPrintf("P2P peers available. Skipped DNS seeding.\n");
+ return;
+ }
+ }
+
const vector<CDNSSeedData> &vSeeds = Params().DNSSeeds();
int found = 0;
@@ -1478,7 +1484,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
if (!pszDest) {
if (IsLocal(addrConnect) ||
FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
- FindNode(addrConnect.ToStringIPPort().c_str()))
+ FindNode(addrConnect.ToStringIPPort()))
return false;
} else if (FindNode(pszDest))
return false;
@@ -1642,14 +1648,9 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
#endif
-#ifdef WIN32
// Set to non-blocking, incoming connections will also inherit this
- if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
-#else
- if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
-#endif
- {
- strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %s)", NetworkErrorString(WSAGetLastError()));
+ if (!SetSocketNonBlocking(hListenSocket, true)) {
+ strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
return false;
}
diff --git a/src/net.h b/src/net.h
index f029a8d935..2d9325abf9 100644
--- a/src/net.h
+++ b/src/net.h
@@ -59,6 +59,7 @@ bool RecvLine(SOCKET hSocket, std::string& strLine);
bool GetMyExternalIP(CNetAddr& ipRet);
void AddressCurrentlyConnected(const CService& addr);
CNode* FindNode(const CNetAddr& ip);
+CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& ip);
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
diff --git a/src/netbase.cpp b/src/netbase.cpp
index e9f3515456..af6d11f0e2 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -7,10 +7,6 @@
#include "bitcoin-config.h"
#endif
-#ifdef HAVE_GETADDRINFO_A
-#include <netdb.h>
-#endif
-
#include "netbase.h"
#include "hash.h"
@@ -18,6 +14,10 @@
#include "uint256.h"
#include "util.h"
+#ifdef HAVE_GETADDRINFO_A
+#include <netdb.h>
+#endif
+
#ifndef WIN32
#if HAVE_INET_PTON
#include <arpa/inet.h>
@@ -331,22 +331,15 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == INVALID_SOCKET)
return false;
+
#ifdef SO_NOSIGPIPE
int set = 1;
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
#endif
-#ifdef WIN32
- u_long fNonblock = 1;
- if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
-#else
- int fFlags = fcntl(hSocket, F_GETFL, 0);
- if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1)
-#endif
- {
- CloseSocket(hSocket);
- return false;
- }
+ // Set to non-blocking
+ if (!SetSocketNonBlocking(hSocket, true))
+ return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
@@ -404,20 +397,10 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
}
}
- // this isn't even strictly necessary
- // CNode::ConnectNode immediately turns the socket back to non-blocking
- // but we'll turn it back to blocking just in case
-#ifdef WIN32
- fNonblock = 0;
- if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
-#else
- fFlags = fcntl(hSocket, F_GETFL, 0);
- if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR)
-#endif
- {
- CloseSocket(hSocket);
- return false;
- }
+ // This is required when using SOCKS5 proxy!
+ // CNode::ConnectNode turns the socket back to non-blocking.
+ if (!SetSocketNonBlocking(hSocket, false))
+ return error("ConnectSocketDirectly: Setting socket to blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
hSocketRet = hSocket;
return true;
@@ -1271,3 +1254,32 @@ bool CloseSocket(SOCKET& hSocket)
hSocket = INVALID_SOCKET;
return ret != SOCKET_ERROR;
}
+
+bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking)
+{
+ if (fNonBlocking) {
+#ifdef WIN32
+ u_long nOne = 1;
+ if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) {
+#else
+ int fFlags = fcntl(hSocket, F_GETFL, 0);
+ if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) {
+#endif
+ CloseSocket(hSocket);
+ return false;
+ }
+ } else {
+#ifdef WIN32
+ u_long nZero = 0;
+ if (ioctlsocket(hSocket, FIONBIO, &nZero) == SOCKET_ERROR) {
+#else
+ int fFlags = fcntl(hSocket, F_GETFL, 0);
+ if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) {
+#endif
+ CloseSocket(hSocket);
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/netbase.h b/src/netbase.h
index 05221a5fde..7d83e35344 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -180,5 +180,7 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest
std::string NetworkErrorString(int err);
/** Close socket and set hSocket to INVALID_SOCKET */
bool CloseSocket(SOCKET& hSocket);
+/** Disable or enable blocking-mode for a socket */
+bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking);
#endif
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index e047c278b7..6466039013 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -9,63 +9,185 @@
#include "qvaluecombobox.h"
#include <QApplication>
-#include <QDoubleSpinBox>
+#include <QAbstractSpinBox>
#include <QHBoxLayout>
#include <QKeyEvent>
-#include <qmath.h> // for qPow()
+#include <QLineEdit>
-// QDoubleSpinBox that shows SI-style thin space thousands separators
-class AmountSpinBox: public QDoubleSpinBox
+/** QSpinBox that uses fixed-point numbers internally and uses our own
+ * formatting/parsing functions.
+ */
+class AmountSpinBox: public QAbstractSpinBox
{
+ Q_OBJECT
public:
explicit AmountSpinBox(QWidget *parent):
- QDoubleSpinBox(parent)
+ QAbstractSpinBox(parent),
+ currentUnit(BitcoinUnits::BTC),
+ singleStep(100000) // satoshis
{
+ setAlignment(Qt::AlignRight);
+
+ connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
+ }
+
+ QValidator::State validate(QString &text, int &pos) const
+ {
+ if(text.isEmpty())
+ return QValidator::Intermediate;
+ bool valid = false;
+ parse(text, &valid);
+ /* Make sure we return Intermediate so that fixup() is called on defocus */
+ return valid ? QValidator::Intermediate : QValidator::Invalid;
+ }
+
+ void fixup(QString &input) const
+ {
+ bool valid = false;
+ qint64 val = parse(input, &valid);
+ if(valid)
+ {
+ input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
+ lineEdit()->setText(input);
+ }
}
- QString textFromValue(double value) const
+
+ qint64 value(bool *valid_out=0) const
{
- QStringList parts = QDoubleSpinBox::textFromValue(value).split(".");
- QString quotient_str = parts[0];
- QString remainder_str;
- if(parts.size() > 1)
- remainder_str = parts[1];
-
- // Code duplication between here and BitcoinUnits::format
- // TODO: Figure out how to share this code
- QChar thin_sp(THIN_SP_CP);
- int q_size = quotient_str.size();
- if (q_size > 4)
- for (int i = 3; i < q_size; i += 3)
- quotient_str.insert(q_size - i, thin_sp);
-
- int r_size = remainder_str.size();
- if (r_size > 4)
- for (int i = 3, adj = 0; i < r_size; i += 3, adj++)
- remainder_str.insert(i + adj, thin_sp);
-
- if(remainder_str.isEmpty())
- return quotient_str;
+ return parse(text(), valid_out);
+ }
+
+ void setValue(qint64 value)
+ {
+ lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways));
+ emit valueChanged();
+ }
+
+ void stepBy(int steps)
+ {
+ bool valid = false;
+ qint64 val = value(&valid);
+ val = val + steps * singleStep;
+ val = qMin(qMax(val, Q_INT64_C(0)), BitcoinUnits::maxMoney());
+ setValue(val);
+ }
+
+ StepEnabled stepEnabled() const
+ {
+ StepEnabled rv = 0;
+ if(text().isEmpty()) // Allow step-up with empty field
+ return StepUpEnabled;
+ bool valid = false;
+ qint64 val = value(&valid);
+ if(valid)
+ {
+ if(val > 0)
+ rv |= StepDownEnabled;
+ if(val < BitcoinUnits::maxMoney())
+ rv |= StepUpEnabled;
+ }
+ return rv;
+ }
+
+ void setDisplayUnit(int unit)
+ {
+ bool valid = false;
+ qint64 val = value(&valid);
+
+ currentUnit = unit;
+
+ if(valid)
+ setValue(val);
else
- return quotient_str + QString(".") + remainder_str;
+ clear();
}
- QValidator::State validate (QString &text, int &pos) const
+
+ void setSingleStep(qint64 step)
{
- QString s(BitcoinUnits::removeSpaces(text));
- return QDoubleSpinBox::validate(s, pos);
+ singleStep = step;
+ }
+
+ QSize minimumSizeHint() const
+ {
+ if(cachedMinimumSizeHint.isEmpty())
+ {
+ ensurePolished();
+
+ const QFontMetrics fm(fontMetrics());
+ int h = lineEdit()->minimumSizeHint().height();
+ int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
+ w += 2; // cursor blinking space
+
+ QStyleOptionSpinBox opt;
+ initStyleOption(&opt);
+ QSize hint(w, h);
+ QSize extra(35, 6);
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ // get closer to final result by repeating the calculation
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ hint += extra;
+
+ opt.rect = rect();
+
+ cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
+ .expandedTo(QApplication::globalStrut());
+ }
+ return cachedMinimumSizeHint;
+ }
+private:
+ int currentUnit;
+ qint64 singleStep;
+ mutable QSize cachedMinimumSizeHint;
+
+ /**
+ * Parse a string into a number of base monetary units and
+ * return validity.
+ * @note Must return 0 if !valid.
+ */
+ qint64 parse(const QString &text, bool *valid_out=0) const
+ {
+ qint64 val = 0;
+ bool valid = BitcoinUnits::parse(currentUnit, text, &val);
+ if(valid)
+ {
+ if(val < 0 || val > BitcoinUnits::maxMoney())
+ valid = false;
+ }
+ if(valid_out)
+ *valid_out = valid;
+ return valid ? val : 0;
}
- double valueFromText(const QString& text) const
+
+protected:
+ bool event(QEvent *event)
{
- return QDoubleSpinBox::valueFromText(BitcoinUnits::removeSpaces(text));
+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
+ {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ if (keyEvent->key() == Qt::Key_Comma)
+ {
+ // Translate a comma into a period
+ QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
+ return QAbstractSpinBox::event(&periodKeyEvent);
+ }
+ }
+ return QAbstractSpinBox::event(event);
}
+
+signals:
+ void valueChanged();
};
+#include "bitcoinamountfield.moc"
+
BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
QWidget(parent),
- amount(0),
- currentUnit(-1)
+ amount(0)
{
- nSingleStep = 100000; // satoshis
-
amount = new AmountSpinBox(this);
amount->setLocale(QLocale::c());
amount->installEventFilter(this);
@@ -85,21 +207,13 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
setFocusProxy(amount);
// If one if the widgets changes, the combined content changes as well
- connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged()));
+ connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
// Set default based on configuration
unitChanged(unit->currentIndex());
}
-void BitcoinAmountField::setText(const QString &text)
-{
- if (text.isEmpty())
- amount->clear();
- else
- amount->setValue(BitcoinUnits::removeSpaces(text).toDouble());
-}
-
void BitcoinAmountField::clear()
{
amount->clear();
@@ -108,16 +222,9 @@ void BitcoinAmountField::clear()
bool BitcoinAmountField::validate()
{
- bool valid = true;
- if (amount->value() == 0.0)
- valid = false;
- else if (!BitcoinUnits::parse(currentUnit, text(), 0))
- valid = false;
- else if (amount->value() > BitcoinUnits::maxAmount(currentUnit))
- valid = false;
-
+ bool valid = false;
+ value(&valid);
setValid(valid);
-
return valid;
}
@@ -129,14 +236,6 @@ void BitcoinAmountField::setValid(bool valid)
amount->setStyleSheet(STYLE_INVALID);
}
-QString BitcoinAmountField::text() const
-{
- if (amount->text().isEmpty())
- return QString();
- else
- return amount->text();
-}
-
bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusIn)
@@ -144,17 +243,6 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
// Clear invalid flag on focus
setValid(true);
}
- else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
- {
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
- if (keyEvent->key() == Qt::Key_Comma)
- {
- // Translate a comma into a period
- QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
- QApplication::sendEvent(object, &periodKeyEvent);
- return true;
- }
- }
return QWidget::eventFilter(object, event);
}
@@ -167,18 +255,12 @@ QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
qint64 BitcoinAmountField::value(bool *valid_out) const
{
- qint64 val_out = 0;
- bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out);
- if (valid_out)
- {
- *valid_out = valid;
- }
- return val_out;
+ return amount->value(valid_out);
}
void BitcoinAmountField::setValue(qint64 value)
{
- setText(BitcoinUnits::format(currentUnit, value));
+ amount->setValue(value);
}
void BitcoinAmountField::setReadOnly(bool fReadOnly)
@@ -195,28 +277,7 @@ void BitcoinAmountField::unitChanged(int idx)
// Determine new unit ID
int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
- // Parse current value and convert to new unit
- bool valid = false;
- qint64 currentValue = value(&valid);
-
- currentUnit = newUnit;
-
- // Set max length after retrieving the value, to prevent truncation
- amount->setDecimals(BitcoinUnits::decimals(currentUnit));
- amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));
- amount->setSingleStep((double)nSingleStep / (double)BitcoinUnits::factor(currentUnit));
-
- if (valid)
- {
- // If value was valid, re-place it in the widget with the new unit
- setValue(currentValue);
- }
- else
- {
- // If current value is invalid, just clear field
- setText("");
- }
- setValid(true);
+ amount->setDisplayUnit(newUnit);
}
void BitcoinAmountField::setDisplayUnit(int newUnit)
@@ -226,6 +287,5 @@ void BitcoinAmountField::setDisplayUnit(int newUnit)
void BitcoinAmountField::setSingleStep(qint64 step)
{
- nSingleStep = step;
- unitChanged(unit->currentIndex());
+ amount->setSingleStep(step);
}
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index 521a9ed561..c713f5d687 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -8,17 +8,18 @@
#include <QWidget>
QT_BEGIN_NAMESPACE
-class QDoubleSpinBox;
class QValueComboBox;
QT_END_NAMESPACE
+class AmountSpinBox;
+
/** Widget for entering bitcoin amounts.
*/
class BitcoinAmountField: public QWidget
{
Q_OBJECT
- Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true)
+ Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY valueChanged USER true)
public:
explicit BitcoinAmountField(QWidget *parent = 0);
@@ -49,20 +50,15 @@ public:
QWidget *setupTabChain(QWidget *prev);
signals:
- void textChanged();
+ void valueChanged();
protected:
/** Intercept focus-in event and ',' key presses */
bool eventFilter(QObject *object, QEvent *event);
private:
- QDoubleSpinBox *amount;
+ AmountSpinBox *amount;
QValueComboBox *unit;
- int currentUnit;
- qint64 nSingleStep;
-
- void setText(const QString &text);
- QString text() const;
private slots:
void unitChanged(int idx);
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 21aed235cf..6f506d3f25 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -4,6 +4,8 @@
#include "bitcoinunits.h"
+#include "core.h"
+
#include <QStringList>
BitcoinUnits::BitcoinUnits(QObject *parent):
@@ -78,28 +80,6 @@ qint64 BitcoinUnits::factor(int unit)
}
}
-qint64 BitcoinUnits::maxAmount(int unit)
-{
- switch(unit)
- {
- case BTC: return Q_INT64_C(21000000);
- case mBTC: return Q_INT64_C(21000000000);
- case uBTC: return Q_INT64_C(21000000000000);
- default: return 0;
- }
-}
-
-int BitcoinUnits::amountDigits(int unit)
-{
- switch(unit)
- {
- case BTC: return 8; // 21,000,000 (# digits, without commas)
- case mBTC: return 11; // 21,000,000,000
- case uBTC: return 14; // 21,000,000,000,000
- default: return 0;
- }
-}
-
int BitcoinUnits::decimals(int unit)
{
switch(unit)
@@ -250,3 +230,8 @@ QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
}
return QVariant();
}
+
+qint64 BitcoinUnits::maxMoney()
+{
+ return MAX_MONEY;
+}
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 7fa24c8542..be9dca6012 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -82,10 +82,6 @@ public:
static QString description(int unit);
//! Number of Satoshis (1e-8) per unit
static qint64 factor(int unit);
- //! Max amount per unit
- static qint64 maxAmount(int unit);
- //! Number of amount digits (to represent max number of coins)
- static int amountDigits(int unit);
//! Number of decimals left
static int decimals(int unit);
//! Format as string
@@ -120,6 +116,9 @@ public:
return text;
}
+ //! Return maximum number of base units (Satoshis)
+ static qint64 maxMoney();
+
private:
QList<BitcoinUnits::Unit> unitlist;
};
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 597be40abd..0117d2e633 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -15,11 +15,11 @@
#include "optionsmodel.h"
#include "main.h" // for MAX_SCRIPTCHECK_THREADS
+#include "netbase.h"
+#include "txdb.h" // for -dbcache defaults
#ifdef ENABLE_WALLET
#include "wallet.h" // for CWallet::minTxFee
#endif
-#include "netbase.h"
-#include "txdb.h" // for -dbcache defaults
#include <QDir>
#include <QIntValidator>
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 89c2ec7453..9699f6eea6 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -32,7 +32,6 @@ public:
ProxyUse, // bool
ProxyIP, // QString
ProxyPort, // int
- ProxySocksVersion, // int
Fee, // qint64
DisplayUnit, // BitcoinUnits::Unit
DisplayAddresses, // bool
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index e0f56f8cd2..52545c3857 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -34,6 +34,12 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
GUIUtil::setupAddressWidget(ui->payTo, this);
// just a label for displaying bitcoin address(es)
ui->payTo_is->setFont(GUIUtil::bitcoinAddressFont());
+
+ // Connect signals
+ connect(ui->payAmount, SIGNAL(valueChanged()), this, SIGNAL(payAmountChanged()));
+ connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
+ connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked()));
+ connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked()));
}
SendCoinsEntry::~SendCoinsEntry()
@@ -72,11 +78,6 @@ void SendCoinsEntry::setModel(WalletModel *model)
if (model && model->getOptionsModel())
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
- connect(ui->payAmount, SIGNAL(textChanged()), this, SIGNAL(payAmountChanged()));
- connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
- connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked()));
- connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked()));
-
clear();
}
@@ -130,6 +131,13 @@ bool SendCoinsEntry::validate()
retval = false;
}
+ // Sending a zero amount is invalid
+ if (ui->payAmount->value(0) <= 0)
+ {
+ ui->payAmount->setValid(false);
+ retval = false;
+ }
+
// Reject dust outputs:
if (retval && GUIUtil::isDust(ui->payTo->text(), ui->payAmount->value())) {
ui->payAmount->setValid(false);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index ac1614efd0..4f6e3169f5 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -11,11 +11,11 @@
#include "db.h"
#include "main.h"
#include "paymentserver.h"
+#include "script.h"
#include "transactionrecord.h"
#include "timedata.h"
#include "ui_interface.h"
#include "wallet.h"
-#include "script.h"
#include <stdint.h>
#include <string>
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 4825713b69..7acb0e8871 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -577,7 +577,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case ConfirmedRole:
return rec->status.countsForBalance;
case FormattedAmountRole:
- // Used for copy/export, so don't include separators
+ // Used for copy/export, so don't include separators
return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
case StatusRole:
return rec->status.status;
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index ad88d14a90..2124d3dd1c 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -5,11 +5,11 @@
#ifndef TRANSACTIONTABLEMODEL_H
#define TRANSACTIONTABLEMODEL_H
+#include "bitcoinunits.h"
+
#include <QAbstractTableModel>
#include <QStringList>
-#include "bitcoinunits.h"
-
class TransactionRecord;
class TransactionTablePriv;
class WalletModel;
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index 5edeecf933..a0921453cc 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -85,6 +85,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getrawmempool", 0 },
{ "estimatefee", 0 },
{ "estimatepriority", 0 },
+ { "prioritisetransaction", 1 },
+ { "prioritisetransaction", 2 },
};
class CRPCConvertTable
diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp
index 4193f41b49..ff2361482b 100644
--- a/src/rpcdump.cpp
+++ b/src/rpcdump.cpp
@@ -72,16 +72,17 @@ Value importprivkey(const Array& params, bool fHelp)
"\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
"\nArguments:\n"
"1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n"
- "2. \"label\" (string, optional) an optional label\n"
+ "2. \"label\" (string, optional, default=\"\") An optional label\n"
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
+ "\nNote: This call can take minutes to complete if rescan is true.\n"
"\nExamples:\n"
"\nDump a private key\n"
+ HelpExampleCli("dumpprivkey", "\"myaddress\"") +
- "\nImport the private key\n"
+ "\nImport the private key with rescan\n"
+ HelpExampleCli("importprivkey", "\"mykey\"") +
- "\nImport using a label\n"
+ "\nImport using a label and without rescan\n"
+ HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
);
@@ -137,8 +138,21 @@ Value importaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 3)
throw runtime_error(
- "importaddress <address> [label] [rescan=true]\n"
- "Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.");
+ "importaddress \"address\" ( \"label\" rescan )\n"
+ "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
+ "\nArguments:\n"
+ "1. \"address\" (string, required) The address\n"
+ "2. \"label\" (string, optional, default=\"\") An optional label\n"
+ "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
+ "\nNote: This call can take minutes to complete if rescan is true.\n"
+ "\nExamples:\n"
+ "\nImport an address with rescan\n"
+ + HelpExampleCli("importaddress", "\"myaddress\"") +
+ "\nImport using a label without rescan\n"
+ + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
+ );
CScript script;
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index 6f72ea7404..edab427cbf 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -10,6 +10,7 @@
#include "main.h"
#include "miner.h"
#include "pow.h"
+#include "core_io.h"
#ifdef ENABLE_WALLET
#include "db.h"
#include "wallet.h"
@@ -252,11 +253,30 @@ Value prioritisetransaction(const Array& params, bool fHelp)
if (fHelp || params.size() != 3)
throw runtime_error(
"prioritisetransaction <txid> <priority delta> <fee delta>\n"
- "Accepts the transaction into mined blocks at a higher (or lower) priority");
+ "Accepts the transaction into mined blocks at a higher (or lower) priority\n"
+ "\nArguments:\n"
+ "1. \"txid\" (string, required) The transaction id.\n"
+ "2. priority delta (numeric, required) The priority to add or subtract.\n"
+ " The transaction selection algorithm considers the tx as it would have a higher priority.\n"
+ " (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n"
+ "3. fee delta (numeric, required) The absolute fee value to add or subtract in bitcoin.\n"
+ " The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
+ " considers the transaction as it would have paid a higher (or lower) fee.\n"
+ "\nResult\n"
+ "true (boolean) Returns true\n"
+ "\nExamples:\n"
+ + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 0.00010000")
+ + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 0.00010000")
+ );
uint256 hash;
hash.SetHex(params[0].get_str());
- mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), params[2].get_int64());
+
+ int64_t nAmount = 0;
+ if (params[2].get_real() != 0.0)
+ nAmount = AmountFromValue(params[2]);
+
+ mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount);
return true;
}
@@ -453,9 +473,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
Object entry;
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << tx;
- entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end())));
+ entry.push_back(Pair("data", EncodeHexTx(tx)));
entry.push_back(Pair("hash", txHash.GetHex()));
diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp
index cff795bdf4..bd992397b8 100644
--- a/src/rpcmisc.cpp
+++ b/src/rpcmisc.cpp
@@ -27,6 +27,19 @@ using namespace boost::assign;
using namespace json_spirit;
using namespace std;
+/**
+ * @note Do not add or change anything in the information returned by this
+ * method. `getinfo` exists for backwards-compatibilty only. It combines
+ * information from wildly different sources in the program, which is a mess,
+ * and is thus planned to be deprecated eventually.
+ *
+ * Based on the source of the information, new information should be added to:
+ * - `getblockchaininfo`,
+ * - `getnetworkinfo` or
+ * - `getwalletinfo`
+ *
+ * Or alternatively, create a specific query method for the information.
+ **/
Value getinfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 1efe38e830..763615120a 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -5,6 +5,7 @@
#include "base58.h"
#include "core.h"
+#include "core_io.h"
#include "init.h"
#include "keystore.h"
#include "main.h"
@@ -36,8 +37,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH
if (fIncludeHex)
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
- {
+ if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
out.push_back(Pair("type", GetTxnOutputType(type)));
return;
}
@@ -57,13 +57,11 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
entry.push_back(Pair("version", tx.nVersion));
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
Array vin;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- {
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
Object in;
if (tx.IsCoinBase())
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- else
- {
+ else {
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
Object o;
@@ -76,8 +74,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
}
entry.push_back(Pair("vin", vin));
Array vout;
- for (unsigned int i = 0; i < tx.vout.size(); i++)
- {
+ for (unsigned int i = 0; i < tx.vout.size(); i++) {
const CTxOut& txout = tx.vout[i];
Object out;
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
@@ -89,15 +86,12 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
}
entry.push_back(Pair("vout", vout));
- if (hashBlock != 0)
- {
+ if (hashBlock != 0) {
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
- if (mi != mapBlockIndex.end() && (*mi).second)
- {
+ if (mi != mapBlockIndex.end() && (*mi).second) {
CBlockIndex* pindex = (*mi).second;
- if (chainActive.Contains(pindex))
- {
+ if (chainActive.Contains(pindex)) {
entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
entry.push_back(Pair("time", pindex->GetBlockTime()));
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
@@ -182,9 +176,7 @@ Value getrawtransaction(const Array& params, bool fHelp)
if (!GetTransaction(hash, tx, hashBlock, true))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << tx;
- string strHex = HexStr(ssTx.begin(), ssTx.end());
+ string strHex = EncodeHexTx(tx);
if (!fVerbose)
return strHex;
@@ -245,11 +237,9 @@ Value listunspent(const Array& params, bool fHelp)
nMaxDepth = params[1].get_int();
set<CBitcoinAddress> setAddress;
- if (params.size() > 2)
- {
+ if (params.size() > 2) {
Array inputs = params[2].get_array();
- BOOST_FOREACH(Value& input, inputs)
- {
+ BOOST_FOREACH(Value& input, inputs) {
CBitcoinAddress address(input.get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str());
@@ -263,13 +253,11 @@ Value listunspent(const Array& params, bool fHelp)
vector<COutput> vecOutputs;
assert(pwalletMain != NULL);
pwalletMain->AvailableCoins(vecOutputs, false);
- BOOST_FOREACH(const COutput& out, vecOutputs)
- {
+ BOOST_FOREACH(const COutput& out, vecOutputs) {
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue;
- if (setAddress.size())
- {
+ if (setAddress.size()) {
CTxDestination address;
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
continue;
@@ -284,18 +272,15 @@ Value listunspent(const Array& params, bool fHelp)
entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
entry.push_back(Pair("vout", out.i));
CTxDestination address;
- if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
- {
+ if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
if (pwalletMain->mapAddressBook.count(address))
entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name));
}
entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
- if (pk.IsPayToScriptHash())
- {
+ if (pk.IsPayToScriptHash()) {
CTxDestination address;
- if (ExtractDestination(pk, address))
- {
+ if (ExtractDestination(pk, address)) {
const CScriptID& hash = boost::get<const CScriptID&>(address);
CScript redeemScript;
if (pwalletMain->GetCScript(hash, redeemScript))
@@ -352,8 +337,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
CMutableTransaction rawTx;
- BOOST_FOREACH(const Value& input, inputs)
- {
+ BOOST_FOREACH(const Value& input, inputs) {
const Object& o = input.get_obj();
uint256 txid = ParseHashO(o, "txid");
@@ -370,8 +354,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
}
set<CBitcoinAddress> setAddress;
- BOOST_FOREACH(const Pair& s, sendTo)
- {
+ BOOST_FOREACH(const Pair& s, sendTo) {
CBitcoinAddress address(s.name_);
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
@@ -388,9 +371,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
rawTx.vout.push_back(out);
}
- CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
- ss << rawTx;
- return HexStr(ss.begin(), ss.end());
+ return EncodeHexTx(rawTx);
}
Value decoderawtransaction(const Array& params, bool fHelp)
@@ -444,15 +425,12 @@ Value decoderawtransaction(const Array& params, bool fHelp)
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
);
- vector<unsigned char> txData(ParseHexV(params[0], "argument"));
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
+ RPCTypeCheck(params, list_of(str_type));
+
CTransaction tx;
- try {
- ssData >> tx;
- }
- catch (std::exception &e) {
+
+ if (!DecodeHexTx(tx, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
Object result;
TxToJSON(tx, 0, result);
@@ -556,8 +534,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
vector<CMutableTransaction> txVariants;
- while (!ssData.empty())
- {
+ while (!ssData.empty()) {
try {
CMutableTransaction tx;
ssData >> tx;
@@ -596,12 +573,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
- if (params.size() > 2 && params[2].type() != null_type)
- {
+ if (params.size() > 2 && params[2].type() != null_type) {
fGivenKeys = true;
Array keys = params[2].get_array();
- BOOST_FOREACH(Value k, keys)
- {
+ BOOST_FOREACH(Value k, keys) {
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(k.get_str());
if (!fGood)
@@ -616,11 +591,9 @@ Value signrawtransaction(const Array& params, bool fHelp)
#endif
// Add previous txouts given in the RPC call:
- if (params.size() > 1 && params[1].type() != null_type)
- {
+ if (params.size() > 1 && params[1].type() != null_type) {
Array prevTxs = params[1].get_array();
- BOOST_FOREACH(Value& p, prevTxs)
- {
+ BOOST_FOREACH(Value& p, prevTxs) {
if (p.type() != obj_type)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
@@ -655,12 +628,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
// if redeemScript given and not using the local wallet (private keys
// given), add redeemScript to the tempKeystore so it can be signed:
- if (fGivenKeys && scriptPubKey.IsPayToScriptHash())
- {
+ if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) {
RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
Value v = find_value(prevOut, "redeemScript");
- if (!(v == Value::null))
- {
+ if (!(v == Value::null)) {
vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
@@ -676,8 +647,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
#endif
int nHashType = SIGHASH_ALL;
- if (params.size() > 3 && params[3].type() != null_type)
- {
+ if (params.size() > 3 && params[3].type() != null_type) {
static map<string, int> mapSigHashValues =
boost::assign::map_list_of
(string("ALL"), int(SIGHASH_ALL))
@@ -697,12 +667,10 @@ Value signrawtransaction(const Array& params, bool fHelp)
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Sign what we can:
- for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
- {
+ for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
CCoins coins;
- if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
- {
+ if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) {
fComplete = false;
continue;
}
@@ -714,8 +682,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
// ... and merge in other signatures:
- BOOST_FOREACH(const CMutableTransaction& txv, txVariants)
- {
+ BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
}
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STANDARD_SCRIPT_VERIFY_FLAGS, 0))
@@ -723,9 +690,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
}
Object result;
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << mergedTx;
- result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
+ result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
result.push_back(Pair("complete", fComplete));
return result;
@@ -754,25 +719,18 @@ Value sendrawtransaction(const Array& params, bool fHelp)
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
+ RPCTypeCheck(params, list_of(str_type)(bool_type));
// parse hex string from parameter
- vector<unsigned char> txData(ParseHexV(params[0], "parameter"));
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx;
+ if (!DecodeHexTx(tx, params[0].get_str()))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ uint256 hashTx = tx.GetHash();
bool fOverrideFees = false;
if (params.size() > 1)
fOverrideFees = params[1].get_bool();
- // deserialize binary data stream
- try {
- ssData >> tx;
- }
- catch (std::exception &e) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
- const uint256 &hashTx = tx.GetHash();
-
CCoinsViewCache &view = *pcoinsTip;
CCoins existingCoins;
bool fHaveMempool = mempool.exists(hashTx);
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 5b83fe900e..667ca33ce1 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
+#include "core_io.h"
#include "rpcserver.h"
#include "init.h"
#include "net.h"
@@ -1550,9 +1551,7 @@ Value gettransaction(const Array& params, bool fHelp)
ListTransactions(wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << static_cast<CTransaction>(wtx);
- string strHex = HexStr(ssTx.begin(), ssTx.end());
+ string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
entry.push_back(Pair("hex", strHex));
return entry;
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index cba582e941..5e35875a8e 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -10,6 +10,7 @@
#include "key.h"
#include "keystore.h"
#include "main.h"
+#include "core_io.h"
#include <fstream>
#include <stdint.h>
@@ -36,76 +37,6 @@ extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo
static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
-CScript
-ParseScript(string s)
-{
- CScript result;
-
- static map<string, opcodetype> mapOpNames;
-
- if (mapOpNames.size() == 0)
- {
- for (int op = 0; op <= OP_NOP10; op++)
- {
- // Allow OP_RESERVED to get into mapOpNames
- if (op < OP_NOP && op != OP_RESERVED)
- continue;
-
- const char* name = GetOpName((opcodetype)op);
- if (strcmp(name, "OP_UNKNOWN") == 0)
- continue;
- string strName(name);
- mapOpNames[strName] = (opcodetype)op;
- // Convenience: OP_ADD and just ADD are both recognized:
- replace_first(strName, "OP_", "");
- mapOpNames[strName] = (opcodetype)op;
- }
- }
-
- vector<string> words;
- split(words, s, is_any_of(" \t\n"), token_compress_on);
-
- BOOST_FOREACH(string w, words)
- {
- if (w.size() == 0)
- {
- // Empty string, ignore. (boost::split given '' will return one word)
- }
- else if (all(w, is_digit()) ||
- (starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit())))
- {
- // Number
- int64_t n = atoi64(w);
- result << n;
- }
- else if (starts_with(w, "0x") && IsHex(string(w.begin()+2, w.end())))
- {
- // Raw hex data, inserted NOT pushed onto stack:
- std::vector<unsigned char> raw = ParseHex(string(w.begin()+2, w.end()));
- result.insert(result.end(), raw.begin(), raw.end());
- }
- else if (w.size() >= 2 && starts_with(w, "'") && ends_with(w, "'"))
- {
- // Single-quoted string, pushed as data. NOTE: this is poor-man's
- // parsing, spaces/tabs/newlines in single-quoted strings won't work.
- std::vector<unsigned char> value(w.begin()+1, w.end()-1);
- result << value;
- }
- else if (mapOpNames.count(w))
- {
- // opcode, e.g. OP_ADD or ADD:
- result << mapOpNames[w];
- }
- else
- {
- BOOST_ERROR("Parse error: " << s);
- return CScript();
- }
- }
-
- return result;
-}
-
Array
read_json(const std::string& jsondata)
{
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 238033f407..03919e7c7d 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -9,6 +9,7 @@
#include "keystore.h"
#include "main.h"
#include "script.h"
+#include "core_io.h"
#include <map>
#include <string>
@@ -24,7 +25,6 @@ using namespace boost::algorithm;
// In script_tests.cpp
extern Array read_json(const std::string& jsondata);
-extern CScript ParseScript(string s);
unsigned int ParseFlags(string strFlags){
unsigned int flags = 0;
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 8a095d26dc..6c3bd9a48d 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -49,6 +49,24 @@ void AddTimeData(const CNetAddr& ip, int64_t nTime)
static CMedianFilter<int64_t> vTimeOffsets(200,0);
vTimeOffsets.input(nOffsetSample);
LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
+
+ // There is a known issue here (see issue #4521):
+ //
+ // - The structure vTimeOffsets contains up to 200 elements, after which
+ // any new element added to it will not increase its size, replacing the
+ // oldest element.
+ //
+ // - The condition to update nTimeOffset includes checking whether the
+ // number of elements in vTimeOffsets is odd, which will never happen after
+ // there are 200 elements.
+ //
+ // But in this case the 'bug' is protective against some attacks, and may
+ // actually explain why we've never seen attacks which manipulate the
+ // clock offset.
+ //
+ // So we should hold off on fixing this and clean it up as part of
+ // a timing cleanup that strengthens it in a number of other ways.
+ //
if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
{
int64_t nMedian = vTimeOffsets.median();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index ebb1369e31..164e2741a2 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -602,14 +602,15 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash)
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) {
- if (base->GetCoins(txid, coins))
- return true;
+ // If an entry in the mempool exists, always return that one, as it's guaranteed to never
+ // conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
+ // transactions. First checking the underlying cache risks returning a pruned entry instead.
CTransaction tx;
if (mempool.lookup(txid, tx)) {
coins = CCoins(tx, MEMPOOL_HEIGHT);
return true;
}
- return false;
+ return (base->GetCoins(txid, coins) && !coins.IsPruned());
}
bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) {
diff --git a/src/univalue/univalue.cpp b/src/univalue/univalue.cpp
new file mode 100644
index 0000000000..afc208bffb
--- /dev/null
+++ b/src/univalue/univalue.cpp
@@ -0,0 +1,211 @@
+// 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;
+}
+
+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..0a7bf3cceb
--- /dev/null
+++ b/src/univalue/univalue.h
@@ -0,0 +1,155 @@
+// 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 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 += "}";
+}
+
diff --git a/src/version.h b/src/version.h
index 3d1abacb94..85c5dbf8d7 100644
--- a/src/version.h
+++ b/src/version.h
@@ -28,7 +28,7 @@ extern const std::string CLIENT_DATE;
static const int PROTOCOL_VERSION = 70002;
-// intial proto version, to be increased after version/verack negotiation
+// initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
// disconnect from peers older than this proto version
@@ -45,7 +45,7 @@ static const int NOBLKS_VERSION_END = 32400;
// BIP 0031, pong message, is enabled for all versions AFTER this one
static const int BIP0031_VERSION = 60000;
-// "mempool" command, enhanced "getdata" behavior starts with this version:
+// "mempool" command, enhanced "getdata" behavior starts with this version
static const int MEMPOOL_GD_VERSION = 60002;
#endif