static_.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 crate::QueryTrait; 17 18 use alphavm_console::{network::prelude::*, program::StatePath, types::Field}; 19 20 use anyhow::{Context, Result, ensure}; 21 use serde::Deserialize; 22 use std::{collections::HashMap, str::FromStr}; 23 24 #[derive(Clone)] 25 pub struct StaticQuery<N: Network> { 26 block_height: u32, 27 state_root: N::StateRoot, 28 state_paths: HashMap<Field<N>, StatePath<N>>, 29 } 30 31 impl<N: Network> StaticQuery<N> { 32 pub fn new(block_height: u32, state_root: N::StateRoot, state_paths: HashMap<Field<N>, StatePath<N>>) -> Self { 33 Self { block_height, state_root, state_paths } 34 } 35 } 36 37 #[derive(Deserialize)] 38 struct StaticQueryInput { 39 state_root: String, 40 height: u32, 41 } 42 43 impl<N: Network> FromStr for StaticQuery<N> { 44 type Err = anyhow::Error; 45 46 fn from_str(s: &str) -> Result<Self> { 47 ensure!(s.trim().starts_with('{'), "Not a static query"); 48 49 let input: StaticQueryInput = serde_json::from_str(s).with_context(|| "Invalid JSON format in static query")?; 50 let state_root = N::StateRoot::from_str(&input.state_root).map_err(|_| anyhow!("Invalid state root format"))?; 51 52 Ok(Self { state_root, block_height: input.height, state_paths: HashMap::new() }) 53 } 54 } 55 56 #[cfg_attr(feature = "async", async_trait::async_trait(?Send))] 57 impl<N: Network> QueryTrait<N> for StaticQuery<N> { 58 /// Returns the current state root. 59 fn current_state_root(&self) -> Result<N::StateRoot> { 60 Ok(self.state_root) 61 } 62 63 /// Returns the current state root (async version). 64 #[cfg(feature = "async")] 65 async fn current_state_root_async(&self) -> Result<N::StateRoot> { 66 // There is no I/O in StaticQuery, so the sync version is identical. 67 self.current_state_root() 68 } 69 70 /// Returns a state path for the given `commitment`. 71 fn get_state_path_for_commitment(&self, commitment: &Field<N>) -> Result<StatePath<N>> { 72 match self.state_paths.get(commitment) { 73 Some(state_path) => Ok(state_path.clone()), 74 None => bail!("Could not find state path for commitment '{commitment}'"), 75 } 76 } 77 78 /// Returns a state path for the given `commitment` (async version). 79 #[cfg(feature = "async")] 80 async fn get_state_path_for_commitment_async(&self, commitment: &Field<N>) -> Result<StatePath<N>> { 81 // There is no I/O in StaticQuery, so the sync version is identical. 82 self.get_state_path_for_commitment(commitment) 83 } 84 85 /// Returns a list of state paths for the given list of `commitment`s. 86 fn get_state_paths_for_commitments(&self, commitments: &[Field<N>]) -> Result<Vec<StatePath<N>>> { 87 commitments 88 .iter() 89 .map(|commitment| self.get_state_path_for_commitment(commitment)) 90 .collect::<Result<Vec<StatePath<N>>>>() 91 } 92 93 /// Returns a list of state paths for the given list of `commitment`s (async version). 94 #[cfg(feature = "async")] 95 async fn get_state_paths_for_commitments_async(&self, commitments: &[Field<N>]) -> Result<Vec<StatePath<N>>> { 96 // There is no I/O in StaticQuery, so the sync version is identical. 97 self.get_state_paths_for_commitments(commitments) 98 } 99 100 /// Returns the current block height. 101 fn current_block_height(&self) -> Result<u32> { 102 Ok(self.block_height) 103 } 104 105 /// Returns the current block height (async version). 106 #[cfg(feature = "async")] 107 async fn current_block_height_async(&self) -> Result<u32> { 108 // There is no I/O in StaticQuery, so the sync version is identical. 109 self.current_block_height() 110 } 111 } 112 113 #[cfg(test)] 114 mod tests { 115 use super::*; 116 use alphavm_console::network::TestnetV0; 117 118 #[test] 119 fn test_static_query_parse() { 120 let json = r#"{"state_root": "sr1dz06ur5spdgzkguh4pr42mvft6u3nwsg5drh9rdja9v8jpcz3czsls9geg", "height": 14}"# 121 .to_string(); 122 let query: Result<StaticQuery<TestnetV0>> = json.parse(); 123 assert!(query.is_ok()); 124 } 125 126 #[test] 127 fn test_static_query_parse_invalid() { 128 let json = r#"{"invalid_key": "sr1dz06ur5spdgzkguh4pr42mvft6u3nwsg5drh9rdja9v8jpcz3czsls9geg", "height": 14}"# 129 .to_string(); 130 let query: Result<StaticQuery<TestnetV0>> = json.parse(); 131 assert!(query.is_err()); 132 } 133 }