diff options
author | Sjors Provoost <sjors@sprovoost.nl> | 2021-05-13 18:51:47 +0200 |
---|---|---|
committer | Sjors Provoost <sjors@sprovoost.nl> | 2021-12-02 13:16:18 +0700 |
commit | dce8c4c38111556ca480aa0e63c46b71f66b508f (patch) | |
tree | d43051c4e187c70cf3d700d8eb308012811b041a /src | |
parent | b884ababc29ce963826d8a4327ed6a5e629ff175 (diff) | |
download | bitcoin-dce8c4c38111556ca480aa0e63c46b71f66b508f.tar.xz |
rpc: getblockfrompeer
Co-authored-by: John Newbery <john@johnnewbery.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/net_processing.cpp | 36 | ||||
-rw-r--r-- | src/net_processing.h | 10 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 55 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/test/fuzz/rpc.cpp | 1 | ||||
-rw-r--r-- | src/validation.cpp | 1 |
6 files changed, 104 insertions, 0 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3f755eb178..866a344dd3 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -312,6 +312,7 @@ public: /** Implement PeerManager */ void StartScheduledTasks(CScheduler& scheduler) override; void CheckForStaleTipAndEvictPeers() override; + bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index) override; bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override; bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; } void SendPings() override; @@ -1427,6 +1428,41 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex) (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT); } +bool PeerManagerImpl::FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index) +{ + if (fImporting || fReindex) return false; + + LOCK(cs_main); + // Ensure this peer exists and hasn't been disconnected + CNodeState* state = State(id); + if (state == nullptr) return false; + // Ignore pre-segwit peers + if (!state->fHaveWitness) return false; + + // Mark block as in-flight unless it already is + if (!BlockRequested(id, index)) return false; + + // Construct message to request the block + std::vector<CInv> invs{CInv(MSG_BLOCK | MSG_WITNESS_FLAG, hash)}; + + // Send block request message to the peer + bool success = m_connman.ForNode(id, [this, &invs](CNode* node) { + const CNetMsgMaker msgMaker(node->GetCommonVersion()); + this->m_connman.PushMessage(node, msgMaker.Make(NetMsgType::GETDATA, invs)); + return true; + }); + + if (success) { + LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", + hash.ToString(), id); + } else { + RemoveBlockRequest(hash); + LogPrint(BCLog::NET, "Failed to request block %s from peer=%d\n", + hash.ToString(), id); + } + return success; +} + std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman, BanMan* banman, ChainstateManager& chainman, CTxMemPool& pool, bool ignore_incoming_txs) diff --git a/src/net_processing.h b/src/net_processing.h index 27bc40687a..6c18e8ddfa 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -42,6 +42,16 @@ public: CTxMemPool& pool, bool ignore_incoming_txs); virtual ~PeerManager() { } + /** + * Attempt to manually fetch block from a given peer. We must already have the header. + * + * @param[in] id The peer id + * @param[in] hash The block hash + * @param[in] pindex The blockindex + * @returns Whether a request was successfully made + */ + virtual bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& pindex) = 0; + /** Begin running background tasks, should only be called once */ virtual void StartScheduledTasks(CScheduler& scheduler) = 0; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 6442d30feb..fdbfe55aaf 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -18,6 +18,8 @@ #include <hash.h> #include <index/blockfilterindex.h> #include <index/coinstatsindex.h> +#include <net.h> +#include <net_processing.h> #include <node/blockstorage.h> #include <node/coinstats.h> #include <node/context.h> @@ -755,6 +757,58 @@ static RPCHelpMan getmempoolentry() }; } +static RPCHelpMan getblockfrompeer() +{ + return RPCHelpMan{"getblockfrompeer", + "\nAttempt to fetch block from a given peer.\n" + "\nWe must have the header for this block, e.g. using submitheader.\n" + "\nReturns {} if a block-request was successfully scheduled\n", + { + {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"}, + {"nodeid", RPCArg::Type::NUM, RPCArg::Optional::NO, "The node ID (see getpeerinfo for node IDs)"}, + }, + RPCResult{RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "warnings", "any warnings"} + }}, + RPCExamples{ + HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") + + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); + PeerManager& peerman = EnsurePeerman(node); + CConnman& connman = EnsureConnman(node); + + uint256 hash(ParseHashV(request.params[0], "hash")); + + const NodeId nodeid = static_cast<NodeId>(request.params[1].get_int64()); + + // Check that the peer with nodeid exists + if (!connman.ForNode(nodeid, [](CNode* node) {return true;})) { + throw JSONRPCError(RPC_MISC_ERROR, strprintf("Peer nodeid %d does not exist", nodeid)); + } + + const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(hash);); + + if (!index) { + throw JSONRPCError(RPC_MISC_ERROR, "Block header missing"); + } + + UniValue result = UniValue::VOBJ; + + if (index->nStatus & BLOCK_HAVE_DATA) { + result.pushKV("warnings", "Block already downloaded"); + } else if (!peerman.FetchBlock(nodeid, hash, *index)) { + throw JSONRPCError(RPC_MISC_ERROR, "Failed to fetch block from peer"); + } + return result; +}, + }; +} + static RPCHelpMan getblockhash() { return RPCHelpMan{"getblockhash", @@ -2612,6 +2666,7 @@ static const CRPCCommand commands[] = { "blockchain", &getbestblockhash, }, { "blockchain", &getblockcount, }, { "blockchain", &getblock, }, + { "blockchain", &getblockfrompeer, }, { "blockchain", &getblockhash, }, { "blockchain", &getblockheader, }, { "blockchain", &getchaintips, }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 90fbd823a4..349efa3bd1 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -56,6 +56,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getbalance", 1, "minconf" }, { "getbalance", 2, "include_watchonly" }, { "getbalance", 3, "avoid_reuse" }, + { "getblockfrompeer", 1, "nodeid" }, { "getblockhash", 0, "height" }, { "waitforblockheight", 0, "height" }, { "waitforblockheight", 1, "timeout" }, diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index 251687104e..30518b4d77 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -110,6 +110,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{ "getblockfilter", "getblockhash", "getblockheader", + "getblockfrompeer", // when no peers are connected, no p2p message is sent "getblockstats", "getblocktemplate", "getchaintips", diff --git a/src/validation.cpp b/src/validation.cpp index 6ed5e7a548..33ce3ffcbe 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3408,6 +3408,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block // This requires some new chain data structure to efficiently look up if a // block is in a chain leading to a candidate for best tip, despite not // being such a candidate itself. + // Note that this would break the getblockfrompeer RPC // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. |