kernel: expose btck_block_tree_entry_get_ancestor #34885

pull pzafonte wants to merge 1 commits into bitcoin:master from pzafonte:kernel-api-block-tree-ancestor changing 4 files +29 −0
  1. pzafonte commented at 7:25 PM on March 20, 2026: none

    Callers building a block locator from a stale tip currently have to walk back one entry at a time using btck_block_tree_entry_get_previous. For large step sizes this means hundreds of C API calls per locator entry.

    This exposes GetAncestor via btck_block_tree_entry_get_ancestor, which uses the existing CBlockIndex skiplist to reach any ancestor height in O(log N) and works on any chain, not just the active chain.

    Includes a C++ convenience wrapper and tests covering same-height, genesis, and out-of-range cases in btck_block_tree_entry_tests.

    See previous discussion here #34843

  2. DrahtBot added the label Validation on Mar 20, 2026
  3. DrahtBot commented at 7:25 PM on March 20, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK stringintech, musaHaruna
    Approach ACK sedited, stickies-v

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    No conflicts as of last run.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. sedited commented at 1:18 PM on March 23, 2026: contributor

    Approach ACK

  5. in src/kernel/bitcoinkernel.h:1017 in 68273fc963
    1012 | + * @brief Return the ancestor of a block tree entry at the given height.
    1013 | + *
    1014 | + * @param[in] block_tree_entry Non-null.
    1015 | + * @param[in] height           The height of the requested ancestor. Must be between 0 and the
    1016 | + *                             height of block_tree_entry, inclusive.
    1017 | + * @return                     The ancestor at the given height, or null if height is out of range.
    


    stickies-v commented at 3:15 PM on March 23, 2026:

    It seems like passing a height < 0 or > block_tree_entry's height is a programming error, which in this interface we typically handle with assert rather than making it part of the interface (i.e. with nullptr). I'm aware btck_chain_get_by_height does the same thing, but I'm wondering if we should update both? That would change the wrapper signature to (a throwing) BlockTreeEntry BlockTreeEntry::GetAncestor(int32_t height) @sedited as the author of btck_chain_get_by_height, is there a reason we shouldn't use assert here?


    maflcko commented at 3:39 PM on March 23, 2026:

    @sedited as the author of btck_chain_get_by_height, is there a reason we shouldn't use assert here?

    I'd presume that the caller can not ensure that height really does still exist when the call is done (there could be a massive, but unlikely re-org on the main chain?) Whereas in this case here, the chain is fixed to be the one referred to be by the passed block_tree_entry, which has a constant and known height?


    sedited commented at 4:22 PM on March 23, 2026:

    What @maflcko said :)


    stickies-v commented at 4:36 PM on March 23, 2026:

    Oh yes of course, btck_Chain is the active chain, I forgot about that. So in that case I agree we should leave btck_chain_get_by_height as-is, but update btck_block_tree_entry_get_ancestor to have the correct height as a precondition.


    pzafonte commented at 5:18 PM on March 24, 2026:

    Addressed in b10b85f

  6. stickies-v commented at 3:16 PM on March 23, 2026: contributor

    Approach ACK

  7. pzafonte force-pushed on Mar 24, 2026
  8. in src/kernel/bitcoinkernel.cpp:893 in b10b85f267
     884 | @@ -895,6 +885,13 @@ const btck_BlockTreeEntry* btck_block_tree_entry_get_previous(const btck_BlockTr
     885 |      return btck_BlockTreeEntry::ref(btck_BlockTreeEntry::get(entry).pprev);
     886 |  }
     887 |  
     888 | +const btck_BlockTreeEntry* btck_block_tree_entry_get_ancestor(const btck_BlockTreeEntry* block_tree_entry, int32_t height)
     889 | +{
     890 | +    const auto& entry{btck_BlockTreeEntry::get(block_tree_entry)};
     891 | +    assert(height >= 0 && height <= entry.nHeight);
     892 | +    return btck_BlockTreeEntry::ref(entry.GetAncestor(height));
     893 | +}
    


    stringintech commented at 5:00 PM on March 30, 2026:

    Since we have the same check in GetAncestor, maybe we can avoid duplication and have this assertion instead?

    const btck_BlockTreeEntry* btck_block_tree_entry_get_ancestor(const btck_BlockTreeEntry* block_tree_entry, int height)
    {
        const auto* ancestor{btck_BlockTreeEntry::get(block_tree_entry).GetAncestor(height)};
        assert(ancestor);
        return btck_BlockTreeEntry::ref(ancestor);
    }
    

    pzafonte commented at 9:10 PM on March 30, 2026:

    Agreed. Done.

  9. in src/kernel/bitcoinkernel.h:1019 in b10b85f267 outdated
    1014 | + * @param[in] block_tree_entry Non-null.
    1015 | + * @param[in] height           The height of the requested ancestor.
    1016 | + * @return                     The ancestor at the given height.
    1017 | + * @pre height >= 0 && height <= btck_block_tree_entry_get_height(block_tree_entry)
    1018 | + */
    1019 | +BITCOINKERNEL_API const btck_BlockTreeEntry* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_tree_entry_get_ancestor(
    


    stringintech commented at 5:06 PM on March 30, 2026:

    This is the first time we are using the @pre tag in the documentation while there are other functions that might hit an assertion on out-of-bounds input. Not sure but maybe it is better to drop it to stay consistent with the rest of the documentation.


    pzafonte commented at 9:11 PM on March 30, 2026:

    Done.

  10. in src/kernel/bitcoinkernel_wrapper.h:891 in b10b85f267
     886 | +    std::optional<BlockTreeEntry> GetAncestor(int32_t height) const
     887 | +    {
     888 | +        auto entry{btck_block_tree_entry_get_ancestor(get(), height)};
     889 | +        if (!entry) return std::nullopt;
     890 | +        return entry;
     891 | +    }
    


    stringintech commented at 5:13 PM on March 30, 2026:

    Since for an out-of-bounds height we would hit an assertion in btck_block_tree_entry_get_ancestor, we wouldn't have a nullptr entry to return nulloptfor; so we could simply return a BlockTreeEntry?

        BlockTreeEntry GetAncestor(int height) const
        {
            return BlockTreeEntry{btck_block_tree_entry_get_ancestor(get(), height)};
        }
    

    pzafonte commented at 9:18 PM on March 30, 2026:

    Done.

  11. in src/test/kernel/test_kernel.cpp:1181 in b10b85f267
    1176 | @@ -1180,3 +1177,34 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
    1177 |      fs::remove_all(test_directory.m_directory / "blocks" / "rev00000.dat");
    1178 |      BOOST_CHECK_THROW(chainman->ReadBlockSpentOutputs(tip), std::runtime_error);
    1179 |  }
    1180 | +
    1181 | +BOOST_AUTO_TEST_CASE(btck_block_tree_entry_ancestor_tests)
    


    stringintech commented at 5:36 PM on March 30, 2026:

    I think the new tests belong to the existing test case btck_block_tree_entry_tests:

    <details><summary>Diff</summary>

    diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
    index 49c7f4a6e4..5399fef761 100644
    --- a/src/test/kernel/test_kernel.cpp
    +++ b/src/test/kernel/test_kernel.cpp
    @@ -973,6 +973,11 @@ BOOST_AUTO_TEST_CASE(btck_block_tree_entry_tests)
         auto prev{entry_1.GetPrevious()};
         BOOST_CHECK(prev.has_value());
         BOOST_CHECK(prev.value() == entry_0);
    +
    +    // Test GetAncestor
    +    BOOST_CHECK(entry_2.GetAncestor(2) == entry_2);
    +    BOOST_CHECK(entry_2.GetAncestor(1) == entry_1);
    +    BOOST_CHECK(entry_2.GetAncestor(0) == entry_0);
     }
     
     BOOST_AUTO_TEST_CASE(btck_chainman_in_memory_tests)
    @@ -1178,33 +1183,3 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
         BOOST_CHECK_THROW(chainman->ReadBlockSpentOutputs(tip), std::runtime_error);
     }
     
    -BOOST_AUTO_TEST_CASE(btck_block_tree_entry_ancestor_tests)
    -{
    -    auto test_directory{TestDirectory{"ancestor_tests"}};
    -    auto notifications{std::make_shared<TestKernelNotifications>()};
    -    auto context{create_context(notifications, ChainType::REGTEST)};
    -    auto chainman{create_chainman(
    -        test_directory, /*reindex=*/false, /*wipe_chainstate=*/false,
    -        /*block_tree_db_in_memory=*/true, /*chainstate_db_in_memory=*/true,
    -        context)};
    -    for (const auto& raw_block : REGTEST_BLOCK_DATA) {
    -        Block block{hex_string_to_byte_vec(raw_block)};
    -        bool new_block{false};
    -        BOOST_CHECK(chainman->ProcessBlock(block, &new_block));
    -        BOOST_CHECK(new_block);
    -    }
    -
    -    auto tip{chainman->GetBestEntry()};
    -    int32_t tip_height{tip.GetHeight()};
    -
    -    // GetAncestor at the tip's own height returns the tip.
    -    auto same{tip.GetAncestor(tip_height)};
    -    BOOST_REQUIRE(same.has_value());
    -    BOOST_CHECK(same.value() == tip);
    -
    -    // GetAncestor at height 0 returns the genesis block.
    -    auto genesis{tip.GetAncestor(0)};
    -    BOOST_REQUIRE(genesis.has_value());
    -    BOOST_CHECK_EQUAL(genesis->GetHeight(), 0);
    -
    -}
    
    

    </details>


    pzafonte commented at 9:19 PM on March 30, 2026:

    Done, moved into btck_block_tree_entry_tests. Thanks!

  12. stringintech commented at 5:41 PM on March 30, 2026: contributor

    Concept ACK.

  13. pzafonte force-pushed on Mar 30, 2026
  14. apoelstra referenced this in commit 0b7ef1159d on Apr 2, 2026
  15. in src/kernel/bitcoinkernel.h:1012 in 23074e0f65
    1007 | @@ -1017,6 +1008,17 @@ BITCOINKERNEL_API const btck_BlockHash* BITCOINKERNEL_WARN_UNUSED_RESULT btck_bl
    1008 |  BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_tree_entry_equals(
    1009 |      const btck_BlockTreeEntry* entry1, const btck_BlockTreeEntry* entry2) BITCOINKERNEL_ARG_NONNULL(1, 2);
    1010 |  
    1011 | +/**
    1012 | + * @brief Return the ancestor of a block tree entry at the given height.
    


    alexanderwiederin commented at 1:10 PM on April 10, 2026:
     * [@brief](/bitcoin-bitcoin/contributor/brief/) Return the ancestor of a btck_BlockTreeEntry at the given height.
    

    pzafonte commented at 10:03 PM on April 10, 2026:

    Done.

  16. alexanderwiederin commented at 1:19 PM on April 10, 2026: contributor

    I remember a discussion about functionality to iterate through non-active chains with an array-like interface. Was this something we would implement in the wrappers or in the C API? @stickies-v

  17. stickies-v commented at 6:44 PM on April 10, 2026: contributor

    I remember a discussion about functionality to iterate through non-active chains with an array-like interface. Was this something we would implement in the wrappers or in the C API?

    That would be in the C API. It would indeed supersede this function, but I don't know if anyone's currently working on it, and it would require a larger overhaul/rename of the interface anyway so I don't think this PR should be blocked on that discussion.

  18. pzafonte force-pushed on Apr 10, 2026
  19. purpleKarrot commented at 6:06 AM on April 11, 2026: contributor

    It would indeed supersede this function, but I don't know if anyone's currently working on it, and it would require a larger overhaul/rename of the interface

    I am working on it. And yes, this is quite an overhaul of the API. It is still in the design phase, but I can share so much:

    The "chain" class will be what is known as a view in C++ and it will represent one distinct path in the tree of blocks, not limited to the longest path.

    There will be no more "block_tree_entry". The fact that blocks are handled as a tree internally is not exposed. Clients will only interact with individual chains. The iterator concept of the chain will be random-acess and its value_type will be something that gives access to both a block and the spent coins.

    The iterator concept of the chain being random-acess addresses the same issue as this PR.

  20. in src/kernel/bitcoinkernel.h:1020 in f09558e7fc outdated
    1015 | + * @param[in] height           The height of the requested ancestor.
    1016 | + * @return                     The ancestor at the given height.
    1017 | + */
    1018 | +BITCOINKERNEL_API const btck_BlockTreeEntry* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_tree_entry_get_ancestor(
    1019 | +    const btck_BlockTreeEntry* block_tree_entry,
    1020 | +    int32_t height) BITCOINKERNEL_ARG_NONNULL(1);
    


    alexanderwiederin commented at 7:58 AM on April 16, 2026:

    I noticed we use int32_t in the header but int in bitcoinkernel.cpp (introduced with #34885 (review)). That mismatch seems to be repeated in the existing API (e.g. btck_chain_get_height).

    Looking at the history of (https://github.com/bitcoin/bitcoin/pull/30595/#discussion_r2404583482), the mismatch in btck_chain_get_height appears to be an incomplete fix.

    Could all height-related functions (btck_chain_get_height, btck_chain_get_by_height, and this new function) not be aligned to int32_t?

    Or is that deliberate? @sedited, @stringintech


    stringintech commented at 1:04 PM on April 16, 2026:

    Oh actually the int in my earlier comment was included unintentionally. I also think the header and the impl should match signatures and both use int32_t. AFAIK we use fixed-width types for data values like the heights (regardless whether the internal core code uses fixed-width types or plain int), and int/unsigned int for boolean-like values and flags.


    pzafonte commented at 1:31 AM on April 17, 2026:

    I see. I think they should match too, but also saw btck_chain_get_height and wasn't sure if it was a purposeful design choice.


    alexanderwiederin commented at 9:16 AM on April 17, 2026:

    I agree. int32_t is unambiguous and self-documenting. I have opened a PR for the other functions. #35096

    Let's see what the others say. Maybe we are missing something.


    alexanderwiederin commented at 9:23 AM on April 17, 2026:

    In the meantime, I would recommend aligning the types on this PR too @pzafonte.

  21. musaHaruna commented at 12:10 PM on April 17, 2026: contributor

    Concept ACK. I’ve read through the discussion in this PR and the previously closed #34843, including the concerns about whether block locator construction belongs in the kernel boundary or higher-level p2p logic.

    From my understanding, the current change improves efficiency by exposing btck_block_tree_entry_get_ancestor, which allows callers to leverage the existing skip list instead of performing repeated get_previous traversal when building locators. This reduces API call overhead, especially when starting from stale tips or deep histories.

    I also think the design direction of exposing a low-level primitive rather than a full locator abstraction in the kernel makes sense. This keeps the kernel interface minimal and composable, while allowing higher-level layers (like p2p code) to define their own locators.

    I’m still familiarizing myself with the kernel project, so my review is mostly at the conceptual level, but the approach here looks reasonable and consistent with existing design patterns.

  22. kernel: expose btck_block_tree_entry_get_ancestor
    Allows callers to jump to any ancestor of a block tree entry by height
    using the skiplist-backed GetAncestor, which runs in O(log N) rather
    than walking back one entry at a time with btck_block_tree_entry_get_previous.
    
    Includes a C++ convenience wrapper and tests in btck_block_tree_entry_tests.
    df44afdc98
  23. pzafonte force-pushed on Apr 17, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-21 09:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me