Merge #6097: backport: merge bitcoin#24024, #23880, #24909, #24178, #24171, #25404, #25514, #25720, partial bitcoin#23832, #24169, #25454 (headers backports)

c92b0f57da merge bitcoin#25720: Reduce bandwidth during initial headers sync when a block is found (Kittywhiskers Van Gogh)
0f9ece0ed9 merge bitcoin#25514: Move CNode::nServices and CNode::nLocalServices to Peer (Kittywhiskers Van Gogh)
c9923ca36b partial bitcoin#25454: Avoid multiple getheaders messages in flight to the same peer (Kittywhiskers Van Gogh)
26d477b6ae revert: Fix duplicate initial headers sync (Kittywhiskers Van Gogh)
abccb2dd03 test: drop genesis block from `blockheader_testnet3` (Kittywhiskers Van Gogh)
0574a7d19e merge bitcoin#25404: Use MAX_BLOCKS_TO_ANNOUNCE consistently (Kittywhiskers Van Gogh)
ed871d2a07 merge bitcoin#24171: Sync chain more readily from inbound peers during IBD (Kittywhiskers Van Gogh)
a04290fc5c merge bitcoin#24178: Respond to getheaders if we have sufficient chainwork (Kittywhiskers Van Gogh)
bcafa282a3 merge bitcoin#24909: Move and rename pindexBestHeader, fHavePruned (Kittywhiskers Van Gogh)
70485cb2f5 partial bitcoin#24169: Add --enable-c++20 option (Kittywhiskers Van Gogh)
27e885de5f merge bitcoin#23880: Serialize cmpctblock at most once in NewPoWValidBlock (Kittywhiskers Van Gogh)
9f7ac69a7e merge bitcoin#24024: Remove cs_main lock annotation from ChainstateManager.m_blockman (Kittywhiskers Van Gogh)
9399f90a13 partial bitcoin#23832: Changes time variables from int to chrono (Kittywhiskers Van Gogh)

Pull request description:

  ## Additional Information

  * Dependent on https://github.com/dashpay/dash/pull/6085

  * Dependency for https://github.com/dashpay/dash/pull/6098

  * ~~[bitcoin#25514](https://github.com/bitcoin/bitcoin/pull/25514) removes `peers.spvNodeConnections` and `peers.fullNodeConnections` reporting from `CalculateNumConnectionsChangedStats` as the services flags used to distingush between the two have been moved to the `Peer` struct, accessable only through `PeerManager`.~~

    ~~As `PeerManager` isn't accessable to `CConnman`, even if a new public function was exposed through `PeerManger` (as we have for `IsInvInFilter` and others or we try to access the value through `GetNodeStateStats`), `CConnman` would be unable to leverage it.~~ Resolved with patch by UdjinM6, thanks!

  * [bitcoin#23880](https://github.com/bitcoin/bitcoin/pull/23880) introduces code that is not valid C++20 (but valid C++17) and therefore, required a partial backport of [bitcoin#24169](https://github.com/bitcoin/bitcoin/pull/24169) (fae679065e4ef0c6383bbdd1876aaed6c1e40104) to make the code C++20 legal.

  * [bitcoin#25454](https://github.com/bitcoin/bitcoin/pull/25454) introduces a 10-point penalty for remitting more than `MAX_BLOCKS_TO_ANNOUNCE` unconnected block headers. `blockheader_testnet3.hex` (introduced in [bitcoin#16551](https://github.com/bitcoin/bitcoin/pull/16551), part of [dash#5963](https://github.com/dashpay/dash/pull/5963)), unlike in Bitcoin, includes the genesis block.

    By definition of a genesis block, there is no block before it that connects to, which causes the 10-point penalty to trip and `p2p_dos_header_tree.py` to fail (see below). This has been remedied by removing the genesis block from the test data to match upstream and also because no node has a good reason to ever broadcast the genesis block as-is over P2P.

    <details>

    <summary>Test Failure</summary>

    ```
    dash@6a2649cc721f:/src/dash$ ./test/functional/p2p_dos_header_tree.py
    2024-06-17T17:59:35.874000Z TestFramework (INFO): Initializing test directory /tmp/dash_func_test_h5hfluy1
    2024-06-17T17:59:36.892000Z TestFramework (INFO): Read headers data
    2024-06-17T17:59:36.895000Z TestFramework (INFO): Feed all non-fork headers, including and up to the first checkpoint
    2024-06-17T17:59:38.411000Z TestFramework (ERROR): Assertion failed
    Traceback (most recent call last):
      File "/src/dash/test/functional/test_framework/test_framework.py", line 158, in main
        self.run_test()
      File "./test/functional/p2p_dos_header_tree.py", line 53, in run_test
        assert {
    AssertionError
    2024-06-17T17:59:38.913000Z TestFramework (INFO): Stopping nodes
    2024-06-17T17:59:39.917000Z TestFramework (WARNING): Not cleaning up dir /tmp/dash_func_test_h5hfluy1
    2024-06-17T17:59:39.917000Z TestFramework (ERROR): Test failed. Test logging available at /tmp/dash_func_test_h5hfluy1/test_framework.log
    2024-06-17T17:59:39.917000Z TestFramework (ERROR):
    2024-06-17T17:59:39.917000Z TestFramework (ERROR): Hint: Call /src/dash/test/functional/combine_logs.py '/tmp/dash_func_test_h5hfluy1' to consolidate all logs
    2024-06-17T17:59:39.917000Z TestFramework (ERROR):
    2024-06-17T17:59:39.917000Z TestFramework (ERROR): If this failure happened unexpectedly or intermittently, please file a bug and provide a link or upload of the combined log.
    2024-06-17T17:59:39.917000Z TestFramework (ERROR): https://github.com/dashpay/dash/issues
    2024-06-17T17:59:39.917000Z TestFramework (ERROR):
    ```

    </details>

  * [bitcoin#25454](https://github.com/bitcoin/bitcoin/pull/25454) has a goal similar to [dash#2032](https://github.com/dashpay/dash/pull/2032) (and its predecessor, [dash#1589](https://github.com/dashpay/dash/pull/1589)), namely, avoiding `getheaders`(`2`) duplication to the same peer. Unfortunately, Dash's mitigation seems to conflict with Bitcoin's mitigation and this results in `feature_minchainwork.py` failing (see below). This has been remedied by partially reverting [dash#2032](https://github.com/dashpay/dash/pull/2032).

    <details>

    <summary>Test Failure</summary>

    ```
    dash@1bebec413839:/src/dash$ ./test/functional/feature_minchainwork.py
    2024-08-01T17:29:41.116000Z TestFramework (INFO): Initializing test directory /tmp/dash_func_test_co8xkx43
    2024-08-01T17:29:42.145000Z TestFramework (INFO): Testing relay across node 1 (minChainWork = 101)
    2024-08-01T17:29:42.145000Z TestFramework (INFO): Generating 49 blocks on node0
    [...]
    2024-08-01T17:29:51.707000Z TestFramework (INFO): Generating one more block
    2024-08-01T17:29:51.709000Z TestFramework (INFO): Verifying nodes are all synced
    2024-08-01T17:30:51.989000Z TestFramework (ERROR): Assertion failed
    Traceback (most recent call last):
      File "/src/dash/test/functional/test_framework/test_framework.py", line 159, in main
        self.run_test()
      File "./test/functional/feature_minchainwork.py", line 101, in run_test
        self.sync_all()
      File "/src/dash/test/functional/test_framework/test_framework.py", line 811, in sync_all
        self.sync_blocks(nodes)
      File "/src/dash/test/functional/test_framework/test_framework.py", line 777, in sync_blocks
        raise AssertionError("Block sync timed out after {}s:{}".format(
    AssertionError: Block sync timed out after 60s:
      '00ab061e30aebd2f97153d26cd72f921af896f05c6469ad73c7de4fc283d9590'
      '00ab061e30aebd2f97153d26cd72f921af896f05c6469ad73c7de4fc283d9590'
      '000008ca1832a4baf228eb1553c03d3a2c8e02399550dd6ea8d65cec3ef23d2e'
    2024-08-01T17:30:52.490000Z TestFramework (INFO): Stopping nodes
    2024-08-01T17:30:53.495000Z TestFramework (WARNING): Not cleaning up dir /tmp/dash_func_test_co8xkx43
    2024-08-01T17:30:53.495000Z TestFramework (ERROR): Test failed. Test logging available at /tmp/dash_func_test_co8xkx43/test_framework.log
    2024-08-01T17:30:53.495000Z TestFramework (ERROR):
    2024-08-01T17:30:53.495000Z TestFramework (ERROR): Hint: Call /src/dash/test/functional/combine_logs.py '/tmp/dash_func_test_co8xkx43' to consolidate all logs
    2024-08-01T17:30:53.495000Z TestFramework (ERROR):
    2024-08-01T17:30:53.495000Z TestFramework (ERROR): If this failure happened unexpectedly or intermittently, please file a bug and provide a link or upload of the combined log.
    2024-08-01T17:30:53.495000Z TestFramework (ERROR): https://github.com/dashpay/dash/issues
    2024-08-01T17:30:53.495000Z TestFramework (ERROR):
    ```

    </details>

  * [bitcoin#25720](https://github.com/bitcoin/bitcoin/pull/25720) introduces a new test, `p2p_initial_headers_sync.py`, to validate that when a client has a stale tip, it will only engage in headers sync with one peer (emit a `getheaders2`\* message).

    Unmodified, this test fails (see below) as while the backport deals with one source of `getheaders2` messages, the test setup unwittingly triggers another ([source](2379462294/src/net_processing.cpp (L5446-L5448))), specifically, allowing the `pindexBestHeader->GetBlockTime() > GetAdjustedTime() - nMaxTipAge` condition to evaluate `true`.

    This is because, unlike in Bitcoin test suite's `setup_chain()` ([source](22d96d76ab/test/functional/test_framework/test_framework.py (L379-L385))), Dash sets the mocktime to match the mock chain ([source](2379462294/test/functional/test_framework/test_framework.py (L408-L416))) during setup, while the test assumes that the mock chain is stale enough to not trigger this source of `getheaders2` messages.

    As the tip is barely stale, it emits the `getheaders2` message, which is detected, causing the test to fail. This has been remedied by overriding `setup_chain()` to behave more like Bitcoin's test suite.

    _\* - `getheaders2` is a Dash-specific message that is courtesy of compressed headers, Bitcoin would be checking for `getheaders`_

    <details>

    <summary>Test Failure</summary>

    ```
    dash@6a2649cc721f:/src/dash$ ./test/functional/p2p_initial_headers_sync.py
    2024-06-17T21:24:09.921000Z TestFramework (INFO): Initializing test directory /tmp/dash_func_test_3gypo3ab
    2024-06-17T21:24:10.681000Z TestFramework (INFO): Adding a peer to node0
    2024-06-17T21:24:11.684000Z TestFramework (INFO): Connecting two more peers to node0
    2024-06-17T21:24:13.689000Z TestFramework (INFO): Verify that peer2 and peer3 don't receive a getheaders after connecting
    2024-06-17T21:24:15.193000Z TestFramework (ERROR): Assertion failed
    Traceback (most recent call last):
      File "/src/dash/test/functional/test_framework/test_framework.py", line 158, in main
        self.run_test()
      File "./test/functional/p2p_initial_headers_sync.py", line 60, in run_test
        assert "getheaders2" not in peer2.last_message
    AssertionError
    2024-06-17T21:24:15.695000Z TestFramework (INFO): Stopping nodes
    2024-06-17T21:24:16.698000Z TestFramework (WARNING): Not cleaning up dir /tmp/dash_func_test_3gypo3ab
    2024-06-17T21:24:16.698000Z TestFramework (ERROR): Test failed. Test logging available at /tmp/dash_func_test_3gypo3ab/test_framework.log
    2024-06-17T21:24:16.699000Z TestFramework (ERROR):
    2024-06-17T21:24:16.699000Z TestFramework (ERROR): Hint: Call /src/dash/test/functional/combine_logs.py '/tmp/dash_func_test_3gypo3ab' to consolidate all logs
    2024-06-17T21:24:16.699000Z TestFramework (ERROR):
    2024-06-17T21:24:16.699000Z TestFramework (ERROR): If this failure happened unexpectedly or intermittently, please file a bug and provide a link or upload of the combined log.
    2024-06-17T21:24:16.699000Z TestFramework (ERROR): https://github.com/dashpay/dash/issues
    2024-06-17T21:24:16.699000Z TestFramework (ERROR):
    ```

    </details>

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

Top commit has no ACKs.

Tree-SHA512: 16b7c42195e197e8b6800c3425b4ff15a124b0e3e0da5c98ca6e22486b52c4ea82f2f05b83e28e838860b1ce76daa1e435c5eae4fb7591a161f26a5fff6189cc
This commit is contained in:
pasta 2024-08-09 17:35:14 +07:00
commit 117dda9d6f
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
36 changed files with 874 additions and 532 deletions

View File

@ -45,7 +45,7 @@ static void BlockToJsonVerbose(benchmark::Bench& bench)
TestBlockAndIndex data;
const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx;
bench.run([&] {
auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true);
auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true);
ankerl::nanobench::doNotOptimizeAway(univalue);
});
}
@ -56,7 +56,7 @@ static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
{
TestBlockAndIndex data;
const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx;
auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true);
auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true);
bench.run([&] {
auto str = univalue.write();
ankerl::nanobench::doNotOptimizeAway(str);

View File

@ -79,7 +79,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
if (pindexNew == pindexFork) // blocks were disconnected without any new ones
return;
m_mn_sync.UpdatedBlockTip(pindexNew, fInitialDownload);
m_mn_sync.UpdatedBlockTip(m_chainman.m_best_header, pindexNew, fInitialDownload);
// Update global DIP0001 activation status
fDIP0001ActiveAtTip = pindexNew->nHeight >= Params().GetConsensus().DIP0001Height;

View File

@ -60,7 +60,7 @@ void CMNAuth::PushMNAUTH(CNode& peer, CConnman& connman, const CActiveMasternode
connman.PushMessage(&peer, CNetMsgMaker(peer.GetCommonVersion()).Make(NetMsgType::MNAUTH, mnauth));
}
PeerMsgRet CMNAuth::ProcessMessage(CNode& peer, CConnman& connman, CMasternodeMetaMan& mn_metaman, const CActiveMasternodeManager* const mn_activeman,
PeerMsgRet CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman, const CActiveMasternodeManager* const mn_activeman,
const CChain& active_chain, const CMasternodeSync& mn_sync, const CDeterministicMNList& tip_mn_list,
std::string_view msg_type, CDataStream& vRecv)
{
@ -79,7 +79,7 @@ PeerMsgRet CMNAuth::ProcessMessage(CNode& peer, CConnman& connman, CMasternodeMe
return tl::unexpected{MisbehavingError{100, "duplicate mnauth"}};
}
if ((~peer.nServices) & (NODE_NETWORK | NODE_BLOOM)) {
if ((~node_services) & (NODE_NETWORK | NODE_BLOOM)) {
// either NODE_NETWORK or NODE_BLOOM bit is missing in node's services
return tl::unexpected{MisbehavingError{100, "mnauth from a node with invalid services"}};
}

View File

@ -24,6 +24,8 @@ class CNode;
class UniValue;
enum ServiceFlags : uint64_t;
/**
* This class handles the p2p message MNAUTH. MNAUTH is sent directly after VERACK and authenticates the sender as a
* masternode. It is only sent when the sender is actually a masternode.
@ -59,7 +61,7 @@ public:
* @pre CMasternodeMetaMan's database must be successfully loaded before
* attempting to call this function regardless of sync state
*/
static PeerMsgRet ProcessMessage(CNode& peer, CConnman& connman, CMasternodeMetaMan& mn_metaman, const CActiveMasternodeManager* const mn_activeman,
static PeerMsgRet ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman, const CActiveMasternodeManager* const mn_activeman,
const CChain& active_chain, const CMasternodeSync& mn_sync, const CDeterministicMNList& tip_mn_list,
std::string_view msg_type, CDataStream& vRecv);
static void NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff, CConnman& connman);

View File

@ -1890,7 +1890,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (ShutdownRequested()) break;
// LoadBlockIndex will load fHavePruned if we've ever removed a
// LoadBlockIndex will load m_have_pruned if we've ever removed a
// block file from disk.
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something different!
@ -1936,7 +1936,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
// in the past, but is now trying to run unpruned.
if (fHavePruned && !fPruneMode) {
if (chainman.m_blockman.m_have_pruned && !fPruneMode) {
strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
break;
}
@ -2022,7 +2022,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
uiInterface.InitMessage(_("Verifying blocks…").translated);
if (fHavePruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
if (chainman.m_blockman.m_have_pruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
@ -2328,9 +2328,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
tip_info->block_hash = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockHash() : Params().GenesisBlock().GetHash();
tip_info->verification_progress = GuessVerificationProgress(Params().TxData(), chainman.ActiveChain().Tip());
}
if (tip_info && ::pindexBestHeader) {
tip_info->header_height = ::pindexBestHeader->nHeight;
tip_info->header_time = ::pindexBestHeader->GetBlockTime();
if (tip_info && chainman.m_best_header) {
tip_info->header_height = chainman.m_best_header->nHeight;
tip_info->header_time = chainman.m_best_header->GetBlockTime();
}
}
LogPrintf("nBestHeight = %d\n", chain_active_height);

View File

@ -329,7 +329,7 @@ void CMasternodeSync::NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitia
}
}
void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload)
void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexTip, const CBlockIndex *pindexNew, bool fInitialDownload)
{
LogPrint(BCLog::MNSYNC, "CMasternodeSync::UpdatedBlockTip -- pindexNew->nHeight: %d fInitialDownload=%d\n", pindexNew->nHeight, fInitialDownload);
nTimeLastUpdateBlockTip = GetTime<std::chrono::seconds>().count();
@ -353,7 +353,6 @@ void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitia
}
// Note: since we sync headers first, it should be ok to use this
CBlockIndex* pindexTip = WITH_LOCK(cs_main, return pindexBestHeader);
if (pindexTip == nullptr) return;
bool fReachedBestHeaderNew = pindexNew->GetBlockHash() == pindexTip->GetBlockHash();

View File

@ -75,7 +75,7 @@ public:
void AcceptedBlockHeader(const CBlockIndex *pindexNew);
void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload);
void UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload);
void UpdatedBlockTip(const CBlockIndex *pindexTip, const CBlockIndex *pindexNew, bool fInitialDownload);
void DoMaintenance();
};

View File

@ -230,15 +230,13 @@ static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn)
// Otherwise, return the unroutable 0.0.0.0 but filled in with
// the normal parameters, since the IP may be changed to a useful
// one by discovery.
CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
CService GetLocalAddress(const CNetAddr& addrPeer)
{
CAddress ret(CService(CNetAddr(),GetListenPort()), nLocalServices);
CService ret{CNetAddr(), GetListenPort()};
CService addr;
if (GetLocal(addr, paddrPeer))
{
ret = CAddress(addr, nLocalServices);
if (GetLocal(addr, &addrPeer)) {
ret = CService{addr};
}
ret.nTime = GetAdjustedTime();
return ret;
}
@ -257,35 +255,35 @@ bool IsPeerAddrLocalGood(CNode *pnode)
IsReachable(addrLocal.GetNetwork());
}
std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode)
std::optional<CService> GetLocalAddrForPeer(CNode& node)
{
CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
CService addrLocal{GetLocalAddress(node.addr)};
if (gArgs.GetBoolArg("-addrmantest", false)) {
// use IPv4 loopback during addrmantest
addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices());
addrLocal = CService(LookupNumeric("127.0.0.1", GetListenPort()));
}
// If discovery is enabled, sometimes give our peer the address it
// tells us that it sees us as in case it has a better idea of our
// address than we do.
FastRandomContext rng;
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
if (IsPeerAddrLocalGood(&node) && (!addrLocal.IsRoutable() ||
rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
{
if (pnode->IsInboundConn()) {
if (node.IsInboundConn()) {
// For inbound connections, assume both the address and the port
// as seen from the peer.
addrLocal = CAddress{pnode->GetAddrLocal(), addrLocal.nServices, addrLocal.nTime};
addrLocal = CService{node.GetAddrLocal()};
} else {
// For outbound connections, assume just the address as seen from
// the peer and leave the port in `addrLocal` as returned by
// `GetLocalAddress()` above. The peer has no way to observe our
// listening port when we have initiated the connection.
addrLocal.SetIP(pnode->GetAddrLocal());
addrLocal.SetIP(node.GetAddrLocal());
}
}
if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
{
LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToString(), pnode->GetId());
LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToString(), node.GetId());
return addrLocal;
}
// Address is unroutable. Don't advertise.
@ -610,7 +608,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addr_bind = GetBindAddress(*sock);
}
CNode* pnode = new CNode(id,
nLocalServices,
std::move(sock),
addrConnect,
CalculateKeyedNetGroup(addrConnect),
@ -733,7 +730,6 @@ Network CNode::ConnectedThroughNetwork() const
void CNode::CopyStats(CNodeStats& stats)
{
stats.nodeid = this->GetId();
X(nServices);
X(addr);
X(addrBind);
stats.m_network = ConnectedThroughNetwork();
@ -1247,7 +1243,7 @@ bool CConnman::AttemptToEvictConnection()
NodeEvictionCandidate candidate = {node->GetId(), node->m_connected, node->m_min_ping_time,
node->m_last_block_time, node->m_last_tx_time,
HasAllDesirableServiceFlags(node->nServices),
node->m_has_all_wanted_services,
node->m_relays_txs.load(), node->m_bloom_filter_loaded.load(),
node->nKeyedNetGroup, node->m_prefer_evict, node->addr.IsLocal(),
node->ConnectedThroughNetwork()};
@ -1404,7 +1400,6 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
CNode* pnode = new CNode(id,
nodeServices,
std::move(sock),
addr,
CalculateKeyedNetGroup(addr),
@ -1418,7 +1413,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
pnode->m_legacyWhitelisted = legacyWhitelisted;
pnode->m_prefer_evict = discouraged;
m_msgproc->InitializeNode(pnode);
m_msgproc->InitializeNode(*pnode, nodeServices);
{
LOCK(pnode->m_sock_mutex);
@ -1645,10 +1640,11 @@ void CConnman::CalculateNumConnectionsChangedStats()
for (const mapMsgTypeSize::value_type &i : pnode->mapSendBytesPerMsgType)
mapSentBytesMsgStats[i.first] += i.second;
}
if(pnode->fClient)
if (pnode->m_bloom_filter_loaded.load()) {
spvNodes++;
else
} else {
fullNodes++;
}
if(pnode->IsInboundConn())
inboundNodes++;
else
@ -3139,7 +3135,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
mapSocketToNode.emplace(pnode->m_sock->Get(), pnode);
}
m_msgproc->InitializeNode(pnode);
m_msgproc->InitializeNode(*pnode, nLocalServices);
{
LOCK(m_nodes_mutex);
m_nodes.push_back(pnode);
@ -4133,7 +4129,6 @@ ServiceFlags CConnman::GetLocalServices() const
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
CNode::CNode(NodeId idIn,
ServiceFlags nLocalServicesIn,
std::shared_ptr<Sock> sock,
const CAddress& addrIn,
uint64_t nKeyedNetGroupIn,
@ -4155,7 +4150,6 @@ CNode::CNode(NodeId idIn,
id{idIn},
nLocalHostNonce{nLocalHostNonceIn},
m_conn_type{conn_type_in},
nLocalServices{nLocalServicesIn},
m_i2p_sam_session{std::move(i2p_sam_session)}
{
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);

View File

@ -133,15 +133,22 @@ struct AddedNodeInfo
class CNodeStats;
class CClientUIInterface;
struct CSerializedNetMsg
{
struct CSerializedNetMsg {
CSerializedNetMsg() = default;
CSerializedNetMsg(CSerializedNetMsg&&) = default;
CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default;
// No copying, only moves.
// No implicit copying, only moves.
CSerializedNetMsg(const CSerializedNetMsg& msg) = delete;
CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete;
CSerializedNetMsg Copy() const
{
CSerializedNetMsg copy;
copy.data = data;
copy.m_type = m_type;
return copy;
}
std::vector<unsigned char> data;
std::string m_type;
};
@ -240,8 +247,8 @@ enum
};
bool IsPeerAddrLocalGood(CNode *pnode);
/** Returns a local address that we should advertise to this peer */
std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode);
/** Returns a local address that we should advertise to this peer. */
std::optional<CService> GetLocalAddrForPeer(CNode& node);
/**
* Mark a network as reachable or unreachable (no automatic connects to it)
@ -259,7 +266,7 @@ void RemoveLocal(const CService& addr);
bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr);
CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices);
CService GetLocalAddress(const CNetAddr& addrPeer);
extern bool fDiscover;
@ -283,7 +290,6 @@ class CNodeStats
{
public:
NodeId nodeid;
ServiceFlags nServices;
std::chrono::seconds m_last_send;
std::chrono::seconds m_last_recv;
std::chrono::seconds m_last_tx_time;
@ -449,7 +455,6 @@ public:
const std::unique_ptr<const TransportSerializer> m_serializer;
NetPermissionFlags m_permissionFlags{NetPermissionFlags::None}; // treated as const outside of fuzz tester
std::atomic<ServiceFlags> nServices{NODE_NONE};
/**
* Socket used for communication with the node.
@ -509,8 +514,6 @@ public:
}
// This boolean is unusued in actual processing, only present for backward compatibility at RPC/QT level
bool m_legacyWhitelisted{false};
bool fClient{false}; // set by version message
bool m_limited_node{false}; //after BIP159, set by version message
/** fSuccessfullyConnected is set to true on receiving VERACK from the peer. */
std::atomic_bool fSuccessfullyConnected{false};
// Setting fDisconnect to true will cause the node to be disconnected the
@ -610,6 +613,9 @@ public:
// Peer selected us as (compact blocks) high-bandwidth peer (BIP152)
std::atomic<bool> m_bip152_highbandwidth_from{false};
/** Whether this peer provides all services that we want. Used for eviction decisions */
std::atomic_bool m_has_all_wanted_services{false};
/** Whether we should relay transactions to this peer (their version
* message did not include fRelay=false and this is not a block-relay-only
* connection). This only changes from false to true. It will never change
@ -651,7 +657,6 @@ public:
bool IsBlockRelayOnly() const;
CNode(NodeId id,
ServiceFlags nLocalServicesIn,
std::shared_ptr<Sock> sock,
const CAddress &addrIn,
uint64_t nKeyedNetGroupIn,
@ -718,11 +723,6 @@ public:
void CopyStats(CNodeStats& stats) EXCLUSIVE_LOCKS_REQUIRED(!m_subver_mutex, !m_addr_local_mutex, !cs_vSend, !cs_vRecv);
ServiceFlags GetLocalServices() const
{
return nLocalServices;
}
std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); }
/** A ping-pong round trip has completed successfully. Update latest and minimum ping times. */
@ -781,23 +781,6 @@ private:
const ConnectionType m_conn_type;
std::atomic<int> m_greatest_common_version{INIT_PROTO_VERSION};
//! Services offered to this peer.
//!
//! This is supplied by the parent CConnman during peer connection
//! (CConnman::ConnectNode()) from its attribute of the same name.
//!
//! This is const because there is no protocol defined for renegotiating
//! services initially offered to a peer. The set of local services we
//! offer should not change after initialization.
//!
//! An interesting example of this is NODE_NETWORK and initial block
//! download: a node which starts up from scratch doesn't have any blocks
//! to serve, but still advertises NODE_NETWORK because it will eventually
//! fulfill this role after IBD completes. P2P code is written in such a
//! way that it can gracefully handle peers who don't make good on their
//! service advertisements.
const ServiceFlags nLocalServices;
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
// Our address, as reported by the peer
@ -834,7 +817,7 @@ class NetEventsInterface
{
public:
/** Initialize a peer (setup state, queue any initial messages) */
virtual void InitializeNode(CNode* pnode) = 0;
virtual void InitializeNode(CNode& node, ServiceFlags our_services) = 0;
/** Handle removal of a peer (clear state) */
virtual void FinalizeNode(const CNode& node) = 0;
@ -1496,16 +1479,14 @@ private:
std::map<uint64_t, CachedAddrResponse> m_addr_response_caches;
/**
* Services this instance offers.
* Services this node offers.
*
* This data is replicated in each CNode instance we create during peer
* connection (in ConnectNode()) under a member also called
* nLocalServices.
* This data is replicated in each Peer instance we create.
*
* This data is not marked const, but after being set it should not
* change. See the note in CNode::nLocalServices documentation.
* change.
*
* \sa CNode::nLocalServices
* \sa Peer::our_services
*/
ServiceFlags nLocalServices;

File diff suppressed because it is too large Load Diff

View File

@ -51,6 +51,7 @@ struct CNodeStateStats {
uint64_t m_addr_processed = 0;
uint64_t m_addr_rate_limited = 0;
bool m_addr_relay_enabled{false};
ServiceFlags their_services;
};
class PeerManager : public CValidationInterface, public NetEventsInterface

View File

@ -25,7 +25,6 @@
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
bool fHavePruned = false;
bool fPruneMode = false;
uint64_t nPruneTarget = 0;
@ -86,7 +85,8 @@ const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
return it == m_block_index.end() ? nullptr : &it->second;
}
CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, const uint256& hash, enum BlockStatus nStatus)
CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, const uint256& hash, CBlockIndex*& best_header,
enum BlockStatus nStatus)
{
assert(!(nStatus & BLOCK_FAILED_MASK)); // no failed blocks allowed
AssertLockHeld(cs_main);
@ -113,8 +113,8 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, const uint
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
if (nStatus & BLOCK_VALID_MASK) {
pindexNew->RaiseValidity(nStatus);
if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork) {
pindexBestHeader = pindexNew;
if (best_header == nullptr || best_header->nChainWork < pindexNew->nChainWork) {
best_header = pindexNew;
}
} else {
pindexNew->RaiseValidity(BLOCK_VALID_TREE); // required validity level
@ -309,8 +309,6 @@ bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params)
if (pindex->pprev) {
pindex->BuildSkip();
}
if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
pindexBestHeader = pindex;
}
return true;
@ -327,6 +325,8 @@ void BlockManager::Unload()
m_last_blockfile = 0;
m_dirty_blockindex.clear();
m_dirty_fileinfo.clear();
m_have_pruned = false;
}
bool BlockManager::WriteBlockIndexDB()
@ -389,8 +389,8 @@ bool BlockManager::LoadBlockIndexDB()
}
// Check whether we have ever pruned block & undo files
m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
if (fHavePruned) {
m_block_tree_db->ReadFlag("prunedblockfiles", m_have_pruned);
if (m_have_pruned) {
LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
}
@ -428,10 +428,10 @@ const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
return nullptr;
}
bool IsBlockPruned(const CBlockIndex* pblockindex)
bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex)
{
AssertLockHeld(::cs_main);
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
}
// If we're using -prune with -reindex, then delete block files that will be ignored by the

View File

@ -49,8 +49,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
/** Pruning-related variables and constants */
/** True if any block files have ever been pruned. */
extern bool fHavePruned;
/** True if we're running in -prune mode. */
extern bool fPruneMode;
/** Number of bytes of block files that we're trying to stay below. */
@ -160,7 +158,9 @@ public:
/** Clear all data members. */
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block, const uint256& hash, enum BlockStatus nStatus = BLOCK_VALID_TREE) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block, const uint256& hash, CBlockIndex*& best_header,
enum BlockStatus nStatus = BLOCK_VALID_TREE)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -184,6 +184,12 @@ public:
//! Returns last CBlockIndex* that is a checkpoint
const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** True if any block files have ever been pruned. */
bool m_have_pruned = false;
//! Check whether the block associated with this index entry is pruned or not.
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/**
* Return the spend height, which is one more than the inputs.GetBestBlock().
* While checking, GetBestBlock() refers to the parent block. (protected by cs_main)
@ -197,9 +203,6 @@ public:
}
};
//! Check whether the block associated with this index entry is pruned or not.
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
void CleanupBlockRevFiles();
/** Open a block file (blk?????.dat) */

View File

@ -419,9 +419,10 @@ public:
bool getHeaderTip(int& height, int64_t& block_time) override
{
LOCK(::cs_main);
if (::pindexBestHeader) {
height = ::pindexBestHeader->nHeight;
block_time = ::pindexBestHeader->GetBlockTime();
auto best_header = chainman().m_best_header;
if (best_header) {
height = best_header->nHeight;
block_time = best_header->GetBlockTime();
return true;
}
return false;
@ -934,7 +935,7 @@ public:
bool havePruned() override
{
LOCK(cs_main);
return ::fHavePruned;
return m_node.chainman->m_blockman.m_have_pruned;
}
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override {

View File

@ -1295,7 +1295,6 @@ void RPCConsole::updateDetailWidget()
if (!stats->nodeStats.addrLocal.empty())
peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
ui->peerHeading->setText(peerAddrDetails);
ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
const auto time_now{GetTime<std::chrono::seconds>()};
ui->peerConnTime->setText(GUIUtil::formatDurationStr(time_now - stats->nodeStats.m_connected));
ui->peerLastBlock->setText(TimeDurationField(time_now, stats->nodeStats.m_last_block_time));
@ -1342,6 +1341,7 @@ void RPCConsole::updateDetailWidget()
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStateStats.their_services));
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1) {
ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));

View File

@ -273,10 +273,10 @@ static bool rest_block(const CoreContext& context,
CBlock block;
const CBlockIndex* pblockindex = nullptr;
const CBlockIndex* tip = nullptr;
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
ChainstateManager& chainman = *maybe_chainman;
{
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
tip = chainman.ActiveChain().Tip();
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
@ -284,7 +284,7 @@ static bool rest_block(const CoreContext& context,
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
if (IsBlockPruned(pblockindex))
if (chainman.m_blockman.IsBlockPruned(pblockindex))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
@ -311,7 +311,7 @@ static bool rest_block(const CoreContext& context,
}
case RetFormat::JSON: {
UniValue objBlock = blockToJSON(block, tip, pblockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, showTxDetails);
UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, showTxDetails);
std::string strJSON = objBlock.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);

View File

@ -156,7 +156,7 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
return result;
}
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails)
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails)
{
UniValue result = blockheaderToJSON(tip, blockindex, clhandler, isman);
@ -164,7 +164,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
UniValue txs(UniValue::VARR);
if (txDetails) {
CBlockUndo blockUndo;
const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
const bool have_undo{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
// coinbase transaction (i == 0) doesn't have undo data
@ -1060,11 +1060,11 @@ static RPCHelpMan getblockheaders()
};
}
static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
CBlock block;
if (IsBlockPruned(pblockindex)) {
if (blockman.IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
}
@ -1078,11 +1078,11 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_RE
return block;
}
static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(::cs_main);
CBlockUndo blockUndo;
if (IsBlockPruned(pblockindex)) {
if (blockman.IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
}
@ -1137,7 +1137,7 @@ static RPCHelpMan getmerkleblocks()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Count is out of range");
}
CBlock block = GetBlockChecked(pblockindex);
CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex);
UniValue arrMerkleBlocks(UniValue::VARR);
@ -1247,8 +1247,8 @@ static RPCHelpMan getblock()
CBlock block;
const CBlockIndex* pblockindex;
const CBlockIndex* tip;
ChainstateManager& chainman = EnsureChainman(node);
{
ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main);
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
tip = chainman.ActiveChain().Tip();
@ -1257,7 +1257,7 @@ static RPCHelpMan getblock()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
block = GetBlockChecked(pblockindex);
block = GetBlockChecked(chainman.m_blockman, pblockindex);
}
if (verbosity <= 0)
@ -1269,7 +1269,7 @@ static RPCHelpMan getblock()
}
LLMQContext& llmq_ctx = EnsureLLMQContext(node);
return blockToJSON(block, tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, verbosity >= 2);
return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, verbosity >= 2);
},
};
}
@ -1760,18 +1760,18 @@ RPCHelpMan getblockchaininfo()
const auto ehfSignals = node.mnhf_manager->GetSignalsStage(tip);
UniValue obj(UniValue::VOBJ);
obj.pushKV("chain", strChainName);
obj.pushKV("blocks", height);
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
obj.pushKV("time", (int64_t)tip->nTime);
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("chain", strChainName);
obj.pushKV("blocks", height);
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
obj.pushKV("time", (int64_t)tip->nTime);
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
obj.pushKV("pruned", fPruneMode);
obj.pushKV("pruned", fPruneMode);
if (fPruneMode) {
const CBlockIndex* block = tip;
CHECK_NONFATAL(block);
@ -2358,8 +2358,8 @@ static RPCHelpMan getblockstats()
}
}
const CBlock block = GetBlockChecked(pindex);
const CBlockUndo blockUndo = GetUndoChecked(pindex);
const CBlock block = GetBlockChecked(chainman.m_blockman, pindex);
const CBlockUndo blockUndo = GetUndoChecked(chainman.m_blockman, pindex);
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
@ -2571,7 +2571,7 @@ static RPCHelpMan getspecialtxes()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
const CBlock block = GetBlockChecked(pblockindex);
const CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex);
int nTxNum = 0;
UniValue result(UniValue::VARR);

View File

@ -9,6 +9,7 @@
#include <core_io.h>
#include <streams.h>
#include <sync.h>
#include <validation.h>
#include <stdint.h>
#include <vector>
@ -40,7 +41,7 @@ double GetDifficulty(const CBlockIndex* blockindex);
void RPCNotifyBlockChange(const CBlockIndex*);
/** Block description to JSON */
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails = false) LOCKS_EXCLUDED(cs_main);
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails = false) LOCKS_EXCLUDED(cs_main);
/** Mempool information to JSON */
UniValue MempoolInfoToJSON(const CTxMemPool& pool, llmq::CInstantSendManager& isman);

View File

@ -103,16 +103,16 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"},
{RPCResult::Type::STR, "mapped_as", "The AS in the BGP route to the peer used for diversifying peer selection"},
{RPCResult::Type::STR_HEX, "services", "The services offered"},
{RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form",
{
{RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
}},
{RPCResult::Type::STR_HEX, "verified_proregtx_hash", true /*optional*/, "Only present when the peer is a masternode and successfully "
"authenticated via MNAUTH. In this case, this field contains the "
"protx hash of the masternode"},
{RPCResult::Type::STR_HEX, "verified_pubkey_hash", true /*optional*/, "Only present when the peer is a masternode and successfully "
"authenticated via MNAUTH. In this case, this field contains the "
"hash of the masternode's operator public key"},
{RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form",
{
{RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
}},
{RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"},
{RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
{RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
@ -198,14 +198,15 @@ static RPCHelpMan getpeerinfo()
if (stats.m_mapped_as != 0) {
obj.pushKV("mapped_as", uint64_t(stats.m_mapped_as));
}
obj.pushKV("services", strprintf("%016x", stats.nServices));
ServiceFlags services{fStateStats ? statestats.their_services : ServiceFlags::NODE_NONE};
obj.pushKV("services", strprintf("%016x", services));
obj.pushKV("servicesnames", GetServicesNames(services));
if (!stats.verifiedProRegTxHash.IsNull()) {
obj.pushKV("verified_proregtx_hash", stats.verifiedProRegTxHash.ToString());
}
if (!stats.verifiedPubKeyHash.IsNull()) {
obj.pushKV("verified_pubkey_hash", stats.verifiedPubKeyHash.ToString());
}
obj.pushKV("servicesnames", GetServicesNames(stats.nServices));
obj.pushKV("lastsend", count_seconds(stats.m_last_send));
obj.pushKV("lastrecv", count_seconds(stats.m_last_recv));
obj.pushKV("last_transaction", count_seconds(stats.m_last_tx_time));

View File

@ -14,6 +14,7 @@
#include <script/standard.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <timedata.h>
#include <util/string.h>
#include <util/system.h>
#include <util/time.h>
@ -58,20 +59,15 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
const CChainParams& chainparams = Params();
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
ConnmanTestMsg& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
// Disable inactivity checks for this test to avoid interference
static_cast<ConnmanTestMsg*>(connman.get())->SetPeerConnectTimeout(99999s);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
m_node.cj_ctx, m_node.llmq_ctx, /* ignore_incoming_txs = */ false);
connman.SetPeerConnectTimeout(99999s);
PeerManager& peerman = *m_node.peerman;
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
NodeId id{0};
CNode dummyNode1{id++,
ServiceFlags(NODE_NETWORK),
/*sock=*/nullptr,
addr1,
/*nKeyedNetGroupIn=*/0,
@ -80,10 +76,16 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
/*addrNameIn=*/"",
ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false};
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.fSuccessfullyConnected = true;
connman.Handshake(
/*node=*/dummyNode1,
/*successfully_connected=*/true,
/*remote_services=*/ServiceFlags(NODE_NETWORK),
/*local_services=*/ServiceFlags(NODE_NETWORK),
/*permission_flags=*/NetPermissionFlags::None,
/*version=*/PROTOCOL_VERSION,
/*relay_txs=*/true);
TestOnlyResetTimeData();
// This test requires that we have a chain with non-zero work.
{
@ -95,7 +97,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// Test starts here
{
LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in getheaders
}
{
LOCK(dummyNode1.cs_vSend);
@ -109,7 +111,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
SetMockTime(nStartTime+21*60);
{
LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in getheaders
}
{
LOCK(dummyNode1.cs_vSend);
@ -119,18 +121,17 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
SetMockTime(nStartTime+24*60);
{
LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in disconnect
BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in disconnect
}
BOOST_CHECK(dummyNode1.fDisconnect == true);
peerLogic->FinalizeNode(dummyNode1);
peerman.FinalizeNode(dummyNode1);
}
static void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman, ConnectionType connType)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
vNodes.emplace_back(new CNode{id++,
ServiceFlags(NODE_NETWORK),
/*sock=*/nullptr,
addr,
/*nKeyedNetGroupIn=*/0,
@ -142,7 +143,7 @@ static void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerM
CNode &node = *vNodes.back();
node.SetCommonVersion(PROTOCOL_VERSION);
peerLogic.InitializeNode(&node);
peerLogic.InitializeNode(node, ServiceFlags(NODE_NETWORK));
node.fSuccessfullyConnected = true;
connman.AddTestNode(node);
@ -319,7 +320,6 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
banman->ClearBanned();
NodeId id{0};
nodes[0] = new CNode{id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr[0],
/*nKeyedNetGroupIn=*/0,
@ -329,7 +329,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
ConnectionType::INBOUND,
/*inbound_onion=*/false};
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[0]);
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
nodes[0]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[0]);
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
@ -342,7 +342,6 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_CHECK(!banman->IsDiscouraged(other_addr)); // Different address, not discouraged
nodes[1] = new CNode{id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr[1],
/*nKeyedNetGroupIn=*/1,
@ -352,7 +351,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
ConnectionType::INBOUND,
/*inbound_onion=*/false};
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[1]);
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
nodes[1]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[1]);
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1);
@ -380,7 +379,6 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
// Make sure non-IP peers are discouraged and disconnected properly.
nodes[2] = new CNode{id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr[2],
/*nKeyedNetGroupIn=*/1,
@ -390,7 +388,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false};
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[2]);
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
nodes[2]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[2]);
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
@ -428,7 +426,6 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
CAddress addr(ip(0xa0b0c001), NODE_NONE);
NodeId id{0};
CNode dummyNode{id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr,
/*nKeyedNetGroupIn=*/4,
@ -438,7 +435,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
ConnectionType::INBOUND,
/*inbound_onion=*/false};
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode);
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
dummyNode.fSuccessfullyConnected = true;
peerLogic->Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);

View File

@ -270,7 +270,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
(void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_setup->m_node.chainman->m_blockman)), stats);
(void)GetUTXOStats(&coins_view_cache, g_setup->m_node.chainman->m_blockman, stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}

View File

@ -71,7 +71,6 @@ FUZZ_TARGET_INIT(net, initialize_net)
(void)node.GetAddrLocal();
(void)node.GetId();
(void)node.GetLocalNonce();
(void)node.GetLocalServices();
const int ref_count = node.GetRefCount();
assert(ref_count >= 0);
(void)node.GetCommonVersion();

View File

@ -287,6 +287,7 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman,
connman.Handshake(node,
/*successfully_connected=*/fuzzed_data_provider.ConsumeBool(),
/*remote_services=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS),
/*local_services=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS),
/*permission_flags=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS),
/*version=*/fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max()),
/*relay_txs=*/fuzzed_data_provider.ConsumeBool());

View File

@ -360,7 +360,6 @@ template <bool ReturnUniquePtr = false>
auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept
{
const NodeId node_id = node_id_in.value_or(fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, std::numeric_limits<NodeId>::max()));
const ServiceFlags local_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS);
const auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider);
const CAddress address = ConsumeAddress(fuzzed_data_provider);
const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
@ -372,7 +371,6 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
if constexpr (ReturnUniquePtr) {
return std::make_unique<CNode>(node_id,
local_services,
sock,
address,
keyed_net_group,
@ -383,7 +381,6 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
inbound_onion);
} else {
return CNode{node_id,
local_services,
sock,
address,
keyed_net_group,

View File

@ -59,7 +59,6 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
std::string pszDest;
std::unique_ptr<CNode> pnode1 = std::make_unique<CNode>(id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr,
/*nKeyedNetGroupIn=*/0,
@ -78,7 +77,6 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode2 = std::make_unique<CNode>(id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr,
/*nKeyedNetGroupIn=*/1,
@ -97,7 +95,6 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode3 = std::make_unique<CNode>(id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr,
/*nKeyedNetGroupIn=*/0,
@ -116,7 +113,6 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode4 = std::make_unique<CNode>(id++,
NODE_NETWORK,
/*sock=*/nullptr,
addr,
/*nKeyedNetGroupIn=*/1,
@ -630,7 +626,6 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
ipv4AddrPeer.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
std::unique_ptr<CNode> pnode = std::make_unique<CNode>(/*id=*/0,
NODE_NETWORK,
/*sock=*/nullptr,
addr,
/*nKeyedNetGroupIn=*/0,
@ -649,7 +644,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
pnode->SetAddrLocal(addrLocal);
// before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
GetLocalAddrForPeer(&*pnode);
GetLocalAddrForPeer(*pnode);
// suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
BOOST_CHECK(1);
@ -682,13 +677,12 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
// Our address:port as seen from the peer, completely different from the above.
in_addr peer_us_addr;
peer_us_addr.s_addr = htonl(0x02030405);
const CAddress peer_us{CService{peer_us_addr, 20002}, NODE_NETWORK, current_time};
const CService peer_us{peer_us_addr, 20002};
// Create a peer with a routable IPv4 address (outbound).
in_addr peer_out_in_addr;
peer_out_in_addr.s_addr = htonl(0x01020304);
CNode peer_out{/*id=*/0,
/*nLocalServicesIn=*/NODE_NETWORK,
/*sock=*/nullptr,
/*addrIn=*/CAddress{CService{peer_out_in_addr, 8333}, NODE_NETWORK},
/*nKeyedNetGroupIn=*/0,
@ -701,7 +695,7 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
peer_out.SetAddrLocal(peer_us);
// Without the fix peer_us:8333 is chosen instead of the proper peer_us:bind_port.
auto chosen_local_addr = GetLocalAddrForPeer(&peer_out);
auto chosen_local_addr = GetLocalAddrForPeer(peer_out);
BOOST_REQUIRE(chosen_local_addr);
const CAddress expected{CService{peer_us_addr, bind_port}, NODE_NETWORK, current_time};
BOOST_CHECK(*chosen_local_addr == expected);
@ -710,7 +704,6 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
in_addr peer_in_in_addr;
peer_in_in_addr.s_addr = htonl(0x05060708);
CNode peer_in{/*id=*/0,
/*nLocalServicesIn=*/NODE_NETWORK,
/*sock=*/nullptr,
/*addrIn=*/CAddress{CService{peer_in_in_addr, 8333}, NODE_NETWORK},
/*nKeyedNetGroupIn=*/0,
@ -723,7 +716,7 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
peer_in.SetAddrLocal(peer_us);
// Without the fix peer_us:8333 is chosen instead of the proper peer_us:peer_us.GetPort().
chosen_local_addr = GetLocalAddrForPeer(&peer_in);
chosen_local_addr = GetLocalAddrForPeer(peer_in);
BOOST_REQUIRE(chosen_local_addr);
BOOST_CHECK(*chosen_local_addr == peer_us);
@ -838,7 +831,6 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
in_addr peer_in_addr;
peer_in_addr.s_addr = htonl(0x01020304);
CNode peer{/*id=*/0,
/*nLocalServicesIn=*/NODE_NETWORK,
/*sock=*/nullptr,
/*addrIn=*/CAddress{CService{peer_in_addr, 8333}, NODE_NETWORK},
/*nKeyedNetGroupIn=*/0,
@ -858,7 +850,7 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
*static_cast<TestChainState*>(&m_node.chainman->ActiveChainstate());
chainstate.JumpOutOfIbd();
m_node.peerman->InitializeNode(&peer);
m_node.peerman->InitializeNode(peer, NODE_NETWORK);
std::atomic<bool> interrupt_dummy{false};
std::chrono::microseconds time_received_dummy{0};

View File

@ -15,6 +15,7 @@
void ConnmanTestMsg::Handshake(CNode& node,
bool successfully_connected,
ServiceFlags remote_services,
ServiceFlags local_services,
NetPermissionFlags permission_flags,
int32_t version,
bool relay_txs)
@ -23,7 +24,7 @@ void ConnmanTestMsg::Handshake(CNode& node,
auto& connman{*this};
const CNetMsgMaker mm{0};
peerman.InitializeNode(&node);
peerman.InitializeNode(node, local_services);
CSerializedNetMsg msg_version{
mm.Make(NetMsgType::VERSION,
@ -50,10 +51,10 @@ void ConnmanTestMsg::Handshake(CNode& node,
if (node.fDisconnect) return;
assert(node.nVersion == version);
assert(node.GetCommonVersion() == std::min(version, PROTOCOL_VERSION));
assert(node.nServices == remote_services);
CNodeStateStats statestats;
assert(peerman.GetNodeStateStats(node.GetId(), statestats));
assert(statestats.m_relay_txs == (relay_txs && !node.IsBlockOnlyConn()));
assert(statestats.their_services == remote_services);
node.m_permissionFlags = permission_flags;
if (successfully_connected) {
CSerializedNetMsg msg_verack{mm.Make(NetMsgType::VERACK)};

View File

@ -41,6 +41,7 @@ struct ConnmanTestMsg : public CConnman {
void Handshake(CNode& node,
bool successfully_connected,
ServiceFlags remote_services,
ServiceFlags local_services,
NetPermissionFlags permission_flags,
int32_t version,
bool relay_txs);

View File

@ -105,7 +105,6 @@ const std::vector<std::string> CHECKLEVEL_DOC {
*/
RecursiveMutex cs_main;
CBlockIndex* pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
uint256 g_best_block;
@ -348,8 +347,9 @@ static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_
return false;
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1)
if (active_chainstate.m_chain.Height() < active_chainstate.m_chainman.m_best_header->nHeight - 1) {
return false;
}
return true;
}
@ -1296,8 +1296,8 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
if (!m_chainman.m_best_invalid || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork) {
m_chainman.m_best_invalid = pindexNew;
}
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
pindexBestHeader = m_chain.Tip();
if (m_chainman.m_best_header != nullptr && m_chainman.m_best_header->GetAncestor(pindexNew->nHeight) == pindexNew) {
m_chainman.m_best_header = m_chain.Tip();
}
LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n", __func__,
@ -1888,8 +1888,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
if (it != m_blockman.m_block_index.end()) {
if (it->second.GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= nMinimumChainWork) {
m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex &&
m_chainman.m_best_header->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
// Script verification is skipped when connecting blocks under the
// assumevalid block. Assuming the assumevalid block is valid this
@ -1904,7 +1904,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// artificially set the default assumed verified block further back.
// The test against nMinimumChainWork prevents the skipping when denied access to any chain at
// least as good as the expected chain.
fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
}
}
}
@ -2348,9 +2348,9 @@ bool CChainState::FlushStateToDisk(
}
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
if (!fHavePruned) {
if (!m_blockman.m_have_pruned) {
m_blockman.m_block_tree_db->WriteFlag("prunedblockfiles", true);
fHavePruned = true;
m_blockman.m_have_pruned = true;
}
}
}
@ -2911,7 +2911,7 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
CBlockIndex* pindexHeader = nullptr;
{
LOCK(cs_main);
pindexHeader = pindexBestHeader;
pindexHeader = chainstate.m_chainman.m_best_header;
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
@ -3129,9 +3129,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
CBlockIndex *invalid_walk_tip = m_chain.Tip();
const CBlockIndex* pindexOldTip = m_chain.Tip();
if (pindex == pindexBestHeader) {
m_chainman.m_best_invalid = pindexBestHeader;
pindexBestHeader = pindexBestHeader->pprev;
if (pindex == m_chainman.m_best_header) {
m_chainman.m_best_invalid = m_chainman.m_best_header;
m_chainman.m_best_header = m_chainman.m_best_header->pprev;
}
// ActivateBestChain considers blocks already in m_chain
@ -3147,9 +3147,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
if (pindexOldTip == pindexBestHeader) {
m_chainman.m_best_invalid = pindexBestHeader;
pindexBestHeader = pindexBestHeader->pprev;
if (pindexOldTip == m_chainman.m_best_header) {
m_chainman.m_best_invalid = m_chainman.m_best_header;
m_chainman.m_best_header = m_chainman.m_best_header->pprev;
}
// We immediately mark the disconnected blocks as invalid.
@ -3264,8 +3264,8 @@ bool CChainState::MarkConflictingBlock(BlockValidationState& state, CBlockIndex
bool pindex_was_in_chain = false;
CBlockIndex *conflicting_walk_tip = m_chain.Tip();
if (pindex == pindexBestHeader) {
pindexBestHeader = pindexBestHeader->pprev;
if (pindex == m_chainman.m_best_header) {
m_chainman.m_best_header = m_chainman.m_best_header->pprev;
}
{
@ -3282,8 +3282,8 @@ bool CChainState::MarkConflictingBlock(BlockValidationState& state, CBlockIndex
MaybeUpdateMempoolForReorg(disconnectpool, false);
return false;
}
if (pindexOldTip == pindexBestHeader) {
pindexBestHeader = pindexBestHeader->pprev;
if (pindexOldTip == m_chainman.m_best_header) {
m_chainman.m_best_header = m_chainman.m_best_header->pprev;
}
}
@ -3749,13 +3749,13 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
if (llmq::chainLocksHandler->HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) {
if (miSelf == m_blockman.m_block_index.end()) {
m_blockman.AddToBlockIndex(block, hash, BLOCK_CONFLICT_CHAINLOCK);
m_blockman.AddToBlockIndex(block, hash, m_best_header, BLOCK_CONFLICT_CHAINLOCK);
}
LogPrintf("ERROR: %s: header %s conflicts with chainlock\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_CHAINLOCK, "bad-chainlock");
}
}
CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, hash)};
CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, hash, m_best_header)};
if (ppindex)
*ppindex = pindex;
@ -4342,13 +4342,11 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
{
LOCK(cs_main);
chainman.Unload();
pindexBestHeader = nullptr;
if (mempool) mempool->clear();
g_versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
}
fHavePruned = false;
}
bool ChainstateManager::LoadBlockIndex()
@ -4423,6 +4421,8 @@ bool ChainstateManager::LoadBlockIndex()
if (pindex->nStatus & BLOCK_FAILED_MASK && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) {
m_best_invalid = pindex;
}
if (pindex->IsValid(BLOCK_VALID_TREE) && (m_best_header == nullptr || CBlockIndexWorkComparator()(m_best_header, pindex)))
m_best_header = pindex;
}
needs_init = m_blockman.m_block_index.empty();
@ -4458,7 +4458,7 @@ bool CChainState::AddGenesisBlock(const CBlock& block, BlockValidationState& sta
if (blockPos.IsNull()) {
return error("%s: writing genesis block to disk failed (%s)", __func__, state.ToString());
}
CBlockIndex* pindex = m_blockman.AddToBlockIndex(block, block.GetHash());
CBlockIndex* pindex = m_blockman.AddToBlockIndex(block, block.GetHash(), m_chainman.m_best_header);
ReceivedBlockTransactions(block, pindex, blockPos);
return true;
}
@ -4700,7 +4700,7 @@ void CChainState::CheckBlockIndex()
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
// Unless these indexes are assumed valid and pending block download on a
// background chainstate.
if (!fHavePruned && !pindex->IsAssumedValid()) {
if (!m_blockman.m_have_pruned && !pindex->IsAssumedValid()) {
// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
assert(pindexFirstMissing == pindexFirstNeverProcessed);
@ -4778,7 +4778,7 @@ void CChainState::CheckBlockIndex()
if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked.
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) {
// We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent.
assert(fHavePruned); // We must have pruned.
assert(m_blockman.m_have_pruned); // We must have pruned.
// This block may have entered m_blocks_unlinked if:
// - it has a descendant that at some point had more work than the
// tip, and
@ -5341,7 +5341,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, breakpoint_fnc)) {
if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) {
LogPrintf("[snapshot] failed to generate coins stats\n");
return false;
}
@ -5424,6 +5424,7 @@ void ChainstateManager::Unload()
m_failed_blocks.clear();
m_blockman.Unload();
m_best_header = nullptr;
m_best_invalid = nullptr;
}

View File

@ -150,9 +150,6 @@ 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;
/** Documentation for argument 'checklevel'. */
extern const std::vector<std::string> CHECKLEVEL_DOC;
@ -858,7 +855,7 @@ public:
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
//! chainstate to avoid duplicating block metadata.
BlockManager m_blockman GUARDED_BY(::cs_main);
BlockManager m_blockman;
/**
* In order to efficiently track invalidity of headers, we keep the set of
@ -881,6 +878,9 @@ public:
*/
std::set<CBlockIndex*> m_failed_blocks;
/** Best header we've seen so far (used for getheaders queries' starting points). */
CBlockIndex* m_best_header = nullptr;
//! The total number of bytes available for us to use across all in-memory
//! coins caches. This will be split somehow across chainstates.
int64_t m_total_coinstip_cache{0};

View File

@ -1,6 +1,5 @@
fork:000000202cbcf83b62913d56f605c0e581a48872839428c92e5eb76cd7ad94bcaf0b0000c41f893a0d296b3b17730b42beb432aedc62473d0e6839910209d7f21af31a9bb9968054ffff0f1efd6b0e00
fork:000000201ed4bb12db90cfb7c68051d82f922b86b24c6a247de6168678c9a489ef020000e5968c16ea68f5f8d6ce1f1a987a7484986710c7cae2ac81ca1b5552f059aeceba968054f0ff0f1ef38f0c00
010000000000000000000000000000000000000000000000000000000000000000000000c762a6567f3cc092f0684bb62b7e00a84890b990f07cc71a6bb58d64b98e02e0dee1e352f0ff0f1ec3c927e6
020000002cbcf83b62913d56f605c0e581a48872839428c92e5eb76cd7ad94bcaf0b00007f11dcce14075520e8f74cc4ddf092b4e26ebd23b8d8665a1ae5bfc41b58fdb4c3a95e53ffff0f1ef37a0000
02000000c108f2910109954fcdec2f962f1a9094be266cb6aeaae37b345e63247d0400004d29e4f9b2e05a9ac97dd5ae4128b3c0104bdc95aabaa566cc8eeb682e336d0dc4a95e53f0ff0f1e7b190000
02000000c2cc6fa3fe4d9fb73476bc3cf02248aa762af4960399232dbab4fa64620c000058968247522cc488db01996de610d6f3b8c348e748198dc528a305941211c71cc6a95e53f0ff0f1ecacf0000

View File

@ -91,11 +91,10 @@ class BIP68_112_113Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
# Must also set '-maxtipage=600100' to allow syncing from very old blocks
# and '-dip3params=2000:2000' to create pre-dip3 blocks only
# Must set '-dip3params=2000:2000' to create pre-dip3 blocks only
self.extra_args = [[
'-whitelist=noban@127.0.0.1',
'-maxtipage=600100', '-dip3params=2000:2000',
'-dip3params=2000:2000',
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
self.supports_cli = False

View File

@ -38,7 +38,6 @@ class MaxUploadTest(BitcoinTestFramework):
self.extra_args = [[
"-maxuploadtarget=200",
"-blockmaxsize=999000",
"-maxtipage="+str(2*60*60*24*7),
"-acceptnonstdtxn=1"
]]
self.supports_cli = False
@ -149,7 +148,7 @@ class MaxUploadTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
self.log.info("Restarting node 0 with download permission and 1MB maxuploadtarget")
self.restart_node(0, ["-whitelist=download@127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000", "-maxtipage="+str(2*60*60*24*7), "-mocktime="+str(current_mocktime)])
self.restart_node(0, ["-whitelist=download@127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000", "-mocktime="+str(current_mocktime)])
# Reconnect to self.nodes[0]
peer = self.nodes[0].add_p2p_connection(TestP2PConn())

View File

@ -17,6 +17,7 @@ only succeeds past a given node once its nMinimumChainWork has been exceeded.
import time
from test_framework.p2p import P2PInterface, msg_getheaders
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@ -43,6 +44,9 @@ class MinimumChainWorkTest(BitcoinTestFramework):
for i in range(self.num_nodes-1):
self.connect_nodes(i+1, i)
# Set clock of node2 2 days ahead, to keep it in IBD during this test.
self.nodes[2].setmocktime(int(time.time()) + 48*60*60)
def run_test(self):
# Start building a chain on node0. node2 shouldn't be able to sync until node1's
# minchainwork is exceeded
@ -74,6 +78,15 @@ class MinimumChainWorkTest(BitcoinTestFramework):
assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash()
assert_equal(self.nodes[2].getblockcount(), starting_blockcount)
self.log.info("Check that getheaders requests to node2 are ignored")
peer = self.nodes[2].add_p2p_connection(P2PInterface())
msg = msg_getheaders()
msg.locator.vHave = [int(self.nodes[2].getbestblockhash(), 16)]
msg.hashstop = 0
peer.send_and_ping(msg)
time.sleep(5)
assert ("headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0)
self.log.info("Generating one more block")
self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)
@ -88,6 +101,14 @@ class MinimumChainWorkTest(BitcoinTestFramework):
self.sync_all()
self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes])
self.log.info("Test that getheaders requests to node2 are not ignored")
peer.send_and_ping(msg)
assert "headers" in peer.last_message
# Verify that node2 is in fact still in IBD (otherwise this test may
# not be exercising the logic we want!)
assert_equal(self.nodes[2].getblockchaininfo()['initialblockdownload'], True)
self.log.info("Test -minimumchainwork with a non-hex value")
self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# Copyright (c) 2022 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 block download
Ensure that even in IBD, we'll eventually sync chain from inbound peers
(whether we have only inbound peers or both inbound and outbound peers).
"""
from test_framework.test_framework import BitcoinTestFramework
class BlockSyncTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
def setup_network(self):
self.setup_nodes()
# Construct a network:
# node0 -> node1 -> node2
# So node1 has both an inbound and outbound peer.
# In our test, we will mine a block on node0, and ensure that it makes
# to to both node1 and node2.
self.connect_nodes(0, 1)
self.connect_nodes(1, 2)
def run_test(self):
self.log.info("Setup network: node0->node1->node2")
self.log.info("Mining one block on node0 and verify all nodes sync")
self.nodes[0].generate(1)
self.log.info("Success!")
if __name__ == '__main__':
BlockSyncTest().main()

View File

@ -0,0 +1,119 @@
#!/usr/bin/env python3
# Copyright (c) 2022 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 initial headers download
Test that we only try to initially sync headers from one peer (until our chain
is close to caught up), and that each block announcement results in only one
additional peer receiving a getheaders message.
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import (
CInv,
MSG_BLOCK,
msg_headers2,
msg_inv,
)
from test_framework.p2p import (
p2p_lock,
P2PInterface,
)
from test_framework.util import (
assert_equal,
)
import random
class HeadersSyncTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
def setup_chain(self):
# This test operates under the assumption that the adjusted time is well ahead of block
# time.
#
# By default when we setup a new chain, we also adjust the mocktime (this is not done in
# Bitcoin's test suite), which violates this test's assumption and causes it to fail. We
# remedy this by ensuring the test's assumptions are met (i.e. we don't adjust mocktime)
#
self.log.info("Initializing test directory " + self.options.tmpdir)
if self.setup_clean_chain:
self._initialize_chain_clean()
else:
self._initialize_chain()
def announce_random_block(self, peers):
new_block_announcement = msg_inv(inv=[CInv(MSG_BLOCK, random.randrange(1<<256))])
for p in peers:
p.send_and_ping(new_block_announcement)
def run_test(self):
self.log.info("Adding a peer to node0")
peer1 = self.nodes[0].add_p2p_connection(P2PInterface())
# Wait for peer1 to receive a getheaders
peer1.wait_for_getheaders()
# An empty reply will clear the outstanding getheaders request,
# allowing additional getheaders requests to be sent to this peer in
# the future.
peer1.send_message(msg_headers2())
self.log.info("Connecting two more peers to node0")
# Connect 2 more peers; they should not receive a getheaders yet
peer2 = self.nodes[0].add_p2p_connection(P2PInterface())
peer3 = self.nodes[0].add_p2p_connection(P2PInterface())
all_peers = [peer1, peer2, peer3]
self.log.info("Verify that peer2 and peer3 don't receive a getheaders after connecting")
for p in all_peers:
p.sync_with_ping()
with p2p_lock:
assert "getheaders2" not in peer2.last_message
assert "getheaders2" not in peer3.last_message
with p2p_lock:
peer1.last_message.pop("getheaders2", None)
self.log.info("Have all peers announce a new block")
self.announce_random_block(all_peers)
self.log.info("Check that peer1 receives a getheaders in response")
peer1.wait_for_getheaders()
peer1.send_message(msg_headers2()) # Send empty response, see above
with p2p_lock:
peer1.last_message.pop("getheaders2", None)
self.log.info("Check that exactly 1 of {peer2, peer3} received a getheaders in response")
count = 0
peer_receiving_getheaders = None
for p in [peer2, peer3]:
with p2p_lock:
if "getheaders2" in p.last_message:
count += 1
peer_receiving_getheaders = p
p.last_message.pop("getheaders2", None)
p.send_message(msg_headers2()) # Send empty response, see above
assert_equal(count, 1)
self.log.info("Announce another new block, from all peers")
self.announce_random_block(all_peers)
self.log.info("Check that peer1 receives a getheaders in response")
peer1.wait_for_getheaders()
self.log.info("Check that the remaining peer received a getheaders as well")
expected_peer = peer2
if peer2 == peer_receiving_getheaders:
expected_peer = peer3
expected_peer.wait_for_getheaders()
self.log.info("Success!")
if __name__ == '__main__':
HeadersSyncTest().main()

View File

@ -172,6 +172,7 @@ BASE_SCRIPTS = [
'wallet_avoidreuse.py --descriptors',
'mempool_reorg.py',
'mempool_persist.py',
'p2p_block_sync.py',
'wallet_multiwallet.py --legacy-wallet',
'wallet_multiwallet.py --descriptors',
'wallet_multiwallet.py --usecli',
@ -254,6 +255,7 @@ BASE_SCRIPTS = [
'rpc_generate.py',
'wallet_balance.py --legacy-wallet',
'wallet_balance.py --descriptors',
'p2p_initial_headers_sync.py',
'feature_nulldummy.py --legacy-wallet',
'feature_nulldummy.py --descriptors',
'mempool_accept.py',