miden_client/rpc/domain/
smt.rs1use alloc::collections::BTreeSet;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4
5use miden_protocol::Word;
6use miden_protocol::crypto::merkle::NodeIndex;
7use miden_protocol::crypto::merkle::smt::{
8 LeafIndex,
9 NodeValue,
10 PartialSmt,
11 SMT_DEPTH,
12 SmtLeaf,
13 SmtProof,
14 UniqueNodes,
15};
16
17use crate::rpc::domain::MissingFieldHelper;
18use crate::rpc::errors::RpcConversionError;
19use crate::rpc::generated as proto;
20
21impl From<&(Word, Word)> for proto::primitives::SmtLeafEntry {
25 fn from(value: &(Word, Word)) -> Self {
26 proto::primitives::SmtLeafEntry {
27 key: Some(value.0.into()),
28 value: Some(value.1.into()),
29 }
30 }
31}
32
33impl TryFrom<&proto::primitives::SmtLeafEntry> for (Word, Word) {
34 type Error = RpcConversionError;
35
36 fn try_from(value: &proto::primitives::SmtLeafEntry) -> Result<Self, Self::Error> {
37 let key = match value.key {
38 Some(key) => key.try_into()?,
39 None => return Err(proto::primitives::SmtLeafEntry::missing_field(stringify!(key))),
40 };
41
42 let value: Word = match value.value {
43 Some(value) => value.try_into()?,
44 None => return Err(proto::primitives::SmtLeafEntry::missing_field(stringify!(value))),
45 };
46
47 Ok((key, value))
48 }
49}
50
51impl From<SmtLeaf> for proto::primitives::SmtLeaf {
55 fn from(value: SmtLeaf) -> Self {
56 (&value).into()
57 }
58}
59
60impl From<&SmtLeaf> for proto::primitives::SmtLeaf {
61 fn from(value: &SmtLeaf) -> Self {
62 match value {
63 SmtLeaf::Empty(index) => proto::primitives::SmtLeaf {
64 leaf: Some(proto::primitives::smt_leaf::Leaf::EmptyLeafIndex(index.position())),
65 },
66 SmtLeaf::Single(entry) => proto::primitives::SmtLeaf {
67 leaf: Some(proto::primitives::smt_leaf::Leaf::Single(entry.into())),
68 },
69 SmtLeaf::Multiple(entries) => proto::primitives::SmtLeaf {
70 leaf: Some(proto::primitives::smt_leaf::Leaf::Multiple(
71 proto::primitives::SmtLeafEntryList {
72 entries: entries.iter().map(Into::into).collect(),
73 },
74 )),
75 },
76 }
77 }
78}
79
80impl TryFrom<&proto::primitives::SmtLeaf> for SmtLeaf {
81 type Error = RpcConversionError;
82
83 fn try_from(value: &proto::primitives::SmtLeaf) -> Result<Self, Self::Error> {
84 match &value.leaf {
85 Some(proto::primitives::smt_leaf::Leaf::EmptyLeafIndex(index)) => Ok(SmtLeaf::Empty(
86 LeafIndex::<SMT_DEPTH>::new(*index)
87 .map_err(|err| RpcConversionError::InvalidField(err.to_string()))?,
88 )),
89 Some(proto::primitives::smt_leaf::Leaf::Single(entry)) => {
90 Ok(SmtLeaf::Single(entry.try_into()?))
91 },
92 Some(proto::primitives::smt_leaf::Leaf::Multiple(entries)) => {
93 let entries =
94 entries.entries.iter().map(TryInto::try_into).collect::<Result<_, _>>()?;
95 Ok(SmtLeaf::Multiple(entries))
96 },
97 None => Err(proto::primitives::SmtLeaf::missing_field(stringify!(leaf))),
98 }
99 }
100}
101
102impl From<SmtProof> for proto::primitives::SmtOpening {
106 fn from(value: SmtProof) -> Self {
107 let (path, leaf) = value.into_parts();
108
109 proto::primitives::SmtOpening {
110 leaf: Some(leaf.into()),
111 path: Some(path.into()),
112 }
113 }
114}
115
116impl TryFrom<proto::primitives::PartialSmt> for UniqueNodes {
120 type Error = RpcConversionError;
121
122 fn try_from(value: proto::primitives::PartialSmt) -> Result<Self, Self::Error> {
128 use proto::primitives::partial_smt_node::Value;
129
130 let proto::primitives::PartialSmt {
131 root,
132 node_levels,
133 leaves,
134 value_only_leaves,
135 } = value;
136
137 let root: Word = root
138 .ok_or(proto::primitives::PartialSmt::missing_field(stringify!(root)))?
139 .try_into()?;
140
141 let mut seen_depths = BTreeSet::new();
142 let mut decoded_levels = Vec::with_capacity(node_levels.len());
143 for level in node_levels {
144 let depth = u8::try_from(level.depth)?;
145 if depth == 0 || depth >= SMT_DEPTH {
148 return Err(RpcConversionError::InvalidField(format!(
149 "partial SMT node depth {depth} must be in the range 1..{SMT_DEPTH}"
150 )));
151 }
152 if !seen_depths.insert(depth) {
153 return Err(RpcConversionError::InvalidField(format!(
154 "partial SMT contains duplicate node depth {depth}"
155 )));
156 }
157
158 let mut seen_indices = BTreeSet::new();
159 let mut decoded_nodes = Vec::with_capacity(level.nodes.len());
160 for node in level.nodes {
161 NodeIndex::new(depth, node.index)?;
162 if !seen_indices.insert(node.index) {
163 return Err(RpcConversionError::InvalidField(format!(
164 "partial SMT contains duplicate node index {} at depth {depth}",
165 node.index
166 )));
167 }
168
169 let node_value = match node
170 .value
171 .ok_or(proto::primitives::PartialSmtNode::missing_field(stringify!(value)))?
172 {
173 Value::Digest(digest) => NodeValue::Present(digest.try_into()?),
174 Value::EmptySubtreeRoot(true) => NodeValue::EmptySubtreeRoot,
175 Value::EmptySubtreeRoot(false) => {
176 return Err(RpcConversionError::InvalidField(
177 "partial SMT empty_subtree_root marker must be true".into(),
178 ));
179 },
180 };
181 decoded_nodes.push((node.index, node_value));
182 }
183 decoded_levels.push((depth, decoded_nodes));
184 }
185
186 let mut seen_leaf_indices = BTreeSet::new();
187 let mut decoded_leaves = Vec::with_capacity(leaves.len());
188 for indexed_leaf in leaves {
189 if !seen_leaf_indices.insert(indexed_leaf.index) {
190 return Err(RpcConversionError::InvalidField(format!(
191 "partial SMT contains duplicate leaf index {}",
192 indexed_leaf.index
193 )));
194 }
195 let leaf: SmtLeaf = indexed_leaf
196 .leaf
197 .as_ref()
198 .ok_or(proto::primitives::IndexedSmtLeaf::missing_field(stringify!(leaf)))?
199 .try_into()?;
200 decoded_leaves.push((indexed_leaf.index, leaf));
201 }
202
203 let mut seen_value_only_indices = BTreeSet::new();
204 let mut decoded_value_only_leaves = Vec::with_capacity(value_only_leaves.len());
205 for indexed_digest in value_only_leaves {
206 if !seen_value_only_indices.insert(indexed_digest.index) {
207 return Err(RpcConversionError::InvalidField(format!(
208 "partial SMT contains duplicate value-only leaf index {}",
209 indexed_digest.index
210 )));
211 }
212 if seen_leaf_indices.contains(&indexed_digest.index) {
213 return Err(RpcConversionError::InvalidField(format!(
214 "partial SMT leaf index {} has both a leaf and a value-only leaf",
215 indexed_digest.index
216 )));
217 }
218 let digest: Word = indexed_digest
219 .value
220 .ok_or(proto::primitives::IndexedDigest::missing_field(stringify!(value)))?
221 .try_into()?;
222 decoded_value_only_leaves.push((indexed_digest.index, digest));
223 }
224
225 Ok(UniqueNodes {
226 root,
227 nodes: decoded_levels.into_iter().collect(),
228 leaves: decoded_leaves,
229 value_only_leaves: decoded_value_only_leaves,
230 })
231 }
232}
233
234impl TryFrom<proto::primitives::PartialSmt> for PartialSmt {
235 type Error = RpcConversionError;
236
237 fn try_from(value: proto::primitives::PartialSmt) -> Result<Self, Self::Error> {
238 let unique_nodes = UniqueNodes::try_from(value)?;
239 Ok(PartialSmt::from_unique_nodes(unique_nodes)?)
240 }
241}