Skip to content

Implement flyclient prover functionality#9693

Draft
Metalcape wants to merge 9 commits intoZcashFoundation:mainfrom
Metalcape:flyclient
Draft

Implement flyclient prover functionality#9693
Metalcape wants to merge 9 commits intoZcashFoundation:mainfrom
Metalcape:flyclient

Conversation

@Metalcape
Copy link
Contributor

@Metalcape Metalcape commented Jul 9, 2025

This is what I've done so far on flyclient, as discussed on discord (sorry for the delay). I'm publishing this as draft mainly for discussion and feedback on the general implementation.

Update (Nov 1 2025): Added RPC methods for retrieving authdataroot and to support the difficulty-aware Flyclient protocol.

Motivation

Zcash already supports flyclient through the history tree/chainhistoryroot commitment, but zebrad is not currently able to generate a proof that allows a sublinear client to validate the blockchain with it. This PR implements the necessary state changes that zebrad needs to generate a flyclient proof using history tree nodes, and also a new RPC method that can be used by clients to request the nodes.

Solution

  • zebra-state:
    • Change the database format to add a HistoryNode column which stores all MMR nodes, indexed by network upgrade and node index; this is to be kept consistent with the chaintip history tree
    • Add an automated database format upgrade for version 27.1.0 which generates history nodes from saved blocks
    • Add functions to read/write nodes and to rebuild a past history tree from saved nodes
    • Add history nodes to the non finalized state
    • Handle writing history nodes to the database when blocks are finalized, for both contextually verified and checkpoint verified blocks
    • Add state service requests/responses for both history nodes and history trees before chain tip
  • zebra-chain:
    • Make HistoryTree::push return the list of entries added to the tree by the push
    • Add some functions used to navigate the MMR tree in its "array" form, using node index only
  • zebra-rpc:
    • Add a new gethistorynode method to request a single MMR node from the database
    • Add the chainhistoryroot field to all getblock and getblockheader verbose responses
    • Add a new method (getauthdataroot) to download the auth data root of a block, used to validate its chainhistoryroot against the blockcommitments field.
    • Add new methods related to cumulative difficulty (gettotalwork and getfirstblockwithtotalwork) needed to implement difficulty-aware flyclient bootstrapping.

Existing tests have been updated to take these changes into account.

Tests

Testing mostly requires a full sync so I mainly tested manually using a client script that simulates the flyclient protocol bootstrap, sampling random blocks and then downloading MMR ancestry proofs for those blocks. I'm open to suggestions for automated tests.

Specifications & References

Follow-up Work

Needs a testing strategy for the new state changes and RPC methods. There is also a temporary workaround I did in the get_history_node method implementation, which can be replaced as soon as the zcash_history crate is updated with the changes I contributed in zcash/librustzcash#1809 .

PR Checklist

  • The PR name is suitable for the release notes.
  • The solution is tested.
  • The documentation is up to date.

commit 22314a9
Author: Dario Capecchi <[email protected]>
Date:   Wed Jul 9 08:42:57 2025 +0000

    Fix serialization tests

commit 1296c70
Merge: f348b06 76eeecc
Author: Dario Capecchi <[email protected]>
Date:   Wed Jul 9 10:09:13 2025 +0200

    Merge branch 'ZcashFoundation:main' into flyclient

commit f348b06
Merge: 301ef3a 70909eb
Author: Dario Capecchi <[email protected]>
Date:   Tue Jul 8 08:53:17 2025 +0000

    Merge branch 'main' into flyclient

commit 301ef3a
Author: Metalcape <[email protected]>
Date:   Fri Jul 4 14:46:42 2025 +0200

    Fix more errors

commit 7123d85
Author: Metalcape <[email protected]>
Date:   Fri Jul 4 11:10:45 2025 +0200

    Fix errors and format after merge

commit 2d25be1
Merge: c36a8b2 5bac400
Author: Metalcape <[email protected]>
Date:   Fri Jul 4 10:58:16 2025 +0200

    Merge branch 'main' into flyclient

commit c36a8b2
Merge: 5456d19 1ab3b0f
Author: Dario Capecchi <[email protected]>
Date:   Tue Jun 3 17:24:19 2025 +0200

    Merge branch 'ZcashFoundation:main' into flyclient

commit 5456d19
Merge: 205740b 806fcab
Author: Dario Capecchi <[email protected]>
Date:   Tue May 27 12:42:31 2025 +0200

    Merge branch 'ZcashFoundation:main' into flyclient

commit 205740b
Author: Metalcape <[email protected]>
Date:   Fri May 23 17:31:46 2025 +0200

    fix: add_history_nodes verification using incorrect height values

commit 9ae0669
Merge: cf6ab07 cbd1bb7
Author: Dario Capecchi <[email protected]>
Date:   Fri May 23 12:32:57 2025 +0200

    Merge branch 'ZcashFoundation:main' into flyclient

commit cf6ab07
Merge: 62baf1a a77de50
Author: Dario Capecchi <[email protected]>
Date:   Sun May 18 12:53:05 2025 +0200

    Merge branch 'ZcashFoundation:main' into flyclient

commit 62baf1a
Author: Metalcape <[email protected]>
Date:   Sat May 17 19:38:37 2025 +0200

    fix: history node finalization when height is zero

commit 95f4697
Author: Metalcape <[email protected]>
Date:   Sat May 17 19:37:30 2025 +0200

    add_history_nodes: verify update against block commitments

commit 33af2d3
Author: Metalcape <[email protected]>
Date:   Fri May 16 15:06:45 2025 +0200

    cargo fmt

commit 6aa97bc
Author: Metalcape <[email protected]>
Date:   Fri May 16 15:05:16 2025 +0200

    Update flyclient implementation
    - (fix) correctly update non-finalized state
    - store new history nodes as part of treestate
    - revert changes to db upgrade trait
    - add_history_nodes: verify tree at all heights
    - remove unused db access functions

commit 68e6ba2
Merge: de4e7ff 89f8252
Author: Metalcape <[email protected]>
Date:   Sun May 11 11:13:10 2025 +0200

    Merge branch 'main' into flyclient

commit de4e7ff
Author: Metalcape <[email protected]>
Date:   Thu May 8 14:59:31 2025 +0200

    Update Cargo.lock

commit a4bebd4
Merge: fb71684 ca51c39
Author: Metalcape <[email protected]>
Date:   Thu May 8 14:52:40 2025 +0200

    Merge branch 'main' into flyclient

commit fb71684
Author: Metalcape <[email protected]>
Date:   Thu May 8 14:50:05 2025 +0200

    - fix: history tree db request off-by-one error
    - fix: history_tree leaf/peak functions being confusing
    - cargo fmt

commit 226cf43
Merge: 7f35aaf 7cd1858
Author: Metalcape <[email protected]>
Date:   Thu May 8 13:01:52 2025 +0200

    Merge branch 'main' into flyclient

commit 7f35aaf
Author: Metalcape <[email protected]>
Date:   Sun Apr 13 16:56:30 2025 +0200

    Add RPC method to fetch history nodes

commit 8cac881
Merge: 3bcf9f3 32de8ef
Author: Metalcape <[email protected]>
Date:   Mon Mar 24 13:13:06 2025 +0100

    Merge branch 'ZcashFoundation:main' into flyclient

commit 3bcf9f3
Author: Metalcape <[email protected]>
Date:   Mon Mar 24 13:12:40 2025 +0100

    Correcly upgrade database format to store history nodes
    - Fix off-by-one error in peaks and leaves number calculation for history trees
    - Rewrite run and check functions for upgrade
    - Fix writing new nodes when committing blocks to finalized state
    - Add additional checks to `append_history_nodes`

commit 242d363
Author: Metalcape <[email protected]>
Date:   Tue Mar 18 22:10:18 2025 +0100

    Fix column family names

commit 1a7f0f5
Author: Metalcape <[email protected]>
Date:   Tue Mar 18 17:26:35 2025 +0100

    Add `chainhistoryroot` to `getblockheader` rpc

commit 6481ba2
Merge: 62cb355 619c01c
Author: Metalcape <[email protected]>
Date:   Tue Mar 18 14:47:33 2025 +0100

    Merge branch 'history-db' into flyclient

commit 619c01c
Author: Metalcape <[email protected]>
Date:   Tue Mar 18 14:39:38 2025 +0100

    clippy

commit 62cb355
Merge: be7a3b9 f0140a4
Author: Metalcape <[email protected]>
Date:   Tue Mar 18 14:38:03 2025 +0100

    Merge branch 'ZcashFoundation:main' into get_block

commit 44426b1
Merge: a821734 49741e8
Author: Metalcape <[email protected]>
Date:   Thu Mar 13 18:11:57 2025 +0100

    Merge branch 'ZcashFoundation:main' into history-db

commit be7a3b9
Merge: f235914 49741e8
Author: Metalcape <[email protected]>
Date:   Thu Mar 13 18:11:46 2025 +0100

    Merge branch 'ZcashFoundation:main' into get_block

commit a821734
Author: Metalcape <[email protected]>
Date:   Thu Mar 13 18:11:14 2025 +0100

    Upgrade database version for history nodes
    - Handle upgrade from previous version
    - Fix incorrect write to database on block commit
    - Add history tree functions to calculate leaf and node index of a block
    - Add history tree function to get the peak indexes at a certain block height
    - Update write_block function to handle history nodes

commit 7578d6c
Author: Metalcape <[email protected]>
Date:   Mon Mar 10 19:46:07 2025 +0100

    Add function to read history nodes from state

commit 3c715da
Merge: 5c36142 f873aa1
Author: Metalcape <[email protected]>
Date:   Mon Mar 10 19:06:12 2025 +0100

    Merge branch 'ZcashFoundation:main' into history-db

commit f235914
Merge: 6988d13 f873aa1
Author: Metalcape <[email protected]>
Date:   Mon Mar 10 19:06:04 2025 +0100

    Merge branch 'ZcashFoundation:main' into get_block

commit 5c36142
Author: Metalcape <[email protected]>
Date:   Wed Mar 5 18:03:34 2025 +0100

    Update history nodes:
    - When blocks are added to the non finalized chaintip;
    - When blocks are added to the finalized chaintip.

commit 4ce98e3
Merge: b49a160 de7e5b5
Author: Metalcape <[email protected]>
Date:   Tue Mar 4 16:56:51 2025 +0100

    Merge branch 'ZcashFoundation:main' into history-db

commit 6988d13
Merge: c3430cc de7e5b5
Author: Metalcape <[email protected]>
Date:   Tue Mar 4 16:56:40 2025 +0100

    Merge branch 'ZcashFoundation:main' into get_block

commit b49a160
Author: Metalcape <[email protected]>
Date:   Tue Mar 4 16:55:58 2025 +0100

    Store full history tree
    - Add database column family for history tree nodes
    - Add functions for reading and writing to finalized state
    - Add functions for tracking nodes in non finalized state
    - Modify the `push` function for history trees to return newly added nodes

commit c3430cc
Merge: b003012 29ed501
Author: Metalcape <[email protected]>
Date:   Fri Feb 28 12:01:59 2025 +0100

    Merge branch 'ZcashFoundation:main' into get_block

commit b003012
Merge: b503876 361fa65
Author: Metalcape <[email protected]>
Date:   Sun Feb 23 11:16:19 2025 +0100

    Merge branch 'ZcashFoundation:main' into get_block

commit b503876
Merge: 33502d0 5cf5178
Author: Metalcape <[email protected]>
Date:   Tue Feb 18 10:33:46 2025 +0100

    Merge remote-tracking branch 'origin/main' into get_block

commit 33502d0
Author: Metalcape <[email protected]>
Date:   Mon Feb 17 22:34:13 2025 +0100

    Added `chainhistoryroot` to `getblock`

    - Modified the GetBlock and GetBlockHeader response objects
    - Added a state ReadRequest to obtain the history tree for a given block
      or height (used for blocks from NU5 and onwards)
    - Updated tests and snapshots
@conradoplg conradoplg added the do-not-merge Tells Mergify not to merge this PR label Jul 10, 2025
@conradoplg
Copy link
Collaborator

Thank you for the great contribution! It might take a bit until we're ready to review this, but we'll get to it eventually.

@conradoplg conradoplg removed the do-not-merge Tells Mergify not to merge this PR label Jul 11, 2025
@conradoplg conradoplg added do-not-merge Tells Mergify not to merge this PR and removed do-not-merge Tells Mergify not to merge this PR labels Jul 22, 2025
@oxarbitrage oxarbitrage added do-not-merge Tells Mergify not to merge this PR and removed do-not-merge Tells Mergify not to merge this PR labels Aug 7, 2025
@oxarbitrage oxarbitrage added do-not-merge Tells Mergify not to merge this PR and removed do-not-merge Tells Mergify not to merge this PR labels Oct 14, 2025
commit e36497a
Author: Metalcape <[email protected]>
Date:   Sat Nov 1 11:13:42 2025 +0100

    Fix clippy warnings

commit 6ceb1a6
Author: Metalcape <[email protected]>
Date:   Wed Oct 29 16:18:08 2025 +0100

    Remove unnecessary RPC method

commit 0ec9223
Author: Metalcape <[email protected]>
Date:   Wed Oct 29 16:12:25 2025 +0100

    Fix height of inner tree in `history_tree_by_height`

commit 7435fa8
Author: Metalcape <[email protected]>
Date:   Sun Oct 19 12:50:14 2025 +0200

    Handle edge case in `first_block_with_total_work`

commit c1fd2ea
Author: Metalcape <[email protected]>
Date:   Wed Oct 8 11:05:15 2025 +0200

    Implement rpc methods for difficulty-aware sampling of blocks
    - `gettotalwork`
    - `getfirstblockwithtotalwork`

commit 18e107e
Author: Metalcape <[email protected]>
Date:   Sat Sep 13 13:27:30 2025 +0200

    Handle edge case for activation blocks when reading history trees

commit f6e907b
Author: Metalcape <[email protected]>
Date:   Sat Sep 13 13:27:04 2025 +0200

    Fix some terminology

commit 8222be6
Author: Metalcape <[email protected]>
Date:   Wed Sep 10 19:35:49 2025 +0200

    Serialize as hex

commit 6d12291
Author: Metalcape <[email protected]>
Date:   Wed Sep 10 19:15:24 2025 +0200

    Add RPC methods
    - `getauthdataroot` for protocol correctness
    - `getshieldedtxcount` to calculate leaf nodes from downloaded headers
    - Fixed some typos and clippy warnings
@Metalcape
Copy link
Contributor Author

Updated to include the progress we've made in the last couple of months, as of now it should be more or less complete in terms of features (bare minimum that a client needs to validate the blockchain using the protocol).

- Fix clippy warnings in db upgrade task
- Fix a bug `get_first_block_with_total_work` on pending upgrades
- Remove some hardcoded references to NU6 instead of current upgrade
@arya2 arya2 added do-not-merge Tells Mergify not to merge this PR and removed do-not-merge Tells Mergify not to merge this PR labels Nov 17, 2025
@conradoplg conradoplg added the do-not-merge Tells Mergify not to merge this PR label Jan 20, 2026
@mergify
Copy link
Contributor

mergify bot commented Jan 20, 2026

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟠 ❄️ releae 4.0.0 [Scheduled Freeze]

Waiting for scheduled freeze to end.

A freeze on the repository is scheduled for the following reason: releae 4.0.0

  • current-datetime < 2026-01-20T17:25:08[America/Sao_Paulo]

@conradoplg conradoplg removed the do-not-merge Tells Mergify not to merge this PR label Jan 21, 2026
@mpguerra mpguerra requested a review from Copilot February 16, 2026 10:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements flyclient prover functionality for zebrad, enabling the generation of proofs that allow sublinear blockchain validation using the history tree/chainhistoryroot commitment. The implementation spans multiple layers of the Zebra architecture, adding database storage for history tree nodes, new state service operations, and RPC methods to support the difficulty-aware Flyclient protocol.

Changes:

  • Added database storage for history tree MMR nodes with automated upgrade for version 27.1.0
  • Modified HistoryTree::push to return the list of entries added, enabling tracking of new nodes
  • Implemented four new RPC methods: gethistorynode, getauthdataroot, gettotalwork, and getfirstblockwithtotalwork
  • Added chainhistoryroot field to all getblock and getblockheader verbose responses

Reviewed changes

Copilot reviewed 50 out of 51 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
zebra-test/src/command.rs Style update: use format string interpolation
zebra-state/src/service/read/tree.rs Add functions to read history trees by height, history nodes, and calculate total work
zebra-state/src/service/read/find.rs Implement binary search to find first block with given total work threshold
zebra-state/src/service/read/block.rs Add function to retrieve auth data root for blocks
zebra-state/src/service/read.rs Export new read functions for history nodes and trees
zebra-state/src/service/non_finalized_state/tests/prop.rs Update test to include history_nodes_by_height field
zebra-state/src/service/non_finalized_state/chain.rs Add history node tracking to non-finalized chain state
zebra-state/src/service/finalized_state/zebra_db/shielded.rs Write history nodes when finalizing blocks
zebra-state/src/service/finalized_state/zebra_db/chain.rs Add history_node column family and methods to read/write/rebuild history trees
zebra-state/src/service/finalized_state/zebra_db/block.rs Add method to retrieve auth_data_root from blocks
zebra-state/src/service/finalized_state/disk_format/upgrade/add_history_nodes.rs Implement database upgrade to generate and store history nodes from existing blocks
zebra-state/src/service/finalized_state/disk_format/upgrade.rs Register new database format upgrade for version 27.1.0
zebra-state/src/service/finalized_state/disk_format/tests/snapshots/*.snap Update test snapshots to include new history_node column family
zebra-state/src/service/finalized_state/disk_format/chain.rs Implement serialization for NetworkUpgrade, HistoryNodeIndex, and Entry types
zebra-state/src/service/finalized_state.rs Update block finalization to capture and store new history nodes
zebra-state/src/service.rs Add request handlers for new read operations (HistoryTree, HistoryNode, AuthDataRoot, TotalWork)
zebra-state/src/response.rs Add response types for new read requests
zebra-state/src/request.rs Add request types and update Treestate to include new_history_nodes
zebra-state/src/constants.rs Bump database minor version to 27.1
zebra-rpc/tests/vectors/getblock_response_1.json Add chainhistoryroot field to test vector
zebra-rpc/tests/serialization_tests.rs Update tests to verify chainhistoryroot field in responses
zebra-rpc/src/methods/tests/vectors.rs Update test helper and assertions to handle chainhistoryroot
zebra-rpc/src/methods/tests/snapshots/*.snap Update all snapshot tests to include chainhistoryroot field
zebra-rpc/src/methods.rs Implement four new RPC methods and add chainhistoryroot to block/header responses
zebra-rpc/Cargo.toml Add dependencies for zcash_history and bytemuck
zebra-network/proptest-regressions/peer_set/set/tests/prop.txt Add proptest regression seed
zebra-chain/src/primitives/zcash_history.rs Add Entry::inner(), From<&Vec> for Entry, HistoryNodeIndex, and root_node_data() method
zebra-chain/src/history_tree.rs Modify push() to return entries, add MMR navigation functions and HistoryNodeData type

Comment on lines +728 to +730
// Binary search over subtree_total_work within this upgrade
while start_height != end_height {
let middle = Height(((start_height.as_usize() + end_height.as_usize()) / 2) as u32);
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The binary search implementation has a potential integer overflow issue. On line 730, (start_height.as_usize() + end_height.as_usize()) / 2 could overflow if both heights are large (close to u32::MAX). While this is unlikely in practice for block heights, it's a pattern that should be avoided.

Consider using the overflow-safe pattern: start_height.as_usize() + (end_height.as_usize() - start_height.as_usize()) / 2, or verify that this overflow cannot occur with a comment explaining the invariant.

Copilot uses AI. Check for mistakes.
Comment on lines +1896 to +1933
timer.finish(module_path!(), line!(), "ReadRequest::HistoryNode");

Ok(ReadResponse::AuthDataRoot(auth_data_root))
})
})
.wait_for_panics()
}

ReadRequest::FirstBlockWithTotalWork(threshold) => {
let state = self.clone();

tokio::task::spawn_blocking(move || {
span.in_scope(move || {
let block_index =
state.non_finalized_state_receiver.with_watch_data(|_| {
read::first_block_with_total_work(&state.db, threshold)
});

// The work is done in the future.
timer.finish(module_path!(), line!(), "ReadRequest::HistoryNode");

Ok(ReadResponse::FirstBlockWithTotalWork(block_index))
})
})
.wait_for_panics()
}

ReadRequest::TotalWork(hash_or_height) => {
let state = self.clone();

tokio::task::spawn_blocking(move || {
span.in_scope(move || {
let total_work = state
.non_finalized_state_receiver
.with_watch_data(|_| read::total_work(&state.db, hash_or_height));

// The work is done in the future.
timer.finish(module_path!(), line!(), "ReadRequest::HistoryNode");
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timer finish calls on lines 1847, 1872, 1896, 1915, and 1933 use incorrect labels. Lines 1896, 1915, and 1933 all say "ReadRequest::HistoryNode" but should be "ReadRequest::AuthDataRoot", "ReadRequest::FirstBlockWithTotalWork", and "ReadRequest::TotalWork" respectively. This will make performance debugging and metrics confusing.

Copilot uses AI. Check for mistakes.
.await
.map_misc_error()?
else {
panic!("unexpected response to HistoryNode request")
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The panic message on line 2309 says "unexpected response to HistoryNode request" but this is actually handling an AuthDataRoot request. This will be confusing during debugging. It should say "unexpected response to AuthDataRoot request".

Suggested change
panic!("unexpected response to HistoryNode request")
panic!("unexpected response to AuthDataRoot request")

Copilot uses AI. Check for mistakes.
.await
.map_misc_error()?
else {
panic!("unexpected response to HistoryNode request")
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The panic message says "unexpected response to HistoryNode request" but this is handling a FirstBlockWithTotalWork request. It should say "unexpected response to FirstBlockWithTotalWork request".

Suggested change
panic!("unexpected response to HistoryNode request")
panic!("unexpected response to FirstBlockWithTotalWork request")

Copilot uses AI. Check for mistakes.
Comment on lines +203 to +211
let block = self.block(HashOrHeight::Height(
Height::try_from(temp_tree_height.as_usize() as u32).ok()?,
))?;
let sapling_root = self
.sapling_tree_by_height(&Height::try_from(temp_tree_height.as_usize() as u32).ok()?)?
.root();
let orchard_root = self
.orchard_tree_by_height(&Height::try_from(temp_tree_height.as_usize() as u32).ok()?)?
.root();
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are redundant Height conversions here - temp_tree_height is already a Height, so converting it .as_usize() as u32 and then back with Height::try_from() is unnecessary. You can use temp_tree_height directly in the function calls.

Copilot uses AI. Check for mistakes.
Comment on lines +2218 to +2260
let entry_object =
zcash_history::Entry::<V1>::from_bytes(branch_id.into(), entry.inner())
.expect("entry object should be valid");
let node_bytes = match entry_object.leaf() {
true => entry.inner().split_at(1).1,
false => entry.inner().split_at(9).1,
};
let mut cursor = Cursor::new(&node_bytes);
let node = V1::read(branch_id.into(), &mut cursor)
.expect("node data should be valid");
let mut total_work: [u8; 32] = bytemuck::cast(node.subtree_total_work.0);
total_work.reverse();
GetHistoryNodeObject {
subtree_commitment: node.subtree_commitment,
consensus_branch_id: ConsensusBranchIdHex(
node.consensus_branch_id.into(),
),
start_time: node.start_time,
end_time: node.end_time,
start_target: node.start_target,
end_target: node.end_target,
start_sapling_root: node.start_sapling_root,
end_sapling_root: node.end_sapling_root,
subtree_total_work: total_work,
start_height: node.start_height,
end_height: node.end_height,
sapling_tx: node.sapling_tx,
start_orchard_root: [0; 32],
end_orchard_root: [0; 32],
orchard_tx: 0,
}
}
_ => {
let entry_object =
zcash_history::Entry::<V2>::from_bytes(branch_id.into(), entry.inner())
.expect("entry object should be valid");
let node_bytes = match entry_object.leaf() {
true => entry.inner().split_at(1).1,
false => entry.inner().split_at(9).1,
};
let mut cursor = Cursor::new(&node_bytes);
let node = V2::read(branch_id.into(), &mut cursor)
.expect("node data should be valid");
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .expect() calls at lines 2220, 2227, 2253, and 2260 could panic if the database contains corrupted data. Since this data comes from the database (which could potentially be corrupted or malicious in some scenarios), these should be converted to proper error handling that returns an error to the RPC caller instead of panicking.

Consider replacing these expects with proper error handling using map_err or similar patterns to return an appropriate RPC error.

Copilot uses AI. Check for mistakes.
)
})?;

let branch_id = network_upgrade.branch_id().unwrap();
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .unwrap() on line 2197 could panic if the network upgrade doesn't have a branch_id. While the current network upgrades in the UPGRADE_LIST all have branch IDs, this creates a potential future maintenance hazard. Consider handling this case explicitly with proper error handling.

Copilot uses AI. Check for mistakes.
Comment on lines +1220 to +1231
fn history_nodes_at_chain_tip(&self) -> Option<Vec<Entry>> {
let mut result = Vec::<Entry>::new();

if self.history_nodes_by_height.is_empty() {
return None;
}

for (_key, mut entries) in self.history_nodes_by_height.clone() {
result.append(&mut entries);
}

Some(result)
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method clones the entire history_nodes_by_height BTreeMap and then iterates over it, which is inefficient. The clone is unnecessary here - you can iterate directly over the BTreeMap values using .values() and then .flatten() or collect into the result vector.

Consider changing to: self.history_nodes_by_height.values().flatten().cloned().collect() to avoid the BTreeMap clone.

Copilot uses AI. Check for mistakes.
.await
.map_misc_error()?
else {
panic!("unexpected response to TotalWork request")
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to line 2309, the panic message here says "unexpected response to HistoryNode request" but this is actually handling a TotalWork request. It should say "unexpected response to TotalWork request".

Copilot uses AI. Check for mistakes.
| ReadResponse::HistoryTree(_)
| ReadResponse::HistoryNode(_)
| ReadResponse::AuthDataRoot(_)
| ReadResponse:: FirstBlockWithTotalWork(_)
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an extra space before FirstBlockWithTotalWork on line 545. It should be ReadResponse::FirstBlockWithTotalWork(_) without the double colon spacing.

Suggested change
| ReadResponse:: FirstBlockWithTotalWork(_)
| ReadResponse::FirstBlockWithTotalWork(_)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants