Skip to main content

zebra_rpc/methods/
trees.rs

1//! Types and functions for note commitment tree RPCs.
2
3use derive_getters::Getters;
4use derive_new::new;
5use zebra_chain::{
6    block::Hash,
7    block::Height,
8    subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
9};
10
11/// A subtree data type that can hold Sapling or Orchard subtree roots.
12pub type SubtreeRpcData = NoteCommitmentSubtreeData<String>;
13
14/// Response to a `z_getsubtreesbyindex` RPC request.
15///
16/// Contains the Sapling or Orchard pool label, the index of the first subtree in the list,
17/// and a list of subtree roots and end heights.
18#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
19pub struct GetSubtreesByIndexResponse {
20    /// The shielded pool to which the subtrees belong.
21    //
22    // TODO: consider an enum with a string conversion?
23    pub(crate) pool: String,
24
25    /// The index of the first subtree.
26    #[getter(copy)]
27    pub(crate) start_index: NoteCommitmentSubtreeIndex,
28
29    /// A sequential list of complete subtrees, in `index` order.
30    ///
31    /// The generic subtree root type is a hex-encoded Sapling or Orchard subtree root string.
32    //
33    // TODO: is this needed?
34    //#[serde(skip_serializing_if = "Vec::is_empty")]
35    pub(crate) subtrees: Vec<SubtreeRpcData>,
36}
37
38impl Default for GetSubtreesByIndexResponse {
39    fn default() -> Self {
40        Self {
41            pool: "sapling | orchard".to_string(),
42            start_index: NoteCommitmentSubtreeIndex(u16::default()),
43            subtrees: vec![],
44        }
45    }
46}
47
48/// Response to a `z_gettreestate` RPC request.
49///
50/// Contains the Sapling & Orchard note commitment trees and their corresponding
51/// [`struct@Hash`], [`Height`], and block time. The tree and hash bytes are
52/// stored raw and hex-encoded only when serialized to JSON.
53///
54/// The format of the serialized trees represents `CommitmentTree`s from the crate
55/// `incrementalmerkletree` and not `Frontier`s from the same crate, even though `zebrad`'s
56/// `NoteCommitmentTree`s are implemented using `Frontier`s. Zebra follows the former format to stay
57/// consistent with `zcashd`'s RPCs.
58///
59/// The formats are semantically equivalent. The difference is that in `Frontier`s, the vector of
60/// ommers is dense (we know where the gaps are from the position of the leaf in the overall tree);
61/// whereas in `CommitmentTree`, the vector of ommers is sparse with [`None`] values in the gaps.
62///
63/// The dense format might be used in future RPCs.
64#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
65pub struct GetTreestateResponse {
66    /// The block hash corresponding to the treestate.
67    ///
68    /// Stored as a raw [`struct@Hash`]; serialized to JSON as a hex-encoded string.
69    #[serde(with = "hex")]
70    #[getter(copy)]
71    hash: Hash,
72
73    /// The block height corresponding to the treestate, numeric.
74    #[getter(copy)]
75    height: Height,
76
77    /// Unix time when the block corresponding to the treestate was mined,
78    /// numeric.
79    ///
80    /// UTC seconds since the Unix 1970-01-01 epoch.
81    time: u32,
82
83    /// A treestate containing a Sprout note commitment tree; its bytes are
84    /// serialized to JSON as hex-encoded strings. Zebra does not support
85    /// returning it, but the field is here to enable parsing responses from
86    /// other implementations.
87    #[serde(skip_serializing_if = "Option::is_none")]
88    sprout: Option<Treestate>,
89
90    /// A treestate containing a Sapling note commitment tree; its bytes are
91    /// serialized to JSON as hex-encoded strings.
92    sapling: Treestate,
93
94    /// A treestate containing an Orchard note commitment tree; its bytes are
95    /// serialized to JSON as hex-encoded strings.
96    orchard: Treestate,
97
98    /// A treestate containing an Ironwood note commitment tree; its bytes are
99    /// serialized to JSON as hex-encoded strings. Only present from NU6.3, so
100    /// that pre-NU6.3 responses are unchanged.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    ironwood: Option<Treestate>,
103}
104
105impl GetTreestateResponse {
106    /// Constructs [`Treestate`] from its constituent parts.
107    #[deprecated(note = "Use `new` instead.")]
108    pub fn from_parts(
109        hash: Hash,
110        height: Height,
111        time: u32,
112        sapling: Option<Vec<u8>>,
113        orchard: Option<Vec<u8>>,
114    ) -> Self {
115        let sapling = Treestate {
116            commitments: Commitments {
117                final_state: sapling,
118                final_root: None,
119            },
120        };
121        let orchard = Treestate {
122            commitments: Commitments {
123                final_state: orchard,
124                final_root: None,
125            },
126        };
127
128        Self {
129            hash,
130            height,
131            time,
132            sprout: None,
133            sapling,
134            orchard,
135            ironwood: None,
136        }
137    }
138
139    /// Returns the contents of ['GetTreeState'].
140    #[deprecated(note = "Use getters instead.")]
141    pub fn into_parts(self) -> (Hash, Height, u32, Option<Vec<u8>>, Option<Vec<u8>>) {
142        (
143            self.hash,
144            self.height,
145            self.time,
146            self.sapling.commitments.final_state,
147            self.orchard.commitments.final_state,
148        )
149    }
150}
151
152impl Default for GetTreestateResponse {
153    fn default() -> Self {
154        Self {
155            hash: Hash([0; 32]),
156            height: Height::MIN,
157            time: Default::default(),
158            sprout: Default::default(),
159            sapling: Default::default(),
160            orchard: Default::default(),
161            ironwood: None,
162        }
163    }
164}
165
166/// A treestate that is included in the [`z_gettreestate`][1] RPC response.
167///
168/// [1]: https://zcash.github.io/rpc/z_gettreestate.html
169#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
170pub struct Treestate {
171    /// The Orchard or Sapling note commitment tree, stored as raw bytes and
172    /// serialized to JSON as hex-encoded strings (see [`Commitments`]).
173    commitments: Commitments,
174}
175
176impl Treestate {
177    /// Returns a reference to the commitments.
178    #[deprecated(note = "Use `commitments()` instead.")]
179    pub fn inner(&self) -> &Commitments {
180        self.commitments()
181    }
182}
183
184impl Default for Treestate {
185    fn default() -> Self {
186        Self {
187            commitments: Commitments {
188                final_root: None,
189                final_state: None,
190            },
191        }
192    }
193}
194
195/// A wrapper that contains either an Orchard or Sapling note commitment tree.
196///
197/// # Data representation
198///
199/// The fields hold **raw bytes** (`Vec<u8>`). They are hex-encoded only when the
200/// struct is serialized to JSON (via `serde_as` / `serde_with::hex`), so callers
201/// that read the Rust fields directly — through the generated getters or `new` —
202/// receive unencoded bytes, not hex strings.
203///
204/// Note that in the original [`z_gettreestate`][1] RPC, [`Commitments`] also
205/// contains the field `finalRoot`. Zebra does *not* use this field.
206///
207/// [1]: https://zcash.github.io/rpc/z_gettreestate.html
208#[serde_with::serde_as]
209#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
210pub struct Commitments {
211    /// The raw bytes of the Orchard or Sapling note commitment tree root.
212    ///
213    /// Serialized to JSON as a hex-encoded string in the `finalRoot` field.
214    #[serde_as(as = "Option<serde_with::hex::Hex>")]
215    #[serde(skip_serializing_if = "Option::is_none")]
216    #[serde(rename = "finalRoot")]
217    final_root: Option<Vec<u8>>,
218    /// The raw bytes of the Orchard or Sapling serialized note commitment tree.
219    ///
220    /// Serialized to JSON as a hex-encoded string in the `finalState` field.
221    #[serde_as(as = "Option<serde_with::hex::Hex>")]
222    #[serde(skip_serializing_if = "Option::is_none")]
223    #[serde(rename = "finalState")]
224    final_state: Option<Vec<u8>>,
225}
226
227impl Commitments {
228    /// Returns a reference to the optional `final_state`.
229    #[deprecated(note = "Use `final_state()` instead.")]
230    pub fn inner(&self) -> &Option<Vec<u8>> {
231        &self.final_state
232    }
233}