diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 5022b1229d..804e388905 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -1,223 +1,224 @@ # Copyright (c) 2013-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. bin_PROGRAMS += test/test_bitcoin noinst_PROGRAMS += test/test_bitcoin_fuzzy TEST_SRCDIR = test TEST_BINARY=test/test_bitcoin$(EXEEXT) JSON_TEST_FILES = \ test/data/base58_encode_decode.json \ test/data/blockfilters.json \ test/data/key_io_valid.json \ test/data/key_io_invalid.json \ test/data/script_tests.json \ test/data/sighash.json \ test/data/tx_invalid.json \ test/data/tx_valid.json RAW_TEST_FILES = GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) BITCOIN_TEST_SUITE = \ test/jsonutil.cpp \ test/scriptflags.cpp \ test/sigutil.cpp \ test/test_bitcoin_main.cpp \ test/test_bitcoin.h \ test/test_bitcoin.cpp # test_bitcoin binary # BITCOIN_TESTS =\ test/scriptnum10.h \ test/activation_tests.cpp \ test/addrman_tests.cpp \ test/allocator_tests.cpp \ test/amount_tests.cpp \ test/arith_uint256_tests.cpp \ test/avalanche_tests.cpp \ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ test/bip32_tests.cpp \ test/bitmanip_tests.cpp \ test/blockchain_tests.cpp \ test/blockcheck_tests.cpp \ test/blockencodings_tests.cpp \ test/blockfilter_tests.cpp \ test/blockindex_tests.cpp \ test/blockstatus_tests.cpp \ test/bloom_tests.cpp \ test/bswap_tests.cpp \ test/cashaddr_tests.cpp \ test/cashaddrenc_tests.cpp \ test/checkdatasig_tests.cpp \ test/checkpoints_tests.cpp \ test/checkqueue_tests.cpp \ test/coins_tests.cpp \ test/compress_tests.cpp \ test/config_tests.cpp \ test/core_io_tests.cpp \ test/crypto_tests.cpp \ test/cuckoocache_tests.cpp \ test/dbwrapper_tests.cpp \ test/denialofservice_tests.cpp \ test/dstencode_tests.cpp \ test/excessiveblock_tests.cpp \ test/feerate_tests.cpp \ test/finalization_tests.cpp \ test/flatfile_tests.cpp \ test/getarg_tests.cpp \ test/hash_tests.cpp \ test/inv_tests.cpp \ test/jsonutil.h \ test/key_io_tests.cpp \ test/key_tests.cpp \ test/lcg_tests.cpp \ test/lcg.h \ test/limitedmap_tests.cpp \ test/main_tests.cpp \ test/mempool_tests.cpp \ test/merkle_tests.cpp \ test/miner_tests.cpp \ test/monolith_opcodes_tests.cpp \ test/multisig_tests.cpp \ test/net_tests.cpp \ test/netbase_tests.cpp \ test/pmt_tests.cpp \ test/policyestimator_tests.cpp \ test/pow_tests.cpp \ test/prevector_tests.cpp \ test/radix_tests.cpp \ test/raii_event_tests.cpp \ test/random_tests.cpp \ test/rcu_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ test/rpc_server_tests.cpp \ test/rwcollection_tests.cpp \ test/sanity_tests.cpp \ test/scheduler_tests.cpp \ test/schnorr_tests.cpp \ test/script_commitment_tests.cpp \ test/script_bitfield_tests.cpp \ test/script_p2sh_tests.cpp \ test/script_standard_tests.cpp \ test/script_tests.cpp \ test/scriptflags.h \ test/scriptnum_tests.cpp \ test/serialize_tests.cpp \ test/sigcache_tests.cpp \ test/sigencoding_tests.cpp \ test/sighash_tests.cpp \ test/sighashtype_tests.cpp \ test/sigopcount_tests.cpp \ test/sigutil.h \ test/skiplist_tests.cpp \ test/streams_tests.cpp \ test/sync_tests.cpp \ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ test/txindex_tests.cpp \ test/txvalidation_tests.cpp \ test/txvalidationcache_tests.cpp \ test/uint256_tests.cpp \ test/undo_tests.cpp \ test/util_tests.cpp \ test/validation_block_tests.cpp \ test/validation_tests.cpp \ test/work_comparator_tests.cpp \ rpc/test/server_tests.cpp if ENABLE_WALLET BITCOIN_TESTS += \ wallet/test/accounting_tests.cpp \ + wallet/test/psbt_wallet_tests.cpp \ wallet/test/wallet_tests.cpp \ wallet/test/walletdb_tests.cpp \ wallet/test/wallet_crypto_tests.cpp \ wallet/test/coinselector_tests.cpp BITCOIN_TEST_SUITE += \ wallet/test/wallet_test_fixture.cpp \ wallet/test/wallet_test_fixture.h endif test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS) test_test_bitcoin_LDADD = if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) endif test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \ $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ test_test_bitcoin_LDADD += $(ZMQ_LIBS) endif # # test_bitcoin_fuzzy binary # test_test_bitcoin_fuzzy_SOURCES = test/test_bitcoin_fuzzy.cpp test_test_bitcoin_fuzzy_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) test_test_bitcoin_fuzzy_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_test_bitcoin_fuzzy_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_test_bitcoin_fuzzy_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) $(BITCOIN_TESTS): $(GENERATED_TEST_FILES) CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES) CLEANFILES += $(CLEAN_BITCOIN_TEST) bitcoin_test: $(TEST_BINARY) bitcoin_test_check: $(TEST_BINARY) FORCE $(MAKE) check-TESTS TESTS=$^ bitcoin_test_clean : FORCE rm -f $(CLEAN_BITCOIN_TEST) $(test_test_bitcoin_OBJECTS) $(TEST_BINARY) check-local: $(BITCOIN_TESTS:.cpp=.cpp.test) @echo "Running test/util/bitcoin-util-test.py..." $(top_builddir)/test/util/bitcoin-util-test.py @echo "Running test/util/rpcauth-test.py..." $(top_builddir)/test/util/rpcauth-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check if EMBEDDED_UNIVALUE $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check endif %.cpp.test: %.cpp @echo Running tests: `cat $< | grep "BOOST_FIXTURE_TEST_SUITE(\|BOOST_AUTO_TEST_SUITE(" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1` from $< $(AM_V_at)$(TEST_BINARY) -l test_suite -t "`cat $< | grep "BOOST_FIXTURE_TEST_SUITE(\|BOOST_AUTO_TEST_SUITE(" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" > $@.log 2>&1 || (cat $@.log && false) %.json.h: %.json @$(MKDIR_P) $(@D) @{ \ echo "namespace json_tests{" && \ echo "static unsigned const char $(*F)[] = {" && \ $(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \ echo "};};"; \ } > "$@.new" && mv -f "$@.new" "$@" @echo "Generated $@" diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index ac74b0c6b8..d41d7fee64 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,185 +1,186 @@ # Copyright (c) 2018 The Bitcoin developers project(bitcoin-test) # Process json files. file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/data") function(gen_json_header NAME) set(HEADERS "") foreach(f ${ARGN}) set(h "${CMAKE_CURRENT_BINARY_DIR}/${f}.h") # Get the proper name for the test variable. get_filename_component(TEST_NAME ${f} NAME_WE) add_custom_command(OUTPUT ${h} COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/data/generate_header.py" "${TEST_NAME}" "${CMAKE_CURRENT_SOURCE_DIR}/${f}" > ${h} MAIN_DEPENDENCY ${f} DEPENDS "data/generate_header.py" VERBATIM ) list(APPEND HEADERS ${h}) endforeach(f) set(${NAME} "${HEADERS}" PARENT_SCOPE) endfunction() gen_json_header(JSON_HEADERS data/base58_encode_decode.json data/blockfilters.json data/key_io_valid.json data/key_io_invalid.json data/script_tests.json data/sighash.json data/tx_invalid.json data/tx_valid.json ) include(TestSuite) create_test_suite(bitcoin) add_dependencies(check check-bitcoin) add_test_to_suite(bitcoin test_bitcoin activation_tests.cpp addrman_tests.cpp allocator_tests.cpp amount_tests.cpp arith_uint256_tests.cpp avalanche_tests.cpp base32_tests.cpp base58_tests.cpp base64_tests.cpp bip32_tests.cpp bitmanip_tests.cpp blockchain_tests.cpp blockcheck_tests.cpp blockencodings_tests.cpp blockfilter_tests.cpp blockindex_tests.cpp blockstatus_tests.cpp bloom_tests.cpp bswap_tests.cpp cashaddr_tests.cpp cashaddrenc_tests.cpp checkdatasig_tests.cpp checkpoints_tests.cpp checkqueue_tests.cpp coins_tests.cpp compress_tests.cpp config_tests.cpp core_io_tests.cpp crypto_tests.cpp cuckoocache_tests.cpp dbwrapper_tests.cpp denialofservice_tests.cpp dstencode_tests.cpp excessiveblock_tests.cpp feerate_tests.cpp finalization_tests.cpp flatfile_tests.cpp getarg_tests.cpp hash_tests.cpp inv_tests.cpp jsonutil.cpp key_io_tests.cpp key_tests.cpp lcg_tests.cpp limitedmap_tests.cpp main_tests.cpp mempool_tests.cpp merkle_tests.cpp miner_tests.cpp monolith_opcodes_tests.cpp multisig_tests.cpp net_tests.cpp netbase_tests.cpp pmt_tests.cpp policyestimator_tests.cpp pow_tests.cpp prevector_tests.cpp radix_tests.cpp raii_event_tests.cpp random_tests.cpp rcu_tests.cpp reverselock_tests.cpp rpc_tests.cpp rpc_server_tests.cpp rwcollection_tests.cpp sanity_tests.cpp scheduler_tests.cpp schnorr_tests.cpp script_bitfield_tests.cpp script_commitment_tests.cpp script_p2sh_tests.cpp script_standard_tests.cpp script_tests.cpp scriptflags.cpp scriptnum_tests.cpp serialize_tests.cpp sigcache_tests.cpp sigencoding_tests.cpp sighash_tests.cpp sighashtype_tests.cpp sigopcount_tests.cpp sigutil.cpp skiplist_tests.cpp streams_tests.cpp sync_tests.cpp test_bitcoin.cpp test_bitcoin_main.cpp timedata_tests.cpp torcontrol_tests.cpp transaction_tests.cpp txindex_tests.cpp txvalidation_tests.cpp txvalidationcache_tests.cpp uint256_tests.cpp undo_tests.cpp util_tests.cpp validation_block_tests.cpp validation_tests.cpp work_comparator_tests.cpp # RPC Tests ../rpc/test/server_tests.cpp # Tests generated from JSON ${JSON_HEADERS} ) find_package(Boost 1.58 REQUIRED unit_test_framework) target_link_libraries(test_bitcoin Boost::unit_test_framework rpcclient server) if(TARGET bitcoinconsensus-shared) target_link_libraries(test_bitcoin bitcoinconsensus-shared) else() target_link_libraries(test_bitcoin bitcoinconsensus) endif() # We need to detect if the BOOST_TEST_DYN_LINK flag is required. set(CMAKE_REQUIRED_LIBRARIES Boost::unit_test_framework) check_cxx_source_compiles(" #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN #include " BOOST_TEST_DYN_LINK) if(BOOST_TEST_DYN_LINK) target_compile_definitions(test_bitcoin PRIVATE BOOST_TEST_DYN_LINK) endif(BOOST_TEST_DYN_LINK) if(BUILD_BITCOIN_WALLET) target_sources(test_bitcoin PRIVATE - ../wallet/test/wallet_test_fixture.cpp ../wallet/test/accounting_tests.cpp + ../wallet/test/coinselector_tests.cpp + ../wallet/test/psbt_wallet_tests.cpp + ../wallet/test/wallet_test_fixture.cpp ../wallet/test/wallet_tests.cpp ../wallet/test/walletdb_tests.cpp ../wallet/test/wallet_crypto_tests.cpp - ../wallet/test/coinselector_tests.cpp ) endif() diff --git a/src/test/cashaddrenc_tests.cpp b/src/test/cashaddrenc_tests.cpp index 9a06b9fc0b..4648fe5ae0 100644 --- a/src/test/cashaddrenc_tests.cpp +++ b/src/test/cashaddrenc_tests.cpp @@ -1,455 +1,461 @@ // Copyright (c) 2017 The Bitcoin 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 #include namespace { std::vector GetNetworks() { return {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST}; } uint160 insecure_GetRandUInt160(FastRandomContext &rand) { uint160 n; for (uint8_t *c = n.begin(); c != n.end(); ++c) { *c = static_cast(rand.rand32()); } return n; } std::vector insecure_GetRandomByteArray(FastRandomContext &rand, size_t n) { std::vector out; out.reserve(n); for (size_t i = 0; i < n; i++) { out.push_back(uint8_t(rand.randbits(8))); } return out; } class DstTypeChecker : public boost::static_visitor { public: void operator()(const CKeyID &id) { isKey = true; } void operator()(const CScriptID &id) { isScript = true; } void operator()(const CNoDestination &) {} static bool IsScriptDst(const CTxDestination &d) { DstTypeChecker checker; boost::apply_visitor(checker, d); return checker.isScript; } static bool IsKeyDst(const CTxDestination &d) { DstTypeChecker checker; boost::apply_visitor(checker, d); return checker.isKey; } private: DstTypeChecker() : isKey(false), isScript(false) {} bool isKey; bool isScript; }; // Map all possible size bits in the version to the expected size of the // hash in bytes. const std::array, 8> valid_sizes = { {{0, 20}, {1, 24}, {2, 28}, {3, 32}, {4, 40}, {5, 48}, {6, 56}, {7, 64}}}; } // namespace BOOST_FIXTURE_TEST_SUITE(cashaddrenc_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(encode_decode_all_sizes) { FastRandomContext rand(true); const std::string prefix = "bitcoincash"; for (auto ps : valid_sizes) { std::vector data = insecure_GetRandomByteArray(rand, ps.second); CashAddrContent content = {PUBKEY_TYPE, data}; std::vector packed_data = PackCashAddrContent(content); // Check that the packed size is correct BOOST_CHECK_EQUAL(packed_data[1] >> 2, ps.first); std::string address = cashaddr::Encode(prefix, packed_data); // Check that the address decodes properly CashAddrContent decoded = DecodeCashAddrContent(address, prefix); BOOST_CHECK_EQUAL_COLLECTIONS( std::begin(content.hash), std::end(content.hash), std::begin(decoded.hash), std::end(decoded.hash)); } } BOOST_AUTO_TEST_CASE(check_packaddr_throws) { FastRandomContext rand(true); for (auto ps : valid_sizes) { std::vector data = insecure_GetRandomByteArray(rand, ps.second - 1); CashAddrContent content = {PUBKEY_TYPE, data}; BOOST_CHECK_THROW(PackCashAddrContent(content), std::runtime_error); } } BOOST_AUTO_TEST_CASE(encode_decode) { std::vector toTest = {CNoDestination{}, CKeyID(uint160S("badf00d")), CScriptID(uint160S("f00dbad"))}; for (auto dst : toTest) { for (auto net : GetNetworks()) { const auto netParams = CreateChainParams(net); std::string encoded = EncodeCashAddr(dst, *netParams); CTxDestination decoded = DecodeCashAddr(encoded, *netParams); BOOST_CHECK(dst == decoded); } } } // Check that an encoded cash address is not valid on another network. BOOST_AUTO_TEST_CASE(invalid_on_wrong_network) { const CTxDestination dst = CKeyID(uint160S("c0ffee")); const CTxDestination invalidDst = CNoDestination{}; for (auto net : GetNetworks()) { for (auto otherNet : GetNetworks()) { if (net == otherNet) continue; const auto netParams = CreateChainParams(net); std::string encoded = EncodeCashAddr(dst, *netParams); const auto otherNetParams = CreateChainParams(otherNet); CTxDestination decoded = DecodeCashAddr(encoded, *otherNetParams); BOOST_CHECK(decoded != dst); BOOST_CHECK(decoded == invalidDst); } } } BOOST_AUTO_TEST_CASE(random_dst) { FastRandomContext rand(true); const size_t NUM_TESTS = 5000; const auto params = CreateChainParams(CBaseChainParams::MAIN); for (size_t i = 0; i < NUM_TESTS; ++i) { uint160 hash = insecure_GetRandUInt160(rand); const CTxDestination dst_key = CKeyID(hash); const CTxDestination dst_scr = CScriptID(hash); const std::string encoded_key = EncodeCashAddr(dst_key, *params); const CTxDestination decoded_key = DecodeCashAddr(encoded_key, *params); const std::string encoded_scr = EncodeCashAddr(dst_scr, *params); const CTxDestination decoded_scr = DecodeCashAddr(encoded_scr, *params); std::string err("cashaddr failed for hash: "); err += hash.ToString(); BOOST_CHECK_MESSAGE(dst_key == decoded_key, err); BOOST_CHECK_MESSAGE(dst_scr == decoded_scr, err); BOOST_CHECK_MESSAGE(DstTypeChecker::IsKeyDst(decoded_key), err); BOOST_CHECK_MESSAGE(DstTypeChecker::IsScriptDst(decoded_scr), err); } } /** * Cashaddr payload made of 5-bit nibbles. The last one is padded. When * converting back to bytes, this extra padding is truncated. In order to ensure * cashaddr are cannonicals, we check that the data we truncate is zeroed. */ BOOST_AUTO_TEST_CASE(check_padding) { uint8_t version = 0; std::vector data = {version}; for (size_t i = 0; i < 33; ++i) { data.push_back(1); } BOOST_CHECK_EQUAL(data.size(), 34UL); const CTxDestination nodst = CNoDestination{}; const auto params = CreateChainParams(CBaseChainParams::MAIN); for (uint8_t i = 0; i < 32; i++) { data[data.size() - 1] = i; std::string fake = cashaddr::Encode(params->CashAddrPrefix(), data); CTxDestination dst = DecodeCashAddr(fake, *params); // We have 168 bits of payload encoded as 170 bits in 5 bits nimbles. As // a result, we must have 2 zeros. if (i & 0x03) { BOOST_CHECK(dst == nodst); } else { BOOST_CHECK(dst != nodst); } } } /** * We ensure type is extracted properly from the version. */ BOOST_AUTO_TEST_CASE(check_type) { std::vector data; data.resize(34); const std::string prefix = "bitcoincash"; for (uint8_t v = 0; v < 16; v++) { std::fill(begin(data), end(data), 0); data[0] = v; auto content = DecodeCashAddrContent(cashaddr::Encode(prefix, data), prefix); BOOST_CHECK_EQUAL(content.type, v); BOOST_CHECK_EQUAL(content.hash.size(), 20UL); // Check that using the reserved bit result in a failure. data[0] |= 0x10; content = DecodeCashAddrContent(cashaddr::Encode(prefix, data), prefix); BOOST_CHECK_EQUAL(content.type, 0); BOOST_CHECK_EQUAL(content.hash.size(), 0UL); } } /** * We ensure size is extracted and checked properly. */ BOOST_AUTO_TEST_CASE(check_size) { const CTxDestination nodst = CNoDestination{}; const std::string prefix = "bitcoincash"; std::vector data; for (auto ps : valid_sizes) { // Number of bytes required for a 5-bit packed version of a hash, with // version byte. Add half a byte(4) so integer math provides the next // multiple-of-5 that would fit all the data. size_t expectedSize = (8 * (1 + ps.second) + 4) / 5; data.resize(expectedSize); std::fill(begin(data), end(data), 0); // After conversion from 8 bit packing to 5 bit packing, the size will // be in the second 5-bit group, shifted left twice. data[1] = ps.first << 2; auto content = DecodeCashAddrContent(cashaddr::Encode(prefix, data), prefix); BOOST_CHECK_EQUAL(content.type, 0); BOOST_CHECK_EQUAL(content.hash.size(), ps.second); data.push_back(0); content = DecodeCashAddrContent(cashaddr::Encode(prefix, data), prefix); BOOST_CHECK_EQUAL(content.type, 0); BOOST_CHECK_EQUAL(content.hash.size(), 0UL); data.pop_back(); data.pop_back(); content = DecodeCashAddrContent(cashaddr::Encode(prefix, data), prefix); BOOST_CHECK_EQUAL(content.type, 0); BOOST_CHECK_EQUAL(content.hash.size(), 0UL); } } BOOST_AUTO_TEST_CASE(test_encode_address) { const auto params = CreateChainParams(CBaseChainParams::MAIN); std::vector> hash{ {118, 160, 64, 83, 189, 160, 168, 139, 218, 81, 119, 184, 106, 21, 195, 178, 159, 85, 152, 115}, {203, 72, 18, 50, 41, 156, 213, 116, 49, 81, 172, 75, 45, 99, 174, 25, 142, 123, 176, 169}, {1, 31, 40, 228, 115, 201, 95, 64, 19, 215, 213, 62, 197, 251, 195, 180, 45, 248, 237, 16}}; std::vector pubkey = { "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a", "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy", "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"}; std::vector script = { "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq", "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e", "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"}; for (size_t i = 0; i < hash.size(); ++i) { const CTxDestination dstKey = CKeyID(uint160(hash[i])); BOOST_CHECK_EQUAL(pubkey[i], EncodeCashAddr(dstKey, *params)); CashAddrContent keyContent{PUBKEY_TYPE, hash[i]}; BOOST_CHECK_EQUAL(pubkey[i], EncodeCashAddr("bitcoincash", keyContent)); const CTxDestination dstScript = CScriptID(uint160(hash[i])); BOOST_CHECK_EQUAL(script[i], EncodeCashAddr(dstScript, *params)); CashAddrContent scriptContent{SCRIPT_TYPE, hash[i]}; BOOST_CHECK_EQUAL(script[i], EncodeCashAddr("bitcoincash", scriptContent)); } } struct CashAddrTestVector { std::string prefix; CashAddrType type; std::vector hash; std::string addr; }; BOOST_AUTO_TEST_CASE(test_vectors) { std::vector cases = { // 20 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"), "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2"}, {"bchtest", SCRIPT_TYPE, ParseHex("F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"), "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t"}, {"prefix", CashAddrType(15), ParseHex("F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"), "prefix:0r6m7j9njldwwzlg9v7v53unlr4jkmx6ey3qnjwsrf"}, + {"bchreg", PUBKEY_TYPE, + ParseHex("d85c2b71d0060b09c9886aeb815e50991dda124d"), + "bchreg:qrv9c2m36qrqkzwf3p4whq272zv3mksjf5ln6v9le5"}, + {"bchreg", PUBKEY_TYPE, + ParseHex("00aea9a2e5f0f876a588df5546e8742d1d87008f"), + "bchreg:qqq2a2dzuhc0sa493r0423hgwsk3mpcq3upac4z3wr"}, // 24 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA"), "bitcoincash:q9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2ws4mr9g0"}, {"bchtest", SCRIPT_TYPE, ParseHex("7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA"), "bchtest:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2u94tsynr"}, {"prefix", CashAddrType(15), ParseHex("7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA"), "prefix:09adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2p29kc2lp"}, // 28 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B"), "bitcoincash:qgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcw59jxxuz"}, {"bchtest", SCRIPT_TYPE, ParseHex("3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B"), "bchtest:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcvs7md7wt"}, {"prefix", CashAddrType(15), ParseHex("3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B"), "prefix:0gagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkc5djw8s9g"}, // 32 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825" "C060"), "bitcoincash:" "qvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq5nlegake"}, {"bchtest", SCRIPT_TYPE, ParseHex("3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825" "C060"), "bchtest:" "pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6"}, {"prefix", CashAddrType(15), ParseHex("3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825" "C060"), "prefix:" "0vch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxqsh6jgp6w"}, // 40 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B256311" "97D194B5C238BEB136FB"), "bitcoincash:" "qnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv39g" "r3uvz"}, {"bchtest", SCRIPT_TYPE, ParseHex("C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B256311" "97D194B5C238BEB136FB"), "bchtest:" "pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvmgm" "6ynej"}, {"prefix", CashAddrType(15), ParseHex("C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B256311" "97D194B5C238BEB136FB"), "prefix:" "0nq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvwsv" "ctzqy"}, // 48 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696" "B98241223D8CE62AD48D863F4CB18C930E4C"), "bitcoincash:" "qh3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7" "n933jfsunqex2w82sl"}, {"bchtest", SCRIPT_TYPE, ParseHex("E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696" "B98241223D8CE62AD48D863F4CB18C930E4C"), "bchtest:" "ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7" "n933jfsunqnzf7mt6x"}, {"prefix", CashAddrType(15), ParseHex("E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696" "B98241223D8CE62AD48D863F4CB18C930E4C"), "prefix:" "0h3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7" "n933jfsunqakcssnmn"}, // 56 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E" "6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041"), "bitcoincash:" "qmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhv" "w8ym5d8qx7sz7zz0zvcypqscw8jd03f"}, {"bchtest", SCRIPT_TYPE, ParseHex("D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E" "6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041"), "bchtest:" "pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhv" "w8ym5d8qx7sz7zz0zvcypqs6kgdsg2g"}, {"prefix", CashAddrType(15), ParseHex("D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E" "6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041"), "prefix:" "0mvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhv" "w8ym5d8qx7sz7zz0zvcypqsgjrqpnw8"}, // 64 bytes {"bitcoincash", PUBKEY_TYPE, ParseHex("D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E" "67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA0" "8884175B"), "bitcoincash:" "qlg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5fl" "ttj6ydvjc0pv3nchp52amk97tqa5zygg96mtky5sv5w"}, {"bchtest", SCRIPT_TYPE, ParseHex("D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E" "67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA0" "8884175B"), "bchtest:" "plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5fl" "ttj6ydvjc0pv3nchp52amk97tqa5zygg96mc773cwez"}, {"prefix", CashAddrType(15), ParseHex("D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E" "67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA0" "8884175B"), "prefix:" "0lg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5fl" "ttj6ydvjc0pv3nchp52amk97tqa5zygg96ms92w6845"}, }; for (const auto &t : cases) { CashAddrContent content{t.type, t.hash}; BOOST_CHECK_EQUAL(t.addr, EncodeCashAddr(t.prefix, content)); std::string err("hash mistmatch for address: "); err += t.addr; content = DecodeCashAddrContent(t.addr, t.prefix); BOOST_CHECK_EQUAL(t.type, content.type); BOOST_CHECK_MESSAGE(t.hash == content.hash, err); } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 892c30d7ef..c9e722d710 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1,5518 +1,5518 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The 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 // for GetConsensus. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Input src/init.h (not wallet/init.h) for StartShutdown #include #include #include #include static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; static std::string urlDecode(const std::string &urlEncoded) { std::string res; if (!urlEncoded.empty()) { char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr); if (decoded) { res = std::string(decoded); free(decoded); } } return res; } bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest &request, std::string &wallet_name) { if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { // wallet endpoint was used wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); return true; } return false; } std::shared_ptr GetWalletForJSONRPCRequest(const JSONRPCRequest &request) { std::string wallet_name; if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) { std::shared_ptr pwallet = GetWallet(wallet_name); if (!pwallet) { throw JSONRPCError( RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); } return pwallet; } std::vector> wallets = GetWallets(); return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet *const pwallet) { return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to " "be set with walletpassphrase " "call." : ""; } bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) { if (pwallet) { return true; } if (avoidException) { return false; } if (!HasWallets()) { throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet " "method is disabled because " "no wallet is loaded)"); } throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED, "Wallet file not specified (must request wallet RPC " "through /wallet/ uri-path)."); } void EnsureWalletIsUnlocked(CWallet *const pwallet) { if (pwallet->IsLocked()) { throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the " "wallet passphrase with " "walletpassphrase first."); } } static void WalletTxToJSON(const CWalletTx &wtx, UniValue &entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { int confirms = wtx.GetDepthInMainChain(); entry.pushKV("confirmations", confirms); if (wtx.IsCoinBase()) { entry.pushKV("generated", true); } if (confirms > 0) { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); } else { entry.pushKV("trusted", wtx.IsTrusted()); } uint256 hash = wtx.GetId(); entry.pushKV("txid", hash.GetHex()); UniValue conflicts(UniValue::VARR); for (const uint256 &conflict : wtx.GetConflicts()) { conflicts.push_back(conflict.GetHex()); } entry.pushKV("walletconflicts", conflicts); entry.pushKV("time", wtx.GetTxTime()); entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived); for (const std::pair &item : wtx.mapValue) { entry.pushKV(item.first, item.second); } } static std::string LabelFromValue(const UniValue &value) { std::string label = value.get_str(); if (label == "*") { throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name"); } return label; } static UniValue getnewaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "getnewaddress ( \"label\" )\n" "\nReturns a new Bitcoin address for receiving payments.\n" "If 'label' is specified, it is added to the address book \n" "so payments received with the address will be associated with " "'label'.\n" "\nArguments:\n" "1. \"label\" (string, optional) The label name for the " "address to be linked to. If not provided, the default label \"\" " "is used. It can also be set to the empty string \"\" to represent " "the default label. The label does not need to exist, it will be " "created if there is no label by the given name.\n" "\nResult:\n" "\"address\" (string) The new bitcoin address\n" "\nExamples:\n" + HelpExampleRpc("getnewaddress", "")); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label; if (!request.params[0].isNull()) { label = LabelFromValue(request.params[0]); } OutputType output_type = pwallet->m_default_address_type; if (!request.params[1].isNull()) { output_type = ParseOutputType(request.params[1].get_str(), pwallet->m_default_address_type); if (output_type == OutputType::NONE) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str())); } } if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } // Generate a new key that is added to wallet CPubKey newKey; if (!pwallet->GetKeyFromPool(newKey)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } pwallet->LearnRelatedScripts(newKey, output_type); CTxDestination dest = GetDestinationForKey(newKey, output_type); pwallet->SetAddressBook(dest, label, "receive"); return EncodeDestination(dest, config); } CTxDestination GetLabelDestination(CWallet *const pwallet, const std::string &label, bool bForceNew = false) { CTxDestination dest; if (!pwallet->GetLabelDestination(dest, label, bForceNew)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } return dest; } static UniValue getlabeladdress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && request.strMethod == "getaccountaddress") { if (request.fHelp) { throw std::runtime_error( "getaccountaddress (Deprecated, will be removed in v0.21. To " "use this command, start bitcoind with " "-deprecatedrpc=accounts)"); } throw JSONRPCError( RPC_METHOD_DEPRECATED, "getaccountaddress is deprecated and will be removed in v0.21. To " "use this command, start bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getlabeladdress \"label\" ( force ) \n" "\nReturns the default receiving address for this label. This will " "reset to a fresh address once there's a transaction that spends " "to it.\n" "\nArguments:\n" "1. \"label\" (string, required) The label for the " "address. It can also be set to the empty string \"\" to represent " "the default label.\n" "2. \"force\" (bool, optional) Whether the label should be " "created if it does not yet exist. If False, the RPC will return " "an error if called with a label that doesn't exist.\n" " Defaults to false (unless the " "getaccountaddress method alias is being called, in which case " "defaults to true for backwards compatibility).\n" "\nResult:\n" "\"address\" (string) The current receiving address for " "the label.\n" "\nExamples:\n" + HelpExampleCli("getlabeladdress", "") + HelpExampleCli("getlabeladdress", "\"\"") + HelpExampleCli("getlabeladdress", "\"mylabel\"") + HelpExampleRpc("getlabeladdress", "\"mylabel\"")); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label = LabelFromValue(request.params[0]); bool force = request.strMethod == "getaccountaddress"; if (!request.params[1].isNull()) { force = request.params[1].get_bool(); } bool label_found = false; for (const std::pair &item : pwallet->mapAddressBook) { if (item.second.name == label) { label_found = true; break; } } if (!force && !label_found) { throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label)); } UniValue ret(UniValue::VSTR); ret = EncodeDestination(GetLabelDestination(pwallet, label), config); return ret; } static UniValue getrawchangeaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getrawchangeaddress\n" "\nReturns a new Bitcoin address, for receiving change.\n" "This is for use with raw transactions, NOT normal use.\n" "\nResult:\n" "\"address\" (string) The address\n" "\nExamples:\n" + HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", "")); } LOCK2(cs_main, pwallet->cs_wallet); if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } OutputType output_type = pwallet->m_default_change_type != OutputType::NONE ? pwallet->m_default_change_type : pwallet->m_default_address_type; if (!request.params[0].isNull()) { output_type = ParseOutputType(request.params[0].get_str(), output_type); if (output_type == OutputType::NONE) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str())); } } CReserveKey reservekey(pwallet); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey, true)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } reservekey.KeepKey(); pwallet->LearnRelatedScripts(vchPubKey, output_type); CTxDestination dest = GetDestinationForKey(vchPubKey, output_type); return EncodeDestination(dest, config); } static UniValue setlabel(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && request.strMethod == "setaccount") { if (request.fHelp) { throw std::runtime_error( "setaccount (Deprecated, will be removed in v0.21. To use this " "command, start bitcoind with -deprecatedrpc=accounts)"); } throw JSONRPCError( RPC_METHOD_DEPRECATED, "setaccount is deprecated and will be removed in v0.21. To use " "this command, start bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "setlabel \"address\" \"label\"\n" "\nSets the label associated with the given address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "be associated with a label.\n" "2. \"label\" (string, required) The label to assign to " "the address.\n" "\nExamples:\n" + HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + HelpExampleRpc( "setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string label = LabelFromValue(request.params[1]); if (IsMine(*pwallet, dest)) { // Detect when changing the label of an address that is the receiving // address of another label. If so, delete the account record for it. // Labels, unlike addresses, can be deleted, and if we wouldn't do this, // the record would stick around forever. if (pwallet->mapAddressBook.count(dest)) { std::string old_label = pwallet->mapAddressBook[dest].name; if (old_label != label && dest == GetLabelDestination(pwallet, old_label)) { pwallet->DeleteLabel(old_label); } } pwallet->SetAddressBook(dest, label, "receive"); } else { pwallet->SetAddressBook(dest, label, "send"); } return NullUniValue; } static UniValue getaccount(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { if (request.fHelp) { throw std::runtime_error( "getaccount (Deprecated, will be removed in v0.21. To use this " "command, start bitcoind with -deprecatedrpc=accounts)"); } throw JSONRPCError( RPC_METHOD_DEPRECATED, "getaccount is deprecated and will be removed in v0.21. To use " "this command, start bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaccount \"address\"\n" "\nDEPRECATED. Returns the account associated with the given " "address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "account lookup.\n" "\nResult:\n" "\"accountname\" (string) the account address\n" "\nExamples:\n" + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string strAccount; std::map::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } return strAccount; } static UniValue getaddressesbyaccount(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { if (request.fHelp) { throw std::runtime_error( "getaddressbyaccount (Deprecated, will be removed in v0.21. To " "use this command, start bitcoind with " "-deprecatedrpc=accounts)"); } throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaddressesbyaccount is deprecated and will be " "removed in v0.21. To use this command, start " "bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaddressesbyaccount \"account\"\n" "\nDEPRECATED. Returns the list of addresses for the given " "account.\n" "\nArguments:\n" "1. \"account\" (string, required) The account name.\n" "\nResult:\n" "[ (json array of string)\n" " \"address\" (string) a bitcoin address associated with " "the given account\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = LabelFromValue(request.params[0]); // Find all addresses that have the given account UniValue ret(UniValue::VARR); for (const std::pair &item : pwallet->mapAddressBook) { const CTxDestination &dest = item.first; const std::string &strName = item.second.name; if (strName == strAccount) { ret.push_back(EncodeDestination(dest, config)); } } return ret; } static CTransactionRef SendMoney(CWallet *const pwallet, const CTxDestination &address, Amount nValue, bool fSubtractFeeFromAmount, mapValue_t mapValue, std::string fromAccount) { Amount curBalance = pwallet->GetBalance(); // Check amount if (nValue <= Amount::zero()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); } if (nValue > curBalance) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); } if (pwallet->GetBroadcastTransactions() && !g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } // Parse Bitcoin address CScript scriptPubKey = GetScriptForDestination(address); // Create and send the transaction CReserveKey reservekey(pwallet); Amount nFeeRequired; std::string strError; std::vector vecSend; int nChangePosRet = -1; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); CCoinControl coinControl; CTransactionRef tx; if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coinControl)) { if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { strError = strprintf("Error: This transaction requires a " "transaction fee of at least %s", FormatMoney(nFeeRequired)); } throw JSONRPCError(RPC_WALLET_ERROR, strError); } CValidationState state; if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(fromAccount), reservekey, g_connman.get(), state)) { strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strError); } return tx; } static UniValue sendtoaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) { throw std::runtime_error( "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" " "subtractfeefromamount )\n" "\nSend an amount to a given address.\n" + HelpRequiringPassphrase(pwallet) + "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address " "to send to.\n" "2. \"amount\" (numeric or string, required) The " "amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" "3. \"comment\" (string, optional) A comment used to " "store what the transaction is for. \n" " This is not part of the transaction, " "just kept in your wallet.\n" "4. \"comment_to\" (string, optional) A comment to store " "the name of the person or organization \n" " to which you're sending the " "transaction. This is not part of the \n" " transaction, just kept in your " "wallet.\n" "5. subtractfeefromamount (boolean, optional, default=false) The " "fee will be deducted from the amount being sent.\n" " The recipient will receive less " "bitcoins than you enter in the amount field.\n" "\nResult:\n" "\"txid\" (string) The transaction id.\n" "\nExamples:\n" + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\" 0.1 \"donation\" \"seans " "outpost\"") + HelpExampleCli( "sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\", 0.1, \"donation\", \"seans " "outpost\"")); } // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } // Amount Amount nAmount = AmountFromValue(request.params[1]); if (nAmount <= Amount::zero()) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); } // Wallet comments mapValue_t mapValue; if (!request.params[2].isNull() && !request.params[2].get_str().empty()) { mapValue["comment"] = request.params[2].get_str(); } if (!request.params[3].isNull() && !request.params[3].get_str().empty()) { mapValue["to"] = request.params[3].get_str(); } bool fSubtractFeeFromAmount = false; if (!request.params[4].isNull()) { fSubtractFeeFromAmount = request.params[4].get_bool(); } EnsureWalletIsUnlocked(pwallet); CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, std::move(mapValue), {} /* fromAccount */); return tx->GetId().GetHex(); } static UniValue listaddressgroupings(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "listaddressgroupings\n" "\nLists groups of addresses which have had their common " "ownership\n" "made public by common use as inputs or as the resulting change\n" "in past transactions\n" "\nResult:\n" "[\n" " [\n" " [\n" " \"address\", (string) The bitcoin address\n" " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" (string, optional) The label\n" " ]\n" " ,...\n" " ]\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("listaddressgroupings", "") + HelpExampleRpc("listaddressgroupings", "")); } // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); std::map balances = pwallet->GetAddressBalances(); for (const std::set &grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); for (const CTxDestination &address : grouping) { UniValue addressInfo(UniValue::VARR); addressInfo.push_back(EncodeDestination(address, config)); addressInfo.push_back(ValueFromAmount(balances[address])); if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) { addressInfo.push_back( pwallet->mapAddressBook.find(address)->second.name); } jsonGrouping.push_back(addressInfo); } jsonGroupings.push_back(jsonGrouping); } return jsonGroupings; } static UniValue signmessage(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "signmessage \"address\" \"message\"\n" "\nSign a message with the private key of an address" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "use for the private key.\n" "2. \"message\" (string, required) The message to create a " "signature of.\n" "\nResult:\n" "\"signature\" (string) The signature of the message " "encoded in base 64\n" "\nExamples:\n" "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" + HelpExampleCli( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs json rpc\n" + HelpExampleRpc( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")); } LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); CTxDestination dest = DecodeDestination(strAddress, config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } const CKeyID *keyID = boost::get(&dest); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } CKey key; if (!pwallet->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; std::vector vchSig; if (!key.SignCompact(ss.GetHash(), vchSig)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); } return EncodeBase64(vchSig.data(), vchSig.size()); } static UniValue getreceivedbyaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbyaddress \"address\" ( minconf )\n" "\nReturns the total amount received by the given address in " "transactions with at least minconf confirmations.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "transactions.\n" "2. minconf (numeric, optional, default=1) Only " "include transactions confirmed at least this many times.\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" "\nExamples:\n" "\nThe amount from transactions with at least 1 confirmation\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + "\nThe amount including unconfirmed transactions, zero " "confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + "\nThe amount with at least 6 confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + "\nAs a json rpc call\n" + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")); } // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); } // Minimum confirmations int nMinDepth = 1; if (!request.params[1].isNull()) { nMinDepth = request.params[1].get_int(); } // Tally Amount nAmount = Amount::zero(); for (const std::pair &pairWtx : pwallet->mapWallet) { const CWalletTx &wtx = pairWtx.second; CValidationState state; if (wtx.IsCoinBase() || !ContextualCheckTransactionForCurrentBlock( config.GetChainParams().GetConsensus(), *wtx.tx, state)) { continue; } for (const CTxOut &txout : wtx.tx->vout) { if (txout.scriptPubKey == scriptPubKey) { if (wtx.GetDepthInMainChain() >= nMinDepth) { nAmount += txout.nValue; } } } } return ValueFromAmount(nAmount); } static UniValue getreceivedbylabel(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && request.strMethod == "getreceivedbyaccount") { if (request.fHelp) { throw std::runtime_error( "getreceivedbyaccount (Deprecated, will be removed in v0.21. " "To use this command, start bitcoind with " "-deprecatedrpc=accounts)"); } throw JSONRPCError(RPC_METHOD_DEPRECATED, "getreceivedbyaccount is deprecated and will be " "removed in v0.21. To use this command, start " "bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbylabel \"label\" ( minconf )\n" "\nReturns the total amount received by addresses with