snarkvm_ledger_block/transaction/
merkle.rs1use super::*;
17
18impl<N: Network> Transaction<N> {
19 pub const MAX_TRANSITIONS: usize = usize::pow(2, TRANSACTION_DEPTH as u32);
21
22 pub fn to_root(&self) -> Result<Field<N>> {
24 Ok(*self.to_tree()?.root())
25 }
26
27 pub fn to_leaf(&self, id: &Field<N>) -> Result<TransactionLeaf<N>> {
29 match self {
30 Self::Deploy(_, _, _, deployment, fee) => {
31 if *id == **fee.id() {
33 return Ok(TransactionLeaf::new_fee(
35 u16::try_from(deployment.program().functions().len())?, *id,
37 ));
38 }
39
40 for (index, function) in deployment.program().functions().values().enumerate() {
42 if *id == N::hash_bhp1024(&function.to_bytes_le()?.to_bits_le())? {
44 return Ok(TransactionLeaf::new_deployment(u16::try_from(index)?, *id));
46 }
47 }
48 bail!("Function hash not found in deployment transaction");
50 }
51 Self::Execute(_, _, execution, fee) => {
52 if let Some(fee) = fee {
54 if *id == **fee.id() {
55 return Ok(TransactionLeaf::new_execution(
57 u16::try_from(execution.len())?, *id,
59 ));
60 }
61 }
62
63 for (index, transition) in execution.transitions().enumerate() {
65 if *id == **transition.id() {
67 return Ok(TransactionLeaf::new_execution(u16::try_from(index)?, *id));
69 }
70 }
71 bail!("Transition ID not found in execution transaction");
73 }
74 Self::Fee(_, fee) => {
75 if *id == **fee.id() {
76 return Ok(TransactionLeaf::new_fee(0, **fee.id()));
78 }
79 bail!("Transition ID not found in fee transaction");
81 }
82 }
83 }
84
85 pub fn to_path(&self, leaf: &TransactionLeaf<N>) -> Result<TransactionPath<N>> {
87 self.to_tree()?.prove(leaf.index() as usize, &leaf.to_bits_le())
89 }
90
91 pub fn to_tree(&self) -> Result<TransactionTree<N>> {
93 match self {
94 Transaction::Deploy(_, _, _, deployment, fee) => {
96 Self::transaction_tree(Self::deployment_tree(deployment)?, Some(fee))
97 }
98 Transaction::Execute(_, _, execution, fee) => {
100 Self::transaction_tree(Self::execution_tree(execution)?, fee.as_ref())
101 }
102 Transaction::Fee(_, fee) => Self::fee_tree(fee),
104 }
105 }
106}
107
108impl<N: Network> Transaction<N> {
109 pub fn transaction_tree(
111 mut deployment_or_execution_tree: TransactionTree<N>,
112 fee: Option<&Fee<N>>,
113 ) -> Result<TransactionTree<N>> {
114 let fee_index = deployment_or_execution_tree.number_of_leaves();
116 ensure!(
118 fee_index <= N::MAX_FUNCTIONS,
119 "The fee index ('{fee_index}') in the transaction tree must be at most {}",
120 N::MAX_FUNCTIONS
121 );
122 ensure!(
124 fee_index < Self::MAX_TRANSITIONS,
125 "The fee index ('{fee_index}') in the transaction tree must be less than {}",
126 Self::MAX_TRANSITIONS
127 );
128
129 if let Some(fee) = fee {
131 let leaf = TransactionLeaf::new_fee(u16::try_from(fee_index)?, **fee.transition_id()).to_bits_le();
133 deployment_or_execution_tree.append(&[leaf])?;
135 }
136 Ok(deployment_or_execution_tree)
138 }
139
140 pub fn deployment_tree(deployment: &Deployment<N>) -> Result<DeploymentTree<N>> {
142 match deployment.version() {
147 Ok(DeploymentVersion::V1) => Self::deployment_tree_v1(deployment),
148 Ok(DeploymentVersion::V2) => Self::deployment_tree_v2(deployment),
149 Err(e) => bail!("Malformed deployment - {e}"),
150 }
151 }
152
153 pub fn execution_tree(execution: &Execution<N>) -> Result<ExecutionTree<N>> {
155 Self::transitions_tree(execution.transitions())
156 }
157
158 pub fn transitions_tree<'a>(
160 transitions: impl ExactSizeIterator<Item = &'a Transition<N>>,
161 ) -> Result<ExecutionTree<N>> {
162 let num_transitions = transitions.len();
164 Self::check_execution_size(num_transitions)?;
166 let leaves = transitions.enumerate().map(|(index, transition)| {
168 Ok::<_, Error>(TransactionLeaf::new_execution(u16::try_from(index)?, **transition.id()).to_bits_le())
170 });
171 N::merkle_tree_bhp::<TRANSACTION_DEPTH>(&leaves.collect::<Result<Vec<_>, _>>()?)
173 }
174
175 pub fn fee_tree(fee: &Fee<N>) -> Result<TransactionTree<N>> {
177 let leaf = TransactionLeaf::new_fee(0u16, **fee.transition_id()).to_bits_le();
179 N::merkle_tree_bhp::<TRANSACTION_DEPTH>(&[leaf])
181 }
182
183 pub fn check_deployment_size(deployment: &Deployment<N>) -> Result<()> {
185 let program = deployment.program();
187 let functions = program.functions();
189 let verifying_keys = deployment.verifying_keys();
191 let num_functions = functions.len();
193
194 ensure!(
196 num_functions == verifying_keys.len(),
197 "Number of functions ('{num_functions}') and verifying keys ('{}') do not match",
198 verifying_keys.len()
199 );
200 ensure!(num_functions != 0, "Deployment must contain at least one function");
202 ensure!(
204 num_functions <= N::MAX_FUNCTIONS,
205 "Deployment must contain at most {} functions, found {num_functions}",
206 N::MAX_FUNCTIONS,
207 );
208 ensure!(
210 num_functions < Self::MAX_TRANSITIONS, "Deployment must contain less than {} functions, found {num_functions}",
212 Self::MAX_TRANSITIONS,
213 );
214 Ok(())
215 }
216
217 pub fn check_execution_size(num_transitions: usize) -> Result<()> {
219 ensure!(num_transitions > 0, "Execution must contain at least one transition");
221 ensure!(
223 num_transitions < Self::MAX_TRANSITIONS, "Execution must contain less than {} transitions, found {num_transitions}",
225 Self::MAX_TRANSITIONS,
226 );
227 Ok(())
228 }
229}
230
231impl<N: Network> Transaction<N> {
232 pub fn deployment_tree_v1(deployment: &Deployment<N>) -> Result<DeploymentTree<N>> {
234 Self::check_deployment_size(deployment)?;
236 let header = deployment.program().id().to_bits_le();
238 let leaves = deployment.program().functions().values().enumerate().map(|(index, function)| {
240 Ok(TransactionLeaf::new_deployment(
242 u16::try_from(index)?,
243 N::hash_bhp1024(&to_bits_le![header, function.to_bytes_le()?])?,
244 )
245 .to_bits_le())
246 });
247 N::merkle_tree_bhp::<TRANSACTION_DEPTH>(&leaves.collect::<Result<Vec<_>>>()?)
249 }
250
251 pub fn deployment_tree_v2(deployment: &Deployment<N>) -> Result<DeploymentTree<N>> {
253 Self::check_deployment_size(deployment)?;
255 let deployment_hash = N::hash_sha3_256(&to_bits_le!(deployment.to_bytes_le()?))?;
257 let header = to_bits_le![deployment.version()? as u8, deployment_hash];
259 let leaves = deployment.program().functions().values().enumerate().map(|(index, function)| {
261 Ok(TransactionLeaf::new_deployment(
263 u16::try_from(index)?,
264 N::hash_bhp1024(&to_bits_le![header, function.to_bytes_le()?])?,
265 )
266 .to_bits_le())
267 });
268 N::merkle_tree_bhp::<TRANSACTION_DEPTH>(&leaves.collect::<Result<Vec<_>>>()?)
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 type CurrentNetwork = console::network::MainnetV0;
278
279 #[test]
280 fn test_transaction_depth_is_correct() {
281 assert_eq!(
284 2u32.checked_pow(TRANSACTION_DEPTH as u32).unwrap() as usize,
285 Transaction::<CurrentNetwork>::MAX_TRANSITIONS
286 );
287 assert_eq!(
288 CurrentNetwork::MAX_FUNCTIONS.checked_add(1).unwrap(),
289 Transaction::<CurrentNetwork>::MAX_TRANSITIONS
290 );
291 }
292}