Browse Source

Merge #11592: 0.15: Backports

8195cb0d7 rpc: further constrain the libevent workaround (Cory Fields)
34153a7e4 rpc: work-around an upstream libevent bug (Cory Fields)
fc308a6cd Add unit test for stale tip checking (Suhas Daftuar)
2ed0647ac Add CConnmanTest to mutate g_connman in tests (João Barbosa)
a607a95d8 Connect to an extra outbound peer if our tip is stale (Suhas Daftuar)
459f2db42 Track tip update time and last new block announcement from each peer (Suhas Daftuar)
49bf09018 net: Allow connecting to extra outbound peers (Suhas Daftuar)
bb83fe190 Add release notes describing blockmaxweight deprecation (Matt Corallo)
4c82cea99 Use a sensible default for blockmaxweight (Matt Corallo)
7871a7d3b Deprecate confusing blockmaxsize, fix getmininginfo output (Matt Corallo)
6baa317b5 Fix minchainwork test for 0.15 backport (Suhas Daftuar)
55b7abfa8 Make p2p-acceptablock not an extended test (Matt Corallo)
5bec7744d [qa] test that invalid blocks on an invalid chain get a disconnect (Matt Corallo)
92d6105c4 Reject headers building on invalid chains by tracking invalidity (Matt Corallo)
51001d684 Accept unrequested blocks with work equal to our tip (Matt Corallo)
c6e4d0ce8 Stop always storing blocks from whitelisted peers (Matt Corallo)
e976c36dd Rewrite p2p-acceptblock in preparation for slight behavior changes (Matt Corallo)
ec8dedff4 net: Add missing lock in ProcessHeadersMessage(...) (practicalswift)
59b210d9a Disconnect outbound peers relaying invalid headers (Suhas Daftuar)
fc966bbd2 moveonly: factor out headers processing into separate function (Suhas Daftuar)
e3272242e Add unit test for outbound peer eviction (Suhas Daftuar)
9961abf9e Permit disconnection of outbound peers on bad/slow chains (Suhas Daftuar)
bf191a718 Disconnecting from bad outbound peers in IBD (Suhas Daftuar)
d570aa429 Fix uninitialized g_connman crash in Shutdown() (MeshCollider)
0a5477c7e net: stop both net/net_processing before destroying them (Cory Fields)
b4136f21c net: drop unused connman param (Cory Fields)
dc897e53d net: use an interface class rather than signals for message processing (Cory Fields)
8aee55af3 net: pass CConnman via pointer rather than reference (Cory Fields)
6f279652b Rename fAddnode to a more-descriptive "manual_connection" (Matt Corallo)
ffb6ea4e5 Add comment explaining forced processing of compact blocks (Suhas Daftuar)
2df65eeb9 qa: add test for minchainwork use in acceptblock (Suhas Daftuar)
3acec3878 Don't process unrequested, low-work blocks (Suhas Daftuar)
0e9d04bf0 [qa] Test nMinimumChainWork (Suhas Daftuar)
da4908c3a Allow setting nMinimumChainWork on command line (Suhas Daftuar)
41088795d qa: Remove never used return value of sync_with_ping (MarcoFalke)
f3457d0e8 qa: Make tmpdir option an absolute path (MarcoFalke)
9c8006dc3 Avoid opening copied wallet databases simultaneously (Russell Yanofsky)
de7053f11 [wallet] Fix leak in CDB constructor (João Barbosa)
fd79ed6b2 Ensure backupwallet fails when attempting to backup to source file (Tomas van der Wansem)
d94fc336c scripted-diff: rename assert_raises_jsonrpc to assert_raises_rpc error (John Newbery)
623de0acb [tests] do not allow assert_raises_message to be called with JSONRPCException (John Newbery)
5b728c8e9 [tests] remove direct testing on JSONRPCException from individual test cases (John Newbery)

Pull request description:

Tree-SHA512: 9fdb5c47844a899271023d8d445f7fc728e3ad71916490cd9783464684967594b07cda05dd644b722bfcea9fade74d06cfc501e1a68abf118d6d03fbbf7d7707
tags/v0.15.1
Pieter Wuille 3 years ago
parent
commit
f518d9ae6a
No account linked to committer's email address
60 changed files with 1617 additions and 684 deletions
  1. 16
    0
      doc/release-notes.md
  2. 28
    4
      src/httpserver.cpp
  3. 33
    4
      src/init.cpp
  4. 2
    36
      src/miner.cpp
  5. 1
    3
      src/miner.h
  6. 49
    18
      src/net.cpp
  7. 36
    15
      src/net.h
  8. 599
    293
      src/net_processing.cpp
  9. 37
    20
      src/net_processing.h
  10. 1
    3
      src/policy/policy.h
  11. 0
    2
      src/rpc/mining.cpp
  12. 1
    1
      src/rpc/net.cpp
  13. 151
    11
      src/test/DoS_tests.cpp
  14. 15
    3
      src/test/test_bitcoin.cpp
  15. 8
    0
      src/test/test_bitcoin.h
  16. 25
    0
      src/test/util_tests.cpp
  17. 13
    0
      src/utilstrencodings.cpp
  18. 6
    0
      src/utilstrencodings.h
  19. 82
    17
      src/validation.cpp
  20. 5
    2
      src/validation.h
  21. 56
    18
      src/wallet/db.cpp
  22. 1
    1
      src/wallet/db.h
  23. 5
    5
      test/functional/bip68-sequence.py
  24. 2
    2
      test/functional/blockchain.py
  25. 7
    7
      test/functional/bumpfee.py
  26. 2
    2
      test/functional/disablewallet.py
  27. 6
    6
      test/functional/disconnect_ban.py
  28. 6
    6
      test/functional/fundrawtransaction.py
  29. 13
    17
      test/functional/import-rescan.py
  30. 2
    2
      test/functional/importmulti.py
  31. 2
    2
      test/functional/importprunedfunds.py
  32. 4
    4
      test/functional/keypool.py
  33. 2
    2
      test/functional/mempool_packages.py
  34. 2
    2
      test/functional/mempool_reorg.py
  35. 1
    1
      test/functional/mempool_spendcoinbase.py
  36. 4
    4
      test/functional/merkle_blocks.py
  37. 93
    0
      test/functional/minchainwork.py
  38. 5
    6
      test/functional/mining.py
  39. 9
    3
      test/functional/multiwallet.py
  40. 2
    2
      test/functional/net.py
  41. 3
    3
      test/functional/nulldummy.py
  42. 204
    98
      test/functional/p2p-acceptblock.py
  43. 1
    1
      test/functional/prioritise_transaction.py
  44. 4
    4
      test/functional/pruning.py
  45. 6
    6
      test/functional/rawtransactions.py
  46. 15
    15
      test/functional/replace-by-fee.py
  47. 2
    2
      test/functional/resendwallettransactions.py
  48. 1
    1
      test/functional/rpcbind_test.py
  49. 2
    2
      test/functional/rpcnamedargs.py
  50. 5
    9
      test/functional/segwit.py
  51. 1
    1
      test/functional/signrawtransactions.py
  52. 0
    1
      test/functional/test_framework/mininode.py
  53. 3
    0
      test/functional/test_framework/test_framework.py
  54. 12
    2
      test/functional/test_framework/util.py
  55. 2
    1
      test/functional/test_runner.py
  56. 2
    2
      test/functional/wallet-dump.py
  57. 6
    6
      test/functional/wallet-encryption.py
  58. 4
    4
      test/functional/wallet.py
  59. 10
    0
      test/functional/walletbackup.py
  60. 2
    2
      test/functional/zapwallettxes.py

+ 16
- 0
doc/release-notes.md View File

@@ -72,6 +72,22 @@ Notable changes
value is passed, instead of returning a list of all wallet transactions since
the genesis block.

Miner block size limiting deprecated
------------------------------------

Though blockmaxweight has been preferred for limiting the size of blocks returned by
getblocktemplate since 0.13.0, blockmaxsize remained as an option for those who wished
to limit their block size directly. Using this option resulted in a few UI issues as
well as non-optimal fee selection and ever-so-slightly worse performance, and has thus
now been deprecated. Further, the blockmaxsize option is now used only to calculate an
implied blockmaxweight, instead of limiting block size directly. Any miners who wish
to limit their blocks by size, instead of by weight, will have to do so manually by
removing transactions from their block template directly.

Low-level RPC changes
----------------------
- The "currentblocksize" value in getmininginfo has been removed.

Credits
=======


+ 28
- 4
src/httpserver.cpp View File

@@ -24,6 +24,7 @@

#include <event2/thread.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>

@@ -239,6 +240,16 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m)
/** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg)
{
// Disable reading to work around a libevent bug, fixed in 2.2.0.
if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
evhttp_connection* conn = evhttp_request_get_connection(req);
if (conn) {
bufferevent* bev = evhttp_connection_get_bufferevent(conn);
if (bev) {
bufferevent_disable(bev, EV_READ);
}
}
}
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));

LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
@@ -599,11 +610,24 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent* ev = new HTTPEvent(eventBase, true,
std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr));
ev->trigger(0);
auto req_copy = req;
HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
// Re-enable reading from the socket. This is the second part of the libevent
// workaround above.
if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
evhttp_connection* conn = evhttp_request_get_connection(req_copy);
if (conn) {
bufferevent* bev = evhttp_connection_get_bufferevent(conn);
if (bev) {
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
}
}
});
ev->trigger(nullptr);
replySent = true;
req = 0; // transferred back to main thread
req = nullptr; // transferred back to main thread
}

CService HTTPRequest::GetPeer()

+ 33
- 4
src/init.cpp View File

@@ -193,12 +193,15 @@ void Shutdown()
}
#endif
MapPort(false);

// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
UnregisterValidationInterface(peerLogic.get());
if(g_connman) g_connman->Stop();
peerLogic.reset();
g_connman.reset();

StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool();
}
@@ -362,6 +365,9 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
if (showDebug) {
strUsage += HelpMessageOpt("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()));
}
strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL));
strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
@@ -494,7 +500,7 @@ std::string HelpMessage(HelpMessageMode mode)

strUsage += HelpMessageGroup(_("Block creation options:"));
strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT));
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
strUsage += HelpMessageOpt("-blockmaxsize=<n>", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight"));
strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)));
if (showDebug)
strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios");
@@ -796,6 +802,15 @@ void InitParameterInteraction()
if (gArgs.SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}

if (gArgs.IsArgSet("-blockmaxsize")) {
unsigned int max_size = gArgs.GetArg("-blockmaxsize", 0);
if (gArgs.SoftSetArg("blockmaxweight", strprintf("%d", max_size * WITNESS_SCALE_FACTOR))) {
LogPrintf("%s: parameter interaction: -blockmaxsize=%d -> setting -blockmaxweight=%d (-blockmaxsize is deprecated!)\n", __func__, max_size, max_size * WITNESS_SCALE_FACTOR);
} else {
LogPrintf("%s: Ignoring blockmaxsize setting which is overridden by blockmaxweight", __func__);
}
}
}

static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
@@ -979,6 +994,20 @@ bool AppInitParameterInteraction()
else
LogPrintf("Validating signatures for all blocks.\n");

if (gArgs.IsArgSet("-minimumchainwork")) {
const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", "");
if (!IsHexNumber(minChainWorkStr)) {
return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr));
}
nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
} else {
nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
}
LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
if (nMinimumChainWork < UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex());
}

// mempool limits
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
@@ -1258,9 +1287,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
CConnman& connman = *g_connman;

peerLogic.reset(new PeerLogicValidation(&connman));
peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
RegisterValidationInterface(peerLogic.get());
RegisterNodeSignals(GetNodeSignals());

// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
@@ -1651,6 +1679,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
connOptions.nMaxFeeler = 1;
connOptions.nBestHeight = chainActive.Height();
connOptions.uiInterface = &uiInterface;
connOptions.m_msgproc = peerLogic.get();
connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);


+ 2
- 36
src/miner.cpp View File

@@ -43,7 +43,6 @@
// its ancestors.

uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;
uint64_t nLastBlockWeight = 0;

int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
@@ -64,7 +63,6 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
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)
@@ -72,10 +70,6 @@ BlockAssembler::BlockAssembler(const CChainParams& params, const Options& option
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)
@@ -85,20 +79,7 @@ static BlockAssembler::Options DefaultOptions(const CChainParams& params)
// If only one is given, only restrict the specified resource.
// If both are given, restrict both.
BlockAssembler::Options options;
options.nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
options.nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE;
bool fWeightSet = false;
if (gArgs.IsArgSet("-blockmaxweight")) {
options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE;
fWeightSet = true;
}
if (gArgs.IsArgSet("-blockmaxsize")) {
options.nBlockMaxSize = gArgs.GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
if (!fWeightSet) {
options.nBlockMaxWeight = options.nBlockMaxSize * WITNESS_SCALE_FACTOR;
}
}
options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
if (gArgs.IsArgSet("-blockmintxfee")) {
CAmount n = 0;
ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n);
@@ -116,7 +97,6 @@ void BlockAssembler::resetBlock()
inBlock.clear();

// Reserve space for coinbase tx
nBlockSize = 1000;
nBlockWeight = 4000;
nBlockSigOpsCost = 400;
fIncludeWitness = false;
@@ -175,7 +155,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
int64_t nTime1 = GetTimeMicros();

nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
nLastBlockWeight = nBlockWeight;

// Create coinbase transaction.
@@ -190,8 +169,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
pblocktemplate->vTxFees[0] = -nFees;

uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION);
LogPrintf("CreateNewBlock(): total size: %u block weight: %u txs: %u fees: %ld sigops %d\n", nSerializeSize, GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);

// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
@@ -238,22 +216,13 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
// - transaction finality (locktime)
// - premature witness (in case segwit transactions are added to mempool before
// segwit activation)
// - serialized size (in case -blockmaxsize is in use)
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package)
{
uint64_t nPotentialBlockSize = nBlockSize; // only used with fNeedSizeAccounting
for (const CTxMemPool::txiter it : package) {
if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff))
return false;
if (!fIncludeWitness && it->GetTx().HasWitness())
return false;
if (fNeedSizeAccounting) {
uint64_t nTxSize = ::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
if (nPotentialBlockSize + nTxSize >= nBlockMaxSize) {
return false;
}
nPotentialBlockSize += nTxSize;
}
}
return true;
}
@@ -263,9 +232,6 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
pblock->vtx.emplace_back(iter->GetSharedTx());
pblocktemplate->vTxFees.push_back(iter->GetFee());
pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost());
if (fNeedSizeAccounting) {
nBlockSize += ::GetSerializeSize(iter->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
}
nBlockWeight += iter->GetTxWeight();
++nBlockTx;
nBlockSigOpsCost += iter->GetSigOpCost();

+ 1
- 3
src/miner.h View File

@@ -139,13 +139,11 @@ private:

// Configuration parameters for the block size
bool fIncludeWitness;
unsigned int nBlockMaxWeight, nBlockMaxSize;
bool fNeedSizeAccounting;
unsigned int nBlockMaxWeight;
CFeeRate blockMinFeeRate;

// Information on the current status of the block
uint64_t nBlockWeight;
uint64_t nBlockSize;
uint64_t nBlockTx;
uint64_t nBlockSigOpsCost;
CAmount nFees;

+ 49
- 18
src/net.cpp View File

@@ -89,10 +89,6 @@ std::string strSubVersion;

limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);

// Signals for message handling
static CNodeSignals g_signals;
CNodeSignals& GetNodeSignals() { return g_signals; }

void CConnman::AddOneShot(const std::string& strDest)
{
LOCK(cs_vOneShots);
@@ -665,7 +661,7 @@ void CNode::copyStats(CNodeStats &stats)
X(cleanSubVer);
}
X(fInbound);
X(fAddnode);
X(m_manual_connection);
X(nStartingHeight);
{
LOCK(cs_vSend);
@@ -1114,7 +1110,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
pnode->AddRef();
pnode->fWhitelisted = whitelisted;
GetNodeSignals().InitializeNode(pnode, *this);
m_msgproc->InitializeNode(pnode);

LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString());

@@ -1674,6 +1670,37 @@ void CConnman::ProcessOneShot()
}
}

bool CConnman::GetTryNewOutboundPeer()
{
return m_try_another_outbound_peer;
}

void CConnman::SetTryNewOutboundPeer(bool flag)
{
m_try_another_outbound_peer = flag;
LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false");
}

// Return the number of peers we have over our outbound connection limit
// Exclude peers that are marked for disconnect, or are going to be
// disconnected soon (eg one-shots and feelers)
// Also exclude peers that haven't finished initial connection handshake yet
// (so that we don't decide we're over our desired connection limit, and then
// evict some peer that has finished the handshake)
int CConnman::GetExtraOutboundCount()
{
int nOutbound = 0;
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (!pnode->fInbound && !pnode->m_manual_connection && !pnode->fFeeler && !pnode->fDisconnect && !pnode->fOneShot && pnode->fSuccessfullyConnected) {
++nOutbound;
}
}
}
return std::max(nOutbound - nMaxOutbound, 0);
}

void CConnman::ThreadOpenConnections()
{
// Connect to specific addresses
@@ -1738,7 +1765,7 @@ void CConnman::ThreadOpenConnections()
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (!pnode->fInbound && !pnode->fAddnode) {
if (!pnode->fInbound && !pnode->m_manual_connection) {

// Count the peers that have all relevant services
if (pnode->fSuccessfullyConnected && !pnode->fFeeler && ((pnode->nServices & nRelevantServices) == nRelevantServices)) {
@@ -1768,7 +1795,8 @@ void CConnman::ThreadOpenConnections()
// * Only make a feeler connection once every few minutes.
//
bool fFeeler = false;
if (nOutbound >= nMaxOutbound) {

if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
if (nTime > nNextFeeler) {
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
@@ -1934,7 +1962,7 @@ void CConnman::ThreadOpenAddedConnections()
}

// if successful, this moves the passed grant to the constructed node
bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode)
bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection)
{
//
// Initiate outbound network connection
@@ -1963,10 +1991,10 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
pnode->fOneShot = true;
if (fFeeler)
pnode->fFeeler = true;
if (fAddnode)
pnode->fAddnode = true;
if (manual_connection)
pnode->m_manual_connection = true;

GetNodeSignals().InitializeNode(pnode, *this);
m_msgproc->InitializeNode(pnode);
{
LOCK(cs_vNodes);
vNodes.push_back(pnode);
@@ -1996,16 +2024,16 @@ void CConnman::ThreadMessageHandler()
continue;

// Receive messages
bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc);
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
if (flagInterruptMsgProc)
return;

// Send messages
{
LOCK(pnode->cs_sendProcessing);
GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc);
m_msgproc->SendMessages(pnode, flagInterruptMsgProc);
}

if (flagInterruptMsgProc)
return;
}
@@ -2211,6 +2239,7 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe
semOutbound = nullptr;
semAddnode = nullptr;
flagInterruptMsgProc = false;
SetTryNewOutboundPeer(false);

Options connOptions;
Init(connOptions);
@@ -2324,6 +2353,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
//
// Start threads
//
assert(m_msgproc);
InterruptSocks5(false);
interruptNet.reset();
flagInterruptMsgProc = false;
@@ -2443,9 +2473,10 @@ void CConnman::DeleteNode(CNode* pnode)
{
assert(pnode);
bool fUpdateConnectionTime = false;
GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
if(fUpdateConnectionTime)
m_msgproc->FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
if(fUpdateConnectionTime) {
addrman.Connected(pnode->addr);
}
delete pnode;
}

@@ -2704,7 +2735,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
strSubVer = "";
fWhitelisted = false;
fOneShot = false;
fAddnode = false;
m_manual_connection = false;
fClient = false; // set by version message
fFeeler = false;
fSuccessfullyConnected = false;

+ 36
- 15
src/net.h View File

@@ -33,7 +33,6 @@
#include <arpa/inet.h>
#endif

#include <boost/signals2/signal.hpp>

class CScheduler;
class CNode;
@@ -116,7 +115,7 @@ struct CSerializedNetMsg
std::string command;
};

class NetEventsInterface;
class CConnman
{
public:
@@ -138,6 +137,7 @@ public:
int nMaxFeeler = 0;
int nBestHeight = 0;
CClientUIInterface* uiInterface = nullptr;
NetEventsInterface* m_msgproc = nullptr;
unsigned int nSendBufferMaxSize = 0;
unsigned int nReceiveFloodSize = 0;
uint64_t nMaxOutboundTimeframe = 0;
@@ -156,6 +156,7 @@ public:
nMaxFeeler = connOptions.nMaxFeeler;
nBestHeight = connOptions.nBestHeight;
clientInterface = connOptions.uiInterface;
m_msgproc = connOptions.m_msgproc;
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
nReceiveFloodSize = connOptions.nReceiveFloodSize;
nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
@@ -170,7 +171,7 @@ public:
void Interrupt();
bool GetNetworkActive() const { return fNetworkActive; };
void SetNetworkActive(bool active);
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false);
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false);
bool CheckIncomingNonce(uint64_t nonce);

bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -250,6 +251,19 @@ public:
void GetBanned(banmap_t &banmap);
void SetBanned(const banmap_t &banmap);

// This allows temporarily exceeding nMaxOutbound, with the goal of finding
// a peer that is better than all our current peers.
void SetTryNewOutboundPeer(bool flag);
bool GetTryNewOutboundPeer();

// Return the number of outbound peers we have in excess of our target (eg,
// if we previously called SetTryNewOutboundPeer(true), and have since set
// to false, we may have extra peers that we wish to disconnect). This may
// return a value less than (num_outbound_connections - num_outbound_slots)
// in cases where some outbound connections are not yet fully connected, or
// not yet fully disconnected.
int GetExtraOutboundCount();

bool AddNode(const std::string& node);
bool RemoveAddedNode(const std::string& node);
std::vector<AddedNodeInfo> GetAddedNodeInfo();
@@ -396,6 +410,7 @@ private:
int nMaxFeeler;
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
NetEventsInterface* m_msgproc;

/** SipHasher seeds for deterministic randomness */
const uint64_t nSeed0, nSeed1;
@@ -414,6 +429,13 @@ private:
std::thread threadOpenAddedConnections;
std::thread threadOpenConnections;
std::thread threadMessageHandler;

/** flag for deciding to connect to an extra outbound peer,
* in excess of nMaxOutbound
* This takes the place of a feeler connection */
std::atomic_bool m_try_another_outbound_peer;

friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
void Discover(boost::thread_group& threadGroup);
@@ -436,19 +458,18 @@ struct CombinerAll
}
};

// Signals for message handling
struct CNodeSignals
/**
* Interface for message handling
*/
class NetEventsInterface
{
boost::signals2::signal<bool (CNode*, CConnman&, std::atomic<bool>&), CombinerAll> ProcessMessages;
boost::signals2::signal<bool (CNode*, CConnman&, std::atomic<bool>&), CombinerAll> SendMessages;
boost::signals2::signal<void (CNode*, CConnman&)> InitializeNode;
boost::signals2::signal<void (NodeId, bool&)> FinalizeNode;
public:
virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
virtual void InitializeNode(CNode* pnode) = 0;
virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0;
};


CNodeSignals& GetNodeSignals();


enum
{
LOCAL_NONE, // unknown
@@ -508,7 +529,7 @@ public:
int nVersion;
std::string cleanSubVer;
bool fInbound;
bool fAddnode;
bool m_manual_connection;
int nStartingHeight;
uint64_t nSendBytes;
mapMsgCmdSize mapSendBytesPerMsgCmd;
@@ -618,7 +639,7 @@ public:
bool fWhitelisted; // This peer can bypass DoS banning.
bool fFeeler; // If true this node is being used as a short lived feeler.
bool fOneShot;
bool fAddnode;
bool m_manual_connection;
bool fClient;
const bool fInbound;
std::atomic_bool fSuccessfullyConnected;

+ 599
- 293
src/net_processing.cpp
File diff suppressed because it is too large
View File


+ 37
- 20
src/net_processing.h View File

@@ -8,6 +8,7 @@

#include "net.h"
#include "validationinterface.h"
#include "consensus/params.h"

/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
@@ -21,23 +22,51 @@ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
* Timeout = base + per_header * (expected number of headers) */
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
/** Protect at least this many outbound peers from disconnection due to slow/
* behind headers chain.
*/
static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
/** How frequently to check for stale tips, in seconds */
static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
/** How frequently to check for extra outbound peers and disconnect, in seconds */
static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
static constexpr int64_t MINIMUM_CONNECT_TIME = 30;

/** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals);
/** Unregister a network node */
void UnregisterNodeSignals(CNodeSignals& nodeSignals);

class PeerLogicValidation : public CValidationInterface {
class PeerLogicValidation : public CValidationInterface, public NetEventsInterface {
private:
CConnman* connman;
CConnman* const connman;

public:
PeerLogicValidation(CConnman* connmanIn);
explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler);

void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
void BlockChecked(const CBlock& block, const CValidationState& state) override;
void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;


void InitializeNode(CNode* pnode) override;
void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override;
/** Process protocol messages received from a given node */
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override;
/**
* Send queued protocol messages to be sent to a give node.
*
* @param[in] pto The node which we are sending messages to.
* @param[in] interrupt Interrupt condition for processing threads
* @return True if there is more work to be done
*/
bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override;

void ConsiderEviction(CNode *pto, int64_t time_in_seconds);
void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
void EvictExtraOutboundPeers(int64_t time_in_seconds);

private:
int64_t m_stale_tip_check_time; //! Next time to check for stale tip
};

struct CNodeStateStats {
@@ -52,16 +81,4 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
/** Increase a node's misbehavior score. */
void Misbehaving(NodeId nodeid, int howmuch);

/** Process protocol messages received from a given node */
bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& interrupt);
/**
* Send queued protocol messages to be sent to a give node.
*
* @param[in] pto The node which we are sending messages to.
* @param[in] connman The connection manager for that node.
* @param[in] interrupt Interrupt condition for processing threads
* @return True if there is more work to be done
*/
bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interrupt);

#endif // BITCOIN_NET_PROCESSING_H

+ 1
- 3
src/policy/policy.h View File

@@ -16,10 +16,8 @@
class CCoinsViewCache;
class CTxOut;

/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = 3000000;
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000;
/** The maximum weight for transactions we're willing to relay/mine */

+ 0
- 2
src/rpc/mining.cpp View File

@@ -195,7 +195,6 @@ UniValue getmininginfo(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"blocks\": nnn, (numeric) The current block\n"
" \"currentblocksize\": nnn, (numeric) The last block size\n"
" \"currentblockweight\": nnn, (numeric) The last block weight\n"
" \"currentblocktx\": nnn, (numeric) The last block transaction\n"
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
@@ -214,7 +213,6 @@ UniValue getmininginfo(const JSONRPCRequest& request)

UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight));
obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));

+ 1
- 1
src/rpc/net.cpp View File

@@ -156,7 +156,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
// their ver message.
obj.push_back(Pair("subver", stats.cleanSubVer));
obj.push_back(Pair("inbound", stats.fInbound));
obj.push_back(Pair("addnode", stats.fAddnode));
obj.push_back(Pair("addnode", stats.m_manual_connection));
obj.push_back(Pair("startingheight", stats.nStartingHeight));
if (fStateStats) {
obj.push_back(Pair("banscore", statestats.nMisbehavior));

+ 151
- 11
src/test/DoS_tests.cpp View File

@@ -40,8 +40,138 @@ CService ip(uint32_t i)

static NodeId id = 0;

void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds);

BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)

// Test eviction of an outbound peer whose chain never advances
// Mock a node connection, and use mocktime to simulate a peer
// which never sends any headers messages. PeerLogic should
// decide to evict that outbound peer, after the appropriate timeouts.
// Note that we protect 4 outbound nodes from being subject to
// this logic; this test takes advantage of that protection only
// being applied to nodes which send headers with sufficient
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
std::atomic<bool> interruptDummy(false);

// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", /*fInboundIn=*/ false);
dummyNode1.SetSendVersion(PROTOCOL_VERSION);

peerLogic->InitializeNode(&dummyNode1);
dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;

// This test requires that we have a chain with non-zero work.
BOOST_CHECK(chainActive.Tip() != nullptr);
BOOST_CHECK(chainActive.Tip()->nChainWork > 0);

// Test starts here
peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
dummyNode1.vSendMsg.clear();

int64_t nStartTime = GetTime();
// Wait 21 minutes
SetMockTime(nStartTime+21*60);
peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
// Wait 3 more minutes
SetMockTime(nStartTime+24*60);
peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in disconnect
BOOST_CHECK(dummyNode1.fDisconnect == true);
SetMockTime(0);

bool dummy;
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}

void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
{
CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
CNode &node = *vNodes.back();
node.SetSendVersion(PROTOCOL_VERSION);

peerLogic.InitializeNode(&node);
node.nVersion = 1;
node.fSuccessfullyConnected = true;

CConnmanTest::AddNode(node);
}

BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
constexpr int nMaxOutbound = 8;
CConnman::Options options;
options.nMaxConnections = 125;
options.nMaxOutbound = nMaxOutbound;
options.nMaxFeeler = 1;

connman->Init(options);
std::vector<CNode *> vNodes;

// Mock some outbound peers
for (int i=0; i<nMaxOutbound; ++i) {
AddRandomOutboundPeer(vNodes, *peerLogic);
}

peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);

// No nodes should be marked for disconnection while we have no extra peers
for (const CNode *node : vNodes) {
BOOST_CHECK(node->fDisconnect == false);
}

SetMockTime(GetTime() + 3*consensusParams.nPowTargetSpacing + 1);

// Now tip should definitely be stale, and we should look for an extra
// outbound peer
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
BOOST_CHECK(connman->GetTryNewOutboundPeer());

// Still no peers should be marked for disconnection
for (const CNode *node : vNodes) {
BOOST_CHECK(node->fDisconnect == false);
}

// If we add one more peer, something should get marked for eviction
// on the next check (since we're mocking the time to be in the future, the
// required time connected check should be satisfied).
AddRandomOutboundPeer(vNodes, *peerLogic);

peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
for (int i=0; i<nMaxOutbound; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
// Last added node should get marked for eviction
BOOST_CHECK(vNodes.back()->fDisconnect == true);

vNodes.back()->fDisconnect = false;

// Update the last announced block time for the last
// peer, and check that the next newest node gets evicted.
UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());

peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
for (int i=0; i<nMaxOutbound-1; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes[nMaxOutbound-1]->fDisconnect == true);
BOOST_CHECK(vNodes.back()->fDisconnect == false);

bool dummy;
for (const CNode *node : vNodes) {
peerLogic->FinalizeNode(node->GetId(), dummy);
}

CConnmanTest::ClearNodes();
}

BOOST_AUTO_TEST_CASE(DoS_banning)
{
std::atomic<bool> interruptDummy(false);
@@ -50,27 +180,31 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true);
dummyNode1.SetSendVersion(PROTOCOL_VERSION);
GetNodeSignals().InitializeNode(&dummyNode1, *connman);
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
Misbehaving(dummyNode1.GetId(), 100); // Should get banned
SendMessages(&dummyNode1, *connman, interruptDummy);
peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr1));
BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned

CAddress addr2(ip(0xa0b0c002), NODE_NONE);
CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true);
dummyNode2.SetSendVersion(PROTOCOL_VERSION);
GetNodeSignals().InitializeNode(&dummyNode2, *connman);
peerLogic->InitializeNode(&dummyNode2);
dummyNode2.nVersion = 1;
dummyNode2.fSuccessfullyConnected = true;
Misbehaving(dummyNode2.GetId(), 50);
SendMessages(&dummyNode2, *connman, interruptDummy);
peerLogic->SendMessages(&dummyNode2, interruptDummy);
BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet...
BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be
Misbehaving(dummyNode2.GetId(), 50);
SendMessages(&dummyNode2, *connman, interruptDummy);
peerLogic->SendMessages(&dummyNode2, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr2));

bool dummy;
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
peerLogic->FinalizeNode(dummyNode2.GetId(), dummy);
}

BOOST_AUTO_TEST_CASE(DoS_banscore)
@@ -82,19 +216,22 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true);
dummyNode1.SetSendVersion(PROTOCOL_VERSION);
GetNodeSignals().InitializeNode(&dummyNode1, *connman);
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
Misbehaving(dummyNode1.GetId(), 100);
SendMessages(&dummyNode1, *connman, interruptDummy);
peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(!connman->IsBanned(addr1));
Misbehaving(dummyNode1.GetId(), 10);
SendMessages(&dummyNode1, *connman, interruptDummy);
peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(!connman->IsBanned(addr1));
Misbehaving(dummyNode1.GetId(), 1);
SendMessages(&dummyNode1, *connman, interruptDummy);
peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr1));
gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD));

bool dummy;
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}

BOOST_AUTO_TEST_CASE(DoS_bantime)
@@ -108,12 +245,12 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
CAddress addr(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", true);
dummyNode.SetSendVersion(PROTOCOL_VERSION);
GetNodeSignals().InitializeNode(&dummyNode, *connman);
peerLogic->InitializeNode(&dummyNode);
dummyNode.nVersion = 1;
dummyNode.fSuccessfullyConnected = true;

Misbehaving(dummyNode.GetId(), 100);
SendMessages(&dummyNode, *connman, interruptDummy);
peerLogic->SendMessages(&dummyNode, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr));

SetMockTime(nStartTime+60*60);
@@ -121,6 +258,9 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)

SetMockTime(nStartTime+60*60*24+1);
BOOST_CHECK(!connman->IsBanned(addr));

bool dummy;
peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
}

CTransactionRef RandomOrphan()

+ 15
- 3
src/test/test_bitcoin.cpp View File

@@ -24,6 +24,18 @@

#include <memory>

void CConnmanTest::AddNode(CNode& node)
{
LOCK(g_connman->cs_vNodes);
g_connman->vNodes.push_back(&node);
}

void CConnmanTest::ClearNodes()
{
LOCK(g_connman->cs_vNodes);
g_connman->vNodes.clear();
}

uint256 insecure_rand_seed = GetRandHash();
FastRandomContext insecure_rand_ctx(insecure_rand_seed);

@@ -48,7 +60,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
BasicTestingSetup::~BasicTestingSetup()
{
ECC_Stop();
g_connman.reset();
}

TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
@@ -86,16 +97,17 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(&ThreadScriptCheck);
g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
connman = g_connman.get();
RegisterNodeSignals(GetNodeSignals());
peerLogic.reset(new PeerLogicValidation(connman, scheduler));
}

TestingSetup::~TestingSetup()
{
UnregisterNodeSignals(GetNodeSignals());
threadGroup.interrupt_all();
threadGroup.join_all();
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
g_connman.reset();
peerLogic.reset();
UnloadBlockIndex();
delete pcoinsTip;
delete pcoinsdbview;

+ 8
- 0
src/test/test_bitcoin.h View File

@@ -49,12 +49,20 @@ struct BasicTestingSetup {
* Included are data directory, coins database, script check threads setup.
*/
class CConnman;
class CNode;
struct CConnmanTest {
static void AddNode(CNode& node);
static void ClearNodes();
};

class PeerLogicValidation;
struct TestingSetup: public BasicTestingSetup {
CCoinsViewDB *pcoinsdbview;
fs::path pathTemp;
boost::thread_group threadGroup;
CConnman* connman;
CScheduler scheduler;
std::unique_ptr<PeerLogicValidation> peerLogic;

TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~TestingSetup();

+ 25
- 0
src/test/util_tests.cpp View File

@@ -253,6 +253,31 @@ BOOST_AUTO_TEST_CASE(util_IsHex)
BOOST_CHECK(!IsHex("0x0000"));
}

BOOST_AUTO_TEST_CASE(util_IsHexNumber)
{
BOOST_CHECK(IsHexNumber("0x0"));
BOOST_CHECK(IsHexNumber("0"));
BOOST_CHECK(IsHexNumber("0x10"));
BOOST_CHECK(IsHexNumber("10"));
BOOST_CHECK(IsHexNumber("0xff"));
BOOST_CHECK(IsHexNumber("ff"));
BOOST_CHECK(IsHexNumber("0xFfa"));
BOOST_CHECK(IsHexNumber("Ffa"));
BOOST_CHECK(IsHexNumber("0x00112233445566778899aabbccddeeffAABBCCDDEEFF"));
BOOST_CHECK(IsHexNumber("00112233445566778899aabbccddeeffAABBCCDDEEFF"));

BOOST_CHECK(!IsHexNumber("")); // empty string not allowed
BOOST_CHECK(!IsHexNumber("0x")); // empty string after prefix not allowed
BOOST_CHECK(!IsHexNumber("0x0 ")); // no spaces at end,
BOOST_CHECK(!IsHexNumber(" 0x0")); // or beginning,
BOOST_CHECK(!IsHexNumber("0x 0")); // or middle,
BOOST_CHECK(!IsHexNumber(" ")); // etc.
BOOST_CHECK(!IsHexNumber("0x0ga")); // invalid character
BOOST_CHECK(!IsHexNumber("x0")); // broken prefix
BOOST_CHECK(!IsHexNumber("0x0x00")); // two prefixes not allowed

}

BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
{
SeedInsecureRand(true);

+ 13
- 0
src/utilstrencodings.cpp View File

@@ -65,6 +65,19 @@ bool IsHex(const std::string& str)
return (str.size() > 0) && (str.size()%2 == 0);
}

bool IsHexNumber(const std::string& str)
{
size_t starting_location = 0;
if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') {
starting_location = 2;
}
for (auto c : str.substr(starting_location)) {
if (HexDigit(c) < 0) return false;
}
// Return false for empty string or "0x".
return (str.size() > starting_location);
}

std::vector<unsigned char> ParseHex(const char* psz)
{
// convert hex dump to vector

+ 6
- 0
src/utilstrencodings.h View File

@@ -38,7 +38,13 @@ std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
signed char HexDigit(char c);
/* Returns true if each character in str is a hex character, and has an even
* number of hex digits.*/
bool IsHex(const std::string& str);
/**
* Return true if the string is a hex number, optionally prefixed with "0x"
*/
bool IsHexNumber(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = nullptr);
std::string DecodeBase64(const std::string& str);
std::string EncodeBase64(const unsigned char* pch, size_t len);

+ 82
- 17
src/validation.cpp View File

@@ -80,6 +80,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;

uint256 hashAssumeValid;
arith_uint256 nMinimumChainWork;

CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
@@ -152,6 +153,26 @@ namespace {
/** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0;

/** In order to efficiently track invalidity of headers, we keep the set of
* blocks which we tried to connect and found to be invalid here (ie which
* were set to BLOCK_FAILED_VALID since the last restart). We can then
* walk this set and check if a new header is a descendant of something in
* this set, preventing us from having to walk mapBlockIndex when we try
* to connect a bad block and fail.
*
* While this is more complicated than marking everything which descends
* from an invalid block as invalid at the time we discover it to be
* invalid, doing so would require walking all of mapBlockIndex to find all
* descendants. Since this case should be very rare, keeping track of all
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
* well.
*
* Because we alreardy walk mapBlockIndex in height-order at startup, we go
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
* instead of putting things in this set.
*/
std::set<CBlockIndex*> g_failed_blocks;

/** Dirty block index entries. */
std::set<CBlockIndex*> setDirtyBlockIndex;

@@ -1032,8 +1053,6 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)

bool IsInitialBlockDownload()
{
const CChainParams& chainParams = Params();

// Once this function has returned false, it must remain false.
static std::atomic<bool> latchToFalse{false};
// Optimization: pre-test latch before taking the lock.
@@ -1047,7 +1066,7 @@ bool IsInitialBlockDownload()
return true;
if (chainActive.Tip() == nullptr)
return true;
if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork))
if (chainActive.Tip()->nChainWork < nMinimumChainWork)
return true;
if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
return true;
@@ -1169,6 +1188,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
g_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
@@ -1664,7 +1684,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
if (it != mapBlockIndex.end()) {
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
pindexBestHeader->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
// The equivalent time check discourages hash power from extorting the network via DOS attack
// into accepting an invalid block through telling users they must manually set assumevalid.
@@ -2518,17 +2538,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
{
AssertLockHeld(cs_main);

// Mark the block itself as invalid.
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
// We first disconnect backwards and then mark the blocks as invalid.
// This prevents a case where pruned nodes may fail to invalidateblock
// and be left unable to start as they have no tip candidates (as there
// are no blocks that meet the "have data and are not invalid per
// nStatus" criteria for inclusion in setBlockIndexCandidates).

bool pindex_was_in_chain = false;
CBlockIndex *invalid_walk_tip = chainActive.Tip();

DisconnectedBlockTransactions disconnectpool;
while (chainActive.Contains(pindex)) {
CBlockIndex *pindexWalk = chainActive.Tip();
pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindexWalk);
setBlockIndexCandidates.erase(pindexWalk);
pindex_was_in_chain = true;
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
@@ -2539,6 +2560,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
}
}

// Now mark the blocks we just disconnected as descendants invalid
// (note this may not be all descendants).
while (pindex_was_in_chain && invalid_walk_tip != pindex) {
invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(invalid_walk_tip);
setBlockIndexCandidates.erase(invalid_walk_tip);
invalid_walk_tip = invalid_walk_tip->pprev;
}

// Mark the block itself as invalid.
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
g_failed_blocks.insert(pindex);

// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
@@ -2576,6 +2612,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
g_failed_blocks.erase(it->second);
}
it++;
}
@@ -3052,6 +3089,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));

if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
for (const CBlockIndex* failedit : g_failed_blocks) {
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
assert(failedit->nStatus & BLOCK_FAILED_VALID);
CBlockIndex* invalid_walk = pindexPrev;
while (invalid_walk != failedit) {
invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(invalid_walk);
invalid_walk = invalid_walk->pprev;
}
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
}
}
}
}
if (pindex == nullptr)
pindex = AddToBlockIndex(block);
@@ -3065,13 +3117,15 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
}

// Exposed wrapper for AcceptBlockHeader
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid)
{
if (first_invalid != nullptr) first_invalid->SetNull();
{
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
if (!AcceptBlockHeader(header, state, chainparams, &pindex)) {
if (first_invalid) *first_invalid = header;
return false;
}
if (ppindex) {
@@ -3101,7 +3155,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// process an unrequested block if it's new and has enough work to
// advance our tip, and isn't too many blocks ahead.
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true);
bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true);
// Blocks that are too out-of-order needlessly limit the effectiveness of
// pruning, because pruning will not delete block files that contain any
// blocks which are too close in height to the tip. Apply this test
@@ -3118,9 +3172,15 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// and unrequested blocks.
if (fAlreadyHave) return true;
if (!fRequested) { // If we didn't ask for it:
if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
if (!fHasMoreWork) return true; // Don't process less-work chains
if (fTooFarAhead) return true; // Block height is too high
if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
if (!fHasMoreOrSameWork) return true; // Don't process less-work chains
if (fTooFarAhead) return true; // Block height is too high

// Protect against DoS attacks from low-work chains.
// If our tip is behind, a peer could try to send us
// low-work blocks on a fake chain that we would never
// request; don't process these.
if (pindex->nChainWork < nMinimumChainWork) return true;
}
if (fNewBlock) *fNewBlock = true;

@@ -3470,6 +3530,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pindex->nChainTx = pindex->nTx;
}
}
if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
pindex->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindex);
}
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr))
setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
@@ -3860,6 +3924,7 @@ void UnloadBlockIndex()
nLastBlockFile = 0;
nBlockSequenceId = 1;
setDirtyBlockIndex.clear();
g_failed_blocks.clear();
setDirtyFileInfo.clear();
versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {

+ 5
- 2
src/validation.h View File

@@ -161,7 +161,6 @@ extern CTxMemPool mempool;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap mapBlockIndex;
extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
extern uint64_t nLastBlockWeight;
extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock;
@@ -186,6 +185,9 @@ extern bool fEnableReplacement;
/** Block hash whose ancestors we will assume to have valid scripts without checking them. */
extern uint256 hashAssumeValid;

/** Minimum work we will assume exists on some valid chain. */
extern arith_uint256 nMinimumChainWork;

/** Best header we've seen so far (used for getheaders queries' starting points). */
extern CBlockIndex *pindexBestHeader;

@@ -245,8 +247,9 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
* @param[out] state This may be set to an Error state if any error occurred processing them
* @param[in] chainparams The params for the chain we want to connect to
* @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
* @param[out] first_invalid First header that fails validation, if one exists
*/
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr);
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr, CBlockHeader *first_invalid=nullptr);

/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);

+ 56
- 18
src/wallet/db.cpp View File

@@ -20,6 +20,40 @@

#include <boost/thread.hpp>

namespace {
//! Make sure database has a unique fileid within the environment. If it
//! doesn't, throw an error. BDB caches do not work properly when more than one
//! open database has the same fileid (values written to one database may show
//! up in reads to other databases).
//!
//! BerkeleyDB generates unique fileids by default
//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
//! so bitcoin should never create different databases with the same fileid, but
//! this error can be triggered if users manually copy database files.
void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db)
{
if (env.IsMock()) return;

u_int8_t fileid[DB_FILE_ID_LEN];
int ret = db.get_mpf()->get_fileid(fileid);
if (ret != 0) {
throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret));
}

for (const auto& item : env.mapDb) {
u_int8_t item_fileid[DB_FILE_ID_LEN];
if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
const char* item_filename = nullptr;
item.second->get_dbname(&item_filename, nullptr);
throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename,
HexStr(std::begin(item_fileid), std::end(item_fileid)),
item_filename ? item_filename : "(unknown database)"));
}
}
}
} // namespace

//
// CDB
//
@@ -379,35 +413,34 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
if (!env->Open(GetDataDir()))
throw std::runtime_error("CDB: Failed to open database environment.");

strFile = strFilename;
++env->mapFileUseCount[strFile];
pdb = env->mapDb[strFile];
pdb = env->mapDb[strFilename];
if (pdb == nullptr) {
int ret;
pdb = new Db(env->dbenv, 0);
std::unique_ptr<Db> pdb_temp(new Db(env->dbenv, 0));

bool fMockDb = env->IsMock();
if (fMockDb) {
DbMpoolFile* mpf = pdb->get_mpf();
DbMpoolFile* mpf = pdb_temp->get_mpf();
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
if (ret != 0)
throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFile));
if (ret != 0) {
throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFilename));
}
}

ret = pdb->open(nullptr, // Txn pointer
fMockDb ? nullptr : strFile.c_str(), // Filename
fMockDb ? strFile.c_str() : "main", // Logical db name
DB_BTREE, // Database type
nFlags, // Flags
ret = pdb_temp->open(nullptr, // Txn pointer
fMockDb ? nullptr : strFilename.c_str(), // Filename
fMockDb ? strFilename.c_str() : "main", // Logical db name
DB_BTREE, // Database type
nFlags, // Flags
0);

if (ret != 0) {
delete pdb;
pdb = nullptr;
--env->mapFileUseCount[strFile];
strFile = "";
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
}
CheckUniqueFileid(*env, strFilename, *pdb_temp);

pdb = pdb_temp.release();
env->mapDb[strFilename] = pdb;

if (fCreate && !Exists(std::string("version"))) {
bool fTmp = fReadOnly;
@@ -415,9 +448,9 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
WriteVersion(CLIENT_VERSION);
fReadOnly = fTmp;
}

env->mapDb[strFile] = pdb;
}
++env->mapFileUseCount[strFilename];
strFile = strFilename;
}
}

@@ -673,6 +706,11 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
pathDest /= strFile;

try {
if (fs::equivalent(pathSrc, pathDest)) {
LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
return false;
}

fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
LogPrintf("copied %s to %s\n", strFile, pathDest.string());
return true;

+ 1
- 1
src/wallet/db.h View File

@@ -45,7 +45,7 @@ public:
void Reset();

void MakeMock();
bool IsMock() { return fMockDb; }
bool IsMock() const { return fMockDb; }

/**
* Verify that database file strFile is OK. If it is not,

+ 5
- 5
test/functional/bip68-sequence.py View File

@@ -83,7 +83,7 @@ class BIP68Test(BitcoinTestFramework):
tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))]
tx2.rehash()

assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))

# Setting the version back down to 1 should disable the sequence lock,
# so this should be accepted.
@@ -180,7 +180,7 @@ class BIP68Test(BitcoinTestFramework):

if (using_sequence_locks and not should_pass):
# This transaction should be rejected
assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx)
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx)
else:
# This raw transaction should be accepted
self.nodes[0].sendrawtransaction(rawtx)
@@ -227,7 +227,7 @@ class BIP68Test(BitcoinTestFramework):

if (orig_tx.hash in node.getrawmempool()):
# sendrawtransaction should fail if the tx is in the mempool
assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx))
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx))
else:
# sendrawtransaction should succeed if the tx is not in the mempool
node.sendrawtransaction(ToHex(tx))
@@ -280,7 +280,7 @@ class BIP68Test(BitcoinTestFramework):
tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN)
raw_tx5 = self.nodes[0].signrawtransaction(ToHex(tx5))["hex"]

assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5)
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5)

# Test mempool-BIP68 consistency after reorg
#
@@ -353,7 +353,7 @@ class BIP68Test(BitcoinTestFramework):
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))]
tx3.rehash()

assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))

# make a block that violates bip68; ensure that the tip updates
tip = int(self.nodes[0].getbestblockhash(), 16)

+ 2
- 2
test/functional/blockchain.py View File

@@ -25,7 +25,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises,
assert_raises_jsonrpc,
assert_raises_rpc_error,
assert_is_hex_string,
assert_is_hash_string,
)
@@ -96,7 +96,7 @@ class BlockchainTest(BitcoinTestFramework):
def _test_getblockheader(self):
node = self.nodes[0]

assert_raises_jsonrpc(-5, "Block not found",
assert_raises_rpc_error(-5, "Block not found",
node.getblockheader, "nonsense")

besthash = node.getbestblockhash()

+ 7
- 7
test/functional/bumpfee.py View File

@@ -133,7 +133,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
def test_nonrbf_bumpfee_fails(peer_node, dest_address):
# cannot replace a non RBF transaction (from node which did not enable RBF)
not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000"))
assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
assert_raises_rpc_error(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)


def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
@@ -153,7 +153,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
signedtx = rbf_node.signrawtransaction(rawtx)
signedtx = peer_node.signrawtransaction(signedtx["hex"])
rbfid = rbf_node.sendrawtransaction(signedtx["hex"])
assert_raises_jsonrpc(-4, "Transaction contains inputs that don't belong to this wallet",
assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet",
rbf_node.bumpfee, rbfid)


@@ -164,7 +164,7 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000})
tx = rbf_node.signrawtransaction(tx)
txid = rbf_node.sendrawtransaction(tx["hex"])
assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)


def test_small_output_fails(rbf_node, dest_address):
@@ -173,7 +173,7 @@ def test_small_output_fails(rbf_node, dest_address):
rbf_node.bumpfee(rbfid, {"totalFee": 50000})

rbfid = spend_one_input(rbf_node, dest_address)
assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})


def test_dust_to_fee(rbf_node, dest_address):
@@ -205,7 +205,7 @@ def test_rebumping(rbf_node, dest_address):
# check that re-bumping the original tx fails, but bumping the bumper succeeds
rbfid = spend_one_input(rbf_node, dest_address)
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000})
assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000})
assert_raises_rpc_error(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000})
rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000})


@@ -213,7 +213,7 @@ def test_rebumping_not_replaceable(rbf_node, dest_address):
# check that re-bumping a non-replaceable bump tx fails
rbfid = spend_one_input(rbf_node, dest_address)
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False})
assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
{"totalFee": 20000})


@@ -264,7 +264,7 @@ def test_bumpfee_metadata(rbf_node, dest_address):
def test_locked_wallet_fails(rbf_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
rbf_node.walletlock()
assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.",
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
rbf_node.bumpfee, rbfid)



+ 2
- 2
test/functional/disablewallet.py View File

@@ -19,7 +19,7 @@ class DisableWalletTest (BitcoinTestFramework):

def run_test (self):
# Make sure wallet is really disabled
assert_raises_jsonrpc(-32601, 'Method not found', self.nodes[0].getwalletinfo)
assert_raises_rpc_error(-32601, 'Method not found', self.nodes[0].getwalletinfo)
x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
assert(x['isvalid'] == False)
x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
@@ -28,7 +28,7 @@ class DisableWalletTest (BitcoinTestFramework):
# Checking mining to an address without a wallet. Generating to a valid address should succeed
# but generating to an invalid address will fail.
self.nodes[0].generatetoaddress(1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
assert_raises_jsonrpc(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')

if __name__ == '__main__':
DisableWalletTest ().main ()

+ 6
- 6
test/functional/disconnect_ban.py View File

@@ -8,7 +8,7 @@ import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_jsonrpc,
assert_raises_rpc_error,
connect_nodes_bi,
wait_until,
)
@@ -34,14 +34,14 @@ class DisconnectBanTest(BitcoinTestFramework):

self.log.info("setban: fail to ban an already banned subnet")
assert_equal(len(self.nodes[1].listbanned()), 1)
assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add")
assert_raises_rpc_error(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add")

self.log.info("setban: fail to ban an invalid subnet")
assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add")
assert_raises_rpc_error(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add")
assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24

self.log.info("setban remove: fail to unban a non-banned subnet")
assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove")
assert_raises_rpc_error(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove")
assert_equal(len(self.nodes[1].listbanned()), 1)

self.log.info("setban remove: successfully unban subnet")
@@ -81,10 +81,10 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid")
address1 = self.nodes[0].getpeerinfo()[0]['addr']
node1 = self.nodes[0].getpeerinfo()[0]['addr']
assert_raises_jsonrpc(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)

self.log.info("disconnectnode: fail to disconnect when calling with junk address")
assert_raises_jsonrpc(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street")
assert_raises_rpc_error(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street")

self.log.info("disconnectnode: successfully disconnect node by address")
address1 = self.nodes[0].getpeerinfo()[0]['addr']

+ 6
- 6
test/functional/fundrawtransaction.py View File

@@ -179,7 +179,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

assert_raises_jsonrpc(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'})
assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'})

############################################################
# test a fundrawtransaction with an invalid change address #
@@ -192,7 +192,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

assert_raises_jsonrpc(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})

############################################################
# test a fundrawtransaction with a provided change address #
@@ -206,7 +206,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

change = self.nodes[2].getnewaddress()
assert_raises_jsonrpc(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2})
assert_raises_rpc_error(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2})
rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0})
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
out = dec_tx['vout'][0]
@@ -315,7 +315,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)

assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)

############################################################
#compare fee of a standard pubkeyhash transaction
@@ -470,14 +470,14 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
# fund a transaction that requires a new key for the change output
# creating the key must be impossible because the wallet is locked
assert_raises_jsonrpc(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx)
assert_raises_rpc_error(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx)

#refill the keypool
self.nodes[1].walletpassphrase("test", 100)
self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address
self.nodes[1].walletlock()

assert_raises_jsonrpc(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)
assert_raises_rpc_error(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)

oldBalance = self.nodes[0].getbalance()


+ 13
- 17
test/functional/import-rescan.py View File

@@ -19,9 +19,8 @@ importing nodes pick up the new transactions regardless of whether rescans
happened previously.
"""

from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (connect_nodes, sync_blocks, assert_equal, set_node_times)
from test_framework.util import (assert_raises_rpc_error, connect_nodes, sync_blocks, assert_equal, set_node_times)

import collections
import enum
@@ -35,21 +34,26 @@ Rescan = enum.Enum("Rescan", "no yes late_timestamp")
class Variant(collections.namedtuple("Variant", "call data rescan prune")):
"""Helper for importing one key and verifying scanned transactions."""

def try_rpc(self, func, *args, **kwargs):
if self.expect_disabled:
assert_raises_rpc_error(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs)
else:
return func(*args, **kwargs)

def do_import(self, timestamp):
"""Call one key import RPC."""

if self.call == Call.single:
if self.data == Data.address:
response, error = try_rpc(self.node.importaddress, self.address["address"], self.label,
self.rescan == Rescan.yes)
response = self.try_rpc(self.node.importaddress, self.address["address"], self.label,
self.rescan == Rescan.yes)
elif self.data == Data.pub:
response, error = try_rpc(self.node.importpubkey, self.address["pubkey"], self.label,
self.rescan == Rescan.yes)
response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label,
self.rescan == Rescan.yes)
elif self.data == Data.priv:
response, error = try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes)
response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes)
assert_equal(response, None)
assert_equal(error, {'message': 'Rescan is disabled in pruned mode',
'code': -4} if self.expect_disabled else None)

elif self.call == Call.multi:
response = self.node.importmulti([{
"scriptPubKey": {
@@ -180,13 +184,5 @@ class ImportRescanTest(BitcoinTestFramework):
else:
variant.check()


def try_rpc(func, *args, **kwargs):
try:
return func(*args, **kwargs), None
except JSONRPCException as e:
return None, e.error


if __name__ == "__main__":
ImportRescanTest().main()

+ 2
- 2
test/functional/importmulti.py View File

@@ -448,11 +448,11 @@ class ImportMultiTest (BitcoinTestFramework):

# Bad or missing timestamps
self.log.info("Should throw on invalid or missing timestamp values")
assert_raises_message(JSONRPCException, 'Missing required timestamp field for key',
assert_raises_rpc_error(-3, 'Missing required timestamp field for key',
self.nodes[1].importmulti, [{
"scriptPubKey": address['scriptPubKey'],
}])
assert_raises_message(JSONRPCException, 'Expected number or "now" timestamp value for key. got type string',
assert_raises_rpc_error(-3, 'Expected number or "now" timestamp value for key. got type string',
self.nodes[1].importmulti, [{
"scriptPubKey": address['scriptPubKey'],
"timestamp": "",

+ 2
- 2
test/functional/importprunedfunds.py View File

@@ -67,7 +67,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.sync_all()

#Import with no affiliated address
assert_raises_jsonrpc(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1)
assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1)

balance1 = self.nodes[1].getbalance("", 0, True)
assert_equal(balance1, Decimal(0))
@@ -98,7 +98,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_equal(address_info['ismine'], True)

#Remove transactions
assert_raises_jsonrpc(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1)
assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1)

balance1 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance1, Decimal('0.075'))

+ 4
- 4
test/functional/keypool.py View File

@@ -28,7 +28,7 @@ class KeyPoolTest(BitcoinTestFramework):
wallet_info = nodes[0].getwalletinfo()
assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)

# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
nodes[0].walletpassphrase('test', 12000)
@@ -47,7 +47,7 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0].getrawchangeaddress()
addr = set()
# the next one should fail
assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].getrawchangeaddress)
assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getrawchangeaddress)

# drain the external keys
addr.add(nodes[0].getnewaddress())
@@ -58,7 +58,7 @@ class KeyPoolTest(BitcoinTestFramework):
addr.add(nodes[0].getnewaddress())
assert(len(addr) == 6)
# the next one should fail
assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)

# refill keypool with three new addresses
nodes[0].walletpassphrase('test', 1)
@@ -72,7 +72,7 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0].generate(1)
nodes[0].generate(1)
nodes[0].generate(1)
assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].generate, 1)
assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].generate, 1)

nodes[0].walletpassphrase('test', 100)
nodes[0].keypoolrefill(100)

+ 2
- 2
test/functional/mempool_packages.py View File

@@ -115,7 +115,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)

# Adding one more transaction on to the chain should fail.
assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)
assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)

# Check that prioritising a tx before it's added to the mempool works
# First clear the mempool by mining a block.
@@ -167,7 +167,7 @@ class MempoolPackagesTest(BitcoinTestFramework):

# Sending one more chained transaction will fail
utxo = transaction_package.pop(0)
assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)

# TODO: check that node1's mempool is as expected


+ 2
- 2
test/functional/mempool_reorg.py View File

@@ -50,14 +50,14 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000"
timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"]
# This will raise an exception because the timelock transaction is too immature to spend
assert_raises_jsonrpc(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx)
assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx)

# Broadcast and mine spend_102 and 103:
spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw)
spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw)
self.nodes[0].generate(1)
# Time-locked transaction is still too immature to spend
assert_raises_jsonrpc(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx)
assert_raises_rpc_error(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx)

# Create 102_1 and 103_1:
spend_102_1_raw = create_tx(self.nodes[0], spend_102_id, node1_address, 49.98)

+ 1
- 1
test/functional/mempool_spendcoinbase.py View File

@@ -36,7 +36,7 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework):
spend_101_id = self.nodes[0].sendrawtransaction(spends_raw[0])

# coinbase at height 102 should be too immature to spend
assert_raises_jsonrpc(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1])
assert_raises_rpc_error(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1])

# mempool should have just spend_101:
assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ])

+ 4
- 4
test/functional/merkle_blocks.py View File

@@ -38,7 +38,7 @@ class MerkleBlockTest(BitcoinTestFramework):
tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99})
txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"])
# This will raise an exception because the transaction is not yet in a block
assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1])
assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1])

self.nodes[0].generate(1)
blockhash = self.nodes[0].getblockhash(chain_height + 1)
@@ -63,11 +63,11 @@ class MerkleBlockTest(BitcoinTestFramework):
txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2

# We can't find the block from a fully-spent tx
assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent])
assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent])
# We can get the proof if we specify the block
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent])
# We can't get the proof if we specify a non-existent block
assert_raises_jsonrpc(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
# We can get the proof if the transaction is unspent
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent])
# We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter.
@@ -76,7 +76,7 @@ class MerkleBlockTest(BitcoinTestFramework):
# We can always get a proof if we have a -txindex
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent])
# We can't get a proof if we specify transactions from different blocks
assert_raises_jsonrpc(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3])
assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3])


if __name__ == '__main__':

+ 93
- 0
test/functional/minchainwork.py View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
# 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.
"""Test logic for setting nMinimumChainWork on command line.

Nodes don't consider themselves out of "initial block download" until
their active chain has more work than nMinimumChainWork.

Nodes don't download blocks from a peer unless the peer's best known block
has more work than nMinimumChainWork.

While in initial block download, nodes won't relay blocks to their peers, so
test that this parameter functions as intended by verifying that block relay
only succeeds past a given node once its nMinimumChainWork has been exceeded.
"""

import time

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import sync_blocks, connect_nodes, assert_equal

# 2 hashes required per regtest block (with no difficulty adjustment)
REGTEST_WORK_PER_BLOCK = 2

class MinimumChainWorkTest(BitcoinTestFramework):
def set_test_params(self):