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}