diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.test.include | 1 | ||||
-rw-r--r-- | src/init.cpp | 49 | ||||
-rw-r--r-- | src/miner.cpp | 47 | ||||
-rw-r--r-- | src/miner.h | 11 | ||||
-rw-r--r-- | src/net.cpp | 5 | ||||
-rw-r--r-- | src/prevector.h | 6 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 29 | ||||
-rw-r--r-- | src/random.cpp | 115 | ||||
-rw-r--r-- | src/random.h | 17 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 8 | ||||
-rw-r--r-- | src/rpc/server.cpp | 3 | ||||
-rw-r--r-- | src/test/data/tx_invalid.json | 2 | ||||
-rw-r--r-- | src/test/data/tx_valid.json | 2 | ||||
-rw-r--r-- | src/test/dbwrapper_tests.cpp | 6 | ||||
-rw-r--r-- | src/test/miner_tests.cpp | 47 | ||||
-rw-r--r-- | src/test/random_tests.cpp | 19 | ||||
-rw-r--r-- | src/test/test_bitcoin.cpp | 4 | ||||
-rw-r--r-- | src/txmempool.h | 1 | ||||
-rw-r--r-- | src/uint256.cpp | 5 | ||||
-rw-r--r-- | src/wallet/rpcdump.cpp | 1 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 30 | ||||
-rw-r--r-- | src/wallet/test/wallet_tests.cpp | 42 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 11 | ||||
-rw-r--r-- | src/wallet/walletdb.cpp | 14 | ||||
-rw-r--r-- | src/wallet/walletdb.h | 6 |
25 files changed, 338 insertions, 143 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 4d44b35bb6..55a587cf87 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -110,6 +110,7 @@ BITCOIN_TESTS =\ test/policyestimator_tests.cpp \ test/pow_tests.cpp \ test/prevector_tests.cpp \ + test/random_tests.cpp \ test/raii_event_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ diff --git a/src/init.cpp b/src/init.cpp index 196b840cb7..da389fd23d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -683,9 +683,15 @@ bool InitSanityCheck(void) InitError("Elliptic curve cryptography sanity check failure. Aborting."); return false; } + if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; + if (!Random_SanityCheck()) { + InitError("OS cryptographic RNG sanity check failure. Aborting."); + return false; + } + return true; } @@ -797,6 +803,19 @@ ServiceFlags nLocalServices = NODE_NETWORK; } +[[noreturn]] static void new_handler_terminate() +{ + // Rather than throwing std::bad-alloc if allocation fails, terminate + // immediately to (try to) avoid chain corruption. + // Since LogPrintf may itself allocate memory, set the handler directly + // to terminate first. + std::set_new_handler(std::terminate); + LogPrintf("Error: Out of memory. Terminating.\n"); + + // The log was successful, terminate now. + std::terminate(); +}; + bool AppInitBasicSetup() { // ********************************************************* Step 1: setup @@ -849,6 +868,9 @@ bool AppInitBasicSetup() // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif + + std::set_new_handler(new_handler_terminate); + return true; } @@ -1349,32 +1371,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) fReindex = GetBoolArg("-reindex", false); bool fReindexChainState = GetBoolArg("-reindex-chainstate", false); - // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ - boost::filesystem::path blocksDir = GetDataDir() / "blocks"; - if (!boost::filesystem::exists(blocksDir)) - { - boost::filesystem::create_directories(blocksDir); - bool linked = false; - for (unsigned int i = 1; i < 10000; i++) { - boost::filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i); - if (!boost::filesystem::exists(source)) break; - boost::filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); - try { - boost::filesystem::create_hard_link(source, dest); - LogPrintf("Hardlinked %s -> %s\n", source.string(), dest.string()); - linked = true; - } catch (const boost::filesystem::filesystem_error& e) { - // Note: hardlink creation failing is not a disaster, it just means - // blocks will get re-downloaded from peers. - LogPrintf("Error hardlinking blk%04u.dat: %s\n", i, e.what()); - break; - } - } - if (linked) - { - fReindex = true; - } - } + boost::filesystem::create_directories(GetDataDir() / "blocks"); // cache size calculations int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); diff --git a/src/miner.cpp b/src/miner.cpp index d01edd93b5..167e74284c 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -72,43 +72,56 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam return nNewTime - nOldTime; } -BlockAssembler::BlockAssembler(const CChainParams& _chainparams) - : chainparams(_chainparams) +BlockAssembler::Options::Options() { + blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); + nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; + nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; +} + +BlockAssembler::BlockAssembler(const CChainParams& params, const Options& options) : chainparams(params) +{ + blockMinFeeRate = options.blockMinFeeRate; + // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity: + nBlockMaxWeight = std::max<size_t>(4000, std::min<size_t>(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight)); + // Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity: + nBlockMaxSize = std::max<size_t>(1000, std::min<size_t>(MAX_BLOCK_SERIALIZED_SIZE - 1000, options.nBlockMaxSize)); + // Whether we need to account for byte usage (in addition to weight usage) + fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE - 1000); +} + +static BlockAssembler::Options DefaultOptions(const CChainParams& params) { // Block resource limits // If neither -blockmaxsize or -blockmaxweight is given, limit to DEFAULT_BLOCK_MAX_* // If only one is given, only restrict the specified resource. // If both are given, restrict both. - nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; - nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; + BlockAssembler::Options options; + options.nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; + options.nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; bool fWeightSet = false; if (IsArgSet("-blockmaxweight")) { - nBlockMaxWeight = GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); - nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; + options.nBlockMaxWeight = GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); + options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; fWeightSet = true; } if (IsArgSet("-blockmaxsize")) { - nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); + options.nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); if (!fWeightSet) { - nBlockMaxWeight = nBlockMaxSize * WITNESS_SCALE_FACTOR; + options.nBlockMaxWeight = options.nBlockMaxSize * WITNESS_SCALE_FACTOR; } } if (IsArgSet("-blockmintxfee")) { CAmount n = 0; ParseMoney(GetArg("-blockmintxfee", ""), n); - blockMinFeeRate = CFeeRate(n); + options.blockMinFeeRate = CFeeRate(n); } else { - blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); + options.blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); } - - // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity: - nBlockMaxWeight = std::max((unsigned int)4000, std::min((unsigned int)(MAX_BLOCK_WEIGHT-4000), nBlockMaxWeight)); - // Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SERIALIZED_SIZE-1000), nBlockMaxSize)); - // Whether we need to account for byte usage (in addition to weight usage) - fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE-1000); + return options; } +BlockAssembler::BlockAssembler(const CChainParams& params) : BlockAssembler(params, DefaultOptions(params)) {} + void BlockAssembler::resetBlock() { inBlock.clear(); diff --git a/src/miner.h b/src/miner.h index 3ba92b16b8..fc2526ff5a 100644 --- a/src/miner.h +++ b/src/miner.h @@ -163,7 +163,16 @@ private: bool blockFinished; public: - BlockAssembler(const CChainParams& chainparams); + struct Options { + Options(); + size_t nBlockMaxWeight; + size_t nBlockMaxSize; + CFeeRate blockMinFeeRate; + }; + + BlockAssembler(const CChainParams& params); + BlockAssembler(const CChainParams& params, const Options& options); + /** Construct a new block template with coinbase to scriptPubKeyIn */ std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn); diff --git a/src/net.cpp b/src/net.cpp index de5fc29693..e38d3b344e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2210,8 +2210,9 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c SetBestHeight(connOptions.nBestHeight); clientInterface = connOptions.uiInterface; - if (clientInterface) - clientInterface->InitMessage(_("Loading addresses...")); + if (clientInterface) { + clientInterface->InitMessage(_("Loading P2P addresses...")); + } // Load addresses from peers.dat int64_t nStart = GetTimeMillis(); { diff --git a/src/prevector.h b/src/prevector.h index 6b2f578f5c..cba2e30057 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -5,6 +5,7 @@ #ifndef _BITCOIN_PREVECTOR_H_ #define _BITCOIN_PREVECTOR_H_ +#include <assert.h> #include <stdlib.h> #include <stdint.h> #include <string.h> @@ -170,10 +171,15 @@ private: } } else { if (!is_direct()) { + /* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert + success. These should instead use an allocator or new/delete so that handlers + are called as necessary, but performance would be slightly degraded by doing so. */ _union.indirect = static_cast<char*>(realloc(_union.indirect, ((size_t)sizeof(T)) * new_capacity)); + assert(_union.indirect); _union.capacity = new_capacity; } else { char* new_indirect = static_cast<char*>(malloc(((size_t)sizeof(T)) * new_capacity)); + assert(new_indirect); T* src = direct_ptr(0); T* dst = reinterpret_cast<T*>(new_indirect); memcpy(dst, src, size() * sizeof(T)); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c31dea2067..662e8037ca 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -178,8 +178,8 @@ public Q_SLOTS: void shutdown(); Q_SIGNALS: - void initializeResult(int retval); - void shutdownResult(int retval); + void initializeResult(bool success); + void shutdownResult(); void runawayException(const QString &message); private: @@ -223,8 +223,8 @@ public: WId getMainWinId() const; public Q_SLOTS: - void initializeResult(int retval); - void shutdownResult(int retval); + void initializeResult(bool success); + void shutdownResult(); /// Handle runaway exceptions. Shows a message box with the problem and quits the program. void handleRunawayException(const QString &message); @@ -284,7 +284,7 @@ void BitcoinCore::initialize() Q_EMIT initializeResult(false); return; } - int rv = AppInitMain(threadGroup, scheduler); + bool rv = AppInitMain(threadGroup, scheduler); Q_EMIT initializeResult(rv); } catch (const std::exception& e) { handleRunawayException(&e); @@ -302,7 +302,7 @@ void BitcoinCore::shutdown() threadGroup.join_all(); Shutdown(); qDebug() << __func__ << ": Shutdown finished"; - Q_EMIT shutdownResult(1); + Q_EMIT shutdownResult(); } catch (const std::exception& e) { handleRunawayException(&e); } catch (...) { @@ -398,8 +398,8 @@ void BitcoinApplication::startThread() executor->moveToThread(coreThread); /* communication to and from thread */ - connect(executor, SIGNAL(initializeResult(int)), this, SLOT(initializeResult(int))); - connect(executor, SIGNAL(shutdownResult(int)), this, SLOT(shutdownResult(int))); + connect(executor, SIGNAL(initializeResult(bool)), this, SLOT(initializeResult(bool))); + connect(executor, SIGNAL(shutdownResult()), this, SLOT(shutdownResult())); connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString))); connect(this, SIGNAL(requestedInitialize()), executor, SLOT(initialize())); connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown())); @@ -450,12 +450,12 @@ void BitcoinApplication::requestShutdown() Q_EMIT requestedShutdown(); } -void BitcoinApplication::initializeResult(int retval) +void BitcoinApplication::initializeResult(bool success) { - qDebug() << __func__ << ": Initialization result: " << retval; - // Set exit result: 0 if successful, 1 if failure - returnValue = retval ? 0 : 1; - if(retval) + qDebug() << __func__ << ": Initialization result: " << success; + // Set exit result. + returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE; + if(success) { // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete qWarning() << "Platform customization:" << platformStyle->getName(); @@ -507,9 +507,8 @@ void BitcoinApplication::initializeResult(int retval) } } -void BitcoinApplication::shutdownResult(int retval) +void BitcoinApplication::shutdownResult() { - qDebug() << __func__ << ": Shutdown result: " << retval; quit(); // Exit main loop after shutdown finished } diff --git a/src/random.cpp b/src/random.cpp index 6634019bea..8284f457c9 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -21,6 +21,17 @@ #include <sys/time.h> #endif +#ifdef HAVE_SYS_GETRANDOM +#include <sys/syscall.h> +#include <linux/random.h> +#endif +#ifdef HAVE_GETENTROPY +#include <unistd.h> +#endif +#ifdef HAVE_SYSCTL_ARND +#include <sys/sysctl.h> +#endif + #include <openssl/err.h> #include <openssl/rand.h> @@ -91,34 +102,86 @@ static void RandAddSeedPerfmon() #endif } +#ifndef WIN32 +/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most + * compatible way to get cryptographic randomness on UNIX-ish platforms. + */ +void GetDevURandom(unsigned char *ent32) +{ + int f = open("/dev/urandom", O_RDONLY); + if (f == -1) { + RandFailure(); + } + int have = 0; + do { + ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); + if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { + RandFailure(); + } + have += n; + } while (have < NUM_OS_RANDOM_BYTES); + close(f); +} +#endif + /** Get 32 bytes of system entropy. */ -static void GetOSRand(unsigned char *ent32) +void GetOSRand(unsigned char *ent32) { -#ifdef WIN32 +#if defined(WIN32) HCRYPTPROV hProvider; int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!ret) { RandFailure(); } - ret = CryptGenRandom(hProvider, 32, ent32); + ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32); if (!ret) { RandFailure(); } CryptReleaseContext(hProvider, 0); -#else - int f = open("/dev/urandom", O_RDONLY); - if (f == -1) { +#elif defined(HAVE_SYS_GETRANDOM) + /* Linux. From the getrandom(2) man page: + * "If the urandom source has been initialized, reads of up to 256 bytes + * will always return as many bytes as requested and will not be + * interrupted by signals." + */ + int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0); + if (rv != NUM_OS_RANDOM_BYTES) { + if (rv < 0 && errno == ENOSYS) { + /* Fallback for kernel <3.17: the return value will be -1 and errno + * ENOSYS if the syscall is not available, in that case fall back + * to /dev/urandom. + */ + GetDevURandom(ent32); + } else { + RandFailure(); + } + } +#elif defined(HAVE_GETENTROPY) + /* On OpenBSD this can return up to 256 bytes of entropy, will return an + * error if more are requested. + * The call cannot return less than the requested number of bytes. + */ + if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } +#elif defined(HAVE_SYSCTL_ARND) + /* FreeBSD and similar. It is possible for the call to return less + * bytes than requested, so need to read in a loop. + */ + static const int name[2] = {CTL_KERN, KERN_ARND}; int have = 0; do { - ssize_t n = read(f, ent32 + have, 32 - have); - if (n <= 0 || n + have > 32) { + size_t len = NUM_OS_RANDOM_BYTES - have; + if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, NULL, 0) != 0) { RandFailure(); } - have += n; - } while (have < 32); - close(f); + have += len; + } while (have < NUM_OS_RANDOM_BYTES); +#else + /* Fall back to /dev/urandom if there is no specific method implemented to + * get system entropy for this OS. + */ + GetDevURandom(ent32); #endif } @@ -195,3 +258,33 @@ FastRandomContext::FastRandomContext(bool fDeterministic) } } +bool Random_SanityCheck() +{ + /* This does not measure the quality of randomness, but it does test that + * OSRandom() overwrites all 32 bytes of the output given a maximum + * number of tries. + */ + static const ssize_t MAX_TRIES = 1024; + uint8_t data[NUM_OS_RANDOM_BYTES]; + bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */ + int num_overwritten; + int tries = 0; + /* Loop until all bytes have been overwritten at least once, or max number tries reached */ + do { + memset(data, 0, NUM_OS_RANDOM_BYTES); + GetOSRand(data); + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + overwritten[x] |= (data[x] != 0); + } + + num_overwritten = 0; + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + if (overwritten[x]) { + num_overwritten += 1; + } + } + + tries += 1; + } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); + return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ +} diff --git a/src/random.h b/src/random.h index 664f030eba..0464bdce14 100644 --- a/src/random.h +++ b/src/random.h @@ -46,4 +46,21 @@ public: uint32_t Rw; }; +/* Number of random bytes returned by GetOSRand. + * When changing this constant make sure to change all call sites, and make + * sure that the underlying OS APIs for all platforms support the number. + * (many cap out at 256 bytes). + */ +static const ssize_t NUM_OS_RANDOM_BYTES = 32; + +/** Get 32 bytes of system entropy. Do not use this in application code: use + * GetStrongRandBytes instead. + */ +void GetOSRand(unsigned char *ent32); + +/** Check that OS randomness is available and returning the requested number + * of bytes. + */ +bool Random_SanityCheck(); + #endif // BITCOIN_RANDOM_H diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index e543781e73..23515f116f 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -367,11 +367,11 @@ UniValue verifymessage(const JSONRPCRequest& request) "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" - + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + "\nAs json rpc\n" - + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"") + + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"") ); LOCK(cs_main); @@ -420,7 +420,7 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request) "\nCreate the signature\n" + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") + "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + "\nAs json rpc\n" + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"") ); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 46f7d35907..f418d71e71 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -197,9 +197,6 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& { const CRPCCommand *pcmd = command.second; string strMethod = pcmd->name; - // We already filter duplicates, but these deprecated screw up the sort order - if (strMethod.find("label") != string::npos) - continue; if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) continue; jreq.strMethod = strMethod; diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index f7d9e1847f..2235bd0ae7 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -1,7 +1,7 @@ [ ["The following are deserialized transactions which are invalid."], ["They are in the form"], -["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["[[[prevout hash, prevout index, prevout scriptPubKey, amount?], [input 2], ...],"], ["serializedTransaction, verifyFlags]"], ["Objects that are only a single string (like this one) are ignored"], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index a3f47fcee2..d70fa54333 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -1,7 +1,7 @@ [ ["The following are deserialized transactions which are valid."], ["They are in the form"], -["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["[[[prevout hash, prevout index, prevout scriptPubKey, amount?], [input 2], ...],"], ["serializedTransaction, verifyFlags]"], ["Objects that are only a single string (like this one) are ignored"], diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index d5d158027b..22c90bd95b 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) CDBWrapper dbw(ph, (1 << 20), true, false, false); for (int x=0x00; x<10; ++x) { for (int y = 0; y < 10; y++) { - sprintf(buf, "%d", x); + snprintf(buf, sizeof(buf), "%d", x); StringContentsSerializer key(buf); for (int z = 0; z < y; z++) key += key; @@ -293,12 +293,12 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) seek_start = 0; else seek_start = 5; - sprintf(buf, "%d", seek_start); + snprintf(buf, sizeof(buf), "%d", seek_start); StringContentsSerializer seek_key(buf); it->Seek(seek_key); for (int x=seek_start; x<10; ++x) { for (int y = 0; y < 10; y++) { - sprintf(buf, "%d", x); + snprintf(buf, sizeof(buf), "%d", x); std::string exp_key(buf); for (int z = 0; z < y; z++) exp_key += exp_key; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index f856d8a91a..5dbbb1b634 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -27,6 +27,15 @@ BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup) static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); +static BlockAssembler AssemblerForTest(const CChainParams& params) { + BlockAssembler::Options options; + + options.nBlockMaxWeight = MAX_BLOCK_WEIGHT; + options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; + options.blockMinFeeRate = blockMinFeeRate; + return BlockAssembler(params, options); +} + static struct { unsigned char extranonce; @@ -110,7 +119,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, uint256 hashHighFeeTx = tx.GetHash(); mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx); BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx); @@ -130,7 +139,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse; uint256 hashLowFeeTx = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse).FromTx(tx)); - pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); // Verify that the free tx and the low fee tx didn't get selected for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx); @@ -144,7 +153,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx)); - pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx); @@ -165,7 +174,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); - pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); // Verify that this tx isn't selected. for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { @@ -178,7 +187,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, tx.vin[0].prevout.n = 1; tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx)); - pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); } @@ -201,7 +210,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) @@ -232,7 +241,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); const CAmount BLOCKSUBSIDY = 50*COIN; const CAmount LOWFEE = CENT; @@ -256,7 +265,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -270,7 +279,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); mempool.clear(); // block size > limit @@ -290,13 +299,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); mempool.clear(); // orphan in mempool, template creation fails hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // child with higher priority than parent @@ -313,7 +322,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); mempool.clear(); // coinbase in mempool, template creation fails @@ -324,7 +333,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); // give it a fee so it'll get mined mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // invalid (pre-p2sh) txn in mempool, template creation fails @@ -341,7 +350,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // double spend txn pair in mempool, template creation fails @@ -354,7 +363,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // subsidy changing @@ -370,7 +379,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); // Extend to a 210000-long block chain. while (chainActive.Tip()->nHeight < 210000) { CBlockIndex* prev = chainActive.Tip(); @@ -382,7 +391,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); // Delete the dummy blocks again. while (chainActive.Tip()->nHeight > nHeight) { CBlockIndex* del = chainActive.Tip(); @@ -468,7 +477,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1; BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); // None of the of the absolute height/time locked tx should have made // it into the template because we still check IsFinalTx in CreateNewBlock, @@ -481,7 +490,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.Tip()->nHeight++; SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1); - BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5); chainActive.Tip()->nHeight--; diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp new file mode 100644 index 0000000000..d2c46c0daa --- /dev/null +++ b/src/test/random_tests.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 2017 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 "random.h" + +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(osrandom_tests) +{ + BOOST_CHECK(Random_SanityCheck()); +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 51fc6ae0ba..6039424480 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -69,11 +69,11 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(pcoinsdbview); - InitBlockIndex(chainparams); + BOOST_REQUIRE(InitBlockIndex(chainparams)); { CValidationState state; bool ok = ActivateBestChain(state, chainparams); - BOOST_CHECK(ok); + BOOST_REQUIRE(ok); } nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) diff --git a/src/txmempool.h b/src/txmempool.h index a7ecb64390..8a8039ded1 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -20,7 +20,6 @@ #include "sync.h" #include "random.h" -#undef foreach #include "boost/multi_index_container.hpp" #include "boost/multi_index/ordered_index.hpp" #include "boost/multi_index/hashed_index.hpp" diff --git a/src/uint256.cpp b/src/uint256.cpp index bd3d017085..c4c7b716fe 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -20,10 +20,7 @@ base_blob<BITS>::base_blob(const std::vector<unsigned char>& vch) template <unsigned int BITS> std::string base_blob<BITS>::GetHex() const { - char psz[sizeof(data) * 2 + 1]; - for (unsigned int i = 0; i < sizeof(data); i++) - sprintf(psz + i * 2, "%02x", data[sizeof(data) - i - 1]); - return std::string(psz, psz + sizeof(data) * 2); + return HexStr(std::reverse_iterator<const uint8_t*>(data + sizeof(data)), std::reverse_iterator<const uint8_t*>(data)); } template <unsigned int BITS> diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 4629311095..182a62cbb1 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1120,6 +1120,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, strprintf("Failed to rescan before time %d, transactions may be missing.", scannedRange->GetBlockTimeMax()))); response.push_back(std::move(result)); } + ++i; } } } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 148f0abe86..5ef93ac532 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -249,8 +249,8 @@ UniValue setaccount(const JSONRPCRequest& request) "1. \"address\" (string, required) The bitcoin address to be associated with an account.\n" "2. \"account\" (string, required) The account to assign the address to.\n" "\nExamples:\n" - + HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"tabby\"") - + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"tabby\"") + + HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"") ); LOCK2(cs_main, pwallet->cs_wallet); @@ -297,8 +297,8 @@ UniValue getaccount(const JSONRPCRequest& request) "\nResult:\n" "\"accountname\" (string) the account address\n" "\nExamples:\n" - + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") - + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") ); LOCK2(cs_main, pwallet->cs_wallet); @@ -526,11 +526,11 @@ UniValue signmessage(const JSONRPCRequest& request) "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" - + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + "\nAs json rpc\n" - + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"my message\"") + + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"") ); LOCK2(cs_main, pwallet->cs_wallet); @@ -582,13 +582,13 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) "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", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + "\nThe amount including unconfirmed transactions, zero confirmations\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 0") + + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + "\nThe amount with at least 6 confirmation, very safe\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 6") + + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + "\nAs a json rpc call\n" - + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", 6") + + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") ); LOCK2(cs_main, pwallet->cs_wallet); @@ -944,13 +944,13 @@ UniValue sendmany(const JSONRPCRequest& request) " the number of addresses.\n" "\nExamples:\n" "\nSend two amounts to two different addresses:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + "\nSend two amounts to two different addresses, subtract fee from amount:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") + + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); LOCK2(cs_main, pwallet->cs_wallet); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 43d7e3c617..fe212e5621 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -395,26 +395,62 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } + // Verify importmulti RPC returns failure for a key whose creation time is + // before the missing block, and success for a key whose creation time is + // after. { CWallet wallet; CWallet *backup = ::pwalletMain; ::pwalletMain = &wallet; + UniValue keys; + keys.setArray(); UniValue key; key.setObject(); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey()))); key.pushKV("timestamp", 0); key.pushKV("internal", UniValue(true)); - UniValue keys; - keys.setArray(); + keys.push_back(key); + key.clear(); + key.setObject(); + CKey futureKey; + futureKey.MakeNewKey(true); + key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey()))); + key.pushKV("timestamp", newTip->GetBlockTimeMax() + 7200); + key.pushKV("internal", UniValue(true)); keys.push_back(key); JSONRPCRequest request; request.params.setArray(); request.params.push_back(keys); UniValue response = importmulti(request); - BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Failed to rescan before time %d, transactions may be missing.\"}}]", newTip->GetBlockTimeMax())); + BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Failed to rescan before time %d, transactions may be missing.\"}},{\"success\":true}]", newTip->GetBlockTimeMax())); ::pwalletMain = backup; } } +// Check that GetImmatureCredit() returns a newly calculated value instead of +// the cached value after a MarkDirty() call. +// +// This is a regression test written to verify a bugfix for the immature credit +// function. Similar tests probably should be written for the other credit and +// debit functions. +BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) +{ + CWallet wallet; + CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back())); + LOCK2(cs_main, wallet.cs_wallet); + wtx.hashBlock = chainActive.Tip()->GetBlockHash(); + wtx.nIndex = 0; + + // Call GetImmatureCredit() once before adding the key to the wallet to + // cache the current immature credit amount, which is 0. + BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0); + + // Invalidate the cached value, add the key, and make sure a new immature + // credit amount is calculated. + wtx.MarkDirty(); + wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 446454e9e3..70fc54350d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2870,12 +2870,16 @@ DBErrors CWallet::ZapSelectTx(vector<uint256>& vHashIn, vector<uint256>& vHashOu { if (!fFileBacked) return DB_LOAD_OK; - DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(this, vHashIn, vHashOut); + AssertLockHeld(cs_wallet); // mapWallet + vchDefaultKey = CPubKey(); + DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(vHashIn, vHashOut); + for (uint256 hash : vHashOut) + mapWallet.erase(hash); + if (nZapSelectTxRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) { - LOCK(cs_wallet); setKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation @@ -2896,7 +2900,8 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) { if (!fFileBacked) return DB_LOAD_OK; - DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx); + vchDefaultKey = CPubKey(); + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(vWtx); if (nZapWalletTxRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 81fdde401e..44a01d4a36 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -659,20 +659,17 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } -DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx) +DBErrors CWalletDB::FindWalletTx(vector<uint256>& vTxHash, vector<CWalletTx>& vWtx) { - pwallet->vchDefaultKey = CPubKey(); bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; try { - LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((string)"minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DB_TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); } // Get cursor @@ -725,12 +722,12 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vec return result; } -DBErrors CWalletDB::ZapSelectTx(CWallet* pwallet, vector<uint256>& vTxHashIn, vector<uint256>& vTxHashOut) +DBErrors CWalletDB::ZapSelectTx(vector<uint256>& vTxHashIn, vector<uint256>& vTxHashOut) { // build list of wallet TXs and hashes vector<uint256> vTxHash; vector<CWalletTx> vWtx; - DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); + DBErrors err = FindWalletTx(vTxHash, vWtx); if (err != DB_LOAD_OK) { return err; } @@ -749,7 +746,6 @@ DBErrors CWalletDB::ZapSelectTx(CWallet* pwallet, vector<uint256>& vTxHashIn, ve break; } else if ((*it) == hash) { - pwallet->mapWallet.erase(hash); if(!EraseTx(hash)) { LogPrint("db", "Transaction was found for deletion but returned database error: %s\n", hash.GetHex()); delerror = true; @@ -764,11 +760,11 @@ DBErrors CWalletDB::ZapSelectTx(CWallet* pwallet, vector<uint256>& vTxHashIn, ve return DB_LOAD_OK; } -DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx) +DBErrors CWalletDB::ZapWalletTx(vector<CWalletTx>& vWtx) { // build list of wallet TXs vector<uint256> vTxHash; - DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); + DBErrors err = FindWalletTx(vTxHash, vWtx); if (err != DB_LOAD_OK) return err; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index c7c65465df..4f1a64de42 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -167,9 +167,9 @@ public: void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries); DBErrors LoadWallet(CWallet* pwallet); - DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx); - DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx); - DBErrors ZapSelectTx(CWallet* pwallet, std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut); + DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx); + DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); + DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut); static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename); |