mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-17 02:50:38 +01:00
6f994882devalidation: Farewell, global Chainstate! (Carl Dong)972c5166eeqt/test: Reset chainman in ~ChainstateManager instead (Carl Dong)6c3b5dc0c1scripted-diff: tree-wide: Remove all review-only assertions (Carl Dong)3e82abb8ddtree-wide: Remove stray review-only assertion (Carl Dong)f323248abaqt/test: Use existing chainman in ::TestGUI (can be scripted-diff) (Carl Dong)6c15de129cscripted-diff: wallet/test: Use existing chainman (Carl Dong)ee0ab1e959fuzz: Initialize a TestingSetup for test_one_input (Carl Dong)0d61634c06scripted-diff: test: Use existing chainman in unit tests (Carl Dong)e197076219test: Pass in CoinsTip to ValidateCheckInputsForAllFlags (Carl Dong)4d99b61014test/miner_tests: Pass in chain tip to CreateBlockIndex (Carl Dong)f0dd5e6bb4test/util: Use existing chainman in ::PrepareBlock (Carl Dong)464c313e30init: Use existing chainman (Carl Dong) Pull request description: Based on: #21767 à la Mr. Sandman ``` Mr. Chainman, bring me a tip (bung, bung, bung, bung) Make it the most work that I've ever seen (bung, bung, bung, bung) Rewind old tip till we're at the fork point (bung, bung, bung, bung) Then tell it that it's time to call Con-nectTip Chainman, I'm so alone (bung, bung, bung, bung) No local objects to call my own (bung, bung, bung, bung) Please make sure I have a ref Mr. Chainman, bring me a tip! ``` This is the last bundle in the #20158 series. Thanks everyone for their diligent review. I would like to call attention to https://github.com/bitcoin/bitcoin/issues/21766, where a few leftover improvements were collated. - Remove globals: - `ChainstateManager g_chainman` - `CChainState& ChainstateActive()` - `CChain& ChainActive()` - Remove all review-only assertions. ACKs for top commit: jamesob: reACK6f994882debased on the contents of ariard: Code Review ACK6f99488. jnewbery: utACK6f994882deachow101: Code Review ACK6f994882deryanofsky: Code review ACK6f994882de. Tree-SHA512: 4052ea79360cf0efd81ad0ee3f982e1d93aab1837dcec75f875a56ceda085de078bb3099a2137935d7cc2222004ad88da94b605ef5efef35cb6bc733725debe6
431 lines
16 KiB
C++
431 lines
16 KiB
C++
// Copyright (c) 2011-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
// Unit tests for denial-of-service detection/prevention code
|
|
|
|
#include <arith_uint256.h>
|
|
#include <banman.h>
|
|
#include <chainparams.h>
|
|
#include <net.h>
|
|
#include <net_processing.h>
|
|
#include <pubkey.h>
|
|
#include <script/sign.h>
|
|
#include <script/signingprovider.h>
|
|
#include <script/standard.h>
|
|
#include <serialize.h>
|
|
#include <test/util/net.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <txorphanage.h>
|
|
#include <util/string.h>
|
|
#include <util/system.h>
|
|
#include <util/time.h>
|
|
#include <validation.h>
|
|
|
|
#include <array>
|
|
#include <stdint.h>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
static CService ip(uint32_t i)
|
|
{
|
|
struct in_addr s;
|
|
s.s_addr = i;
|
|
return CService(CNetAddr(s), Params().GetDefaultPort());
|
|
}
|
|
|
|
static NodeId id = 0;
|
|
|
|
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds);
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(denialofservice_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)
|
|
{
|
|
const CChainParams& chainparams = Params();
|
|
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
|
|
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
|
|
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
|
|
|
|
// Mock an outbound peer
|
|
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
|
|
CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr1, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false);
|
|
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
|
|
|
|
peerLogic->InitializeNode(&dummyNode1);
|
|
dummyNode1.fSuccessfullyConnected = true;
|
|
|
|
// This test requires that we have a chain with non-zero work.
|
|
{
|
|
LOCK(cs_main);
|
|
BOOST_CHECK(m_node.chainman->ActiveChain().Tip() != nullptr);
|
|
BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->nChainWork > 0);
|
|
}
|
|
|
|
// Test starts here
|
|
{
|
|
LOCK(dummyNode1.cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
|
|
}
|
|
{
|
|
LOCK(dummyNode1.cs_vSend);
|
|
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
|
|
dummyNode1.vSendMsg.clear();
|
|
}
|
|
|
|
int64_t nStartTime = GetTime();
|
|
// Wait 21 minutes
|
|
SetMockTime(nStartTime+21*60);
|
|
{
|
|
LOCK(dummyNode1.cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
|
|
}
|
|
{
|
|
LOCK(dummyNode1.cs_vSend);
|
|
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
|
|
}
|
|
// Wait 3 more minutes
|
|
SetMockTime(nStartTime+24*60);
|
|
{
|
|
LOCK(dummyNode1.cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in disconnect
|
|
}
|
|
BOOST_CHECK(dummyNode1.fDisconnect == true);
|
|
|
|
peerLogic->FinalizeNode(dummyNode1);
|
|
}
|
|
|
|
static void AddRandomOutboundPeer(std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman)
|
|
{
|
|
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
|
|
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false));
|
|
CNode &node = *vNodes.back();
|
|
node.SetCommonVersion(PROTOCOL_VERSION);
|
|
|
|
peerLogic.InitializeNode(&node);
|
|
node.fSuccessfullyConnected = true;
|
|
|
|
connman.AddTestNode(node);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
|
|
{
|
|
const CChainParams& chainparams = Params();
|
|
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
|
|
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
|
|
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
|
|
|
|
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
|
|
CConnman::Options options;
|
|
options.nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
|
|
options.m_max_outbound_full_relay = max_outbound_full_relay;
|
|
options.nMaxFeeler = MAX_FEELER_CONNECTIONS;
|
|
|
|
connman->Init(options);
|
|
std::vector<CNode *> vNodes;
|
|
|
|
// Mock some outbound peers
|
|
for (int i = 0; i < max_outbound_full_relay; ++i) {
|
|
AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
|
|
}
|
|
|
|
peerLogic->CheckForStaleTipAndEvictPeers();
|
|
|
|
// 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 * chainparams.GetConsensus().nPowTargetSpacing + 1);
|
|
|
|
// Now tip should definitely be stale, and we should look for an extra
|
|
// outbound peer
|
|
peerLogic->CheckForStaleTipAndEvictPeers();
|
|
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, *connman);
|
|
|
|
peerLogic->CheckForStaleTipAndEvictPeers();
|
|
for (int i = 0; i < max_outbound_full_relay; ++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();
|
|
for (int i = 0; i < max_outbound_full_relay - 1; ++i) {
|
|
BOOST_CHECK(vNodes[i]->fDisconnect == false);
|
|
}
|
|
BOOST_CHECK(vNodes[max_outbound_full_relay-1]->fDisconnect == true);
|
|
BOOST_CHECK(vNodes.back()->fDisconnect == false);
|
|
|
|
for (const CNode *node : vNodes) {
|
|
peerLogic->FinalizeNode(*node);
|
|
}
|
|
|
|
connman->ClearTestNodes();
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(peer_discouragement)
|
|
{
|
|
const CChainParams& chainparams = Params();
|
|
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
|
|
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
|
|
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
|
|
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
|
|
|
|
CNetAddr tor_netaddr;
|
|
BOOST_REQUIRE(
|
|
tor_netaddr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
|
|
const CService tor_service{tor_netaddr, Params().GetDefaultPort()};
|
|
|
|
const std::array<CAddress, 3> addr{CAddress{ip(0xa0b0c001), NODE_NONE},
|
|
CAddress{ip(0xa0b0c002), NODE_NONE},
|
|
CAddress{tor_service, NODE_NONE}};
|
|
|
|
const CNetAddr other_addr{ip(0xa0b0ff01)}; // Not any of addr[].
|
|
|
|
std::array<CNode*, 3> nodes;
|
|
|
|
banman->ClearBanned();
|
|
nodes[0] = new CNode{id++, NODE_NETWORK, INVALID_SOCKET, addr[0], /* nKeyedNetGroupIn */ 0,
|
|
/* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "",
|
|
ConnectionType::INBOUND, /* inbound_onion */ false};
|
|
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
|
|
peerLogic->InitializeNode(nodes[0]);
|
|
nodes[0]->fSuccessfullyConnected = true;
|
|
connman->AddTestNode(*nodes[0]);
|
|
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
|
|
{
|
|
LOCK(nodes[0]->cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(nodes[0]));
|
|
}
|
|
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
|
|
BOOST_CHECK(nodes[0]->fDisconnect);
|
|
BOOST_CHECK(!banman->IsDiscouraged(other_addr)); // Different address, not discouraged
|
|
|
|
nodes[1] = new CNode{id++, NODE_NETWORK, INVALID_SOCKET, addr[1], /* nKeyedNetGroupIn */ 1,
|
|
/* nLocalHostNonceIn */ 1, CAddress(), /* pszDest */ "",
|
|
ConnectionType::INBOUND, /* inbound_onion */ false};
|
|
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
|
|
peerLogic->InitializeNode(nodes[1]);
|
|
nodes[1]->fSuccessfullyConnected = true;
|
|
connman->AddTestNode(*nodes[1]);
|
|
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
|
|
{
|
|
LOCK(nodes[1]->cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
|
|
}
|
|
// [0] is still discouraged/disconnected.
|
|
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
|
|
BOOST_CHECK(nodes[0]->fDisconnect);
|
|
// [1] is not discouraged/disconnected yet.
|
|
BOOST_CHECK(!banman->IsDiscouraged(addr[1]));
|
|
BOOST_CHECK(!nodes[1]->fDisconnect);
|
|
peerLogic->Misbehaving(nodes[1]->GetId(), 1, /* message */ ""); // [1] reaches discouragement threshold
|
|
{
|
|
LOCK(nodes[1]->cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
|
|
}
|
|
// Expect both [0] and [1] to be discouraged/disconnected now.
|
|
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
|
|
BOOST_CHECK(nodes[0]->fDisconnect);
|
|
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
|
|
BOOST_CHECK(nodes[1]->fDisconnect);
|
|
|
|
// Make sure non-IP peers are discouraged and disconnected properly.
|
|
|
|
nodes[2] = new CNode{id++, NODE_NETWORK, INVALID_SOCKET, addr[2], /* nKeyedNetGroupIn */ 1,
|
|
/* nLocalHostNonceIn */ 1, CAddress(), /* pszDest */ "",
|
|
ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false};
|
|
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
|
|
peerLogic->InitializeNode(nodes[2]);
|
|
nodes[2]->fSuccessfullyConnected = true;
|
|
connman->AddTestNode(*nodes[2]);
|
|
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
|
|
{
|
|
LOCK(nodes[2]->cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(nodes[2]));
|
|
}
|
|
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
|
|
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
|
|
BOOST_CHECK(banman->IsDiscouraged(addr[2]));
|
|
BOOST_CHECK(nodes[0]->fDisconnect);
|
|
BOOST_CHECK(nodes[1]->fDisconnect);
|
|
BOOST_CHECK(nodes[2]->fDisconnect);
|
|
|
|
for (CNode* node : nodes) {
|
|
peerLogic->FinalizeNode(*node);
|
|
}
|
|
connman->ClearTestNodes();
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(DoS_bantime)
|
|
{
|
|
const CChainParams& chainparams = Params();
|
|
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
|
|
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
|
|
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
|
|
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
|
|
|
|
banman->ClearBanned();
|
|
int64_t nStartTime = GetTime();
|
|
SetMockTime(nStartTime); // Overrides future calls to GetTime()
|
|
|
|
CAddress addr(ip(0xa0b0c001), NODE_NONE);
|
|
CNode dummyNode(id++, NODE_NETWORK, INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 4, /* nLocalHostNonceIn */ 4, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
|
|
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
|
|
peerLogic->InitializeNode(&dummyNode);
|
|
dummyNode.fSuccessfullyConnected = true;
|
|
|
|
peerLogic->Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
|
|
{
|
|
LOCK(dummyNode.cs_sendProcessing);
|
|
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
|
|
}
|
|
BOOST_CHECK(banman->IsDiscouraged(addr));
|
|
|
|
peerLogic->FinalizeNode(dummyNode);
|
|
}
|
|
|
|
class TxOrphanageTest : public TxOrphanage
|
|
{
|
|
public:
|
|
inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
|
|
{
|
|
return m_orphans.size();
|
|
}
|
|
|
|
CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
|
|
{
|
|
std::map<uint256, OrphanTx>::iterator it;
|
|
it = m_orphans.lower_bound(InsecureRand256());
|
|
if (it == m_orphans.end())
|
|
it = m_orphans.begin();
|
|
return it->second.tx;
|
|
}
|
|
};
|
|
|
|
static void MakeNewKeyWithFastRandomContext(CKey& key)
|
|
{
|
|
std::vector<unsigned char> keydata;
|
|
keydata = g_insecure_rand_ctx.randbytes(32);
|
|
key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn*/ true);
|
|
assert(key.IsValid());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|
{
|
|
// This test had non-deterministic coverage due to
|
|
// randomly selected seeds.
|
|
// This seed is chosen so that all branches of the function
|
|
// ecdsa_signature_parse_der_lax are executed during this test.
|
|
// Specifically branches that run only when an ECDSA
|
|
// signature's R and S values have leading zeros.
|
|
g_insecure_rand_ctx = FastRandomContext(ArithToUint256(arith_uint256(33)));
|
|
|
|
TxOrphanageTest orphanage;
|
|
CKey key;
|
|
MakeNewKeyWithFastRandomContext(key);
|
|
FillableSigningProvider keystore;
|
|
BOOST_CHECK(keystore.AddKey(key));
|
|
|
|
LOCK(g_cs_orphans);
|
|
|
|
// 50 orphan transactions:
|
|
for (int i = 0; i < 50; i++)
|
|
{
|
|
CMutableTransaction tx;
|
|
tx.vin.resize(1);
|
|
tx.vin[0].prevout.n = 0;
|
|
tx.vin[0].prevout.hash = InsecureRand256();
|
|
tx.vin[0].scriptSig << OP_1;
|
|
tx.vout.resize(1);
|
|
tx.vout[0].nValue = 1*CENT;
|
|
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
|
|
|
orphanage.AddTx(MakeTransactionRef(tx), i);
|
|
}
|
|
|
|
// ... and 50 that depend on other orphans:
|
|
for (int i = 0; i < 50; i++)
|
|
{
|
|
CTransactionRef txPrev = orphanage.RandomOrphan();
|
|
|
|
CMutableTransaction tx;
|
|
tx.vin.resize(1);
|
|
tx.vin[0].prevout.n = 0;
|
|
tx.vin[0].prevout.hash = txPrev->GetHash();
|
|
tx.vout.resize(1);
|
|
tx.vout[0].nValue = 1*CENT;
|
|
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
|
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
|
|
|
|
orphanage.AddTx(MakeTransactionRef(tx), i);
|
|
}
|
|
|
|
// This really-big orphan should be ignored:
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
CTransactionRef txPrev = orphanage.RandomOrphan();
|
|
|
|
CMutableTransaction tx;
|
|
tx.vout.resize(1);
|
|
tx.vout[0].nValue = 1*CENT;
|
|
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
|
tx.vin.resize(2777);
|
|
for (unsigned int j = 0; j < tx.vin.size(); j++)
|
|
{
|
|
tx.vin[j].prevout.n = j;
|
|
tx.vin[j].prevout.hash = txPrev->GetHash();
|
|
}
|
|
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
|
|
// Re-use same signature for other inputs
|
|
// (they don't have to be valid for this test)
|
|
for (unsigned int j = 1; j < tx.vin.size(); j++)
|
|
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
|
|
|
|
BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
|
|
}
|
|
|
|
// Test EraseOrphansFor:
|
|
for (NodeId i = 0; i < 3; i++)
|
|
{
|
|
size_t sizeBefore = orphanage.CountOrphans();
|
|
orphanage.EraseForPeer(i);
|
|
BOOST_CHECK(orphanage.CountOrphans() < sizeBefore);
|
|
}
|
|
|
|
// Test LimitOrphanTxSize() function:
|
|
orphanage.LimitOrphans(40);
|
|
BOOST_CHECK(orphanage.CountOrphans() <= 40);
|
|
orphanage.LimitOrphans(10);
|
|
BOOST_CHECK(orphanage.CountOrphans() <= 10);
|
|
orphanage.LimitOrphans(0);
|
|
BOOST_CHECK(orphanage.CountOrphans() == 0);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|