| 1586 | }; |
| 1587 | |
| 1588 | static RPCMethod getchaintips() |
| 1589 | { |
| 1590 | return RPCMethod{"getchaintips", |
| 1591 | "Return information about all known tips in the block tree," |
| 1592 | " including the main chain as well as orphaned branches.\n", |
| 1593 | {}, |
| 1594 | RPCResult{ |
| 1595 | RPCResult::Type::ARR, "", "", |
| 1596 | {{RPCResult::Type::OBJ, "", "", |
| 1597 | { |
| 1598 | {RPCResult::Type::NUM, "height", "height of the chain tip"}, |
| 1599 | {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"}, |
| 1600 | {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"}, |
| 1601 | {RPCResult::Type::STR, "status", "status of the chain, \"active\" for the main chain\n" |
| 1602 | "Possible values for status:\n" |
| 1603 | "1. \"invalid\" This branch contains at least one invalid block\n" |
| 1604 | "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" |
| 1605 | "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" |
| 1606 | "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" |
| 1607 | "5. \"active\" This is the tip of the active main chain, which is certainly valid"}, |
| 1608 | }}}}, |
| 1609 | RPCExamples{ |
| 1610 | HelpExampleCli("getchaintips", "") |
| 1611 | + HelpExampleRpc("getchaintips", "") |
| 1612 | }, |
| 1613 | [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue |
| 1614 | { |
| 1615 | ChainstateManager& chainman = EnsureAnyChainman(request.context); |
| 1616 | LOCK(cs_main); |
| 1617 | CChain& active_chain = chainman.ActiveChain(); |
| 1618 | |
| 1619 | /* |
| 1620 | * Idea: The set of chain tips is the active chain tip, plus orphan blocks which do not have another orphan building off of them. |
| 1621 | * Algorithm: |
| 1622 | * - Make one pass through BlockIndex(), picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers. |
| 1623 | * - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip. |
| 1624 | * - Add the active chain tip |
| 1625 | */ |
| 1626 | std::set<const CBlockIndex*, CompareBlocksByHeight> setTips; |
| 1627 | std::set<const CBlockIndex*> setOrphans; |
| 1628 | std::set<const CBlockIndex*> setPrevs; |
| 1629 | |
| 1630 | for (const auto& [_, block_index] : chainman.BlockIndex()) { |
| 1631 | if (!active_chain.Contains(block_index)) { |
| 1632 | setOrphans.insert(&block_index); |
| 1633 | setPrevs.insert(block_index.pprev); |
| 1634 | } |
| 1635 | } |
| 1636 | |
| 1637 | for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) { |
| 1638 | if (setPrevs.erase(*it) == 0) { |
| 1639 | setTips.insert(*it); |
| 1640 | } |
| 1641 | } |
| 1642 | |
| 1643 | // Always report the currently active tip. |
| 1644 | setTips.insert(active_chain.Tip()); |
| 1645 |
nothing calls this directly
no test coverage detected