diff --git a/doc/bips.md b/doc/bips.md
index 2ac4abb12..7aa8c9a2e 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -1,36 +1,36 @@
BIPs that are implemented by Bitcoin ABC (up-to-date up to **v0.13.0**):
* [`BIP 9`](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki): The changes allowing multiple soft-forks to be deployed in parallel have been implemented since **v0.12.1** ([PR #7575](https://github.com/bitcoin/bitcoin/pull/7575))
* [`BIP 11`](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki): Multisig outputs are standard since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
* [`BIP 13`](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki): The address format for P2SH addresses has been implemented since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
* [`BIP 14`](https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki): The subversion string is being used as User Agent since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
* [`BIP 16`](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki): The pay-to-script-hash evaluation rules have been implemented since **v0.6.0**, and took effect on *April 1st 2012* ([PR #748](https://github.com/bitcoin/bitcoin/pull/748)).
* [`BIP 21`](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki): The URI format for Bitcoin payments has been implemented since **v0.6.0** ([PR #176](https://github.com/bitcoin/bitcoin/pull/176)).
* [`BIP 22`](https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki): The 'getblocktemplate' (GBT) RPC protocol for mining has been implemented since **v0.7.0** ([PR #936](https://github.com/bitcoin/bitcoin/pull/936)).
* [`BIP 23`](https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki): Some extensions to GBT have been implemented since **v0.10.0rc1**, including longpolling and block proposals ([PR #1816](https://github.com/bitcoin/bitcoin/pull/1816)).
* [`BIP 30`](https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki): The evaluation rules to forbid creating new transactions with the same txid as previous not-fully-spent transactions were implemented since **v0.6.0**, and the rule took effect on *March 15th 2012* ([PR #915](https://github.com/bitcoin/bitcoin/pull/915)).
* [`BIP 31`](https://github.com/bitcoin/bips/blob/master/bip-0031.mediawiki): The 'pong' protocol message (and the protocol version bump to 60001) has been implemented since **v0.6.1** ([PR #1081](https://github.com/bitcoin/bitcoin/pull/1081)).
* [`BIP 32`](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki): Hierarchical Deterministic Wallets has been implemented since **v0.13.0** ([PR #8035](https://github.com/bitcoin/bitcoin/pull/8035)).
* [`BIP 34`](https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki): The rule that requires blocks to contain their height (number) in the coinbase input, and the introduction of version 2 blocks has been implemented since **v0.7.0**. The rule took effect for version 2 blocks as of *block 224413* (March 5th 2013), and version 1 blocks are no longer allowed since *block 227931* (March 25th 2013) ([PR #1526](https://github.com/bitcoin/bitcoin/pull/1526)).
* [`BIP 35`](https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki): The 'mempool' protocol message (and the protocol version bump to 60002) has been implemented since **v0.7.0** ([PR #1641](https://github.com/bitcoin/bitcoin/pull/1641)).
* [`BIP 37`](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki): The bloom filtering for transaction relaying, partial Merkle trees for blocks, and the protocol version bump to 70001 (enabling low-bandwidth SPV clients) has been implemented since **v0.8.0** ([PR #1795](https://github.com/bitcoin/bitcoin/pull/1795)).
* [`BIP 42`](https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki): The bug that would have caused the subsidy schedule to resume after block 13440000 was fixed in **v0.9.2** ([PR #3842](https://github.com/bitcoin/bitcoin/pull/3842)).
-* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting *v0.17.0*, whether to send reject messages can be configured with the `-enablebip61` option.
+* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting **v0.19.11**, whether to send reject messages can be configured with the `-enablebip61` option, and support is deprecated (disabled by default) as of **0.21.9**. Support was removed in **v0.22.5** ([PR #15437](https://github.com/bitcoin/bitcoin/pull/15437)).
* [`BIP 65`](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki): The CHECKLOCKTIMEVERIFY softfork was merged in **v0.12.0** ([PR #6351](https://github.com/bitcoin/bitcoin/pull/6351)), and backported to **v0.11.2** and **v0.10.4**. Mempool-only CLTV was added in [PR #6124](https://github.com/bitcoin/bitcoin/pull/6124).
* [`BIP 66`](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki): The strict DER rules and associated version 3 blocks have been implemented since **v0.10.0** ([PR #5713](https://github.com/bitcoin/bitcoin/pull/5713)).
* [`BIP 68`](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki): Sequence locks have been implemented as of **v0.12.1** ([PR #7184](https://github.com/bitcoin/bitcoin/pull/7184)), and have been activated since *block 419328*.
* [`BIP 70`](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki) [`71`](https://github.com/bitcoin/bips/blob/master/bip-0071.mediawiki) [`72`](https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki): Payment Protocol support has been available in Bitcoin Core GUI since **v0.9.0** ([PR #5216](https://github.com/bitcoin/bitcoin/pull/5216)).
* [`BIP 90`](https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki): Trigger mechanism for activation of BIPs 34, 65, and 66 has been simplified to block height checks since **v0.14.0** ([PR #8391](https://github.com/bitcoin/bitcoin/pull/8391)).
* [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, and enforced for all peer versions as of **v0.13.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579) and [PR #6641](https://github.com/bitcoin/bitcoin/pull/6641)).
* [`BIP 112`](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki): The CHECKSEQUENCEVERIFY opcode has been implemented since **v0.12.1** ([PR #7524](https://github.com/bitcoin/bitcoin/pull/7524)) and has been activated since *block 419328*.
* [`BIP 113`](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki): Median time past lock-time calculations have been implemented since **v0.12.1** ([PR #6566](https://github.com/bitcoin/bitcoin/pull/6566)) and have been activated since *block 419328*.
* [`BIP 125`](https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki): Opt-in full replace-by-fee signaling honoured in mempool and mining as of **v0.12.0** ([PR 6871](https://github.com/bitcoin/bitcoin/pull/6871)). BIP 125 Replace-by-Fee has been removed in Bitcoin ABC.
* [`BIP 130`](https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki): direct headers announcement is negotiated with peer versions `>=70012` as of **v0.12.0** ([PR 6494](https://github.com/bitcoin/bitcoin/pull/6494)).
* [`BIP 133`](https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki): feefilter messages are respected and sent for peer versions `>=70013` as of **v0.13.0** ([PR 7542](https://github.com/bitcoin/bitcoin/pull/7542)).
* [`BIP 141`](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki): Segregated Witness (Consensus Layer) as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)), and defined for mainnet as of **v0.13.1** ([PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
* [`BIP 143`](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki): Transaction Signature Verification for Version 0 Witness Program as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)) and defined for mainnet as of **v0.13.1** ([PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)). BIP 143 has been adapted for the replay-protected sighash format in Bitcoin ABC
* [`BIP 144`](https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki): Segregated Witness as of **0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)). BIP 144 has been removed in Bitcoin ABC.
* [`BIP 145`](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki): getblocktemplate updates for Segregated Witness as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)). BIP 145 has been removed in Bitcoin ABC.
* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)). BIP 147 has been removed in Bitcoin ABC.
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)).
* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): NODE_NETWORK_LIMITED service bit [signaling only] is supported as of **v0.18.7**.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 5661448d4..201eaa9f7 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,22 +1,57 @@
# Bitcoin ABC 0.22.5 Release Notes
Bitcoin ABC version 0.22.5 is now available from:
This release includes the following features and fixes:
- The `-upgradewallet` command line flag has been replaced in favor of the `upgradewallet` RPC.
Updated RPCs
------------
- The `getchaintxstats` RPC now returns the additional key of
`window_final_block_height`.
- The `getnetworkinfo` and `getpeerinfo` commands now contain
a new `servicesnames` field with decoded network service flags.
Updated settings
----------------
- The `-debug=db` logging category, which was deprecated in 0.22.4 and replaced by
`-debug=walletdb` to distinguish it from `coindb`, has been removed.
+
+P2P and network changes
+-----------------------
+
+#### Removal of reject network messages from Bitcoin ABC (BIP61)
+
+The command line option to enable BIP61 (`-enablebip61`) has been removed.
+
+This feature has been disabled by default since Bitcoin ABC version 0.21.9.
+Nodes on the network can not generally be trusted to send valid ("reject")
+messages, so this should only ever be used when connected to a trusted node.
+Please use the recommended alternatives if you rely on this deprecated feature:
+
+* Testing or debugging of implementations of the Bitcoin P2P network protocol
+ should be done by inspecting the log messages that are produced by a recent
+ version of Bitcoin ABC. Bitcoin ABC logs debug messages
+ (`-debug=`) to a stream (`-printtoconsole`) or to a file
+ (`-debuglogfile=`).
+
+* Testing the validity of a block can be achieved by specific RPCs:
+ - `submitblock`
+ - `getblocktemplate` with `'mode'` set to `'proposal'` for blocks with
+ potentially invalid POW
+
+* Testing the validity of a transaction can be achieved by specific RPCs:
+ - `sendrawtransaction`
+ - `testmempoolaccept`
+
+* Wallets should not use the absence of "reject" messages to indicate a
+ transaction has propagated the network, nor should wallets use "reject"
+ messages to set transaction fees. Wallets should rather use fee estimation
+ to determine transaction fees and set replace-by-fee if desired. Thus, they
+ could wait until the transaction has confirmed (taking into account the fee
+ target they set (compare the RPC `estimatesmartfee`)) or listen for the
+ transaction announcement by other network peers to check for propagation.
diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp
index ac44b990d..17270e5bc 100644
--- a/src/avalanche/test/processor_tests.cpp
+++ b/src/avalanche/test/processor_tests.cpp
@@ -1,979 +1,979 @@
// Copyright (c) 2018-2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
#include
#include
#include
#include
#include // For PeerLogicValidation
#include
// D6970 moved LookupBlockIndex from chain.h to validation.h TODO: remove this
// when LookupBlockIndex is refactored out of validation
#include
#include
#include
using namespace avalanche;
namespace avalanche {
namespace {
struct AvalancheTest {
static void runEventLoop(avalanche::Processor &p) { p.runEventLoop(); }
static std::vector getInvsForNextPoll(Processor &p) {
return p.getInvsForNextPoll(false);
}
static NodeId getSuitableNodeToQuery(Processor &p) {
return p.getSuitableNodeToQuery();
}
static PeerManager &getPeerManager(Processor &p) {
LOCK(p.cs_peerManager);
return *p.peerManager;
}
static uint64_t getRound(const Processor &p) { return p.round; }
};
} // namespace
} // namespace avalanche
namespace {
struct CConnmanTest : public CConnman {
using CConnman::CConnman;
void AddNode(CNode &node) {
LOCK(cs_vNodes);
vNodes.push_back(&node);
}
void ClearNodes() {
LOCK(cs_vNodes);
for (CNode *node : vNodes) {
delete node;
}
vNodes.clear();
}
};
} // namespace
BOOST_FIXTURE_TEST_SUITE(processor_tests, TestChain100Setup)
#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \
vr.registerVote(NO_NODE, vote); \
BOOST_CHECK_EQUAL(vr.isAccepted(), state); \
BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \
BOOST_CHECK_EQUAL(vr.getConfidence(), confidence);
BOOST_AUTO_TEST_CASE(vote_record) {
VoteRecord vraccepted(true);
// Check initial state.
BOOST_CHECK_EQUAL(vraccepted.isAccepted(), true);
BOOST_CHECK_EQUAL(vraccepted.hasFinalized(), false);
BOOST_CHECK_EQUAL(vraccepted.getConfidence(), 0);
VoteRecord vr(false);
// Check initial state.
BOOST_CHECK_EQUAL(vr.isAccepted(), false);
BOOST_CHECK_EQUAL(vr.hasFinalized(), false);
BOOST_CHECK_EQUAL(vr.getConfidence(), 0);
// We need to register 6 positive votes before we start counting.
for (int i = 0; i < 6; i++) {
REGISTER_VOTE_AND_CHECK(vr, 0, false, false, 0);
}
// Next vote will flip state, and confidence will increase as long as we
// vote yes.
REGISTER_VOTE_AND_CHECK(vr, 0, true, false, 0);
// A single neutral vote do not change anything.
REGISTER_VOTE_AND_CHECK(vr, -1, true, false, 1);
for (int i = 2; i < 8; i++) {
REGISTER_VOTE_AND_CHECK(vr, 0, true, false, i);
}
// Two neutral votes will stall progress.
REGISTER_VOTE_AND_CHECK(vr, -1, true, false, 7);
REGISTER_VOTE_AND_CHECK(vr, -1, true, false, 7);
for (int i = 2; i < 8; i++) {
REGISTER_VOTE_AND_CHECK(vr, 0, true, false, 7);
}
// Now confidence will increase as long as we vote yes.
for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
REGISTER_VOTE_AND_CHECK(vr, 0, true, false, i);
}
// The next vote will finalize the decision.
REGISTER_VOTE_AND_CHECK(vr, 1, true, true, AVALANCHE_FINALIZATION_SCORE);
// Now that we have two no votes, confidence stop increasing.
for (int i = 0; i < 5; i++) {
REGISTER_VOTE_AND_CHECK(vr, 1, true, true,
AVALANCHE_FINALIZATION_SCORE);
}
// Next vote will flip state, and confidence will increase as long as we
// vote no.
REGISTER_VOTE_AND_CHECK(vr, 1, false, false, 0);
// A single neutral vote do not change anything.
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, 1);
for (int i = 2; i < 8; i++) {
REGISTER_VOTE_AND_CHECK(vr, 1, false, false, i);
}
// Two neutral votes will stall progress.
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, 7);
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, 7);
for (int i = 2; i < 8; i++) {
REGISTER_VOTE_AND_CHECK(vr, 1, false, false, 7);
}
// Now confidence will increase as long as we vote no.
for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
REGISTER_VOTE_AND_CHECK(vr, 1, false, false, i);
}
// The next vote will finalize the decision.
REGISTER_VOTE_AND_CHECK(vr, 0, false, true, AVALANCHE_FINALIZATION_SCORE);
// Check that inflight accounting work as expected.
VoteRecord vrinflight(false);
for (int i = 0; i < 2 * AVALANCHE_MAX_INFLIGHT_POLL; i++) {
bool shouldPoll = vrinflight.shouldPoll();
BOOST_CHECK_EQUAL(shouldPoll, i < AVALANCHE_MAX_INFLIGHT_POLL);
BOOST_CHECK_EQUAL(vrinflight.registerPoll(), shouldPoll);
}
// Clear various number of inflight requests and check everything behaves as
// expected.
for (int i = 1; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
vrinflight.clearInflightRequest(i);
BOOST_CHECK(vrinflight.shouldPoll());
for (int j = 1; j < i; j++) {
BOOST_CHECK(vrinflight.registerPoll());
BOOST_CHECK(vrinflight.shouldPoll());
}
BOOST_CHECK(vrinflight.registerPoll());
BOOST_CHECK(!vrinflight.shouldPoll());
}
}
BOOST_AUTO_TEST_CASE(block_update) {
CBlockIndex index;
CBlockIndex *pindex = &index;
std::set status{
BlockUpdate::Status::Invalid,
BlockUpdate::Status::Rejected,
BlockUpdate::Status::Accepted,
BlockUpdate::Status::Finalized,
};
for (auto s : status) {
BlockUpdate abu(pindex, s);
BOOST_CHECK(abu.getBlockIndex() == pindex);
BOOST_CHECK_EQUAL(abu.getStatus(), s);
}
}
CService ip(uint32_t i) {
struct in_addr s;
s.s_addr = i;
return CService(CNetAddr(s), Params().GetDefaultPort());
}
CNode *ConnectNode(const Config &config, ServiceFlags nServices,
PeerLogicValidation &peerLogic, CConnmanTest *connman) {
static NodeId id = 0;
CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
auto node = new CNode(id++, ServiceFlags(NODE_NETWORK), 0, INVALID_SOCKET,
addr, 0, 0, CAddress(), "",
/*fInboundIn=*/false);
node->SetSendVersion(PROTOCOL_VERSION);
node->nServices = nServices;
peerLogic.InitializeNode(config, node);
node->nVersion = 1;
node->fSuccessfullyConnected = true;
connman->AddNode(*node);
return node;
}
std::array ConnectNodes(const Config &config, Processor &p,
ServiceFlags nServices,
PeerLogicValidation &peerLogic,
CConnmanTest *connman) {
PeerManager &pm = AvalancheTest::getPeerManager(p);
Proof proof = buildRandomProof(100);
std::array nodes;
for (CNode *&n : nodes) {
n = ConnectNode(config, nServices, peerLogic, connman);
BOOST_CHECK(pm.addNode(n->GetId(), proof, CPubKey()));
}
return nodes;
}
static Response next(Response &r) {
auto copy = r;
r = {r.getRound() + 1, r.getCooldown(), r.GetVotes()};
return copy;
}
BOOST_AUTO_TEST_CASE(block_register) {
const Config &config = GetConfig();
auto connman = std::make_unique(config, 0x1337, 0x1337);
auto peerLogic = std::make_unique(
- connman.get(), nullptr, *m_node.scheduler, false);
+ connman.get(), nullptr, *m_node.scheduler);
Processor p(connman.get());
std::vector updates;
CBlock block = CreateAndProcessBlock({}, CScript());
const BlockHash blockHash = block.GetHash();
const CBlockIndex *pindex;
{
LOCK(cs_main);
pindex = LookupBlockIndex(blockHash);
}
// Create nodes that supports avalanche.
auto avanodes =
ConnectNodes(config, p, NODE_AVALANCHE, *peerLogic, connman.get());
// Querying for random block returns false.
BOOST_CHECK(!p.isAccepted(pindex));
// Add a new block. Check it is added to the polls.
BOOST_CHECK(p.addBlockToReconcile(pindex));
auto invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHash);
// Newly added blocks' state reflect the blockchain.
BOOST_CHECK(p.isAccepted(pindex));
int nextNodeIndex = 0;
auto registerNewVote = [&](const Response &resp) {
AvalancheTest::runEventLoop(p);
auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
BOOST_CHECK(p.registerVotes(nodeid, resp, updates));
};
// Let's vote for this block a few times.
Response resp{0, 0, {Vote(0, blockHash)}};
for (int i = 0; i < 6; i++) {
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), 0);
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// A single neutral vote do not change anything.
resp = {AvalancheTest::getRound(p), 0, {Vote(-1, blockHash)}};
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), 0);
BOOST_CHECK_EQUAL(updates.size(), 0);
resp = {AvalancheTest::getRound(p), 0, {Vote(0, blockHash)}};
for (int i = 1; i < 7; i++) {
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), i);
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// Two neutral votes will stall progress.
resp = {AvalancheTest::getRound(p), 0, {Vote(-1, blockHash)}};
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), 6);
BOOST_CHECK_EQUAL(updates.size(), 0);
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), 6);
BOOST_CHECK_EQUAL(updates.size(), 0);
resp = {AvalancheTest::getRound(p), 0, {Vote(0, blockHash)}};
for (int i = 2; i < 8; i++) {
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), 6);
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// We vote for it numerous times to finalize it.
for (int i = 7; i < AVALANCHE_FINALIZATION_SCORE; i++) {
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), i);
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// As long as it is not finalized, we poll.
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHash);
// Now finalize the decision.
registerNewVote(next(resp));
BOOST_CHECK_EQUAL(updates.size(), 1);
BOOST_CHECK(updates[0].getBlockIndex() == pindex);
BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Finalized);
updates = {};
// Once the decision is finalized, there is no poll for it.
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 0);
// Now let's undo this and finalize rejection.
BOOST_CHECK(p.addBlockToReconcile(pindex));
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHash);
resp = {AvalancheTest::getRound(p), 0, {Vote(1, blockHash)}};
for (int i = 0; i < 6; i++) {
registerNewVote(next(resp));
BOOST_CHECK(p.isAccepted(pindex));
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// Now the state will flip.
registerNewVote(next(resp));
BOOST_CHECK(!p.isAccepted(pindex));
BOOST_CHECK_EQUAL(updates.size(), 1);
BOOST_CHECK(updates[0].getBlockIndex() == pindex);
BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Rejected);
updates = {};
// Now it is rejected, but we can vote for it numerous times.
for (int i = 1; i < AVALANCHE_FINALIZATION_SCORE; i++) {
registerNewVote(next(resp));
BOOST_CHECK(!p.isAccepted(pindex));
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// As long as it is not finalized, we poll.
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHash);
// Now finalize the decision.
registerNewVote(next(resp));
BOOST_CHECK(!p.isAccepted(pindex));
BOOST_CHECK_EQUAL(updates.size(), 1);
BOOST_CHECK(updates[0].getBlockIndex() == pindex);
BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Invalid);
updates = {};
// Once the decision is finalized, there is no poll for it.
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 0);
// Adding the block twice does nothing.
BOOST_CHECK(p.addBlockToReconcile(pindex));
BOOST_CHECK(!p.addBlockToReconcile(pindex));
BOOST_CHECK(p.isAccepted(pindex));
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(multi_block_register) {
const Config &config = GetConfig();
auto connman = std::make_unique(config, 0x1337, 0x1337);
auto peerLogic = std::make_unique(
- connman.get(), nullptr, *m_node.scheduler, false);
+ connman.get(), nullptr, *m_node.scheduler);
Processor p(connman.get());
CBlockIndex indexA, indexB;
std::vector updates;
// Create several nodes that support avalanche.
auto avanodes =
ConnectNodes(config, p, NODE_AVALANCHE, *peerLogic, connman.get());
// Make sure the block has a hash.
CBlock blockA = CreateAndProcessBlock({}, CScript());
const BlockHash blockHashA = blockA.GetHash();
CBlock blockB = CreateAndProcessBlock({}, CScript());
const BlockHash blockHashB = blockB.GetHash();
const CBlockIndex *pindexA;
const CBlockIndex *pindexB;
{
LOCK(cs_main);
pindexA = LookupBlockIndex(blockHashA);
pindexB = LookupBlockIndex(blockHashB);
}
// Querying for random block returns false.
BOOST_CHECK(!p.isAccepted(pindexA));
BOOST_CHECK(!p.isAccepted(pindexB));
// Start voting on block A.
BOOST_CHECK(p.addBlockToReconcile(pindexA));
auto invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHashA);
uint64_t round = AvalancheTest::getRound(p);
AvalancheTest::runEventLoop(p);
BOOST_CHECK(p.registerVotes(avanodes[0]->GetId(),
{round, 0, {Vote(0, blockHashA)}}, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
// Start voting on block B after one vote.
Response resp{round + 1, 0, {Vote(0, blockHashB), Vote(0, blockHashA)}};
BOOST_CHECK(p.addBlockToReconcile(pindexB));
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 2);
// Ensure B comes before A because it has accumulated more PoW.
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHashB);
BOOST_CHECK_EQUAL(invs[1].type, MSG_BLOCK);
BOOST_CHECK(invs[1].hash == blockHashA);
// Let's vote for these blocks a few times.
for (int i = 0; i < 4; i++) {
NodeId nodeid = AvalancheTest::getSuitableNodeToQuery(p);
AvalancheTest::runEventLoop(p);
BOOST_CHECK(p.registerVotes(nodeid, next(resp), updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// Now it is accepted, but we can vote for it numerous times.
for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) {
NodeId nodeid = AvalancheTest::getSuitableNodeToQuery(p);
AvalancheTest::runEventLoop(p);
BOOST_CHECK(p.registerVotes(nodeid, next(resp), updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
}
// Running two iterration of the event loop so that vote gets triggered on A
// and B.
NodeId firstNodeid = AvalancheTest::getSuitableNodeToQuery(p);
AvalancheTest::runEventLoop(p);
NodeId secondNodeid = AvalancheTest::getSuitableNodeToQuery(p);
AvalancheTest::runEventLoop(p);
BOOST_CHECK(firstNodeid != secondNodeid);
// Next vote will finalize block A.
BOOST_CHECK(p.registerVotes(firstNodeid, next(resp), updates));
BOOST_CHECK_EQUAL(updates.size(), 1);
BOOST_CHECK(updates[0].getBlockIndex() == pindexA);
BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Finalized);
updates = {};
// We do not vote on A anymore.
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHashB);
// Next vote will finalize block B.
BOOST_CHECK(p.registerVotes(secondNodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 1);
BOOST_CHECK(updates[0].getBlockIndex() == pindexB);
BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Finalized);
updates = {};
// There is nothing left to vote on.
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 0);
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(poll_and_response) {
const Config &config = GetConfig();
auto connman = std::make_unique(config, 0x1337, 0x1337);
auto peerLogic = std::make_unique(
- connman.get(), nullptr, *m_node.scheduler, false);
+ connman.get(), nullptr, *m_node.scheduler);
Processor p(connman.get());
std::vector updates;
CBlock block = CreateAndProcessBlock({}, CScript());
const BlockHash blockHash = block.GetHash();
const CBlockIndex *pindex;
{
LOCK(cs_main);
pindex = LookupBlockIndex(blockHash);
}
// There is no node to query.
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), NO_NODE);
// Create a node that supports avalanche and one that doesn't.
ConnectNode(config, NODE_NONE, *peerLogic, connman.get());
auto avanode =
ConnectNode(config, NODE_AVALANCHE, *peerLogic, connman.get());
NodeId avanodeid = avanode->GetId();
BOOST_CHECK(p.addNode(avanodeid, buildRandomProof(100), CPubKey()));
// It returns the avalanche peer.
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// Register a block and check it is added to the list of elements to poll.
BOOST_CHECK(p.addBlockToReconcile(pindex));
auto invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHash);
// Trigger a poll on avanode.
uint64_t round = AvalancheTest::getRound(p);
AvalancheTest::runEventLoop(p);
// There is no more suitable peer available, so return nothing.
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), NO_NODE);
// Respond to the request.
Response resp = {round, 0, {Vote(0, blockHash)}};
BOOST_CHECK(p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
// Now that avanode fullfilled his request, it is added back to the list of
// queriable nodes.
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// Sending a response when not polled fails.
BOOST_CHECK(!p.registerVotes(avanodeid, next(resp), updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
// Trigger a poll on avanode.
round = AvalancheTest::getRound(p);
AvalancheTest::runEventLoop(p);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), NO_NODE);
// Sending responses that do not match the request also fails.
// 1. Too many results.
resp = {round, 0, {Vote(0, blockHash), Vote(0, blockHash)}};
AvalancheTest::runEventLoop(p);
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// 2. Not enough results.
resp = {AvalancheTest::getRound(p), 0, {}};
AvalancheTest::runEventLoop(p);
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// 3. Do not match the poll.
resp = {AvalancheTest::getRound(p), 0, {Vote()}};
AvalancheTest::runEventLoop(p);
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// 4. Invalid round count. Request is not discarded.
uint64_t queryRound = AvalancheTest::getRound(p);
AvalancheTest::runEventLoop(p);
resp = {queryRound + 1, 0, {Vote()}};
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
resp = {queryRound - 1, 0, {Vote()}};
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
// 5. Making request for invalid nodes do not work. Request is not
// discarded.
resp = {queryRound, 0, {Vote(0, blockHash)}};
BOOST_CHECK(!p.registerVotes(avanodeid + 1234, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
// Proper response gets processed and avanode is available again.
resp = {queryRound, 0, {Vote(0, blockHash)}};
BOOST_CHECK(p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// Out of order response are rejected.
CBlock block2 = CreateAndProcessBlock({}, CScript());
const BlockHash blockHash2 = block2.GetHash();
CBlockIndex *pindex2;
{
LOCK(cs_main);
pindex2 = LookupBlockIndex(blockHash2);
}
BOOST_CHECK(p.addBlockToReconcile(pindex2));
resp = {AvalancheTest::getRound(p),
0,
{Vote(0, blockHash), Vote(0, blockHash2)}};
AvalancheTest::runEventLoop(p);
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// But they are accepted in order.
resp = {AvalancheTest::getRound(p),
0,
{Vote(0, blockHash2), Vote(0, blockHash)}};
AvalancheTest::runEventLoop(p);
BOOST_CHECK(p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
// When a block is marked invalid, stop polling.
pindex2->nStatus = pindex2->nStatus.withFailed();
resp = {AvalancheTest::getRound(p), 0, {Vote(0, blockHash)}};
AvalancheTest::runEventLoop(p);
BOOST_CHECK(p.registerVotes(avanodeid, resp, updates));
BOOST_CHECK_EQUAL(updates.size(), 0);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid);
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(poll_inflight_timeout, *boost::unit_test::timeout(60)) {
const Config &config = GetConfig();
auto connman = std::make_unique(config, 0x1337, 0x1337);
auto peerLogic = std::make_unique(
- connman.get(), nullptr, *m_node.scheduler, false);
+ connman.get(), nullptr, *m_node.scheduler);
Processor p(connman.get());
std::vector updates;
CBlock block = CreateAndProcessBlock({}, CScript());
const BlockHash blockHash = block.GetHash();
const CBlockIndex *pindex;
{
LOCK(cs_main);
pindex = LookupBlockIndex(blockHash);
}
// Add the block
BOOST_CHECK(p.addBlockToReconcile(pindex));
// Create a node that supports avalanche.
auto avanode =
ConnectNode(config, NODE_AVALANCHE, *peerLogic, connman.get());
NodeId avanodeid = avanode->GetId();
BOOST_CHECK(p.addNode(avanodeid, buildRandomProof(100), CPubKey()));
// Expire requests after some time.
auto queryTimeDuration = std::chrono::milliseconds(10);
p.setQueryTimeoutDuration(queryTimeDuration);
for (int i = 0; i < 10; i++) {
Response resp = {AvalancheTest::getRound(p), 0, {Vote(0, blockHash)}};
auto start = std::chrono::steady_clock::now();
AvalancheTest::runEventLoop(p);
// We cannot guarantee that we'll wait for just 1ms, so we have to bail
// if we aren't within the proper time range.
std::this_thread::sleep_for(std::chrono::milliseconds(1));
AvalancheTest::runEventLoop(p);
bool ret = p.registerVotes(avanodeid, next(resp), updates);
if (std::chrono::steady_clock::now() > start + queryTimeDuration) {
// We waited for too long, bail. Because we can't know for sure when
// previous steps ran, ret is not deterministic and we do not check
// it.
i--;
continue;
}
// We are within time bounds, so the vote should have worked.
BOOST_CHECK(ret);
// Now try again but wait for expiration.
AvalancheTest::runEventLoop(p);
std::this_thread::sleep_for(queryTimeDuration);
AvalancheTest::runEventLoop(p);
BOOST_CHECK(!p.registerVotes(avanodeid, next(resp), updates));
}
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(poll_inflight_count) {
const Config &config = GetConfig();
auto connman = std::make_unique(config, 0x1337, 0x1337);
auto peerLogic = std::make_unique(
- connman.get(), nullptr, *m_node.scheduler, false);
+ connman.get(), nullptr, *m_node.scheduler);
Processor p(connman.get());
// Create enough nodes so that we run into the inflight request limit.
PeerManager &pm = AvalancheTest::getPeerManager(p);
Proof proof = buildRandomProof(100);
std::array nodes;
for (auto &n : nodes) {
n = ConnectNode(config, NODE_AVALANCHE, *peerLogic, connman.get());
BOOST_CHECK(pm.addNode(n->GetId(), proof, CPubKey()));
}
// Add a block to poll
CBlock block = CreateAndProcessBlock({}, CScript());
const BlockHash blockHash = block.GetHash();
const CBlockIndex *pindex;
{
LOCK(cs_main);
pindex = LookupBlockIndex(blockHash);
}
BOOST_CHECK(p.addBlockToReconcile(pindex));
// Ensure there are enough requests in flight.
std::map node_round_map;
for (int i = 0; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
NodeId nodeid = AvalancheTest::getSuitableNodeToQuery(p);
BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
node_round_map[nodeid] = AvalancheTest::getRound(p);
auto invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHash);
AvalancheTest::runEventLoop(p);
}
// Now that we have enough in flight requests, we shouldn't poll.
auto suitablenodeid = AvalancheTest::getSuitableNodeToQuery(p);
BOOST_CHECK(suitablenodeid != NO_NODE);
auto invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 0);
AvalancheTest::runEventLoop(p);
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), suitablenodeid);
std::vector updates;
// Send one response, now we can poll again.
auto it = node_round_map.begin();
Response resp = {it->second, 0, {Vote(0, blockHash)}};
BOOST_CHECK(p.registerVotes(it->first, resp, updates));
node_round_map.erase(it);
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 1);
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHash);
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(quorum_diversity) {
const Config &config = GetConfig();
auto connman = std::make_unique(config, 0x1337, 0x1337);
auto peerLogic = std::make_unique(
- connman.get(), nullptr, *m_node.scheduler, false);
+ connman.get(), nullptr, *m_node.scheduler);
Processor p(connman.get());
std::vector updates;
CBlock block = CreateAndProcessBlock({}, CScript());
const BlockHash blockHash = block.GetHash();
const CBlockIndex *pindex;
{
LOCK(cs_main);
pindex = LookupBlockIndex(blockHash);
}
// Create nodes that supports avalanche.
auto avanodes =
ConnectNodes(config, p, NODE_AVALANCHE, *peerLogic, connman.get());
// Querying for random block returns false.
BOOST_CHECK(!p.isAccepted(pindex));
// Add a new block. Check it is added to the polls.
BOOST_CHECK(p.addBlockToReconcile(pindex));
// Do one valid round of voting.
uint64_t round = AvalancheTest::getRound(p);
Response resp{round, 0, {Vote(0, blockHash)}};
// Check that all nodes can vote.
for (size_t i = 0; i < avanodes.size(); i++) {
AvalancheTest::runEventLoop(p);
BOOST_CHECK(p.registerVotes(avanodes[i]->GetId(), next(resp), updates));
}
// Generate a query for every single node.
const NodeId firstNodeId = AvalancheTest::getSuitableNodeToQuery(p);
std::map node_round_map;
round = AvalancheTest::getRound(p);
for (size_t i = 0; i < avanodes.size(); i++) {
NodeId nodeid = AvalancheTest::getSuitableNodeToQuery(p);
BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
node_round_map[nodeid] = AvalancheTest::getRound(p);
AvalancheTest::runEventLoop(p);
}
// Now only tge first node can vote. All others would be duplicate in the
// quorum.
auto confidence = p.getConfidence(pindex);
BOOST_REQUIRE(confidence > 0);
for (auto &pair : node_round_map) {
NodeId nodeid = pair.first;
uint64_t r = pair.second;
if (nodeid == firstNodeId) {
// Node 0 is the only one which can vote at this stage.
round = r;
continue;
}
BOOST_CHECK(
p.registerVotes(nodeid, {r, 0, {Vote(0, blockHash)}}, updates));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), confidence);
}
BOOST_CHECK(p.registerVotes(firstNodeId, {round, 0, {Vote(0, blockHash)}},
updates));
BOOST_CHECK_EQUAL(p.getConfidence(pindex), confidence + 1);
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(event_loop) {
const Config &config = GetConfig();
auto connman = std::make_unique(config, 0x1337, 0x1337);
auto peerLogic = std::make_unique(
- connman.get(), nullptr, *m_node.scheduler, false);
+ connman.get(), nullptr, *m_node.scheduler);
Processor p(connman.get());
CScheduler s;
CBlock block = CreateAndProcessBlock({}, CScript());
const BlockHash blockHash = block.GetHash();
const CBlockIndex *pindex;
{
LOCK(cs_main);
pindex = LookupBlockIndex(blockHash);
}
// Starting the event loop.
BOOST_CHECK(p.startEventLoop(s));
// There is one task planned in the next hour (our event loop).
std::chrono::system_clock::time_point start, stop;
BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
// Starting twice doesn't start it twice.
BOOST_CHECK(!p.startEventLoop(s));
// Start the scheduler thread.
std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s));
// Create a node that supports avalanche.
auto avanode =
ConnectNode(config, NODE_AVALANCHE, *peerLogic, connman.get());
NodeId nodeid = avanode->GetId();
BOOST_CHECK(p.addNode(nodeid, buildRandomProof(100), CPubKey()));
// There is no query in flight at the moment.
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), nodeid);
// Add a new block. Check it is added to the polls.
uint64_t queryRound = AvalancheTest::getRound(p);
BOOST_CHECK(p.addBlockToReconcile(pindex));
for (int i = 0; i < 60 * 1000; i++) {
// Technically, this is a race condition, but this should do just fine
// as we wait up to 1 minute for an event that should take 10ms.
UninterruptibleSleep(std::chrono::milliseconds(1));
if (AvalancheTest::getRound(p) != queryRound) {
break;
}
}
// Check that we effectively got a request and not timed out.
BOOST_CHECK(AvalancheTest::getRound(p) > queryRound);
// Respond and check the cooldown time is respected.
uint64_t responseRound = AvalancheTest::getRound(p);
auto queryTime =
std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
std::vector updates;
p.registerVotes(nodeid, {queryRound, 100, {Vote(0, blockHash)}}, updates);
for (int i = 0; i < 10000; i++) {
// We make sure that we do not get a request before queryTime.
UninterruptibleSleep(std::chrono::milliseconds(1));
if (AvalancheTest::getRound(p) != responseRound) {
BOOST_CHECK(std::chrono::steady_clock::now() > queryTime);
break;
}
}
// But we eventually get one.
BOOST_CHECK(AvalancheTest::getRound(p) > responseRound);
// Stop event loop.
BOOST_CHECK(p.stopEventLoop());
// We don't have any task scheduled anymore.
BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
// Can't stop the event loop twice.
BOOST_CHECK(!p.stopEventLoop());
// Wait for the scheduler to stop.
s.stop(true);
schedulerThread.join();
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(destructor) {
CScheduler s;
std::chrono::system_clock::time_point start, stop;
std::thread schedulerThread;
{
Processor p(m_node.connman.get());
BOOST_CHECK(p.startEventLoop(s));
BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
// Start the service thread after the queue size check to prevent a
// race condition where the thread may be processing the event loop
// task during the check.
schedulerThread = std::thread(std::bind(&CScheduler::serviceQueue, &s));
}
// Now that avalanche is destroyed, there is no more scheduled tasks.
BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
// Wait for the scheduler to stop.
s.stop(true);
schedulerThread.join();
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/init.cpp b/src/init.cpp
index c7325c054..666293c5e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,2806 +1,2801 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include