From 8a16a4a089018871f23d12265565b4b23d6983ad Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 31 Aug 2023 10:59:20 +1000 Subject: [PATCH] Implement the z_get_subtrees_by_index RPC method and return type Revert "Temporarily remove the z_get_subtrees_by_index RPC method" This reverts commit 30d049ee9f43ef03e89331b6bd8a1d7243a2ac40. --- zebra-rpc/src/methods.rs | 117 ++++++++++++++++++++++++++++++++- zebra-rpc/src/methods/trees.rs | 31 +++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 zebra-rpc/src/methods/trees.rs diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index f0f178586..2df10ca72 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -25,13 +25,21 @@ use zebra_chain::{ parameters::{ConsensusBranchId, Network, NetworkUpgrade}, sapling, serialization::{SerializationError, ZcashDeserialize}, + subtree::NoteCommitmentSubtreeIndex, transaction::{self, SerializedTransaction, Transaction, UnminedTx}, transparent::{self, Address}, }; use zebra_node_services::mempool; use zebra_state::{HashOrHeight, MinedTx, OutputIndex, OutputLocation, TransactionLocation}; -use crate::{constants::MISSING_BLOCK_ERROR_CODE, queue::Queue}; +use crate::{ + constants::{INVALID_PARAMETERS_ERROR_CODE, MISSING_BLOCK_ERROR_CODE}, + methods::trees::{GetSubtrees, SubtreeRpcData}, + queue::Queue, +}; + +// We don't use a types/ module here, because it is redundant. +pub mod trees; #[cfg(feature = "getblocktemplate-rpcs")] pub mod get_block_template_rpcs; @@ -172,6 +180,31 @@ pub trait Rpc { #[rpc(name = "z_gettreestate")] fn z_get_treestate(&self, hash_or_height: String) -> BoxFuture>; + /// Returns information about a range of Sapling or Orchard subtrees. + /// + /// zcashd reference: [`z_getsubtreesbyindex`](https://zcash.github.io/rpc/z_getsubtreesbyindex.html) + /// + /// # Parameters + /// + /// - `pool`: (string, required) The pool from which subtrees should be returned. + /// Either "sapling" or "orchard". + /// - `start_index`: (numeric, required) The index of the first 2^16-leaf subtree to return. + /// - `limit`: (numeric, optional) The maximum number of subtree values to return. + /// + /// # Notes + /// + /// While Zebra is doing its initial subtree index rebuild, subtrees will become available + /// starting at the chain tip. This RPC will return an empty list if the `start_index` subtree + /// exists, but has not been rebuilt yet. This matches `zcashd`'s behaviour when subtrees aren't + /// available yet. (But `zcashd` does its rebuild before syncing any blocks.) + #[rpc(name = "z_getsubtreesbyindex")] + fn z_get_subtrees_by_index( + &self, + pool: String, + start_index: NoteCommitmentSubtreeIndex, + limit: Option, + ) -> BoxFuture>; + /// Returns the raw transaction data, as a [`GetRawTransaction`] JSON string or structure. /// /// zcashd reference: [`getrawtransaction`](https://zcash.github.io/rpc/getrawtransaction.html) @@ -1108,6 +1141,88 @@ where .boxed() } + fn z_get_subtrees_by_index( + &self, + pool: String, + start_index: NoteCommitmentSubtreeIndex, + limit: Option, + ) -> BoxFuture> { + let mut state = self.state.clone(); + + async move { + const POOL_LIST: &[&str] = &["sapling", "orchard"]; + + if pool == "sapling" { + let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit }; + let response = state + .ready() + .and_then(|service| service.call(request)) + .await + .map_err(|error| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + let subtrees = match response { + zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees, + _ => unreachable!("unmatched response to a subtrees request"), + }; + + let subtrees = subtrees + .values() + .map(|subtree| SubtreeRpcData { + node: subtree.node.encode_hex(), + end: subtree.end, + }) + .collect(); + + Ok(GetSubtrees { + pool, + start_index, + subtrees, + }) + } else if pool == "orchard" { + let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit }; + let response = state + .ready() + .and_then(|service| service.call(request)) + .await + .map_err(|error| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + let subtrees = match response { + zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees, + _ => unreachable!("unmatched response to a subtrees request"), + }; + + let subtrees = subtrees + .values() + .map(|subtree| SubtreeRpcData { + node: subtree.node.encode_hex(), + end: subtree.end, + }) + .collect(); + + Ok(GetSubtrees { + pool, + start_index, + subtrees, + }) + } else { + Err(Error { + code: INVALID_PARAMETERS_ERROR_CODE, + message: format!("invalid pool name, must be one of: {:?}", POOL_LIST), + data: None, + }) + } + } + .boxed() + } + // TODO: use a generic error constructor (#5548) fn get_address_tx_ids( &self, diff --git a/zebra-rpc/src/methods/trees.rs b/zebra-rpc/src/methods/trees.rs new file mode 100644 index 000000000..43389c43d --- /dev/null +++ b/zebra-rpc/src/methods/trees.rs @@ -0,0 +1,31 @@ +//! Types and functions for note commitment tree RPCs. +// +// TODO: move the *Tree and *Commitment types into this module. + +use zebra_chain::subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}; + +/// A subtree data type that can hold Sapling or Orchard subtree roots. +pub type SubtreeRpcData = NoteCommitmentSubtreeData; + +/// Response to a `z_getsubtreesbyindex` RPC request. +/// +/// Contains the Sapling or Orchard pool label, the index of the first subtree in the list, +/// and a list of subtree roots and end heights. +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] +pub struct GetSubtrees { + /// The shielded pool to which the subtrees belong. + // + // TODO: consider an enum with a string conversion? + pub pool: String, + + /// The index of the first subtree. + pub start_index: NoteCommitmentSubtreeIndex, + + /// A sequential list of complete subtrees, in `index` order. + /// + /// The generic subtree root type is a hex-encoded Sapling or Orchard subtree root string. + // + // TODO: is this needed? + //#[serde(skip_serializing_if = "Vec::is_empty")] + pub subtrees: Vec, +}