snarkvm_circuit_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 helpers;
17pub use helpers::*;
18
19mod verify;
20
21#[cfg(test)]
22use snarkvm_circuit_types::environment::assert_scope;
23
24use snarkvm_circuit_collections::merkle_tree::MerklePath;
25use snarkvm_circuit_network::Aleo;
26use snarkvm_circuit_types::{Boolean, Field, U8, environment::prelude::*};
27
28/// The depth of the Merkle tree for the blocks.
29const BLOCKS_DEPTH: u8 = console::BLOCKS_DEPTH;
30/// The depth of the Merkle tree for the block header.
31const HEADER_DEPTH: u8 = console::HEADER_DEPTH;
32/// The depth of the Merkle tree for transactions in a block.
33const TRANSACTIONS_DEPTH: u8 = console::TRANSACTIONS_DEPTH;
34/// The depth of the Merkle tree for the transaction.
35const TRANSACTION_DEPTH: u8 = console::TRANSACTION_DEPTH;
36/// The depth of the Merkle tree for the transition.
37const TRANSITION_DEPTH: u8 = console::TRANSITION_DEPTH;
38
39type BlockPath<A> = MerklePath<A, BLOCKS_DEPTH>;
40type HeaderPath<A> = MerklePath<A, HEADER_DEPTH>;
41type TransactionsPath<A> = MerklePath<A, TRANSACTIONS_DEPTH>;
42type TransactionPath<A> = MerklePath<A, TRANSACTION_DEPTH>;
43type TransitionPath<A> = MerklePath<A, TRANSITION_DEPTH>;
44
45/// The state path proves existence of the transition leaf to either a global or local state root.
46pub struct StatePath<A: Aleo> {
47    /// The global state root (Public).
48    global_state_root: Field<A>,
49    /// The Merkle path for the block hash.
50    block_path: BlockPath<A>,
51    /// The block hash.
52    block_hash: Field<A>,
53    /// The previous block hash.
54    previous_block_hash: Field<A>,
55    /// The block header root.
56    header_root: Field<A>,
57    /// The Merkle path for the block header leaf.
58    header_path: HeaderPath<A>,
59    /// The block header leaf.
60    header_leaf: HeaderLeaf<A>,
61    /// The Merkle path for the transaction ID.
62    transactions_path: TransactionsPath<A>,
63    /// The transaction ID.
64    transaction_id: Field<A>,
65    /// The Merkle path for the transaction leaf.
66    transaction_path: TransactionPath<A>,
67    /// The transaction leaf.
68    transaction_leaf: TransactionLeaf<A>,
69    /// The transition root.
70    transition_root: Field<A>,
71    /// The transition commitment.
72    tcm: Field<A>,
73    /// The Merkle path for the transition leaf.
74    transition_path: TransitionPath<A>,
75    /// The transition leaf.
76    transition_leaf: TransitionLeaf<A>,
77}
78
79impl<A: Aleo> StatePath<A> {
80    /// Returns the transition leaf.
81    pub const fn transition_leaf(&self) -> &TransitionLeaf<A> {
82        &self.transition_leaf
83    }
84
85    /// Returns the block path.
86    pub const fn block_path(&self) -> &BlockPath<A> {
87        &self.block_path
88    }
89}
90
91impl<A: Aleo> Inject for StatePath<A> {
92    type Primitive = console::StatePath<A::Network>;
93
94    /// Initializes a new ciphertext circuit from a primitive.
95    fn new(mode: Mode, state_path: Self::Primitive) -> Self {
96        Self {
97            global_state_root: Field::new(Mode::Public, *state_path.global_state_root()),
98            block_path: BlockPath::new(mode, state_path.block_path().clone()),
99            block_hash: Field::new(mode, *state_path.block_hash()),
100            previous_block_hash: Field::new(mode, *state_path.previous_block_hash()),
101            header_root: Field::new(mode, *state_path.header_root()),
102            header_path: HeaderPath::new(mode, state_path.header_path().clone()),
103            header_leaf: HeaderLeaf::new(mode, *state_path.header_leaf()),
104            transactions_path: TransactionsPath::new(mode, state_path.transactions_path().clone()),
105            transaction_id: Field::new(mode, **state_path.transaction_id()),
106            transaction_path: TransactionPath::new(mode, state_path.transaction_path().clone()),
107            transaction_leaf: TransactionLeaf::new(mode, *state_path.transaction_leaf()),
108            transition_root: Field::new(mode, *state_path.transition_root()),
109            tcm: Field::new(mode, *state_path.tcm()),
110            transition_path: TransitionPath::new(mode, state_path.transition_path().clone()),
111            transition_leaf: TransitionLeaf::new(mode, *state_path.transition_leaf()),
112        }
113    }
114}
115
116impl<A: Aleo> Eject for StatePath<A> {
117    type Primitive = console::StatePath<A::Network>;
118
119    /// Ejects the mode of the state path.
120    fn eject_mode(&self) -> Mode {
121        Mode::combine(self.global_state_root.eject_mode(), [
122            self.block_path.eject_mode(),
123            self.block_hash.eject_mode(),
124            self.previous_block_hash.eject_mode(),
125            self.header_root.eject_mode(),
126            self.header_path.eject_mode(),
127            self.header_leaf.eject_mode(),
128            self.transactions_path.eject_mode(),
129            self.transaction_id.eject_mode(),
130            self.transaction_path.eject_mode(),
131            self.transaction_leaf.eject_mode(),
132            self.transition_root.eject_mode(),
133            self.tcm.eject_mode(),
134            self.transition_path.eject_mode(),
135            self.transition_leaf.eject_mode(),
136        ])
137    }
138
139    /// Ejects the state path.
140    fn eject_value(&self) -> Self::Primitive {
141        Self::Primitive::from(
142            self.global_state_root.eject_value().into(),
143            self.block_path.eject_value(),
144            self.block_hash.eject_value().into(),
145            self.previous_block_hash.eject_value().into(),
146            self.header_root.eject_value(),
147            self.header_path.eject_value(),
148            self.header_leaf.eject_value(),
149            self.transactions_path.eject_value(),
150            self.transaction_id.eject_value().into(),
151            self.transaction_path.eject_value(),
152            self.transaction_leaf.eject_value(),
153            self.transition_root.eject_value(),
154            self.tcm.eject_value(),
155            self.transition_path.eject_value(),
156            self.transition_leaf.eject_value(),
157        )
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164    use crate::Circuit;
165
166    use snarkvm_utilities::TestRng;
167
168    use anyhow::Result;
169
170    type CurrentNetwork = <Circuit as Environment>::Network;
171
172    const ITERATIONS: u64 = 250;
173
174    fn check_new(
175        mode: Mode,
176        num_constants: u64,
177        num_public: u64,
178        num_private: u64,
179        num_constraints: u64,
180    ) -> Result<()> {
181        let rng = &mut TestRng::default();
182
183        for _ in 0..ITERATIONS {
184            // Sample the console state path.
185            let console_state_path =
186                console::state_path::test_helpers::sample_local_state_path::<CurrentNetwork>(None, rng).unwrap();
187
188            Circuit::scope(format!("New {mode}"), || {
189                let candidate = StatePath::<Circuit>::new(mode, console_state_path.clone());
190                assert_eq!(console_state_path, candidate.eject_value());
191                assert_scope!(num_constants, num_public, num_private, num_constraints);
192            });
193            Circuit::reset();
194        }
195        Ok(())
196    }
197
198    #[test]
199    fn test_state_path_new_constant() -> Result<()> {
200        check_new(Mode::Constant, 450, 1, 0, 0)
201    }
202
203    #[test]
204    fn test_state_path_new_public() -> Result<()> {
205        check_new(Mode::Public, 0, 451, 0, 376)
206    }
207
208    #[test]
209    fn test_state_path_new_private() -> Result<()> {
210        check_new(Mode::Private, 0, 1, 450, 376)
211    }
212}