Skip to content

Commit f601326

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#20295: rpc: getblockfrompeer
dce8c4c rpc: getblockfrompeer (Sjors Provoost) b884aba rpc: move Ensure* helpers to server_util.h (Sjors Provoost) Pull request description: This adds an RPC method to fetch a block directly from a peer. This can used to fetch stale blocks with lower proof of work that are normally ignored by the node (`headers-only` in `getchaintips`). Usage: ``` bitcoin-cli getblockfrompeer HASH peer_n ``` Closes #20155 Limitations: * you have to specify which peer to fetch the block from * the node must already have the header ACKs for top commit: jnewbery: ACK dce8c4c fjahr: re-ACK dce8c4c Tree-SHA512: 843ba2b7a308f640770d624d0aa3265fdc5c6ea48e8db32269b96a082b7420f7953d1d8d1ef2e6529392c7172dded9d15639fbc9c24e7bfa5cfb79e13a5498c8
2 parents 84d921e + dce8c4c commit f601326

18 files changed

+296
-93
lines changed

src/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,12 @@ BITCOIN_CORE_H = \
200200
rpc/blockchain.h \
201201
rpc/client.h \
202202
rpc/mining.h \
203-
rpc/net.h \
204203
rpc/protocol.h \
205204
rpc/rawtransaction_util.h \
206205
rpc/register.h \
207206
rpc/request.h \
208207
rpc/server.h \
208+
rpc/server_util.h \
209209
rpc/util.h \
210210
scheduler.h \
211211
script/descriptor.h \
@@ -361,6 +361,7 @@ libbitcoin_server_a_SOURCES = \
361361
rpc/net.cpp \
362362
rpc/rawtransaction.cpp \
363363
rpc/server.cpp \
364+
rpc/server_util.cpp \
364365
script/sigcache.cpp \
365366
shutdown.cpp \
366367
signet.cpp \

src/net_processing.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ class PeerManagerImpl final : public PeerManager
312312
/** Implement PeerManager */
313313
void StartScheduledTasks(CScheduler& scheduler) override;
314314
void CheckForStaleTipAndEvictPeers() override;
315+
bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index) override;
315316
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override;
316317
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
317318
void SendPings() override;
@@ -1427,6 +1428,41 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
14271428
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
14281429
}
14291430

1431+
bool PeerManagerImpl::FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index)
1432+
{
1433+
if (fImporting || fReindex) return false;
1434+
1435+
LOCK(cs_main);
1436+
// Ensure this peer exists and hasn't been disconnected
1437+
CNodeState* state = State(id);
1438+
if (state == nullptr) return false;
1439+
// Ignore pre-segwit peers
1440+
if (!state->fHaveWitness) return false;
1441+
1442+
// Mark block as in-flight unless it already is
1443+
if (!BlockRequested(id, index)) return false;
1444+
1445+
// Construct message to request the block
1446+
std::vector<CInv> invs{CInv(MSG_BLOCK | MSG_WITNESS_FLAG, hash)};
1447+
1448+
// Send block request message to the peer
1449+
bool success = m_connman.ForNode(id, [this, &invs](CNode* node) {
1450+
const CNetMsgMaker msgMaker(node->GetCommonVersion());
1451+
this->m_connman.PushMessage(node, msgMaker.Make(NetMsgType::GETDATA, invs));
1452+
return true;
1453+
});
1454+
1455+
if (success) {
1456+
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
1457+
hash.ToString(), id);
1458+
} else {
1459+
RemoveBlockRequest(hash);
1460+
LogPrint(BCLog::NET, "Failed to request block %s from peer=%d\n",
1461+
hash.ToString(), id);
1462+
}
1463+
return success;
1464+
}
1465+
14301466
std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
14311467
BanMan* banman, ChainstateManager& chainman,
14321468
CTxMemPool& pool, bool ignore_incoming_txs)

src/net_processing.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
4242
CTxMemPool& pool, bool ignore_incoming_txs);
4343
virtual ~PeerManager() { }
4444

45+
/**
46+
* Attempt to manually fetch block from a given peer. We must already have the header.
47+
*
48+
* @param[in] id The peer id
49+
* @param[in] hash The block hash
50+
* @param[in] pindex The blockindex
51+
* @returns Whether a request was successfully made
52+
*/
53+
virtual bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& pindex) = 0;
54+
4555
/** Begin running background tasks, should only be called once */
4656
virtual void StartScheduledTasks(CScheduler& scheduler) = 0;
4757

src/rest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <rpc/blockchain.h>
1616
#include <rpc/protocol.h>
1717
#include <rpc/server.h>
18+
#include <rpc/server_util.h>
1819
#include <streams.h>
1920
#include <sync.h>
2021
#include <txmempool.h>

src/rpc/blockchain.cpp

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <hash.h>
2020
#include <index/blockfilterindex.h>
2121
#include <index/coinstatsindex.h>
22+
#include <net.h>
23+
#include <net_processing.h>
2224
#include <node/blockstorage.h>
2325
#include <logging/timer.h>
2426
#include <node/coinstats.h>
@@ -30,6 +32,7 @@
3032
#include <policy/rbf.h>
3133
#include <primitives/transaction.h>
3234
#include <rpc/server.h>
35+
#include <rpc/server_util.h>
3336
#include <rpc/util.h>
3437
#include <script/descriptor.h>
3538
#include <streams.h>
@@ -39,7 +42,6 @@
3942
#include <undo.h>
4043
#include <util/strencodings.h>
4144
#include <util/string.h>
42-
#include <util/system.h>
4345
#include <util/translation.h>
4446
#include <validation.h>
4547
#include <validationinterface.h>
@@ -64,54 +66,6 @@ static Mutex cs_blockchange;
6466
static std::condition_variable cond_blockchange;
6567
static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
6668

67-
NodeContext& EnsureAnyNodeContext(const std::any& context)
68-
{
69-
auto node_context = util::AnyPtr<NodeContext>(context);
70-
if (!node_context) {
71-
throw JSONRPCError(RPC_INTERNAL_ERROR, "Node context not found");
72-
}
73-
return *node_context;
74-
}
75-
76-
CTxMemPool& EnsureMemPool(const NodeContext& node)
77-
{
78-
if (!node.mempool) {
79-
throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found");
80-
}
81-
return *node.mempool;
82-
}
83-
84-
CTxMemPool& EnsureAnyMemPool(const std::any& context)
85-
{
86-
return EnsureMemPool(EnsureAnyNodeContext(context));
87-
}
88-
89-
ChainstateManager& EnsureChainman(const NodeContext& node)
90-
{
91-
if (!node.chainman) {
92-
throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found");
93-
}
94-
return *node.chainman;
95-
}
96-
97-
ChainstateManager& EnsureAnyChainman(const std::any& context)
98-
{
99-
return EnsureChainman(EnsureAnyNodeContext(context));
100-
}
101-
102-
CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node)
103-
{
104-
if (!node.fee_estimator) {
105-
throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled");
106-
}
107-
return *node.fee_estimator;
108-
}
109-
110-
CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context)
111-
{
112-
return EnsureFeeEstimator(EnsureAnyNodeContext(context));
113-
}
114-
11569
/* Calculate the difficulty for a given block index.
11670
*/
11771
double GetDifficulty(const CBlockIndex* blockindex)
@@ -821,6 +775,58 @@ static RPCHelpMan getmempoolentry()
821775
};
822776
}
823777

778+
static RPCHelpMan getblockfrompeer()
779+
{
780+
return RPCHelpMan{"getblockfrompeer",
781+
"\nAttempt to fetch block from a given peer.\n"
782+
"\nWe must have the header for this block, e.g. using submitheader.\n"
783+
"\nReturns {} if a block-request was successfully scheduled\n",
784+
{
785+
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
786+
{"nodeid", RPCArg::Type::NUM, RPCArg::Optional::NO, "The node ID (see getpeerinfo for node IDs)"},
787+
},
788+
RPCResult{RPCResult::Type::OBJ, "", "",
789+
{
790+
{RPCResult::Type::STR, "warnings", "any warnings"}
791+
}},
792+
RPCExamples{
793+
HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
794+
+ HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
795+
},
796+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
797+
{
798+
const NodeContext& node = EnsureAnyNodeContext(request.context);
799+
ChainstateManager& chainman = EnsureChainman(node);
800+
PeerManager& peerman = EnsurePeerman(node);
801+
CConnman& connman = EnsureConnman(node);
802+
803+
uint256 hash(ParseHashV(request.params[0], "hash"));
804+
805+
const NodeId nodeid = static_cast<NodeId>(request.params[1].get_int64());
806+
807+
// Check that the peer with nodeid exists
808+
if (!connman.ForNode(nodeid, [](CNode* node) {return true;})) {
809+
throw JSONRPCError(RPC_MISC_ERROR, strprintf("Peer nodeid %d does not exist", nodeid));
810+
}
811+
812+
const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(hash););
813+
814+
if (!index) {
815+
throw JSONRPCError(RPC_MISC_ERROR, "Block header missing");
816+
}
817+
818+
UniValue result = UniValue::VOBJ;
819+
820+
if (index->nStatus & BLOCK_HAVE_DATA) {
821+
result.pushKV("warnings", "Block already downloaded");
822+
} else if (!peerman.FetchBlock(nodeid, hash, *index)) {
823+
throw JSONRPCError(RPC_MISC_ERROR, "Failed to fetch block from peer");
824+
}
825+
return result;
826+
},
827+
};
828+
}
829+
824830
static RPCHelpMan getblockhash()
825831
{
826832
return RPCHelpMan{"getblockhash",
@@ -2704,6 +2710,7 @@ static const CRPCCommand commands[] =
27042710
{ "blockchain", &getbestblockhash, },
27052711
{ "blockchain", &getblockcount, },
27062712
{ "blockchain", &getblock, },
2713+
{ "blockchain", &getblockfrompeer, },
27072714
{ "blockchain", &getblockhash, },
27082715
{ "blockchain", &getblockheader, },
27092716
{ "blockchain", &getchaintips, },

src/rpc/blockchain.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ extern RecursiveMutex cs_main;
1919

2020
class CBlock;
2121
class CBlockIndex;
22-
class CBlockPolicyEstimator;
2322
class CChainState;
2423
class CTxMemPool;
2524
class ChainstateManager;
@@ -54,14 +53,6 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
5453
/** Used by getblockstats to get feerates at different percentiles by weight */
5554
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
5655

57-
NodeContext& EnsureAnyNodeContext(const std::any& context);
58-
CTxMemPool& EnsureMemPool(const NodeContext& node);
59-
CTxMemPool& EnsureAnyMemPool(const std::any& context);
60-
ChainstateManager& EnsureChainman(const NodeContext& node);
61-
ChainstateManager& EnsureAnyChainman(const std::any& context);
62-
CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node);
63-
CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context);
64-
6556
/**
6657
* Helper to create UTXO snapshots given a chainstate and a file handle.
6758
* @return a UniValue map containing metadata about the snapshot.

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
6060
{ "getbalance", 1, "minconf" },
6161
{ "getbalance", 2, "include_watchonly" },
6262
{ "getbalance", 3, "avoid_reuse" },
63+
{ "getblockfrompeer", 1, "nodeid" },
6364
{ "getblockhash", 0, "height" },
6465
{ "waitforblockheight", 0, "height" },
6566
{ "waitforblockheight", 1, "timeout" },

src/rpc/mining.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#include <pow.h>
2121
#include <rpc/blockchain.h>
2222
#include <rpc/mining.h>
23-
#include <rpc/net.h>
2423
#include <rpc/server.h>
24+
#include <rpc/server_util.h>
2525
#include <rpc/util.h>
2626
#include <script/descriptor.h>
2727
#include <script/script.h>

src/rpc/misc.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <outputtype.h>
1717
#include <rpc/blockchain.h>
1818
#include <rpc/server.h>
19+
#include <rpc/server_util.h>
1920
#include <rpc/util.h>
2021
#include <scheduler.h>
2122
#include <script/descriptor.h>

src/rpc/net.cpp

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include <chainparams.h>
1010
#include <clientversion.h>
1111
#include <core_io.h>
12-
#include <net.h>
1312
#include <net_permissions.h>
1413
#include <net_processing.h>
1514
#include <net_types.h> // For banmap_t
@@ -18,12 +17,12 @@
1817
#include <policy/settings.h>
1918
#include <rpc/blockchain.h>
2019
#include <rpc/protocol.h>
20+
#include <rpc/server_util.h>
2121
#include <rpc/util.h>
2222
#include <sync.h>
2323
#include <timedata.h>
2424
#include <util/strencodings.h>
2525
#include <util/string.h>
26-
#include <util/system.h>
2726
#include <util/translation.h>
2827
#include <validation.h>
2928
#include <version.h>
@@ -42,22 +41,6 @@ const std::vector<std::string> CONNECTION_TYPE_DOC{
4241
"feeler (short-lived automatic connection for testing addresses)"
4342
};
4443

45-
CConnman& EnsureConnman(const NodeContext& node)
46-
{
47-
if (!node.connman) {
48-
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
49-
}
50-
return *node.connman;
51-
}
52-
53-
PeerManager& EnsurePeerman(const NodeContext& node)
54-
{
55-
if (!node.peerman) {
56-
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
57-
}
58-
return *node.peerman;
59-
}
60-
6144
static RPCHelpMan getconnectioncount()
6245
{
6346
return RPCHelpMan{"getconnectioncount",

0 commit comments

Comments
 (0)