snarkvm_console_program/state_path/
mod.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod configuration;
17pub use configuration::*;
18
19mod header_leaf;
20pub use header_leaf::*;
21
22mod transaction_leaf;
23pub use transaction_leaf::*;
24
25pub mod transition_leaf;
26pub use transition_leaf::*;
27
28mod bytes;
29mod parse;
30mod serialize;
31mod verify;
32
33use snarkvm_console_network::prelude::*;
34use snarkvm_console_types::Field;
35
36/// The state path proves existence of the transition leaf to either a global or local state root.
37#[derive(Clone, PartialEq, Eq)]
38pub struct StatePath<N: Network> {
39    /// The global state root (Public).
40    global_state_root: N::StateRoot,
41    /// The Merkle path for the block hash.
42    block_path: BlockPath<N>,
43    /// The block hash.
44    block_hash: N::BlockHash,
45    /// The previous block hash.
46    previous_block_hash: N::BlockHash,
47    /// The block header root.
48    header_root: Field<N>,
49    /// The Merkle path for the block header leaf.
50    header_path: HeaderPath<N>,
51    /// The block header leaf.
52    header_leaf: HeaderLeaf<N>,
53    /// The Merkle path for the transaction ID.
54    transactions_path: TransactionsPath<N>,
55    /// The transaction ID.
56    transaction_id: N::TransactionID,
57    /// The Merkle path for the transaction leaf.
58    transaction_path: TransactionPath<N>,
59    /// The transaction leaf.
60    transaction_leaf: TransactionLeaf<N>,
61    /// The transition root.
62    transition_root: Field<N>,
63    /// The transition commitment.
64    tcm: Field<N>,
65    /// The Merkle path for the transition leaf.
66    transition_path: TransitionPath<N>,
67    /// The transition leaf.
68    transition_leaf: TransitionLeaf<N>,
69}
70
71impl<N: Network> StatePath<N> {
72    /// Initializes a new instance of `StatePath`.
73    pub fn new_local(
74        global_state_root: N::StateRoot,
75        local_state_root: N::TransactionID,
76        transaction_path: TransactionPath<N>,
77        transaction_leaf: TransactionLeaf<N>,
78        transition_root: Field<N>,
79        tcm: Field<N>,
80        transition_path: TransitionPath<N>,
81        transition_leaf: TransitionLeaf<N>,
82    ) -> Result<Self> {
83        // Compute an arbitrary transactions path.
84        let local_state_root_bits = local_state_root.to_bits_le();
85        let transactions_tree: TransactionsTree<N> = N::merkle_tree_bhp(&[local_state_root_bits.clone()])?;
86        let transactions_path = transactions_tree.prove(0, &local_state_root_bits)?;
87        let transactions_root = transactions_tree.root();
88
89        // Compute an arbitrary block header path.
90        let header_leaf = HeaderLeaf::<N>::new(0, *transactions_root);
91        let header_leaf_bits = header_leaf.to_bits_le();
92        let header_tree: HeaderTree<N> = N::merkle_tree_bhp(&[header_leaf_bits.clone()])?;
93        let header_path = header_tree.prove(0, &header_leaf_bits)?;
94        let header_root = *header_tree.root();
95
96        // Compute an arbitrary block path.
97        let previous_block_hash: N::BlockHash = Field::<N>::zero().into();
98        let block_hash: N::BlockHash = previous_block_hash;
99        let block_hash_bits = block_hash.to_bits_le();
100        let block_tree: BlockTree<N> = N::merkle_tree_bhp(&[block_hash_bits.clone()])?;
101        let block_path = block_tree.prove(0, &block_hash_bits)?;
102
103        // Return the state path.
104        Ok(Self {
105            global_state_root,
106            block_path,
107            block_hash,
108            previous_block_hash,
109            header_root,
110            header_path,
111            header_leaf,
112            transactions_path,
113            transaction_id: local_state_root,
114            transaction_path,
115            transaction_leaf,
116            transition_root,
117            tcm,
118            transition_path,
119            transition_leaf,
120        })
121    }
122
123    /// Initializes a new instance of `StatePath`.
124    #[allow(clippy::too_many_arguments)]
125    pub fn from(
126        global_state_root: N::StateRoot,
127        block_path: BlockPath<N>,
128        block_hash: N::BlockHash,
129        previous_block_hash: N::BlockHash,
130        header_root: Field<N>,
131        header_path: HeaderPath<N>,
132        header_leaf: HeaderLeaf<N>,
133        transactions_path: TransactionsPath<N>,
134        transaction_id: N::TransactionID,
135        transaction_path: TransactionPath<N>,
136        transaction_leaf: TransactionLeaf<N>,
137        transition_root: Field<N>,
138        tcm: Field<N>,
139        transition_path: TransitionPath<N>,
140        transition_leaf: TransitionLeaf<N>,
141    ) -> Self {
142        // Return the state path.
143        Self {
144            global_state_root,
145            block_path,
146            block_hash,
147            previous_block_hash,
148            header_root,
149            header_path,
150            header_leaf,
151            transactions_path,
152            transaction_id,
153            transaction_path,
154            transaction_leaf,
155            transition_root,
156            tcm,
157            transition_path,
158            transition_leaf,
159        }
160    }
161
162    /// Returns the global state root.
163    pub const fn global_state_root(&self) -> N::StateRoot {
164        self.global_state_root
165    }
166
167    /// Returns the block path.
168    pub const fn block_path(&self) -> &BlockPath<N> {
169        &self.block_path
170    }
171
172    /// Returns the block hash.
173    pub const fn block_hash(&self) -> N::BlockHash {
174        self.block_hash
175    }
176
177    /// Returns the previous block hash.
178    pub const fn previous_block_hash(&self) -> N::BlockHash {
179        self.previous_block_hash
180    }
181
182    /// Returns the block header root.
183    pub const fn header_root(&self) -> &Field<N> {
184        &self.header_root
185    }
186
187    /// Returns the header path.
188    pub const fn header_path(&self) -> &HeaderPath<N> {
189        &self.header_path
190    }
191
192    /// Returns the header leaf.
193    pub const fn header_leaf(&self) -> &HeaderLeaf<N> {
194        &self.header_leaf
195    }
196
197    /// Returns the transactions path.
198    pub const fn transactions_path(&self) -> &TransactionsPath<N> {
199        &self.transactions_path
200    }
201
202    /// Returns the transaction ID.
203    pub const fn transaction_id(&self) -> &N::TransactionID {
204        &self.transaction_id
205    }
206
207    /// Returns the Merkle path for the transaction leaf.
208    pub const fn transaction_path(&self) -> &TransactionPath<N> {
209        &self.transaction_path
210    }
211
212    /// Returns the transaction leaf.
213    pub const fn transaction_leaf(&self) -> &TransactionLeaf<N> {
214        &self.transaction_leaf
215    }
216
217    /// Returns the transition root.
218    pub const fn transition_root(&self) -> &Field<N> {
219        &self.transition_root
220    }
221
222    /// Returns the transition commitment.
223    pub const fn tcm(&self) -> &Field<N> {
224        &self.tcm
225    }
226
227    /// Returns the Merkle path for the transition leaf.
228    pub const fn transition_path(&self) -> &TransitionPath<N> {
229        &self.transition_path
230    }
231
232    /// Returns the transition leaf.
233    pub const fn transition_leaf(&self) -> &TransitionLeaf<N> {
234        &self.transition_leaf
235    }
236}
237
238#[cfg(any(test, feature = "test"))]
239pub mod test_helpers {
240    use super::*;
241    use snarkvm_console_network::prelude::TestRng;
242
243    /// Randomly sample a state path to a global state root.
244    /// If a `commitment` is given, it is used. Otherwise, a `commitment` is randomly sampled.
245    pub fn sample_global_state_path<N: Network>(
246        commitment: Option<Field<N>>,
247        rng: &mut TestRng,
248    ) -> Result<StatePath<N>> {
249        // Prepare the commitment.
250        let commitment = match commitment {
251            Some(commitment) => commitment,
252            None => Field::rand(rng),
253        };
254
255        // Prepare the tcm.
256        let tcm = Field::rand(rng);
257
258        // Construct the transition path and transaction leaf.
259        let transition_leaf = TransitionLeaf::new_with_version(0, 3, commitment);
260        let transition_tree: TransitionTree<N> = N::merkle_tree_bhp(&[transition_leaf.to_bits_le()])?;
261        let transition_root = *transition_tree.root();
262        let transition_id = N::hash_bhp512(&(transition_root, tcm).to_bits_le())?;
263        let transition_path = transition_tree.prove(0, &transition_leaf.to_bits_le())?;
264
265        // Construct the transaction path and transaction leaf.
266        let transaction_leaf = TransactionLeaf::new_execution(0, transition_id);
267        let transaction_tree: TransactionTree<N> = N::merkle_tree_bhp(&[transaction_leaf.to_bits_le()])?;
268        let transaction_id = *transaction_tree.root();
269        let transaction_path = transaction_tree.prove(0, &transaction_leaf.to_bits_le())?;
270
271        // Construct the transactions path.
272        let transactions_tree: TransactionsTree<N> = N::merkle_tree_bhp(&[transaction_id.to_bits_le()])?;
273        let transactions_root = transactions_tree.root();
274        let transactions_path = transactions_tree.prove(0, &transaction_id.to_bits_le())?;
275
276        // Construct the block header path.
277        let header_leaf = HeaderLeaf::<N>::new(1, *transactions_root);
278        let header_tree: HeaderTree<N> =
279            N::merkle_tree_bhp(&[Field::<N>::zero().to_bits_le(), header_leaf.to_bits_le()])?;
280        let header_root = header_tree.root();
281        let header_path = header_tree.prove(1, &header_leaf.to_bits_le())?;
282
283        let previous_block_hash: N::BlockHash = Field::<N>::rand(rng).into();
284        let preimage = (*previous_block_hash).to_bits_le().into_iter().chain(header_root.to_bits_le());
285        let block_hash = N::hash_bhp1024(&preimage.collect::<Vec<_>>())?;
286
287        // Construct the global state root and block path.
288        let block_tree: BlockTree<N> = N::merkle_tree_bhp(&[block_hash.to_bits_le()])?;
289        let global_state_root = *block_tree.root();
290        let block_path = block_tree.prove(0, &block_hash.to_bits_le())?;
291
292        Ok(StatePath::<N>::from(
293            global_state_root.into(),
294            block_path,
295            block_hash.into(),
296            previous_block_hash,
297            *header_root,
298            header_path,
299            header_leaf,
300            transactions_path,
301            transaction_id.into(),
302            transaction_path,
303            transaction_leaf,
304            transition_root,
305            tcm,
306            transition_path,
307            transition_leaf,
308        ))
309    }
310
311    /// Randomly sample a state path to a local state root.
312    /// If a `commitment` is given, it is used. Otherwise, a `commitment` is randomly sampled.
313    pub fn sample_local_state_path<N: Network>(
314        commitment: Option<Field<N>>,
315        rng: &mut TestRng,
316    ) -> Result<StatePath<N>> {
317        // Prepare the commitment.
318        let commitment = match commitment {
319            Some(commitment) => commitment,
320            None => Field::rand(rng),
321        };
322
323        // Prepare the tcm.
324        let tcm = Field::rand(rng);
325
326        // Construct the transition path and transaction leaf.
327        let transition_leaf = TransitionLeaf::new_with_version(0, 3, commitment);
328        let transition_tree: TransitionTree<N> = N::merkle_tree_bhp(&[transition_leaf.to_bits_le()])?;
329        let transition_root = *transition_tree.root();
330        let transition_id = N::hash_bhp512(&(transition_root, tcm).to_bits_le())?;
331        let transition_path = transition_tree.prove(0, &transition_leaf.to_bits_le())?;
332
333        // Construct the transaction path and transaction leaf.
334        let transaction_leaf = TransactionLeaf::new_execution(0, transition_id);
335        let transaction_tree: TransactionTree<N> = N::merkle_tree_bhp(&[transaction_leaf.to_bits_le()])?;
336        let transaction_id = *transaction_tree.root();
337        let transaction_path = transaction_tree.prove(0, &transaction_leaf.to_bits_le())?;
338
339        // Construct the transactions path.
340        let transactions_tree: TransactionsTree<N> = N::merkle_tree_bhp(&[transaction_id.to_bits_le()])?;
341        let transactions_root = transactions_tree.root();
342        let transactions_path = transactions_tree.prove(0, &transaction_id.to_bits_le())?;
343
344        // Prepare random header leaves.
345        let random_header_index = rng.gen_range(0..7);
346        let mut random_header_leaves = vec![Field::<N>::zero().to_bits_le(); (random_header_index + 1) as usize];
347        let header_leaf = HeaderLeaf::<N>::new(random_header_index, *transactions_root);
348        random_header_leaves[random_header_index as usize] = header_leaf.to_bits_le();
349
350        // Construct the block header path.
351        let header_tree: HeaderTree<N> = N::merkle_tree_bhp(&random_header_leaves)?;
352        let header_root = header_tree.root();
353        let header_path = header_tree.prove(random_header_index as usize, &header_leaf.to_bits_le())?;
354
355        let previous_block_hash: N::BlockHash = Field::<N>::rand(rng).into();
356        let block_hash: N::BlockHash = Field::<N>::rand(rng).into();
357
358        // Construct the global state root and block path.
359        let block_tree: BlockTree<N> = N::merkle_tree_bhp(&[block_hash.to_bits_le()])?;
360        let global_state_root = *block_tree.root();
361        let block_path = block_tree.prove(0, &block_hash.to_bits_le())?;
362
363        Ok(StatePath::<N>::from(
364            global_state_root.into(),
365            block_path,
366            block_hash,
367            previous_block_hash,
368            *header_root,
369            header_path,
370            header_leaf,
371            transactions_path,
372            transaction_id.into(),
373            transaction_path,
374            transaction_leaf,
375            transition_root,
376            tcm,
377            transition_path,
378            transition_leaf,
379        ))
380    }
381}