| 522 | } |
| 523 | |
| 524 | static RPCMethod getblockfrompeer() |
| 525 | { |
| 526 | return RPCMethod{ |
| 527 | "getblockfrompeer", |
| 528 | "Attempt to fetch block from a given peer.\n\n" |
| 529 | "We must have the header for this block, e.g. using submitheader.\n" |
| 530 | "The block will not have any undo data which can limit the usage of the block data in a context where the undo data is needed.\n" |
| 531 | "Subsequent calls for the same block may cause the response from the previous peer to be ignored.\n" |
| 532 | "Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n" |
| 533 | "When a peer does not respond with a block, we will disconnect.\n" |
| 534 | "Note: The block could be re-pruned as soon as it is received.\n\n" |
| 535 | "Returns an empty JSON object if the request was successfully scheduled.", |
| 536 | { |
| 537 | {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"}, |
| 538 | {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"}, |
| 539 | }, |
| 540 | RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}}, |
| 541 | RPCExamples{ |
| 542 | HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") |
| 543 | + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") |
| 544 | }, |
| 545 | [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue |
| 546 | { |
| 547 | const NodeContext& node = EnsureAnyNodeContext(request.context); |
| 548 | ChainstateManager& chainman = EnsureChainman(node); |
| 549 | PeerManager& peerman = EnsurePeerman(node); |
| 550 | |
| 551 | const uint256& block_hash{ParseHashV(request.params[0], "blockhash")}; |
| 552 | const NodeId peer_id{request.params[1].getInt<int64_t>()}; |
| 553 | |
| 554 | const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash);); |
| 555 | |
| 556 | if (!index) { |
| 557 | throw JSONRPCError(RPC_MISC_ERROR, "Block header missing"); |
| 558 | } |
| 559 | |
| 560 | // Fetching blocks before the node has syncing past their height can prevent block files from |
| 561 | // being pruned, so we avoid it if the node is in prune mode. |
| 562 | if (chainman.m_blockman.IsPruneMode() && index->nHeight > WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->nHeight)) { |
| 563 | throw JSONRPCError(RPC_MISC_ERROR, "In prune mode, only blocks that the node has already synced previously can be fetched from a peer"); |
| 564 | } |
| 565 | |
| 566 | const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA); |
| 567 | if (block_has_data) { |
| 568 | throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded"); |
| 569 | } |
| 570 | |
| 571 | if (const auto res{peerman.FetchBlock(peer_id, *index)}; !res) { |
| 572 | throw JSONRPCError(RPC_MISC_ERROR, res.error()); |
| 573 | } |
| 574 | return UniValue::VOBJ; |
| 575 | }, |
| 576 | }; |
| 577 | } |
| 578 | |
| 579 | static RPCMethod getblockhash() |
| 580 | { |
nothing calls this directly
no test coverage detected