diff --git a/configure.ac b/configure.ac index c1032d167..63f9b75fe 100644 --- a/configure.ac +++ b/configure.ac @@ -1003,7 +1003,7 @@ unset PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no" -AC_CONFIG_SUBDIRS([src/secp256k1]) +AC_CONFIG_SUBDIRS([src/secp256k1 src/univalue]) AC_OUTPUT diff --git a/qa/rpc-tests/wallet_protectcoinbase.py b/qa/rpc-tests/wallet_protectcoinbase.py index fc543a6ef..7eefb8b9b 100755 --- a/qa/rpc-tests/wallet_protectcoinbase.py +++ b/qa/rpc-tests/wallet_protectcoinbase.py @@ -242,14 +242,14 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): self.nodes[0].z_sendmany(myzaddr, recipients, 1, -1) except JSONRPCException,e: errorString = e.error['message'] - assert_equal("Invalid amount" in errorString, True) + assert_equal("Amount out of range" in errorString, True) # Send will fail because fee is larger than MAX_MONEY try: self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('21000000.00000001')) except JSONRPCException,e: errorString = e.error['message'] - assert_equal("Invalid amount" in errorString, True) + assert_equal("Amount out of range" in errorString, True) # Send will fail because fee is larger than sum of outputs try: diff --git a/src/Makefile.am b/src/Makefile.am index 2c3274673..edf928629 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -DIST_SUBDIRS = secp256k1 +DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) @@ -21,6 +21,7 @@ BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include +BITCOIN_INCLUDES += -I$(srcdir)/univalue/include LIBBITCOIN_SERVER=libbitcoin_server.a LIBBITCOIN_WALLET=libbitcoin_wallet.a @@ -28,13 +29,16 @@ 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 LIBSECP256K1=secp256k1/libsecp256k1.la +LIBUNIVALUE=univalue/libunivalue.la LIBZCASH=libzcash.a $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) + +$(LIBUNIVALUE): $(wildcard univalue/lib/*) + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue/ # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: @@ -42,7 +46,6 @@ EXTRA_LIBRARIES = \ crypto/libbitcoin_crypto.a \ libbitcoin_util.a \ libbitcoin_common.a \ - univalue/libbitcoin_univalue.a \ libbitcoin_server.a \ libbitcoin_cli.a \ libzcash.a @@ -287,14 +290,6 @@ crypto_libbitcoin_crypto_a_SOURCES += \ ${EQUIHASH_TROMP_SOURCES} endif -# univalue JSON library -univalue_libbitcoin_univalue_a_SOURCES = \ - univalue/univalue.cpp \ - univalue/univalue.h \ - univalue/univalue_escapes.h \ - univalue/univalue_read.cpp \ - univalue/univalue_write.cpp - # common: shared between bitcoind, and bitcoin-qt and non-server tools libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_common_a_SOURCES = \ @@ -374,7 +369,7 @@ endif zcashd_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ - $(LIBBITCOIN_UNIVALUE) \ + $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH) \ @@ -411,7 +406,7 @@ endif zcash_cli_LDADD = \ $(LIBBITCOIN_CLI) \ - $(LIBBITCOIN_UNIVALUE) \ + $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(BOOST_LIBS) \ $(SSL_LIBS) \ @@ -432,7 +427,7 @@ endif # FIXME: Is libzcash needed for zcash_tx? zcash_tx_LDADD = \ - $(LIBBITCOIN_UNIVALUE) \ + $(LIBUNIVALUE) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBSECP256K1) \ diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index d608bfcc1..8db5aea41 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -40,9 +40,9 @@ zcash_gtest_SOURCES += \ wallet/gtest/test_wallet.cpp endif -zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC +zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES) -zcash_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ +zcash_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) if ENABLE_ZMQ zcash_gtest_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 9513da080..5dde324aa 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -364,7 +364,7 @@ endif if ENABLE_ZMQ qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif -qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ +qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(LIBZCASH_LIBS) qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_bitcoin_qt_LIBTOOLFLAGS = --tag CXX diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 128c3f6d6..6a9d6b1a8 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -33,7 +33,7 @@ endif if ENABLE_ZMQ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif -qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) \ +qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(LIBZCASH_LIBS) qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 56be626d6..4defc09ed 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -98,7 +98,7 @@ endif test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) test_test_bitcoin_CPPFLAGS = -fopenmp $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) -test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ +test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) @@ -131,6 +131,7 @@ check-local: @echo "Running test/bitcoin-util-test.py..." $(AM_V_at)srcdir=$(srcdir) PYTHONPATH=$(builddir)/test $(srcdir)/test/bitcoin-util-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check %.json.h: %.json @$(MKDIR_P) $(@D) diff --git a/src/asyncrpcoperation.h b/src/asyncrpcoperation.h index e095e57b1..5475102a5 100644 --- a/src/asyncrpcoperation.h +++ b/src/asyncrpcoperation.h @@ -15,7 +15,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index d589a5a7c..6e54cd97a 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -12,7 +12,7 @@ #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index b36a01b73..0ba7e9a43 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -11,7 +11,7 @@ #include "primitives/transaction.h" #include "script/script.h" #include "script/sign.h" -#include "univalue/univalue.h" +#include #include "util.h" #include "utilmoneystr.h" #include "utilstrencodings.h" diff --git a/src/core_read.cpp b/src/core_read.cpp index e064955ff..1b3e9025d 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -9,7 +9,7 @@ #include "script/script.h" #include "serialize.h" #include "streams.h" -#include "univalue/univalue.h" +#include #include "util.h" #include "utilstrencodings.h" #include "version.h" diff --git a/src/core_write.cpp b/src/core_write.cpp index c3babec2f..26eeba655 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -10,7 +10,7 @@ #include "script/standard.h" #include "serialize.h" #include "streams.h" -#include "univalue/univalue.h" +#include #include "util.h" #include "utilmoneystr.h" #include "utilstrencodings.h" diff --git a/src/gtest/json_test_vectors.h b/src/gtest/json_test_vectors.h index 27c8757ec..3639c9121 100644 --- a/src/gtest/json_test_vectors.h +++ b/src/gtest/json_test_vectors.h @@ -5,7 +5,7 @@ #include "serialize.h" #include "streams.h" -#include "univalue/univalue.h" +#include UniValue read_json(const std::string& jsondata); diff --git a/src/gtest/test_rpc.cpp b/src/gtest/test_rpc.cpp index baebc7c65..a3fc201cd 100644 --- a/src/gtest/test_rpc.cpp +++ b/src/gtest/test_rpc.cpp @@ -1,5 +1,5 @@ #include -#include "univalue/univalue.h" +#include #include "chain.h" #include "chainparams.h" diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index feb282644..ca3af2574 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -18,7 +18,7 @@ #include -#include "univalue/univalue.h" +#include #ifdef ENABLE_WALLET #include diff --git a/src/rest.cpp b/src/rest.cpp index 1fce7dfc9..82155bab1 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -16,7 +16,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index fef578cc5..365ae3584 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -13,7 +13,7 @@ #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index f0c6611ae..fe442b81c 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -11,7 +11,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/rpcclient.h b/src/rpcclient.h index d68b4ed6a..8937a56f0 100644 --- a/src/rpcclient.h +++ b/src/rpcclient.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_RPCCLIENT_H #define BITCOIN_RPCCLIENT_H -#include "univalue/univalue.h" +#include UniValue RPCConvertValues(const std::string& strMethod, const std::vector& strParams); /** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 8f63b752c..dd622444e 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -28,7 +28,7 @@ #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index ef480034c..38610903b 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -20,7 +20,8 @@ #include #include -#include "univalue/univalue.h" + +#include #include "zcash/Address.hpp" diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index e8eca24f8..522edc741 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -16,7 +16,7 @@ #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index c0aaf2c29..684feb823 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -26,7 +26,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/rpcprotocol.h b/src/rpcprotocol.h index b6e618155..852a3aef1 100644 --- a/src/rpcprotocol.h +++ b/src/rpcprotocol.h @@ -16,7 +16,7 @@ #include #include -#include "univalue/univalue.h" +#include //! HTTP status codes enum HTTPStatusCode diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index a23c1d8ee..5ff035164 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -26,7 +26,7 @@ #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 6fe41193e..7414ec128 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -31,7 +31,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace boost::asio; using namespace RPCServer; @@ -121,25 +121,26 @@ void RPCTypeCheckObj(const UniValue& o, } } -static inline int64_t roundint64(double d) -{ - return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); -} - CAmount AmountFromValue(const UniValue& value) { - double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) + if (!value.isNum()) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number"); + CAmount amount; + if (!ParseFixedPoint(value.getValStr(), 8, &amount)) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - CAmount nAmount = roundint64(dAmount * COIN); - if (!MoneyRange(nAmount)) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - return nAmount; + if (!MoneyRange(amount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); + return amount; } UniValue ValueFromAmount(const CAmount& amount) { - return (double)amount / (double)COIN; + bool sign = amount < 0; + int64_t n_abs = (sign ? -amount : amount); + int64_t quotient = n_abs / COIN; + int64_t remainder = n_abs % COIN; + return UniValue(UniValue::VNUM, + strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder)); } uint256 ParseHashV(const UniValue& v, string strName) diff --git a/src/rpcserver.h b/src/rpcserver.h index 607cff1c2..6ee17da68 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -18,7 +18,7 @@ #include -#include "univalue/univalue.h" +#include class AsyncRPCQueue; class CRPCCommand; diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index bd754cafe..361d8b8ea 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -18,7 +18,7 @@ #include #include -#include "univalue/univalue.h" +#include extern UniValue read_json(const std::string& jsondata); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 9037d3f16..758c02f64 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -13,7 +13,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; @@ -126,6 +126,29 @@ BOOST_AUTO_TEST_CASE(rpc_format_monetary_values) BOOST_CHECK(ValueFromAmount(100000000LL).write() == "1.00000000"); BOOST_CHECK(ValueFromAmount(2099999999999990LL).write() == "20999999.99999990"); BOOST_CHECK(ValueFromAmount(2099999999999999LL).write() == "20999999.99999999"); + + BOOST_CHECK_EQUAL(ValueFromAmount(0).write(), "0.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount((COIN/10000)*123456789).write(), "12345.67890000"); + BOOST_CHECK_EQUAL(ValueFromAmount(-COIN).write(), "-1.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(-COIN/10).write(), "-0.10000000"); + + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100000000).write(), "100000000.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10000000).write(), "10000000.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*1000000).write(), "1000000.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100000).write(), "100000.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10000).write(), "10000.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*1000).write(), "1000.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100).write(), "100.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10).write(), "10.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN).write(), "1.00000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10).write(), "0.10000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100).write(), "0.01000000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000).write(), "0.00100000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000).write(), "0.00010000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000).write(), "0.00001000"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000000).write(), "0.00000100"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000000).write(), "0.00000010"); + BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000000).write(), "0.00000001"); } static UniValue ValueFromString(const std::string &str) @@ -137,6 +160,9 @@ static UniValue ValueFromString(const std::string &str) BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) { + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("-0.00000001")), UniValue); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0")), 0LL); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000000")), 0LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000001")), 1LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.17622195")), 17622195LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.5")), 50000000LL); @@ -145,6 +171,24 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1.00000000")), 100000000LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("20999999.9999999")), 2099999999999990LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("20999999.99999999")), 2099999999999999LL); + + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1e-8")), COIN/100000000); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.1e-7")), COIN/100000000); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.01e-6")), COIN/100000000); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.0000000000000000000000000000000000000000000000000000000000000000000000000001e+68")), COIN/100000000); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("10000000000000000000000000000000000000000000000000000000000000000e-64")), COIN); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000e64")), COIN); + + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e-9")), UniValue); //should fail + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("0.000000019")), UniValue); //should fail + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000001000000")), 1LL); //should pass, cut trailing 0 + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("19e-9")), UniValue); //should fail + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.19e-6")), 19); //should pass, leading 0 is present + + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("92233720368.54775808")), UniValue); //overflow error + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e+11")), UniValue); //overflow error + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e11")), UniValue); //overflow error signless + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("93e+9")), UniValue); //overflow error } BOOST_AUTO_TEST_CASE(json_parse_errors) @@ -154,6 +198,9 @@ BOOST_AUTO_TEST_CASE(json_parse_errors) // Valid, with leading or trailing whitespace BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").get_real(), 1.0); BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").get_real(), 1.0); + + BOOST_CHECK_THROW(AmountFromValue(ParseNonRFCJSONValue(".19e-6")), std::runtime_error); //should fail, missing leading 0, therefore invalid JSON + BOOST_CHECK_EQUAL(AmountFromValue(ParseNonRFCJSONValue("0.00000000000000000000000000000000000001e+30 ")), 1); // Invalid, initial garbage BOOST_CHECK_THROW(ParseNonRFCJSONValue("[1.0"), std::runtime_error); BOOST_CHECK_THROW(ParseNonRFCJSONValue("a1.0"), std::runtime_error); diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index f3d1ce0ed..1dd74eb0e 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -31,7 +31,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index dfb4c2f65..a1e3dbdc2 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -27,7 +27,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index ce554ee3e..989ed7428 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -18,7 +18,7 @@ #include -#include "univalue/univalue.h" +#include extern UniValue read_json(const std::string& jsondata); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 983756625..ad4a6e00e 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -27,7 +27,7 @@ #include #include -#include "univalue/univalue.h" +#include #include "zcash/Note.hpp" #include "zcash/Address.hpp" diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp index de84faca2..feb8afa6f 100644 --- a/src/test/univalue_tests.cpp +++ b/src/test/univalue_tests.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "univalue/univalue.h" +#include #include "test/test_bitcoin.h" #include @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(univalue_constructor) double vd = -7.21; UniValue v7(vd); - BOOST_CHECK(v7.isReal()); + BOOST_CHECK(v7.isNum()); BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); string vs("yawn"); @@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(univalue_set) BOOST_CHECK_EQUAL(v.getValStr(), "zum"); BOOST_CHECK(v.setFloat(-1.01)); - BOOST_CHECK(v.isReal()); + BOOST_CHECK(v.isNum()); BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); BOOST_CHECK(v.setInt((int)1023)); @@ -272,7 +272,7 @@ BOOST_AUTO_TEST_CASE(univalue_object) objTypes["distance"] = UniValue::VNUM; objTypes["time"] = UniValue::VNUM; objTypes["calories"] = UniValue::VNUM; - objTypes["temperature"] = UniValue::VREAL; + objTypes["temperature"] = UniValue::VNUM; objTypes["cat1"] = UniValue::VNUM; objTypes["cat2"] = UniValue::VNUM; BOOST_CHECK(obj.checkObject(objTypes)); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 2224a119d..df8f8f43b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -146,29 +146,27 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_AUTO_TEST_CASE(util_FormatMoney) { - BOOST_CHECK_EQUAL(FormatMoney(0, false), "0.00"); - BOOST_CHECK_EQUAL(FormatMoney((COIN/10000)*123456789, false), "12345.6789"); - BOOST_CHECK_EQUAL(FormatMoney(COIN, true), "+1.00"); - BOOST_CHECK_EQUAL(FormatMoney(-COIN, false), "-1.00"); - BOOST_CHECK_EQUAL(FormatMoney(-COIN, true), "-1.00"); + BOOST_CHECK_EQUAL(FormatMoney(0), "0.00"); + BOOST_CHECK_EQUAL(FormatMoney((COIN/10000)*123456789), "12345.6789"); + BOOST_CHECK_EQUAL(FormatMoney(-COIN), "-1.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*100000000, false), "100000000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*10000000, false), "10000000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*1000000, false), "1000000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*100000, false), "100000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*10000, false), "10000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*1000, false), "1000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*100, false), "100.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*10, false), "10.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN, false), "1.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/10, false), "0.10"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/100, false), "0.01"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/1000, false), "0.001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/10000, false), "0.0001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/100000, false), "0.00001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/1000000, false), "0.000001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/10000000, false), "0.0000001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/100000000, false), "0.00000001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*100000000), "100000000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*10000000), "10000000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*1000000), "1000000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*100000), "100000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*10000), "10000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*1000), "1000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*100), "100.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*10), "10.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN), "1.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/10), "0.10"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/100), "0.01"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/1000), "0.001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/10000), "0.0001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/100000), "0.00001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/1000000), "0.000001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/10000000), "0.0000001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/100000000), "0.00000001"); } BOOST_AUTO_TEST_CASE(util_ParseMoney) @@ -428,4 +426,70 @@ BOOST_AUTO_TEST_CASE(test_FormatSubVersion) BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments2), std::string("/Test:0.9.99-beta1(comment1; comment2)/")); BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99950, comments2), std::string("/Test:0.9.99(comment1; comment2)/")); } + +BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) +{ + int64_t amount = 0; + BOOST_CHECK(ParseFixedPoint("0", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 0LL); + BOOST_CHECK(ParseFixedPoint("1", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 100000000LL); + BOOST_CHECK(ParseFixedPoint("0.0", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 0LL); + BOOST_CHECK(ParseFixedPoint("-0.1", 8, &amount)); + BOOST_CHECK_EQUAL(amount, -10000000LL); + BOOST_CHECK(ParseFixedPoint("1.1", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 110000000LL); + BOOST_CHECK(ParseFixedPoint("1.10000000000000000", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 110000000LL); + BOOST_CHECK(ParseFixedPoint("1.1e1", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 1100000000LL); + BOOST_CHECK(ParseFixedPoint("1.1e-1", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 11000000LL); + BOOST_CHECK(ParseFixedPoint("1000", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 100000000000LL); + BOOST_CHECK(ParseFixedPoint("-1000", 8, &amount)); + BOOST_CHECK_EQUAL(amount, -100000000000LL); + BOOST_CHECK(ParseFixedPoint("0.00000001", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 1LL); + BOOST_CHECK(ParseFixedPoint("0.0000000100000000", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 1LL); + BOOST_CHECK(ParseFixedPoint("-0.00000001", 8, &amount)); + BOOST_CHECK_EQUAL(amount, -1LL); + BOOST_CHECK(ParseFixedPoint("1000000000.00000001", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 100000000000000001LL); + BOOST_CHECK(ParseFixedPoint("9999999999.99999999", 8, &amount)); + BOOST_CHECK_EQUAL(amount, 999999999999999999LL); + BOOST_CHECK(ParseFixedPoint("-9999999999.99999999", 8, &amount)); + BOOST_CHECK_EQUAL(amount, -999999999999999999LL); + + BOOST_CHECK(!ParseFixedPoint("", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("a-1000", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-a1000", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-1000a", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-01000", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("00.1", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint(".1", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("--0.1", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("0.000000001", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-0.000000001", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("0.00000001000000001", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-10000000000.00000000", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("10000000000.00000000", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-10000000000.00000001", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("10000000000.00000001", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-10000000000.00000009", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("10000000000.00000009", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-99999999999.99999999", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("99999909999.09999999", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("92233720368.54775807", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("92233720368.54775808", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-92233720368.54775808", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("-92233720368.54775809", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("1.1e", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("1.1e-", 8, &amount)); + BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/univalue/.gitignore b/src/univalue/.gitignore new file mode 100644 index 000000000..19e42f814 --- /dev/null +++ b/src/univalue/.gitignore @@ -0,0 +1,32 @@ +.deps/ +INSTALL +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +compile +config.log +config.status +config.guess +config.sub +configure +depcomp +install-sh +missing +stamp-h1 +univalue-config.h* +test-driver +libtool +ltmain.sh +test-suite.log + +*.a +*.la +*.lo +*.logs +*.o +*.pc +*.trs + +.dirstamp +.libs diff --git a/src/univalue/.travis.yml b/src/univalue/.travis.yml new file mode 100644 index 000000000..132743d34 --- /dev/null +++ b/src/univalue/.travis.yml @@ -0,0 +1,52 @@ +language: cpp + +compiler: + - clang + - gcc + +os: + - linux + - osx + +sudo: false + +env: + global: + - MAKEJOBS=-j3 + - RUN_TESTS=true + - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out + +cache: + apt: true + +addons: + apt: + packages: + - pkg-config + +before_script: + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew uninstall libtool; brew install libtool; fi + - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi + - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh + +script: + - if [ -n "$UNIVALUE_CONFIG" ]; then unset CC; unset CXX; fi + - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST + - UNIVALUE_CONFIG_ALL="--prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" + - ./configure --cache-file=config.cache $UNIVALUE_CONFIG_ALL $UNIVALUE_CONFIG || ( cat config.log && false) + - make -s $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL ; false ) + - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib + - if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS distcheck; fi + +matrix: + fast_finish: true + include: + - os: linux + compiler: gcc + env: UNIVALUE_CONFIG=--host=x86_64-w64-mingw32 RUN_TESTS=false + addons: + apt: + packages: + - g++-mingw-w64-x86-64 + - gcc-mingw-w64-x86-64 + - binutils-mingw-w64-x86-64 diff --git a/src/univalue/COPYING b/src/univalue/COPYING new file mode 100644 index 000000000..1fb429f35 --- /dev/null +++ b/src/univalue/COPYING @@ -0,0 +1,19 @@ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am new file mode 100644 index 000000000..532fa1949 --- /dev/null +++ b/src/univalue/Makefile.am @@ -0,0 +1,109 @@ +ACLOCAL_AMFLAGS = -I build-aux/m4 +.PHONY: gen +.INTERMEDIATE: $(GENBIN) + +include_HEADERS = include/univalue.h +noinst_HEADERS = lib/univalue_escapes.h lib/univalue_utffilter.h + +lib_LTLIBRARIES = libunivalue.la + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = pc/libunivalue.pc + +libunivalue_la_SOURCES = \ + lib/univalue.cpp \ + lib/univalue_read.cpp \ + lib/univalue_write.cpp + +libunivalue_la_LDFLAGS = \ + -version-info $(LIBUNIVALUE_CURRENT):$(LIBUNIVALUE_REVISION):$(LIBUNIVALUE_AGE) \ + -no-undefined +libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include + +TESTS = test/unitester test/no_nul + +GENBIN = gen/gen$(BUILD_EXEEXT) +GEN_SRCS = gen/gen.cpp + +$(GENBIN): $(GEN_SRCS) + @echo Building $@ + $(AM_V_at)c++ -I$(top_srcdir)/include -o $@ $< + +gen: lib/univalue_escapes.h $(GENBIN) + @echo Updating $< + $(AM_V_at)$(GENBIN) > lib/univalue_escapes.h + +noinst_PROGRAMS = $(TESTS) test/test_json + +TEST_DATA_DIR=test + +test_unitester_SOURCES = test/unitester.cpp +test_unitester_LDADD = libunivalue.la +test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\" +test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_test_json_SOURCES = test/test_json.cpp +test_test_json_LDADD = libunivalue.la +test_test_json_CXXFLAGS = -I$(top_srcdir)/include +test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_no_nul_SOURCES = test/no_nul.cpp +test_no_nul_LDADD = libunivalue.la +test_no_nul_CXXFLAGS = -I$(top_srcdir)/include +test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +TEST_FILES = \ + $(TEST_DATA_DIR)/fail10.json \ + $(TEST_DATA_DIR)/fail11.json \ + $(TEST_DATA_DIR)/fail12.json \ + $(TEST_DATA_DIR)/fail13.json \ + $(TEST_DATA_DIR)/fail14.json \ + $(TEST_DATA_DIR)/fail15.json \ + $(TEST_DATA_DIR)/fail16.json \ + $(TEST_DATA_DIR)/fail17.json \ + $(TEST_DATA_DIR)/fail18.json \ + $(TEST_DATA_DIR)/fail19.json \ + $(TEST_DATA_DIR)/fail1.json \ + $(TEST_DATA_DIR)/fail20.json \ + $(TEST_DATA_DIR)/fail21.json \ + $(TEST_DATA_DIR)/fail22.json \ + $(TEST_DATA_DIR)/fail23.json \ + $(TEST_DATA_DIR)/fail24.json \ + $(TEST_DATA_DIR)/fail25.json \ + $(TEST_DATA_DIR)/fail26.json \ + $(TEST_DATA_DIR)/fail27.json \ + $(TEST_DATA_DIR)/fail28.json \ + $(TEST_DATA_DIR)/fail29.json \ + $(TEST_DATA_DIR)/fail2.json \ + $(TEST_DATA_DIR)/fail30.json \ + $(TEST_DATA_DIR)/fail31.json \ + $(TEST_DATA_DIR)/fail32.json \ + $(TEST_DATA_DIR)/fail33.json \ + $(TEST_DATA_DIR)/fail34.json \ + $(TEST_DATA_DIR)/fail35.json \ + $(TEST_DATA_DIR)/fail36.json \ + $(TEST_DATA_DIR)/fail37.json \ + $(TEST_DATA_DIR)/fail38.json \ + $(TEST_DATA_DIR)/fail39.json \ + $(TEST_DATA_DIR)/fail40.json \ + $(TEST_DATA_DIR)/fail41.json \ + $(TEST_DATA_DIR)/fail42.json \ + $(TEST_DATA_DIR)/fail3.json \ + $(TEST_DATA_DIR)/fail4.json \ + $(TEST_DATA_DIR)/fail5.json \ + $(TEST_DATA_DIR)/fail6.json \ + $(TEST_DATA_DIR)/fail7.json \ + $(TEST_DATA_DIR)/fail8.json \ + $(TEST_DATA_DIR)/fail9.json \ + $(TEST_DATA_DIR)/pass1.json \ + $(TEST_DATA_DIR)/pass2.json \ + $(TEST_DATA_DIR)/pass3.json \ + $(TEST_DATA_DIR)/round1.json \ + $(TEST_DATA_DIR)/round2.json \ + $(TEST_DATA_DIR)/round3.json \ + $(TEST_DATA_DIR)/round4.json \ + $(TEST_DATA_DIR)/round5.json \ + $(TEST_DATA_DIR)/round6.json \ + $(TEST_DATA_DIR)/round7.json + +EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) diff --git a/src/univalue/README b/src/univalue/README new file mode 100644 index 000000000..48167b083 --- /dev/null +++ b/src/univalue/README @@ -0,0 +1,7 @@ + + UniValue + +A universal value object, with JSON encoding (output) and decoding (input). + +Built as a single dynamic RAII C++ object class, and no templates. + diff --git a/src/univalue/TODO b/src/univalue/TODO new file mode 100644 index 000000000..5530048e9 --- /dev/null +++ b/src/univalue/TODO @@ -0,0 +1,10 @@ + +Rearrange tree for easier 'git subtree' style use + +Move towards C++11 etc. + +Namespace support - must come up with useful shorthand, avoiding +long Univalue::Univalue::Univalue usages forced upon library users. + +Improve test suite + diff --git a/src/univalue/autogen.sh b/src/univalue/autogen.sh new file mode 100755 index 000000000..4b38721fa --- /dev/null +++ b/src/univalue/autogen.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e +srcdir="$(dirname $0)" +cd "$srcdir" +if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then + LIBTOOLIZE="${GLIBTOOLIZE}" + export LIBTOOLIZE +fi +autoreconf --install --force diff --git a/src/univalue/build-aux/m4/.gitignore b/src/univalue/build-aux/m4/.gitignore new file mode 100644 index 000000000..f06368652 --- /dev/null +++ b/src/univalue/build-aux/m4/.gitignore @@ -0,0 +1 @@ +/*.m4 diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac new file mode 100644 index 000000000..8298332ac --- /dev/null +++ b/src/univalue/configure.ac @@ -0,0 +1,69 @@ +m4_define([libunivalue_major_version], [1]) +m4_define([libunivalue_minor_version], [1]) +m4_define([libunivalue_micro_version], [3]) +m4_define([libunivalue_interface_age], [3]) +# If you need a modifier for the version number. +# Normally empty, but can be used to make "fixup" releases. +m4_define([libunivalue_extraversion], []) + +dnl libtool versioning from libunivalue +m4_define([libunivalue_current], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version - libunivalue_interface_age)]) +m4_define([libunivalue_binary_age], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version)]) +m4_define([libunivalue_revision], [libunivalue_interface_age]) +m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_interface_age)]) +m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()]) + + +AC_INIT([univalue], [1.0.3], + [http://github.com/jgarzik/univalue/]) + +dnl make the compilation flags quiet unless V=1 is used +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_PREREQ(2.60) +AC_CONFIG_SRCDIR([lib/univalue.cpp]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([build-aux/m4]) +AC_CONFIG_HEADERS([univalue-config.h]) +AM_INIT_AUTOMAKE([subdir-objects foreign]) + +LIBUNIVALUE_MAJOR_VERSION=libunivalue_major_version +LIBUNIVALUE_MINOR_VERSION=libunivalue_minor_version +LIBUNIVALUE_MICRO_VERSION=libunivalue_micro_version +LIBUNIVALUE_INTERFACE_AGE=libunivalue_interface_age + +# ABI version +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +LIBUNIVALUE_CURRENT=libunivalue_current +LIBUNIVALUE_REVISION=libunivalue_revision +LIBUNIVALUE_AGE=libunivalue_age + +AC_SUBST(LIBUNIVALUE_CURRENT) +AC_SUBST(LIBUNIVALUE_REVISION) +AC_SUBST(LIBUNIVALUE_AGE) + +LT_INIT +LT_LANG([C++]) + +case $host in + *mingw*) + LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static" + ;; +esac + +BUILD_EXEEXT= +case $build in + *mingw*) + BUILD_EXEEXT=".exe" + ;; +esac + +AC_CONFIG_FILES([ + Makefile + pc/libunivalue.pc + pc/libunivalue-uninstalled.pc]) + +AC_SUBST(LIBTOOL_APP_LDFLAGS) +AC_SUBST(BUILD_EXEEXT) +AC_OUTPUT + diff --git a/src/univalue/gen.cpp b/src/univalue/gen/gen.cpp similarity index 76% rename from src/univalue/gen.cpp rename to src/univalue/gen/gen.cpp index abebe8863..17f361941 100644 --- a/src/univalue/gen.cpp +++ b/src/univalue/gen/gen.cpp @@ -8,7 +8,6 @@ // $ ./gen > univalue_escapes.h // -#include #include #include #include "univalue.h" @@ -16,18 +15,25 @@ using namespace std; static bool initEscapes; -static const char *escapes[256]; +static std::string escapes[256]; static void initJsonEscape() { + // Escape all lower control characters (some get overridden with smaller sequences below) + for (int ch=0x00; ch<0x20; ++ch) { + char tmpbuf[20]; + snprintf(tmpbuf, sizeof(tmpbuf), "\\u%04x", ch); + escapes[ch] = std::string(tmpbuf); + } + escapes[(int)'"'] = "\\\""; escapes[(int)'\\'] = "\\\\"; - escapes[(int)'/'] = "\\/"; escapes[(int)'\b'] = "\\b"; escapes[(int)'\f'] = "\\f"; escapes[(int)'\n'] = "\\n"; escapes[(int)'\r'] = "\\r"; escapes[(int)'\t'] = "\\t"; + escapes[(int)'\x7f'] = "\\u007f"; // U+007F DELETE initEscapes = true; } @@ -40,13 +46,13 @@ static void outputEscape() "static const char *escapes[256] = {\n"); for (unsigned int i = 0; i < 256; i++) { - if (!escapes[i]) { + if (escapes[i].empty()) { printf("\tNULL,\n"); } else { printf("\t\""); unsigned int si; - for (si = 0; si < strlen(escapes[i]); si++) { + for (si = 0; si < escapes[i].size(); si++) { char ch = escapes[i][si]; switch (ch) { case '"': diff --git a/src/univalue/univalue.h b/src/univalue/include/univalue.h similarity index 74% rename from src/univalue/univalue.h rename to src/univalue/include/univalue.h index 79018bb78..07314bc5c 100644 --- a/src/univalue/univalue.h +++ b/src/univalue/include/univalue.h @@ -1,11 +1,13 @@ // Copyright 2014 BitPay Inc. +// Copyright 2015 Bitcoin Core Developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_UNIVALUE_UNIVALUE_H -#define BITCOIN_UNIVALUE_UNIVALUE_H +#ifndef __UNIVALUE_H__ +#define __UNIVALUE_H__ #include + #include #include #include @@ -16,7 +18,7 @@ class UniValue { public: - enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VREAL, VBOOL, }; + enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, }; UniValue() { typ = VNULL; } UniValue(UniValue::VType initialType, const std::string& initialStr = "") { @@ -54,14 +56,14 @@ public: 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 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; } + const std::string& getValStr() const { return val; } bool empty() const { return (values.size() == 0); } size_t size() const { return values.size(); } @@ -69,8 +71,8 @@ public: bool getBool() const { return isTrue(); } bool checkObject(const std::map& 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); } + const UniValue& operator[](size_t index) const; + bool exists(const std::string& key) const { size_t i; return findKey(key, i); } bool isNull() const { return (typ == VNULL); } bool isTrue() const { return (typ == VBOOL) && (val == "1"); } @@ -78,7 +80,6 @@ public: bool isBool() const { return (typ == VBOOL); } bool isStr() const { return (typ == VSTR); } bool isNum() const { return (typ == VNUM); } - bool isReal() const { return (typ == VREAL); } bool isArray() const { return (typ == VARR); } bool isObject() const { return (typ == VOBJ); } @@ -91,31 +92,43 @@ public: std::string s(val_); return push_back(s); } + bool push_back(uint64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } bool push_backV(const std::vector& vec); bool pushKV(const std::string& key, const UniValue& val); - bool pushKV(const std::string& key, const std::string& val) { - UniValue tmpVal(VSTR, 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); + std::string _val(val_); + return pushKV(key, _val); } - bool pushKV(const std::string& key, int64_t val) { - UniValue tmpVal(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); + 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); + 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); + bool pushKV(const std::string& key, double val_) { + UniValue tmpVal(val_); return pushKV(key, tmpVal); } bool pushKVs(const UniValue& obj); @@ -123,9 +136,10 @@ public: std::string write(unsigned int prettyIndent = 0, unsigned int indentLevel = 0) const; + bool read(const char *raw, size_t len); bool read(const char *raw); bool read(const std::string& rawStr) { - return read(rawStr.c_str()); + return read(rawStr.data(), rawStr.size()); } private: @@ -134,17 +148,17 @@ private: std::vector keys; std::vector values; - int findKey(const std::string& key) const; + bool findKey(const std::string& key, size_t& ret) 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; public: // Strict type-specific getters, these throw std::runtime_error if the // value is of unexpected type - std::vector getKeys() const; - std::vector getValues() const; + const std::vector& getKeys() const; + const std::vector& getValues() const; bool get_bool() const; - std::string get_str() const; + const std::string& get_str() const; int get_int() const; int64_t get_int64() const; double get_real() const; @@ -239,12 +253,44 @@ enum jtokentype { }; extern enum jtokentype getJsonToken(std::string& tokenVal, - unsigned int& consumed, const char *raw); + unsigned int& consumed, const char *raw, const char *end); extern const char *uvTypeName(UniValue::VType t); +static inline bool jsonTokenIsValue(enum jtokentype jtt) +{ + switch (jtt) { + case JTOK_KW_NULL: + case JTOK_KW_TRUE: + case JTOK_KW_FALSE: + case JTOK_NUMBER: + case JTOK_STRING: + return true; + + default: + return false; + } + + // not reached +} + +static inline bool json_isspace(int ch) +{ + switch (ch) { + case 0x20: + case 0x09: + case 0x0a: + case 0x0d: + return true; + + default: + return false; + } + + // not reached +} + extern const UniValue NullUniValue; const UniValue& find_value( const UniValue& obj, const std::string& name); -#endif // BITCOIN_UNIVALUE_UNIVALUE_H - +#endif // __UNIVALUE_H__ diff --git a/src/univalue/lib/.gitignore b/src/univalue/lib/.gitignore new file mode 100644 index 000000000..ee7fc2851 --- /dev/null +++ b/src/univalue/lib/.gitignore @@ -0,0 +1,2 @@ +gen +.libs diff --git a/src/univalue/univalue.cpp b/src/univalue/lib/univalue.cpp similarity index 57% rename from src/univalue/univalue.cpp rename to src/univalue/lib/univalue.cpp index 6920c44c9..47ca7aca7 100644 --- a/src/univalue/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -1,16 +1,77 @@ // Copyright 2014 BitPay Inc. +// Copyright 2015 Bitcoin Core Developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include +#include #include +#include #include -#include // std::runtime_error +#include +#include +#include #include "univalue.h" -#include "utilstrencodings.h" // ParseXX +namespace +{ +static bool ParsePrechecks(const std::string& str) +{ + if (str.empty()) // No empty string allowed + return false; + if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed + return false; + if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + return false; + return true; +} + +bool ParseInt32(const std::string& str, int32_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtol will not set errno if valid + long int n = strtol(str.c_str(), &endp, 10); + if(out) *out = (int32_t)n; + // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit + // platforms the size of these types may be different. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits::min() && + n <= std::numeric_limits::max(); +} + +bool ParseInt64(const std::string& str, int64_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtoll will not set errno if valid + long long int n = strtoll(str.c_str(), &endp, 10); + if(out) *out = (int64_t)n; + // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int64_t*. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits::min() && + n <= std::numeric_limits::max(); +} + +bool ParseDouble(const std::string& str, double *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed + return false; + std::istringstream text(str); + text.imbue(std::locale::classic()); + double result; + text >> result; + if(out) *out = result; + return text.eof() && !text.fail(); +} +} using namespace std; @@ -43,7 +104,7 @@ static bool validNumStr(const string& s) { string tokenVal; unsigned int consumed; - enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str()); + enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size()); return (tt == JTOK_NUMBER); } @@ -58,35 +119,32 @@ bool UniValue::setNumStr(const string& val_) return true; } -bool UniValue::setInt(uint64_t val) +bool UniValue::setInt(uint64_t val_) { - string s; ostringstream oss; - oss << val; + oss << val_; return setNumStr(oss.str()); } -bool UniValue::setInt(int64_t val) +bool UniValue::setInt(int64_t val_) { - string s; ostringstream oss; - oss << val; + oss << val_; return setNumStr(oss.str()); } -bool UniValue::setFloat(double val) +bool UniValue::setFloat(double val_) { - string s; ostringstream oss; - oss << std::setprecision(16) << val; + oss << std::setprecision(16) << val_; bool ret = setNumStr(oss.str()); - typ = VREAL; + typ = VNUM; return ret; } @@ -112,12 +170,12 @@ bool UniValue::setObject() return true; } -bool UniValue::push_back(const UniValue& val) +bool UniValue::push_back(const UniValue& val_) { if (typ != VARR) return false; - values.push_back(val); + values.push_back(val_); return true; } @@ -131,13 +189,13 @@ bool UniValue::push_backV(const std::vector& vec) return true; } -bool UniValue::pushKV(const std::string& key, const UniValue& val) +bool UniValue::pushKV(const std::string& key, const UniValue& val_) { if (typ != VOBJ) return false; keys.push_back(key); - values.push_back(val); + values.push_back(val_); return true; } @@ -148,31 +206,33 @@ bool UniValue::pushKVs(const UniValue& obj) for (unsigned int i = 0; i < obj.keys.size(); i++) { keys.push_back(obj.keys[i]); - values.push_back(obj.values[i]); + values.push_back(obj.values.at(i)); } return true; } -int UniValue::findKey(const std::string& key) const +bool UniValue::findKey(const std::string& key, size_t& ret) const { - for (unsigned int i = 0; i < keys.size(); i++) { - if (keys[i] == key) - return (int) i; + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i] == key) { + ret = i; + return true; + } } - return -1; + return false; } bool UniValue::checkObject(const std::map& t) { for (std::map::const_iterator it = t.begin(); - it != t.end(); it++) { - int idx = findKey(it->first); - if (idx < 0) + it != t.end(); ++it) { + size_t idx; + if (!findKey(it->first, idx)) return false; - if (values[idx].getType() != it->second) + if (values.at(idx).getType() != it->second) return false; } @@ -184,21 +244,21 @@ const UniValue& UniValue::operator[](const std::string& key) const if (typ != VOBJ) return NullUniValue; - int index = findKey(key); - if (index < 0) + size_t index; + if (!findKey(key, index)) return NullUniValue; - return values[index]; + return values.at(index); } -const UniValue& UniValue::operator[](unsigned int index) const +const UniValue& UniValue::operator[](size_t index) const { if (typ != VOBJ && typ != VARR) return NullUniValue; if (index >= values.size()) return NullUniValue; - return values[index]; + return values.at(index); } const char *uvTypeName(UniValue::VType t) @@ -210,34 +270,29 @@ const char *uvTypeName(UniValue::VType t) case UniValue::VARR: return "array"; case UniValue::VSTR: return "string"; case UniValue::VNUM: return "number"; - case UniValue::VREAL: return "number"; } // not reached return NULL; } -const UniValue& find_value( const UniValue& obj, const std::string& name) +const UniValue& find_value(const UniValue& obj, const std::string& name) { for (unsigned int i = 0; i < obj.keys.size(); i++) - { - if( obj.keys[i] == name ) - { - return obj.values[i]; - } - } + if (obj.keys[i] == name) + return obj.values.at(i); return NullUniValue; } -std::vector UniValue::getKeys() const +const std::vector& UniValue::getKeys() const { if (typ != VOBJ) throw std::runtime_error("JSON value is not an object as expected"); return keys; } -std::vector UniValue::getValues() const +const std::vector& UniValue::getValues() const { if (typ != VOBJ && typ != VARR) throw std::runtime_error("JSON value is not an object or array as expected"); @@ -251,7 +306,7 @@ bool UniValue::get_bool() const return getBool(); } -std::string UniValue::get_str() const +const std::string& UniValue::get_str() const { if (typ != VSTR) throw std::runtime_error("JSON value is not a string as expected"); @@ -280,7 +335,7 @@ int64_t UniValue::get_int64() const double UniValue::get_real() const { - if (typ != VREAL && typ != VNUM) + if (typ != VNUM) throw std::runtime_error("JSON value is not a number as expected"); double retval; if (!ParseDouble(getValStr(), &retval)) diff --git a/src/univalue/univalue_escapes.h b/src/univalue/lib/univalue_escapes.h similarity index 84% rename from src/univalue/univalue_escapes.h rename to src/univalue/lib/univalue_escapes.h index 051411828..74596aab6 100644 --- a/src/univalue/univalue_escapes.h +++ b/src/univalue/lib/univalue_escapes.h @@ -2,38 +2,38 @@ #ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H #define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H static const char *escapes[256] = { - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, + "\\u0000", + "\\u0001", + "\\u0002", + "\\u0003", + "\\u0004", + "\\u0005", + "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", - NULL, + "\\u000b", "\\f", "\\r", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, + "\\u000e", + "\\u000f", + "\\u0010", + "\\u0011", + "\\u0012", + "\\u0013", + "\\u0014", + "\\u0015", + "\\u0016", + "\\u0017", + "\\u0018", + "\\u0019", + "\\u001a", + "\\u001b", + "\\u001c", + "\\u001d", + "\\u001e", + "\\u001f", NULL, NULL, "\\\"", @@ -49,7 +49,7 @@ static const char *escapes[256] = { NULL, NULL, NULL, - "\\/", + NULL, NULL, NULL, NULL, @@ -129,7 +129,7 @@ static const char *escapes[256] = { NULL, NULL, NULL, - NULL, + "\\u007f", NULL, NULL, NULL, diff --git a/src/univalue/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp similarity index 61% rename from src/univalue/univalue_read.cpp rename to src/univalue/lib/univalue_read.cpp index 5cea77899..7a9acdd75 100644 --- a/src/univalue/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -6,9 +6,15 @@ #include #include #include "univalue.h" +#include "univalue_utffilter.h" using namespace std; +static bool json_isdigit(int ch) +{ + return ((ch >= '0') && (ch <= '9')); +} + // convert hexadecimal string to unsigned integer static const char *hatoui(const char *first, const char *last, unsigned int& out) @@ -17,7 +23,7 @@ static const char *hatoui(const char *first, const char *last, for (; first != last; ++first) { int digit; - if (isdigit(*first)) + if (json_isdigit(*first)) digit = *first - '0'; else if (*first >= 'a' && *first <= 'f') @@ -37,21 +43,21 @@ static const char *hatoui(const char *first, const char *last, } enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, - const char *raw) + const char *raw, const char *end) { tokenVal.clear(); consumed = 0; const char *rawStart = raw; - while ((*raw) && (isspace(*raw))) // skip whitespace + while (raw < end && (json_isspace(*raw))) // skip whitespace raw++; - switch (*raw) { - - case 0: + if (raw >= end) return JTOK_NONE; + switch (*raw) { + case '{': raw++; consumed = (raw - rawStart); @@ -113,48 +119,48 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, const char *first = raw; const char *firstDigit = first; - if (!isdigit(*firstDigit)) + if (!json_isdigit(*firstDigit)) firstDigit++; - if ((*firstDigit == '0') && isdigit(firstDigit[1])) + if ((*firstDigit == '0') && json_isdigit(firstDigit[1])) return JTOK_ERR; numStr += *raw; // copy first char raw++; - if ((*first == '-') && (!isdigit(*raw))) + if ((*first == '-') && (raw < end) && (!json_isdigit(*raw))) return JTOK_ERR; - while ((*raw) && isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } // part 2: frac - if (*raw == '.') { + if (raw < end && *raw == '.') { numStr += *raw; // copy . raw++; - if (!isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } // part 3: exp - if (*raw == 'e' || *raw == 'E') { + if (raw < end && (*raw == 'e' || *raw == 'E')) { numStr += *raw; // copy E raw++; - if (*raw == '-' || *raw == '+') { // copy +/- + if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/- numStr += *raw; raw++; } - if (!isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } @@ -169,44 +175,35 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, raw++; // skip " string valStr; + JSONUTF8StringFilter writer(valStr); - while (*raw) { - if (*raw < 0x20) + while (raw < end) { + if ((unsigned char)*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { raw++; // skip backslash + if (raw >= end) + return JTOK_ERR; + 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 '"': writer.push_back('\"'); break; + case '\\': writer.push_back('\\'); break; + case '/': writer.push_back('/'); break; + case 'b': writer.push_back('\b'); break; + case 'f': writer.push_back('\f'); break; + case 'n': writer.push_back('\n'); break; + case 'r': writer.push_back('\r'); break; + case 't': writer.push_back('\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) != + if (raw + 1 + 4 >= end || + 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; + writer.push_back_u(codepoint); raw += 4; break; } @@ -224,11 +221,13 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, } else { - valStr += *raw; + writer.push_back(*raw); raw++; } } + if (!writer.finalize()) + return JTOK_ERR; tokenVal = valStr; consumed = (raw - rawStart); return JTOK_STRING; @@ -239,26 +238,73 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, } } -bool UniValue::read(const char *raw) +enum expect_bits { + EXP_OBJ_NAME = (1U << 0), + EXP_COLON = (1U << 1), + EXP_ARR_VALUE = (1U << 2), + EXP_VALUE = (1U << 3), + EXP_NOT_VALUE = (1U << 4), +}; + +#define expect(bit) (expectMask & (EXP_##bit)) +#define setExpect(bit) (expectMask |= EXP_##bit) +#define clearExpect(bit) (expectMask &= ~EXP_##bit) + +bool UniValue::read(const char *raw, size_t size) { clear(); - bool expectName = false; - bool expectColon = false; + uint32_t expectMask = 0; vector stack; + string tokenVal; + unsigned int consumed; enum jtokentype tok = JTOK_NONE; enum jtokentype last_tok = JTOK_NONE; - while (1) { + const char* end = raw + size; + do { last_tok = tok; - string tokenVal; - unsigned int consumed; - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, end); if (tok == JTOK_NONE || tok == JTOK_ERR) - break; + return false; raw += consumed; + bool isValueOpen = jsonTokenIsValue(tok) || + tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN; + + if (expect(VALUE)) { + if (!isValueOpen) + return false; + clearExpect(VALUE); + + } else if (expect(ARR_VALUE)) { + bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE); + if (!isArrValue) + return false; + + clearExpect(ARR_VALUE); + + } else if (expect(OBJ_NAME)) { + bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING); + if (!isObjName) + return false; + + } else if (expect(COLON)) { + if (tok != JTOK_COLON) + return false; + clearExpect(COLON); + + } else if (!expect(COLON) && (tok == JTOK_COLON)) { + return false; + } + + if (expect(NOT_VALUE)) { + if (isValueOpen) + return false; + clearExpect(NOT_VALUE); + } + switch (tok) { case JTOK_OBJ_OPEN: @@ -280,13 +326,15 @@ bool UniValue::read(const char *raw) } if (utyp == VOBJ) - expectName = true; + setExpect(OBJ_NAME); + else + setExpect(ARR_VALUE); break; } case JTOK_OBJ_CLOSE: case JTOK_ARR_CLOSE: { - if (!stack.size() || expectColon || (last_tok == JTOK_COMMA)) + if (!stack.size() || (last_tok == JTOK_COMMA)) return false; VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR); @@ -295,39 +343,39 @@ bool UniValue::read(const char *raw) return false; stack.pop_back(); - expectName = false; + clearExpect(OBJ_NAME); + setExpect(NOT_VALUE); break; } case JTOK_COLON: { - if (!stack.size() || expectName || !expectColon) + if (!stack.size()) return false; UniValue *top = stack.back(); if (top->getType() != VOBJ) return false; - expectColon = false; + setExpect(VALUE); break; } case JTOK_COMMA: { - if (!stack.size() || expectName || expectColon || + if (!stack.size() || (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN)) return false; UniValue *top = stack.back(); if (top->getType() == VOBJ) - expectName = true; + setExpect(OBJ_NAME); + else + setExpect(ARR_VALUE); 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: @@ -342,49 +390,65 @@ bool UniValue::read(const char *raw) default: /* impossible */ break; } + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); + setExpect(NOT_VALUE); break; } case JTOK_NUMBER: { - if (!stack.size() || expectName || expectColon) - return false; - UniValue tmpVal(VNUM, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); + setExpect(NOT_VALUE); break; } case JTOK_STRING: { - if (!stack.size()) - return false; - - UniValue *top = stack.back(); - - if (expectName) { + if (expect(OBJ_NAME)) { + UniValue *top = stack.back(); top->keys.push_back(tokenVal); - expectName = false; - expectColon = true; + clearExpect(OBJ_NAME); + setExpect(COLON); } else { UniValue tmpVal(VSTR, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); } + setExpect(NOT_VALUE); break; } default: return false; } - } + } while (!stack.empty ()); - if (stack.size() != 0) + /* Check that nothing follows the initial construct (parsed above). */ + tok = getJsonToken(tokenVal, consumed, raw, end); + if (tok != JTOK_NONE) return false; return true; } +bool UniValue::read(const char *raw) { + return read(raw, strlen(raw)); +} diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h new file mode 100644 index 000000000..b4a9ddc0a --- /dev/null +++ b/src/univalue/lib/univalue_utffilter.h @@ -0,0 +1,119 @@ +// Copyright 2016 Wladimir J. van der Laan +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef UNIVALUE_UTFFILTER_H +#define UNIVALUE_UTFFILTER_H + +#include + +/** + * Filter that generates and validates UTF-8, as well as collates UTF-16 + * surrogate pairs as specified in RFC4627. + */ +class JSONUTF8StringFilter +{ +public: + JSONUTF8StringFilter(std::string &s): + str(s), is_valid(true), codepoint(0), state(0), surpair(0) + { + } + // Write single 8-bit char (may be part of UTF-8 sequence) + void push_back(unsigned char ch) + { + if (state == 0) { + if (ch < 0x80) // 7-bit ASCII, fast direct pass-through + str.push_back(ch); + else if (ch < 0xc0) // Mid-sequence character, invalid in this state + is_valid = false; + else if (ch < 0xe0) { // Start of 2-byte sequence + codepoint = (ch & 0x1f) << 6; + state = 6; + } else if (ch < 0xf0) { // Start of 3-byte sequence + codepoint = (ch & 0x0f) << 12; + state = 12; + } else if (ch < 0xf8) { // Start of 4-byte sequence + codepoint = (ch & 0x07) << 18; + state = 18; + } else // Reserved, invalid + is_valid = false; + } else { + if ((ch & 0xc0) != 0x80) // Not a continuation, invalid + is_valid = false; + state -= 6; + codepoint |= (ch & 0x3f) << state; + if (state == 0) + push_back_u(codepoint); + } + } + // Write codepoint directly, possibly collating surrogate pairs + void push_back_u(unsigned int codepoint_) + { + if (state) // Only accept full codepoints in open state + is_valid = false; + if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair + if (surpair) // Two subsequent surrogate pair openers - fail + is_valid = false; + else + surpair = codepoint_; + } else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair + if (surpair) { // Open surrogate pair, expect second half + // Compute code point from UTF-16 surrogate pair + append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00)); + surpair = 0; + } else // Second half doesn't follow a first half - fail + is_valid = false; + } else { + if (surpair) // First half of surrogate pair not followed by second - fail + is_valid = false; + else + append_codepoint(codepoint_); + } + } + // Check that we're in a state where the string can be ended + // No open sequences, no open surrogate pairs, etc + bool finalize() + { + if (state || surpair) + is_valid = false; + return is_valid; + } +private: + std::string &str; + bool is_valid; + // Current UTF-8 decoding state + unsigned int codepoint; + int state; // Top bit to be filled in for next UTF-8 byte, or 0 + + // Keep track of the following state to handle the following section of + // RFC4627: + // + // To escape an extended character that is not in the Basic Multilingual + // Plane, the character is represented as a twelve-character sequence, + // encoding the UTF-16 surrogate pair. So, for example, a string + // containing only the G clef character (U+1D11E) may be represented as + // "\uD834\uDD1E". + // + // Two subsequent \u.... may have to be replaced with one actual codepoint. + unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0 + + void append_codepoint(unsigned int codepoint_) + { + if (codepoint_ <= 0x7f) + str.push_back((char)codepoint_); + else if (codepoint_ <= 0x7FF) { + str.push_back((char)(0xC0 | (codepoint_ >> 6))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0xFFFF) { + str.push_back((char)(0xE0 | (codepoint_ >> 12))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0x1FFFFF) { + str.push_back((char)(0xF0 | (codepoint_ >> 18))); + str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } + } +}; + +#endif diff --git a/src/univalue/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp similarity index 83% rename from src/univalue/univalue_write.cpp rename to src/univalue/lib/univalue_write.cpp index d360c253b..cf2783599 100644 --- a/src/univalue/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -2,15 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include #include #include #include #include "univalue.h" #include "univalue_escapes.h" -// TODO: Using UTF8 - using namespace std; static string json_escape(const string& inS) @@ -24,15 +21,8 @@ static string json_escape(const string& inS) if (escStr) outS += escStr; - - else if (isprint(ch)) + else outS += ch; - - else { - char tmpesc[16]; - sprintf(tmpesc, "\\u%04x", ch); - outS += tmpesc; - } } return outS; @@ -61,13 +51,6 @@ string UniValue::write(unsigned int prettyIndent, case VSTR: s += "\"" + json_escape(val) + "\""; break; - case VREAL: - { - std::stringstream ss; - ss << std::showpoint << std::fixed << std::setprecision(8) << get_real(); - s += ss.str(); - } - break; case VNUM: s += val; break; @@ -96,8 +79,6 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s s += values[i].write(prettyIndent, indentLevel + 1); if (i != (values.size() - 1)) { s += ","; - if (prettyIndent) - s += " "; } if (prettyIndent) s += "\n"; @@ -120,7 +101,7 @@ void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, s += "\"" + json_escape(keys[i]) + "\":"; if (prettyIndent) s += " "; - s += values[i].write(prettyIndent, indentLevel + 1); + s += values.at(i).write(prettyIndent, indentLevel + 1); if (i != (values.size() - 1)) s += ","; if (prettyIndent) diff --git a/src/univalue/pc/libunivalue-uninstalled.pc.in b/src/univalue/pc/libunivalue-uninstalled.pc.in new file mode 100644 index 000000000..b7f53e875 --- /dev/null +++ b/src/univalue/pc/libunivalue-uninstalled.pc.in @@ -0,0 +1,9 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libunivalue +Description: libunivalue, C++ universal value object and JSON library +Version: @VERSION@ +Libs: ${pc_top_builddir}/${pcfiledir}/libunivalue.la diff --git a/src/univalue/pc/libunivalue.pc.in b/src/univalue/pc/libunivalue.pc.in new file mode 100644 index 000000000..358a2d5f7 --- /dev/null +++ b/src/univalue/pc/libunivalue.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libunivalue +Description: libunivalue, C++ universal value object and JSON library +Version: @VERSION@ +Libs: -L${libdir} -lunivalue +Cflags: -I${includedir} diff --git a/src/univalue/test/.gitignore b/src/univalue/test/.gitignore new file mode 100644 index 000000000..9d0ed5701 --- /dev/null +++ b/src/univalue/test/.gitignore @@ -0,0 +1,6 @@ +unitester +test_json +no_nul + +*.trs +*.log diff --git a/src/univalue/test/fail1.json b/src/univalue/test/fail1.json new file mode 100644 index 000000000..8feb01a6d --- /dev/null +++ b/src/univalue/test/fail1.json @@ -0,0 +1 @@ +"This is a string that never ends, yes it goes on and on, my friends. diff --git a/src/univalue/test/fail10.json b/src/univalue/test/fail10.json new file mode 100644 index 000000000..5d8c0047b --- /dev/null +++ b/src/univalue/test/fail10.json @@ -0,0 +1 @@ +{"Extra value after close": true} "misplaced quoted value" \ No newline at end of file diff --git a/src/univalue/test/fail11.json b/src/univalue/test/fail11.json new file mode 100644 index 000000000..76eb95b45 --- /dev/null +++ b/src/univalue/test/fail11.json @@ -0,0 +1 @@ +{"Illegal expression": 1 + 2} \ No newline at end of file diff --git a/src/univalue/test/fail12.json b/src/univalue/test/fail12.json new file mode 100644 index 000000000..77580a452 --- /dev/null +++ b/src/univalue/test/fail12.json @@ -0,0 +1 @@ +{"Illegal invocation": alert()} \ No newline at end of file diff --git a/src/univalue/test/fail13.json b/src/univalue/test/fail13.json new file mode 100644 index 000000000..379406b59 --- /dev/null +++ b/src/univalue/test/fail13.json @@ -0,0 +1 @@ +{"Numbers cannot have leading zeroes": 013} \ No newline at end of file diff --git a/src/univalue/test/fail14.json b/src/univalue/test/fail14.json new file mode 100644 index 000000000..0ed366b38 --- /dev/null +++ b/src/univalue/test/fail14.json @@ -0,0 +1 @@ +{"Numbers cannot be hex": 0x14} \ No newline at end of file diff --git a/src/univalue/test/fail15.json b/src/univalue/test/fail15.json new file mode 100644 index 000000000..fc8376b60 --- /dev/null +++ b/src/univalue/test/fail15.json @@ -0,0 +1 @@ +["Illegal backslash escape: \x15"] \ No newline at end of file diff --git a/src/univalue/test/fail16.json b/src/univalue/test/fail16.json new file mode 100644 index 000000000..3fe21d4b5 --- /dev/null +++ b/src/univalue/test/fail16.json @@ -0,0 +1 @@ +[\naked] \ No newline at end of file diff --git a/src/univalue/test/fail17.json b/src/univalue/test/fail17.json new file mode 100644 index 000000000..62b9214ae --- /dev/null +++ b/src/univalue/test/fail17.json @@ -0,0 +1 @@ +["Illegal backslash escape: \017"] \ No newline at end of file diff --git a/src/univalue/test/fail18.json b/src/univalue/test/fail18.json new file mode 100644 index 000000000..edac92716 --- /dev/null +++ b/src/univalue/test/fail18.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/src/univalue/test/fail19.json b/src/univalue/test/fail19.json new file mode 100644 index 000000000..3b9c46fa9 --- /dev/null +++ b/src/univalue/test/fail19.json @@ -0,0 +1 @@ +{"Missing colon" null} \ No newline at end of file diff --git a/src/univalue/test/fail2.json b/src/univalue/test/fail2.json new file mode 100644 index 000000000..6b7c11e5a --- /dev/null +++ b/src/univalue/test/fail2.json @@ -0,0 +1 @@ +["Unclosed array" \ No newline at end of file diff --git a/src/univalue/test/fail20.json b/src/univalue/test/fail20.json new file mode 100644 index 000000000..27c1af3e7 --- /dev/null +++ b/src/univalue/test/fail20.json @@ -0,0 +1 @@ +{"Double colon":: null} \ No newline at end of file diff --git a/src/univalue/test/fail21.json b/src/univalue/test/fail21.json new file mode 100644 index 000000000..62474573b --- /dev/null +++ b/src/univalue/test/fail21.json @@ -0,0 +1 @@ +{"Comma instead of colon", null} \ No newline at end of file diff --git a/src/univalue/test/fail22.json b/src/univalue/test/fail22.json new file mode 100644 index 000000000..a7752581b --- /dev/null +++ b/src/univalue/test/fail22.json @@ -0,0 +1 @@ +["Colon instead of comma": false] \ No newline at end of file diff --git a/src/univalue/test/fail23.json b/src/univalue/test/fail23.json new file mode 100644 index 000000000..494add1ca --- /dev/null +++ b/src/univalue/test/fail23.json @@ -0,0 +1 @@ +["Bad value", truth] \ No newline at end of file diff --git a/src/univalue/test/fail24.json b/src/univalue/test/fail24.json new file mode 100644 index 000000000..caff239bf --- /dev/null +++ b/src/univalue/test/fail24.json @@ -0,0 +1 @@ +['single quote'] \ No newline at end of file diff --git a/src/univalue/test/fail25.json b/src/univalue/test/fail25.json new file mode 100644 index 000000000..8b7ad23e0 --- /dev/null +++ b/src/univalue/test/fail25.json @@ -0,0 +1 @@ +[" tab character in string "] \ No newline at end of file diff --git a/src/univalue/test/fail26.json b/src/univalue/test/fail26.json new file mode 100644 index 000000000..845d26a6a --- /dev/null +++ b/src/univalue/test/fail26.json @@ -0,0 +1 @@ +["tab\ character\ in\ string\ "] \ No newline at end of file diff --git a/src/univalue/test/fail27.json b/src/univalue/test/fail27.json new file mode 100644 index 000000000..6b01a2ca4 --- /dev/null +++ b/src/univalue/test/fail27.json @@ -0,0 +1,2 @@ +["line +break"] \ No newline at end of file diff --git a/src/univalue/test/fail28.json b/src/univalue/test/fail28.json new file mode 100644 index 000000000..621a0101c --- /dev/null +++ b/src/univalue/test/fail28.json @@ -0,0 +1,2 @@ +["line\ +break"] \ No newline at end of file diff --git a/src/univalue/test/fail29.json b/src/univalue/test/fail29.json new file mode 100644 index 000000000..47ec421bb --- /dev/null +++ b/src/univalue/test/fail29.json @@ -0,0 +1 @@ +[0e] \ No newline at end of file diff --git a/src/univalue/test/fail3.json b/src/univalue/test/fail3.json new file mode 100644 index 000000000..168c81eb7 --- /dev/null +++ b/src/univalue/test/fail3.json @@ -0,0 +1 @@ +{unquoted_key: "keys must be quoted"} \ No newline at end of file diff --git a/src/univalue/test/fail30.json b/src/univalue/test/fail30.json new file mode 100644 index 000000000..8ab0bc4b8 --- /dev/null +++ b/src/univalue/test/fail30.json @@ -0,0 +1 @@ +[0e+] \ No newline at end of file diff --git a/src/univalue/test/fail31.json b/src/univalue/test/fail31.json new file mode 100644 index 000000000..1cce602b5 --- /dev/null +++ b/src/univalue/test/fail31.json @@ -0,0 +1 @@ +[0e+-1] \ No newline at end of file diff --git a/src/univalue/test/fail32.json b/src/univalue/test/fail32.json new file mode 100644 index 000000000..45cba7396 --- /dev/null +++ b/src/univalue/test/fail32.json @@ -0,0 +1 @@ +{"Comma instead if closing brace": true, \ No newline at end of file diff --git a/src/univalue/test/fail33.json b/src/univalue/test/fail33.json new file mode 100644 index 000000000..ca5eb19dc --- /dev/null +++ b/src/univalue/test/fail33.json @@ -0,0 +1 @@ +["mismatch"} \ No newline at end of file diff --git a/src/univalue/test/fail34.json b/src/univalue/test/fail34.json new file mode 100644 index 000000000..3f8be1728 --- /dev/null +++ b/src/univalue/test/fail34.json @@ -0,0 +1 @@ +{} garbage \ No newline at end of file diff --git a/src/univalue/test/fail35.json b/src/univalue/test/fail35.json new file mode 100644 index 000000000..de30ca5c4 --- /dev/null +++ b/src/univalue/test/fail35.json @@ -0,0 +1 @@ +[ true true true [] [] [] ] diff --git a/src/univalue/test/fail36.json b/src/univalue/test/fail36.json new file mode 100644 index 000000000..f82eb8e1f --- /dev/null +++ b/src/univalue/test/fail36.json @@ -0,0 +1 @@ +{"a":} diff --git a/src/univalue/test/fail37.json b/src/univalue/test/fail37.json new file mode 100644 index 000000000..3294dc3a4 --- /dev/null +++ b/src/univalue/test/fail37.json @@ -0,0 +1 @@ +{"a":1 "b":2} diff --git a/src/univalue/test/fail38.json b/src/univalue/test/fail38.json new file mode 100644 index 000000000..b245e2e46 --- /dev/null +++ b/src/univalue/test/fail38.json @@ -0,0 +1 @@ +["\ud834"] diff --git a/src/univalue/test/fail39.json b/src/univalue/test/fail39.json new file mode 100644 index 000000000..7c9e263f2 --- /dev/null +++ b/src/univalue/test/fail39.json @@ -0,0 +1 @@ +["\udd61"] diff --git a/src/univalue/test/fail4.json b/src/univalue/test/fail4.json new file mode 100644 index 000000000..9de168bf3 --- /dev/null +++ b/src/univalue/test/fail4.json @@ -0,0 +1 @@ +["extra comma",] \ No newline at end of file diff --git a/src/univalue/test/fail40.json b/src/univalue/test/fail40.json new file mode 100644 index 000000000..664dc9e24 --- /dev/null +++ b/src/univalue/test/fail40.json @@ -0,0 +1 @@ +["…ก"] \ No newline at end of file diff --git a/src/univalue/test/fail41.json b/src/univalue/test/fail41.json new file mode 100644 index 000000000..0de342a2b --- /dev/null +++ b/src/univalue/test/fail41.json @@ -0,0 +1 @@ +["๐…"] \ No newline at end of file diff --git a/src/univalue/test/fail42.json b/src/univalue/test/fail42.json new file mode 100644 index 000000000..9c7565adb Binary files /dev/null and b/src/univalue/test/fail42.json differ diff --git a/src/univalue/test/fail5.json b/src/univalue/test/fail5.json new file mode 100644 index 000000000..ddf3ce3d2 --- /dev/null +++ b/src/univalue/test/fail5.json @@ -0,0 +1 @@ +["double extra comma",,] \ No newline at end of file diff --git a/src/univalue/test/fail6.json b/src/univalue/test/fail6.json new file mode 100644 index 000000000..ed91580e1 --- /dev/null +++ b/src/univalue/test/fail6.json @@ -0,0 +1 @@ +[ , "<-- missing value"] \ No newline at end of file diff --git a/src/univalue/test/fail7.json b/src/univalue/test/fail7.json new file mode 100644 index 000000000..8a96af3e4 --- /dev/null +++ b/src/univalue/test/fail7.json @@ -0,0 +1 @@ +["Comma after the close"], \ No newline at end of file diff --git a/src/univalue/test/fail8.json b/src/univalue/test/fail8.json new file mode 100644 index 000000000..b28479c6e --- /dev/null +++ b/src/univalue/test/fail8.json @@ -0,0 +1 @@ +["Extra close"]] \ No newline at end of file diff --git a/src/univalue/test/fail9.json b/src/univalue/test/fail9.json new file mode 100644 index 000000000..5815574f3 --- /dev/null +++ b/src/univalue/test/fail9.json @@ -0,0 +1 @@ +{"Extra comma": true,} \ No newline at end of file diff --git a/src/univalue/test/no_nul.cpp b/src/univalue/test/no_nul.cpp new file mode 100644 index 000000000..83d292200 --- /dev/null +++ b/src/univalue/test/no_nul.cpp @@ -0,0 +1,8 @@ +#include "univalue.h" + +int main (int argc, char *argv[]) +{ + char buf[] = "___[1,2,3]___"; + UniValue val; + return val.read(buf + 3, 7) ? 0 : 1; +} diff --git a/src/univalue/test/pass1.json b/src/univalue/test/pass1.json new file mode 100644 index 000000000..70e268543 --- /dev/null +++ b/src/univalue/test/pass1.json @@ -0,0 +1,58 @@ +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00,2e+00,2e-00 +,"rosebud"] \ No newline at end of file diff --git a/src/univalue/test/pass2.json b/src/univalue/test/pass2.json new file mode 100644 index 000000000..d3c63c7ad --- /dev/null +++ b/src/univalue/test/pass2.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/src/univalue/test/pass3.json b/src/univalue/test/pass3.json new file mode 100644 index 000000000..4528d51f1 --- /dev/null +++ b/src/univalue/test/pass3.json @@ -0,0 +1,6 @@ +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} diff --git a/src/univalue/test/round1.json b/src/univalue/test/round1.json new file mode 100644 index 000000000..a711e7308 --- /dev/null +++ b/src/univalue/test/round1.json @@ -0,0 +1 @@ +["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f"] diff --git a/src/univalue/test/round2.json b/src/univalue/test/round2.json new file mode 100644 index 000000000..b766cccc6 --- /dev/null +++ b/src/univalue/test/round2.json @@ -0,0 +1 @@ +["aยงโ– ๐Ž’๐…ก"] diff --git a/src/univalue/test/round3.json b/src/univalue/test/round3.json new file mode 100644 index 000000000..7182dc2f9 --- /dev/null +++ b/src/univalue/test/round3.json @@ -0,0 +1 @@ +"abcdefghijklmnopqrstuvwxyz" diff --git a/src/univalue/test/round4.json b/src/univalue/test/round4.json new file mode 100644 index 000000000..7f8f011eb --- /dev/null +++ b/src/univalue/test/round4.json @@ -0,0 +1 @@ +7 diff --git a/src/univalue/test/round5.json b/src/univalue/test/round5.json new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/src/univalue/test/round5.json @@ -0,0 +1 @@ +true diff --git a/src/univalue/test/round6.json b/src/univalue/test/round6.json new file mode 100644 index 000000000..c508d5366 --- /dev/null +++ b/src/univalue/test/round6.json @@ -0,0 +1 @@ +false diff --git a/src/univalue/test/round7.json b/src/univalue/test/round7.json new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/src/univalue/test/round7.json @@ -0,0 +1 @@ +null diff --git a/src/univalue/test/test_json.cpp b/src/univalue/test/test_json.cpp new file mode 100644 index 000000000..2943bae2b --- /dev/null +++ b/src/univalue/test/test_json.cpp @@ -0,0 +1,24 @@ +// Test program that can be called by the JSON test suite at +// https://github.com/nst/JSONTestSuite. +// +// It reads JSON input from stdin and exits with code 0 if it can be parsed +// successfully. It also pretty prints the parsed JSON value to stdout. + +#include +#include +#include "univalue.h" + +using namespace std; + +int main (int argc, char *argv[]) +{ + UniValue val; + if (val.read(string(istreambuf_iterator(cin), + istreambuf_iterator()))) { + cout << val.write(1 /* prettyIndent */, 4 /* indentLevel */) << endl; + return 0; + } else { + cerr << "JSON Parse Error." << endl; + return 1; + } +} diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp new file mode 100644 index 000000000..aa6f91c1b --- /dev/null +++ b/src/univalue/test/unitester.cpp @@ -0,0 +1,169 @@ +// 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 +#include +#include +#include +#include +#include "univalue.h" + +#ifndef JSON_TEST_SRC +#error JSON_TEST_SRC must point to test source directory +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +using namespace std; +string srcdir(JSON_TEST_SRC); +static bool test_failed = false; + +#define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } } +#define f_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", __func__); } } + +static std::string rtrim(std::string s) +{ + s.erase(s.find_last_not_of(" \n\r\t")+1); + return s; +} + +static void runtest(string filename, const string& jdata) +{ + string prefix = filename.substr(0, 4); + + bool wantPass = (prefix == "pass") || (prefix == "roun"); + bool wantFail = (prefix == "fail"); + bool wantRoundTrip = (prefix == "roun"); + assert(wantPass || wantFail); + + UniValue val; + bool testResult = val.read(jdata); + + if (wantPass) { + d_assert(testResult == true); + } else { + d_assert(testResult == false); + } + + if (wantRoundTrip) { + std::string odata = val.write(0, 0); + assert(odata == rtrim(jdata)); + } +} + +static void runtest_file(const char *filename_) +{ + string basename(filename_); + string filename = srcdir + "/" + basename; + FILE *f = fopen(filename.c_str(), "r"); + assert(f != NULL); + + string jdata; + + char buf[4096]; + while (!feof(f)) { + int bread = fread(buf, 1, sizeof(buf), f); + assert(!ferror(f)); + + string s(buf, bread); + jdata += s; + } + + assert(!ferror(f)); + fclose(f); + + runtest(basename, jdata); +} + +static const char *filenames[] = { + "fail10.json", + "fail11.json", + "fail12.json", + "fail13.json", + "fail14.json", + "fail15.json", + "fail16.json", + "fail17.json", + //"fail18.json", // investigate + "fail19.json", + "fail1.json", + "fail20.json", + "fail21.json", + "fail22.json", + "fail23.json", + "fail24.json", + "fail25.json", + "fail26.json", + "fail27.json", + "fail28.json", + "fail29.json", + "fail2.json", + "fail30.json", + "fail31.json", + "fail32.json", + "fail33.json", + "fail34.json", + "fail35.json", + "fail36.json", + "fail37.json", + "fail38.json", // invalid unicode: only first half of surrogate pair + "fail39.json", // invalid unicode: only second half of surrogate pair + "fail40.json", // invalid unicode: broken UTF-8 + "fail41.json", // invalid unicode: unfinished UTF-8 + "fail42.json", // valid json with garbage following a nul byte + "fail3.json", + "fail4.json", // extra comma + "fail5.json", + "fail6.json", + "fail7.json", + "fail8.json", + "fail9.json", // extra comma + "pass1.json", + "pass2.json", + "pass3.json", + "round1.json", // round-trip test + "round2.json", // unicode + "round3.json", // bare string + "round4.json", // bare number + "round5.json", // bare true + "round6.json", // bare false + "round7.json", // bare null +}; + +// Test \u handling +void unescape_unicode_test() +{ + UniValue val; + bool testResult; + // Escaped ASCII (quote) + testResult = val.read("[\"\\u0022\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\""); + // Escaped Basic Plane character, two-byte UTF-8 + testResult = val.read("[\"\\u0191\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\xc6\x91"); + // Escaped Basic Plane character, three-byte UTF-8 + testResult = val.read("[\"\\u2191\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\xe2\x86\x91"); + // Escaped Supplementary Plane character U+1d161 + testResult = val.read("[\"\\ud834\\udd61\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\xf0\x9d\x85\xa1"); +} + +int main (int argc, char *argv[]) +{ + for (unsigned int fidx = 0; fidx < ARRAY_SIZE(filenames); fidx++) { + runtest_file(filenames[fidx]); + } + + unescape_unicode_test(); + + return test_failed ? 1 : 0; +} + diff --git a/src/utilmoneystr.cpp b/src/utilmoneystr.cpp index 2fbc04887..0f3203432 100644 --- a/src/utilmoneystr.cpp +++ b/src/utilmoneystr.cpp @@ -11,7 +11,7 @@ using namespace std; -string FormatMoney(const CAmount& n, bool fPlus) +std::string FormatMoney(const CAmount& n) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. @@ -29,8 +29,6 @@ string FormatMoney(const CAmount& n, bool fPlus) if (n < 0) str.insert((unsigned int)0, 1, '-'); - else if (fPlus && n > 0) - str.insert((unsigned int)0, 1, '+'); return str; } diff --git a/src/utilmoneystr.h b/src/utilmoneystr.h index cd9b04810..99c3ba830 100644 --- a/src/utilmoneystr.h +++ b/src/utilmoneystr.h @@ -14,7 +14,7 @@ #include "amount.h" -std::string FormatMoney(const CAmount& n, bool fPlus=false); +std::string FormatMoney(const CAmount& n); bool ParseMoney(const std::string& str, CAmount& nRet); bool ParseMoney(const char* pszIn, CAmount& nRet); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 81f9e2f9d..142d5a4eb 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -480,11 +480,12 @@ bool ParseDouble(const std::string& str, double *out) return false; if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed return false; - char *endp = NULL; - errno = 0; // strtod will not set errno if valid - double n = strtod(str.c_str(), &endp); - if(out) *out = n; - return endp && *endp == 0 && !errno; + std::istringstream text(str); + text.imbue(std::locale::classic()); + double result; + text >> result; + if(out) *out = result; + return text.eof() && !text.fail(); } std::string FormatParagraph(const std::string in, size_t width, size_t indent) @@ -554,3 +555,123 @@ int atoi(const std::string& str) { return atoi(str.c_str()); } + +/** Upper bound for mantissa. + * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer. + * Larger integers cannot consist of arbitrary combinations of 0-9: + * + * 999999999999999999 1^18-1 + * 9223372036854775807 (1<<63)-1 (max int64_t) + * 9999999999999999999 1^19-1 (would overflow) + */ +static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL; + +/** Helper function for ParseFixedPoint */ +static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros) +{ + if(ch == '0') + ++mantissa_tzeros; + else { + for (int i=0; i<=mantissa_tzeros; ++i) { + if (mantissa > (UPPER_BOUND / 10LL)) + return false; /* overflow */ + mantissa *= 10; + } + mantissa += ch - '0'; + mantissa_tzeros = 0; + } + return true; +} + +bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) +{ + int64_t mantissa = 0; + int64_t exponent = 0; + int mantissa_tzeros = 0; + bool mantissa_sign = false; + bool exponent_sign = false; + int ptr = 0; + int end = val.size(); + int point_ofs = 0; + + if (ptr < end && val[ptr] == '-') { + mantissa_sign = true; + ++ptr; + } + if (ptr < end) + { + if (val[ptr] == '0') { + /* pass single 0 */ + ++ptr; + } else if (val[ptr] >= '1' && val[ptr] <= '9') { + while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) + return false; /* overflow */ + ++ptr; + } + } else return false; /* missing expected digit */ + } else return false; /* empty string or loose '-' */ + if (ptr < end && val[ptr] == '.') + { + ++ptr; + if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') + { + while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) + return false; /* overflow */ + ++ptr; + ++point_ofs; + } + } else return false; /* missing expected digit */ + } + if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E')) + { + ++ptr; + if (ptr < end && val[ptr] == '+') + ++ptr; + else if (ptr < end && val[ptr] == '-') { + exponent_sign = true; + ++ptr; + } + if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + if (exponent > (UPPER_BOUND / 10LL)) + return false; /* overflow */ + exponent = exponent * 10 + val[ptr] - '0'; + ++ptr; + } + } else return false; /* missing expected digit */ + } + if (ptr != end) + return false; /* trailing garbage */ + + /* finalize exponent */ + if (exponent_sign) + exponent = -exponent; + exponent = exponent - point_ofs + mantissa_tzeros; + + /* finalize mantissa */ + if (mantissa_sign) + mantissa = -mantissa; + + /* convert to one 64-bit fixed-point value */ + exponent += decimals; + if (exponent < 0) + return false; /* cannot represent values smaller than 10^-decimals */ + if (exponent >= 18) + return false; /* cannot represent values larger than or equal to 10^(18-decimals) */ + + for (int i=0; i < exponent; ++i) { + if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL)) + return false; /* overflow */ + mantissa *= 10; + } + if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND) + return false; /* overflow */ + + if (amount_out) + *amount_out = mantissa; + + return true; +} + diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index a6184d8f1..08d337727 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -110,4 +110,11 @@ bool TimingResistantEqual(const T& a, const T& b) return accumulator == 0; } +/** Parse number as fixed point according to JSON number syntax. + * See http://json.org/number.gif + * @returns true on success, false on error. + * @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger. + */ +bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); + #endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index e18dd4408..de9744bb8 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -277,12 +277,12 @@ bool AsyncRPCOperation_sendmany::main_impl() { } LogPrint((isfromtaddr_) ? "zrpc" : "zrpcunsafe", "%s: spending %s to send %s with fee %s\n", - getId(), FormatMoney(targetAmount, false), FormatMoney(sendAmount, false), FormatMoney(minersFee, false)); - LogPrint("zrpc", "%s: transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total, false)); - LogPrint("zrpcunsafe", "%s: private input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total, false)); - LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(t_outputs_total, false)); - LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total, false)); - LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee, false)); + getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); + LogPrint("zrpc", "%s: transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total)); + LogPrint("zrpcunsafe", "%s: private input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total)); + LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(t_outputs_total)); + LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total)); + LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); /** * SCENARIO #1 @@ -303,7 +303,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", getId(), - FormatMoney(change, false) + FormatMoney(change) ); } @@ -379,7 +379,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { add_taddr_change_output_to_tx(change); LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", getId(), - FormatMoney(change, false) + FormatMoney(change) ); } } @@ -472,7 +472,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { outPoint.hash.ToString().substr(0, 10), outPoint.js, int(outPoint.n), // uint8_t - FormatMoney(noteFunds, false), + FormatMoney(noteFunds), wtxHeight, wtxDepth ); @@ -498,7 +498,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", getId(), - FormatMoney(jsChange, false) + FormatMoney(jsChange) ); } @@ -594,7 +594,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", getId(), - FormatMoney(plaintext.value, false) + FormatMoney(plaintext.value) ); } catch (const std::exception& e) { @@ -644,7 +644,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { jso.hash.ToString().substr(0, 10), jso.js, int(jso.n), // uint8_t - FormatMoney(noteFunds, false), + FormatMoney(noteFunds), wtxHeight, wtxDepth ); @@ -736,7 +736,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", getId(), - FormatMoney(jsChange, false) + FormatMoney(jsChange) ); } @@ -879,7 +879,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { entry.jsop.hash.ToString().substr(0, 10), entry.jsop.js, int(entry.jsop.n), // uint8_t - FormatMoney(entry.plaintext.value, false), + FormatMoney(entry.plaintext.value), HexStr(data).substr(0, 10) ); } @@ -955,9 +955,9 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", getId(), tx_.vjoinsplit.size(), - FormatMoney(info.vpub_old, false), FormatMoney(info.vpub_new, false), - FormatMoney(info.vjsin[0].note.value, false), FormatMoney(info.vjsin[1].note.value, false), - FormatMoney(info.vjsout[0].value, false), FormatMoney(info.vjsout[1].value, false) + FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), + FormatMoney(info.vjsin[0].note.value), FormatMoney(info.vjsin[1].note.value), + FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value) ); // Generate the proof, this can take over a minute. diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index e870e6039..6fac61160 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -16,7 +16,7 @@ #include #include -#include "univalue/univalue.h" +#include // Default transaction fee if caller does not specify one. #define ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE 10000 diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 7f9c89da2..7ae68b994 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -19,7 +19,7 @@ #include #include -#include "univalue/univalue.h" +#include using namespace std; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5e1efe2db..a23ce1a30 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -31,7 +31,7 @@ #include -#include "univalue/univalue.h" +#include #include @@ -433,6 +433,8 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) // Amount CAmount nAmount = AmountFromValue(params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); // Wallet comments CWalletTx wtx; @@ -824,6 +826,8 @@ UniValue movecmd(const UniValue& params, bool fHelp) string strFrom = AccountFromValue(params[0]); string strTo = AccountFromValue(params[1]); CAmount nAmount = AmountFromValue(params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); if (params.size() > 3) // unused parameter, used to be nMinDepth, keep type-checking it though (void)params[3].get_int(); @@ -903,6 +907,8 @@ UniValue sendfrom(const UniValue& params, bool fHelp) if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); CAmount nAmount = AmountFromValue(params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); int nMinDepth = 1; if (params.size() > 3) nMinDepth = params[3].get_int(); @@ -1002,6 +1008,8 @@ UniValue sendmany(const UniValue& params, bool fHelp) CScript scriptPubKey = GetScriptForDestination(address.Get()); CAmount nAmount = AmountFromValue(sendTo[name_]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); totalAmount += nAmount; bool fSubtractFeeFromAmount = false; @@ -2227,9 +2235,7 @@ UniValue settxfee(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Amount - CAmount nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + CAmount nAmount = AmountFromValue(params[0]); payTxFee = CFeeRate(nAmount, 1000); return true; @@ -3124,9 +3130,9 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth); CAmount nTotalBalance = nBalance + nPrivateBalance; UniValue result(UniValue::VOBJ); - result.push_back(Pair("transparent", FormatMoney(nBalance, false))); - result.push_back(Pair("private", FormatMoney(nPrivateBalance, false))); - result.push_back(Pair("total", FormatMoney(nTotalBalance, false))); + result.push_back(Pair("transparent", FormatMoney(nBalance))); + result.push_back(Pair("private", FormatMoney(nPrivateBalance))); + result.push_back(Pair("total", FormatMoney(nTotalBalance))); return result; } @@ -3177,7 +3183,7 @@ UniValue z_getoperationstatus_IMPL(const UniValue& params, bool fRemoveFinishedO std::set filter; if (params.size()==1) { UniValue ids = params[0].get_array(); - for (UniValue & v : ids.getValues()) { + for (const UniValue & v : ids.getValues()) { filter.insert(v.get_str()); } } @@ -3471,4 +3477,3 @@ UniValue z_listoperationids(const UniValue& params, bool fHelp) return ret; } -