/ console / program / src / state_path / verify.rs
verify.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm 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  
 16  use super::*;
 17  
 18  impl<N: Network> StatePath<N> {
 19      /// Checks if the state path is valid.
 20      ///
 21      /// # Parameters
 22      ///  - `local_state_root` is the local transaction root for the current execution.
 23      ///  - `is_global` is a boolean indicating whether this is a global or local state root.
 24      ///
 25      /// # Diagram
 26      /// The `[[ ]]` notation is used to denote public inputs.
 27      /// ```ignore
 28      ///
 29      ///  [[ global_state_root ]]
 30      ///           |
 31      ///      block_path
 32      ///          |
 33      ///     block_hash := Hash( previous_block_hash || header_root )
 34      ///                                                     |
 35      ///                                                header_path
 36      ///                                                    |
 37      ///                                               header_leaf
 38      ///                                                   |
 39      ///                                            transactions_path          [[ local_state_root ]]
 40      ///                                                  |                               |
 41      ///                                               (true) ------ is_global ------ (false)
 42      ///                                                                 |
 43      ///                                                          transaction_id
 44      ///                                                                |
 45      ///                                                        transaction_path
 46      ///                                                               |
 47      ///                                                       transaction_leaf
 48      ///                                                              |
 49      ///                                                      transition_id := Hash( transition_root || tcm )
 50      ///                                                                                  |
 51      ///                                                                           transition_path
 52      ///                                                                                 |
 53      ///                                                                          transition_leaf
 54      /// ```
 55      pub fn verify(&self, is_global: bool, local_state_root: Field<N>) -> Result<()> {
 56          // Ensure the transition leaf variant is 3 (Input::Record).
 57          ensure!(self.transition_leaf.variant() == 3, "Transition leaf variant must be 3 (Input::Record)");
 58          // Ensure the transition path is valid.
 59          ensure!(
 60              N::verify_merkle_path_bhp(&self.transition_path, &self.transition_root, &self.transition_leaf.to_bits_le()),
 61              "'{}' (an input or output ID) does not belong to '{}' (a function or transition)",
 62              self.transition_leaf.id(),
 63              self.transaction_leaf.id()
 64          );
 65  
 66          // Ensure the transaction leaf is correct.
 67          ensure!(
 68              *self.transaction_leaf.id() == *N::hash_bhp512(&(*self.transition_root, self.tcm).to_bits_le())?,
 69              "Transaction leaf id '{}' is incorrect. Double-check the tcm and transition root.",
 70              self.transaction_leaf.id()
 71          );
 72  
 73          // Ensure the transaction leaf variant is 1 (Transaction::Execution).
 74          ensure!(self.transaction_leaf.variant() == 1, "Transaction leaf variant must be 1 (Transaction::Execution)");
 75          // Ensure the transaction path is valid.
 76          ensure!(
 77              N::verify_merkle_path_bhp(
 78                  &self.transaction_path,
 79                  &self.transaction_id,
 80                  &self.transaction_leaf.to_bits_le()
 81              ),
 82              "'{}' (a function or transition) does not belong to transaction '{}'",
 83              self.transaction_leaf.id(),
 84              self.transaction_id
 85          );
 86  
 87          if is_global {
 88              // Ensure the header leaf index is 1 (Header::transactions_root).
 89              ensure!(self.header_leaf.index() == 1, "Header leaf index must be 1 (Header::transactions_root)");
 90              // Ensure the transactions path is valid.
 91              ensure!(
 92                  N::verify_merkle_path_bhp(
 93                      &self.transactions_path,
 94                      &self.header_leaf.id(),
 95                      &self.transaction_id.to_bits_le()
 96                  ),
 97                  "Transaction '{}' does not belong to '{}' (a header leaf)",
 98                  self.transaction_id,
 99                  self.header_leaf
100              );
101              // Ensure the header path is valid.
102              ensure!(
103                  N::verify_merkle_path_bhp(&self.header_path, &self.header_root, &self.header_leaf.to_bits_le()),
104                  "'{}' (a header leaf) does not belong to '{}' (a block header)",
105                  self.header_leaf,
106                  self.block_hash
107              );
108              // Ensure the block hash is correct.
109              ensure!(
110                  *self.block_hash == N::hash_bhp1024(&to_bits_le![(*self.previous_block_hash), self.header_root])?,
111                  "Block hash '{}' is incorrect. Double-check the previous block hash and block header root.",
112                  self.block_hash
113              );
114              // Ensure the global state root is correct.
115              ensure!(
116                  N::verify_merkle_path_bhp(&self.block_path, &self.global_state_root, &self.block_hash.to_bits_le()),
117                  "'{}' (a block hash) does not belong to '{}' (a global state root)",
118                  self.block_hash,
119                  self.global_state_root
120              );
121          } else {
122              // Ensure the local state root is correct.
123              ensure!(
124                  *self.transaction_id == local_state_root,
125                  "'{}' (a decoded transaction ID) does not match the '{local_state_root}' (a local state root)",
126                  *self.transaction_id
127              );
128          }
129  
130          Ok(())
131      }
132  }
133  
134  #[cfg(test)]
135  mod tests {
136      use super::*;
137      use alphavm_console_network::{MainnetV0, prelude::TestRng};
138  
139      type CurrentNetwork = MainnetV0;
140  
141      const ITERATIONS: usize = 100;
142  
143      #[test]
144      fn test_verify_global() {
145          let rng = &mut TestRng::default();
146  
147          for _ in 0..ITERATIONS {
148              // Sample the state path.
149              let state_path =
150                  crate::state_path::test_helpers::sample_global_state_path::<CurrentNetwork>(None, rng).unwrap();
151              // Sample the local state root.
152              let local_state_root = Field::rand(rng);
153  
154              // Ensure the state path is valid.
155              state_path.verify(true, local_state_root).unwrap();
156              // Ensure the state path is *not* valid for a random local state root.
157              state_path.verify(false, local_state_root).unwrap_err();
158          }
159      }
160  
161      #[test]
162      fn test_verify_local() {
163          let rng = &mut TestRng::default();
164  
165          for _ in 0..ITERATIONS {
166              // Sample the state path.
167              let state_path =
168                  crate::state_path::test_helpers::sample_local_state_path::<CurrentNetwork>(None, rng).unwrap();
169              // Retrieve the local state root.
170              let local_state_root = **state_path.transaction_id();
171  
172              // Ensure the state path is valid.
173              state_path.verify(false, local_state_root).unwrap();
174              // Ensure the state path does *not* match a random local state root.
175              state_path.verify(false, Field::rand(rng)).unwrap_err();
176              // Ensure the state path does *not* match to the random global state root.
177              state_path.verify(true, local_state_root).unwrap_err();
178              // Ensure the state path does *not* match to the random global state root.
179              state_path.verify(true, Field::rand(rng)).unwrap_err();
180          }
181      }
182  
183      #[test]
184      fn test_verify_new_local() {
185          let rng = &mut TestRng::default();
186  
187          for _ in 0..ITERATIONS {
188              // Sample the state path.
189              let state_path =
190                  crate::state_path::test_helpers::sample_local_state_path::<CurrentNetwork>(None, rng).unwrap();
191  
192              // Initialize the state path using `new_local`.
193              let new_local_state_path = StatePath::new_local(
194                  state_path.global_state_root(),
195                  *state_path.transaction_id(),
196                  state_path.transaction_path().clone(),
197                  *state_path.transaction_leaf(),
198                  *state_path.transition_root(),
199                  *state_path.tcm(),
200                  state_path.transition_path().clone(),
201                  *state_path.transition_leaf(),
202              )
203              .unwrap();
204  
205              // Retrieve the local state root.
206              let local_state_root = **new_local_state_path.transaction_id();
207  
208              // Ensure the state path is valid.
209              new_local_state_path.verify(false, local_state_root).unwrap();
210              // Ensure the state path does *not* match a random local state root.
211              new_local_state_path.verify(false, Field::rand(rng)).unwrap_err();
212              // Ensure the state path does *not* match to the random global state root.
213              new_local_state_path.verify(true, local_state_root).unwrap_err();
214              // Ensure the state path does *not* match to the random global state root.
215              new_local_state_path.verify(true, Field::rand(rng)).unwrap_err();
216          }
217      }
218  }