diff options
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/net_processing.cpp | 36 | ||||
-rw-r--r-- | src/net_processing.h | 10 | ||||
-rw-r--r-- | src/rest.cpp | 1 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 105 | ||||
-rw-r--r-- | src/rpc/blockchain.h | 9 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 2 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 1 | ||||
-rw-r--r-- | src/rpc/net.cpp | 19 | ||||
-rw-r--r-- | src/rpc/net.h | 15 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 1 | ||||
-rw-r--r-- | src/rpc/server_util.cpp | 80 | ||||
-rw-r--r-- | src/rpc/server_util.h | 27 | ||||
-rw-r--r-- | src/test/fuzz/rpc.cpp | 1 | ||||
-rw-r--r-- | src/validation.cpp | 1 | ||||
-rwxr-xr-x | test/functional/rpc_getblockfrompeer.py | 76 | ||||
-rwxr-xr-x | test/functional/test_runner.py | 1 |
18 files changed, 296 insertions, 93 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 36da079674..654b322d33 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -200,12 +200,12 @@ BITCOIN_CORE_H = \ rpc/blockchain.h \ rpc/client.h \ rpc/mining.h \ - rpc/net.h \ rpc/protocol.h \ rpc/rawtransaction_util.h \ rpc/register.h \ rpc/request.h \ rpc/server.h \ + rpc/server_util.h \ rpc/util.h \ scheduler.h \ script/descriptor.h \ @@ -361,6 +361,7 @@ libbitcoin_server_a_SOURCES = \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ + rpc/server_util.cpp \ script/sigcache.cpp \ shutdown.cpp \ signet.cpp \ diff --git a/src/net_processing.cpp b/src/net_processing.cpp index f352246b0d..e4b8e9b6a9 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/rest.cpp b/src/rest.cpp index 3746fd752a..2c0ead26c7 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -15,6 +15,7 @@ #include <rpc/blockchain.h> #include <rpc/protocol.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <streams.h> #include <sync.h> #include <txmempool.h> diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index df58052016..9427a9fc59 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -19,6 +19,8 @@ #include <hash.h> #include <index/blockfilterindex.h> #include <index/coinstatsindex.h> +#include <net.h> +#include <net_processing.h> #include <node/blockstorage.h> #include <logging/timer.h> #include <node/coinstats.h> @@ -30,6 +32,7 @@ #include <policy/rbf.h> #include <primitives/transaction.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <script/descriptor.h> #include <streams.h> @@ -39,7 +42,6 @@ #include <undo.h> #include <util/strencodings.h> #include <util/string.h> -#include <util/system.h> #include <util/translation.h> #include <validation.h> #include <validationinterface.h> @@ -64,54 +66,6 @@ static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange); -NodeContext& EnsureAnyNodeContext(const std::any& context) -{ - auto node_context = util::AnyPtr<NodeContext>(context); - if (!node_context) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Node context not found"); - } - return *node_context; -} - -CTxMemPool& EnsureMemPool(const NodeContext& node) -{ - if (!node.mempool) { - throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); - } - return *node.mempool; -} - -CTxMemPool& EnsureAnyMemPool(const std::any& context) -{ - return EnsureMemPool(EnsureAnyNodeContext(context)); -} - -ChainstateManager& EnsureChainman(const NodeContext& node) -{ - if (!node.chainman) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found"); - } - return *node.chainman; -} - -ChainstateManager& EnsureAnyChainman(const std::any& context) -{ - return EnsureChainman(EnsureAnyNodeContext(context)); -} - -CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node) -{ - if (!node.fee_estimator) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled"); - } - return *node.fee_estimator; -} - -CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context) -{ - return EnsureFeeEstimator(EnsureAnyNodeContext(context)); -} - /* Calculate the difficulty for a given block index. */ double GetDifficulty(const CBlockIndex* blockindex) @@ -821,6 +775,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", @@ -2704,6 +2710,7 @@ static const CRPCCommand commands[] = { "blockchain", &getbestblockhash, }, { "blockchain", &getblockcount, }, { "blockchain", &getblock, }, + { "blockchain", &getblockfrompeer, }, { "blockchain", &getblockhash, }, { "blockchain", &getblockheader, }, { "blockchain", &getchaintips, }, diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 0ad68a7fd6..99e744fdb3 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -19,7 +19,6 @@ extern RecursiveMutex cs_main; class CBlock; class CBlockIndex; -class CBlockPolicyEstimator; class CChainState; class CTxMemPool; class ChainstateManager; @@ -54,14 +53,6 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex /** Used by getblockstats to get feerates at different percentiles by weight */ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight); -NodeContext& EnsureAnyNodeContext(const std::any& context); -CTxMemPool& EnsureMemPool(const NodeContext& node); -CTxMemPool& EnsureAnyMemPool(const std::any& context); -ChainstateManager& EnsureChainman(const NodeContext& node); -ChainstateManager& EnsureAnyChainman(const std::any& context); -CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node); -CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context); - /** * Helper to create UTXO snapshots given a chainstate and a file handle. * @return a UniValue map containing metadata about the snapshot. diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c0c9e4f005..a171972da3 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -60,6 +60,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/rpc/mining.cpp b/src/rpc/mining.cpp index 83eab32779..371de8050d 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -20,8 +20,8 @@ #include <pow.h> #include <rpc/blockchain.h> #include <rpc/mining.h> -#include <rpc/net.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <script/descriptor.h> #include <script/script.h> diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 2c05fc39fd..3716d58d4b 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -16,6 +16,7 @@ #include <outputtype.h> #include <rpc/blockchain.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <scheduler.h> #include <script/descriptor.h> diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 021e6ae320..bdcb339e14 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -9,7 +9,6 @@ #include <chainparams.h> #include <clientversion.h> #include <core_io.h> -#include <net.h> #include <net_permissions.h> #include <net_processing.h> #include <net_types.h> // For banmap_t @@ -18,12 +17,12 @@ #include <policy/settings.h> #include <rpc/blockchain.h> #include <rpc/protocol.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <sync.h> #include <timedata.h> #include <util/strencodings.h> #include <util/string.h> -#include <util/system.h> #include <util/translation.h> #include <validation.h> #include <version.h> @@ -42,22 +41,6 @@ const std::vector<std::string> CONNECTION_TYPE_DOC{ "feeler (short-lived automatic connection for testing addresses)" }; -CConnman& EnsureConnman(const NodeContext& node) -{ - if (!node.connman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - } - return *node.connman; -} - -PeerManager& EnsurePeerman(const NodeContext& node) -{ - if (!node.peerman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - } - return *node.peerman; -} - static RPCHelpMan getconnectioncount() { return RPCHelpMan{"getconnectioncount", diff --git a/src/rpc/net.h b/src/rpc/net.h deleted file mode 100644 index 7a4d8440ba..0000000000 --- a/src/rpc/net.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2021 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_RPC_NET_H -#define BITCOIN_RPC_NET_H - -class CConnman; -class PeerManager; -struct NodeContext; - -CConnman& EnsureConnman(const NodeContext& node); -PeerManager& EnsurePeerman(const NodeContext& node); - -#endif // BITCOIN_RPC_NET_H diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index bd56e584e1..a3583e7f12 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -25,6 +25,7 @@ #include <rpc/blockchain.h> #include <rpc/rawtransaction_util.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <script/script.h> #include <script/sign.h> diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp new file mode 100644 index 0000000000..3fc35222e1 --- /dev/null +++ b/src/rpc/server_util.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <rpc/server_util.h> + +#include <net_processing.h> +#include <node/context.h> +#include <policy/fees.h> +#include <rpc/protocol.h> +#include <rpc/request.h> +#include <txmempool.h> +#include <util/system.h> +#include <validation.h> + +#include <any> + +NodeContext& EnsureAnyNodeContext(const std::any& context) +{ + auto node_context = util::AnyPtr<NodeContext>(context); + if (!node_context) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Node context not found"); + } + return *node_context; +} + +CTxMemPool& EnsureMemPool(const NodeContext& node) +{ + if (!node.mempool) { + throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); + } + return *node.mempool; +} + +CTxMemPool& EnsureAnyMemPool(const std::any& context) +{ + return EnsureMemPool(EnsureAnyNodeContext(context)); +} + +ChainstateManager& EnsureChainman(const NodeContext& node) +{ + if (!node.chainman) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found"); + } + return *node.chainman; +} + +ChainstateManager& EnsureAnyChainman(const std::any& context) +{ + return EnsureChainman(EnsureAnyNodeContext(context)); +} + +CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node) +{ + if (!node.fee_estimator) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled"); + } + return *node.fee_estimator; +} + +CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context) +{ + return EnsureFeeEstimator(EnsureAnyNodeContext(context)); +} + +CConnman& EnsureConnman(const NodeContext& node) +{ + if (!node.connman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } + return *node.connman; +} + +PeerManager& EnsurePeerman(const NodeContext& node) +{ + if (!node.peerman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } + return *node.peerman; +} diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h new file mode 100644 index 0000000000..ad3c149c90 --- /dev/null +++ b/src/rpc/server_util.h @@ -0,0 +1,27 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_SERVER_UTIL_H +#define BITCOIN_RPC_SERVER_UTIL_H + +#include <any> + +class CBlockPolicyEstimator; +class CConnman; +class ChainstateManager; +class CTxMemPool; +struct NodeContext; +class PeerManager; + +NodeContext& EnsureAnyNodeContext(const std::any& context); +CTxMemPool& EnsureMemPool(const NodeContext& node); +CTxMemPool& EnsureAnyMemPool(const std::any& context); +ChainstateManager& EnsureChainman(const NodeContext& node); +ChainstateManager& EnsureAnyChainman(const std::any& context); +CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node); +CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context); +CConnman& EnsureConnman(const NodeContext& node); +PeerManager& EnsurePeerman(const NodeContext& node); + +#endif // BITCOIN_RPC_SERVER_UTIL_H diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index 790421d60c..44b98f7852 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -114,6 +114,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 5ab90c2f1a..7efd176e00 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3407,6 +3407,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. diff --git a/test/functional/rpc_getblockfrompeer.py b/test/functional/rpc_getblockfrompeer.py new file mode 100755 index 0000000000..e841cba70e --- /dev/null +++ b/test/functional/rpc_getblockfrompeer.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the getblockfrompeer RPC.""" + +from test_framework.authproxy import JSONRPCException +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, +) + +class GetBlockFromPeerTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + + def setup_network(self): + self.setup_nodes() + + def check_for_block(self, hash): + try: + self.nodes[0].getblock(hash) + return True + except JSONRPCException: + return False + + def run_test(self): + self.log.info("Mine 4 blocks on Node 0") + self.generate(self.nodes[0], 4, sync_fun=self.no_op) + assert_equal(self.nodes[0].getblockcount(), 204) + + self.log.info("Mine competing 3 blocks on Node 1") + self.generate(self.nodes[1], 3, sync_fun=self.no_op) + assert_equal(self.nodes[1].getblockcount(), 203) + short_tip = self.nodes[1].getbestblockhash() + + self.log.info("Connect nodes to sync headers") + self.connect_nodes(0, 1) + self.sync_blocks() + + self.log.info("Node 0 should only have the header for node 1's block 3") + for x in self.nodes[0].getchaintips(): + if x['hash'] == short_tip: + assert_equal(x['status'], "headers-only") + break + else: + raise AssertionError("short tip not synced") + assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, short_tip) + + self.log.info("Fetch block from node 1") + peers = self.nodes[0].getpeerinfo() + assert_equal(len(peers), 1) + peer_0_peer_1_id = peers[0]["id"] + + self.log.info("Arguments must be sensible") + assert_raises_rpc_error(-8, "hash must be of length 64 (not 4, for '1234')", self.nodes[0].getblockfrompeer, "1234", 0) + + self.log.info("We must already have the header") + assert_raises_rpc_error(-1, "Block header missing", self.nodes[0].getblockfrompeer, "00" * 32, 0) + + self.log.info("Non-existent peer generates error") + assert_raises_rpc_error(-1, f"Peer nodeid {peer_0_peer_1_id + 1} does not exist", self.nodes[0].getblockfrompeer, short_tip, peer_0_peer_1_id + 1) + + self.log.info("Successful fetch") + result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id) + self.wait_until(lambda: self.check_for_block(short_tip), timeout=1) + assert(not "warnings" in result) + + self.log.info("Don't fetch blocks we already have") + result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id) + assert("warnings" in result) + assert_equal(result["warnings"], "Block already downloaded") + +if __name__ == '__main__': + GetBlockFromPeerTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 8e7e791538..0a764a1755 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -217,6 +217,7 @@ BASE_SCRIPTS = [ 'wallet_txn_clone.py --mineblock', 'feature_notifications.py', 'rpc_getblockfilter.py', + 'rpc_getblockfrompeer.py', 'rpc_invalidateblock.py', 'feature_utxo_set_hash.py', 'feature_rbf.py --legacy-wallet', |