Documentation Index
Fetch the complete documentation index at: https://mintlify.com/provablehq/snarkvm/llms.txt
Use this file to discover all available pages before exploring further.
The QueryTrait provides a standardized interface for querying blockchain state. It’s used throughout SnarkVM to enable both synchronous and asynchronous state access.
QueryTrait Interface
The query trait defines methods for accessing current state and historical data.
pub trait QueryTrait<N: Network> {
fn current_state_root(&self) -> Result<N::StateRoot>;
fn get_state_path_for_commitment(
&self,
commitment: &Field<N>
) -> Result<StatePath<N>>;
fn get_state_paths_for_commitments(
&self,
commitments: &[Field<N>]
) -> Result<Vec<StatePath<N>>>;
fn current_block_height(&self) -> Result<u32>;
}
Async Variant
When the async feature is enabled, async methods are also available:
#[cfg(feature = "async")]
#[async_trait::async_trait(?Send)]
pub trait QueryTrait<N: Network> {
async fn current_state_root_async(&self) -> Result<N::StateRoot>;
async fn get_state_path_for_commitment_async(
&self,
commitment: &Field<N>
) -> Result<StatePath<N>>;
async fn get_state_paths_for_commitments_async(
&self,
commitments: &[Field<N>]
) -> Result<Vec<StatePath<N>>>;
async fn current_block_height_async(&self) -> Result<u32>;
}
Query Methods
Current State Root
Returns the current state root of the blockchain.
fn current_state_root(&self) -> Result<N::StateRoot>
The state root is a Merkle root that commits to the entire blockchain state at the current block height. It’s used in SNARKs to prove statements about on-chain data.
Example:
use snarkvm_ledger::query::QueryTrait;
let state_root = ledger.current_state_root()?;
println!("Current state root: {}", state_root);
State Path for Commitment
Returns a Merkle path proving a commitment is included in the state.
fn get_state_path_for_commitment(
&self,
commitment: &Field<N>
) -> Result<StatePath<N>>
Parameters:
commitment - The record commitment to prove
Returns:
StatePath<N> - Merkle path from commitment to state root
Use cases:
- Proving record ownership in SNARKs
- Verifying a record exists on-chain
- Generating inclusion proofs for light clients
Example:
// Get a commitment from a transaction output
let commitment = transaction.commitments().next().unwrap();
// Get the state path
let state_path = ledger.get_state_path_for_commitment(commitment)?;
// Verify the path
let state_root = ledger.current_state_root()?;
assert!(state_path.verify(&state_root, commitment));
State Paths for Multiple Commitments
Returns Merkle paths for multiple commitments in a single query.
fn get_state_paths_for_commitments(
&self,
commitments: &[Field<N>]
) -> Result<Vec<StatePath<N>>>
Parameters:
commitments - Slice of commitments to prove
Returns:
Vec<StatePath<N>> - State paths in the same order as inputs
Performance:
This method is more efficient than calling get_state_path_for_commitment repeatedly, as it can batch database lookups.
Example:
// Collect commitments from multiple transactions
let commitments: Vec<_> = transactions
.iter()
.flat_map(|tx| tx.commitments())
.cloned()
.collect();
// Get all state paths in one call
let state_paths = ledger.get_state_paths_for_commitments(&commitments)?;
assert_eq!(state_paths.len(), commitments.len());
Current Block Height
Returns the height of the latest block in the ledger.
fn current_block_height(&self) -> Result<u32>
Returns:
u32 - The current block height (genesis is 0)
Example:
let height = ledger.current_block_height()?;
println!("Current block height: {}", height);
StatePath
A StatePath is a Merkle path proving a commitment is included in the blockchain state.
pub struct StatePath<N: Network> {
global_state_root: N::StateRoot,
path: Vec<(Field<N>, Field<N>)>,
}
Verification
impl<N: Network> StatePath<N> {
pub fn verify(
&self,
state_root: &N::StateRoot,
commitment: &Field<N>,
) -> bool
}
Example:
// Get state path
let state_path = ledger.get_state_path_for_commitment(&commitment)?;
let state_root = ledger.current_state_root()?;
// Verify the path
if state_path.verify(&state_root, &commitment) {
println!("Commitment is in the blockchain state");
} else {
println!("Invalid state path");
}
Implementing QueryTrait
You can implement QueryTrait for custom types to enable querying.
Example: Ledger Implementation
impl<N: Network, C: ConsensusStorage<N>> QueryTrait<N> for Ledger<N, C> {
fn current_state_root(&self) -> Result<N::StateRoot> {
Ok(self.latest_state_root())
}
fn get_state_path_for_commitment(
&self,
commitment: &Field<N>
) -> Result<StatePath<N>> {
self.vm.block_store().get_state_path_for_commitment(commitment)
}
fn get_state_paths_for_commitments(
&self,
commitments: &[Field<N>]
) -> Result<Vec<StatePath<N>>> {
self.vm.block_store().get_state_paths_for_commitments(commitments)
}
fn current_block_height(&self) -> Result<u32> {
Ok(self.latest_height())
}
}
Example: BlockStore Implementation
impl<N: Network, B: BlockStorage<N>> QueryTrait<N> for BlockStore<N, B> {
fn current_state_root(&self) -> Result<N::StateRoot> {
Ok(self.current_state_root())
}
fn get_state_path_for_commitment(
&self,
commitment: &Field<N>
) -> Result<StatePath<N>> {
self.get_state_path_for_commitment(commitment)
}
fn get_state_paths_for_commitments(
&self,
commitments: &[Field<N>]
) -> Result<Vec<StatePath<N>>> {
self.get_state_paths_for_commitments(commitments)
}
fn current_block_height(&self) -> Result<u32> {
Ok(self.current_block_height())
}
}
Query Wrapper
For testing and development, the Query type wraps a BlockStore to implement QueryTrait.
use snarkvm_ledger::query::Query;
use snarkvm_ledger::store::BlockStore;
let block_store = BlockStore::open(storage)?;
let query = Query::from(block_store);
// Now you can use query methods
let state_root = query.current_state_root()?;
let height = query.current_block_height()?;
Usage in Transaction Creation
The QueryTrait is commonly used when creating transactions to provide state access to the VM.
use snarkvm_ledger::prelude::*;
// Create a transfer transaction
let transaction = ledger.create_transfer(
&private_key,
recipient,
amount,
fee,
Some(&ledger as &dyn QueryTrait<N>), // Pass query interface
&mut rng,
)?;
The VM uses the query interface to:
- Get state paths for input records
- Verify records are unspent
- Access the current block height for transaction validation
Async Query Operations
When the async feature is enabled, you can use async query methods:
#[cfg(feature = "async")]
use snarkvm_ledger::query::QueryTrait;
// Async state root query
let state_root = ledger.current_state_root_async().await?;
// Async state path query
let state_path = ledger
.get_state_path_for_commitment_async(&commitment)
.await?;
// Async batch query
let state_paths = ledger
.get_state_paths_for_commitments_async(&commitments)
.await?;
// Async block height
let height = ledger.current_block_height_async().await?;
Example: Complete Query Workflow
use snarkvm_ledger::prelude::*;
use snarkvm_ledger::query::QueryTrait;
// Setup
let ledger = Ledger::load(genesis, storage_mode)?;
// Get current state
let state_root = ledger.current_state_root()?;
let height = ledger.current_block_height()?;
println!("Current state at height {}", height);
println!("State root: {}", state_root);
// Get a block
let block = ledger.get_block(height)?;
// Extract commitments
let commitments: Vec<_> = block.commitments().cloned().collect();
if !commitments.is_empty() {
// Get state paths for all commitments
let state_paths = ledger.get_state_paths_for_commitments(&commitments)?;
// Verify each path
for (commitment, state_path) in commitments.iter().zip(state_paths.iter()) {
if state_path.verify(&state_root, commitment) {
println!("✓ Verified commitment: {}", commitment);
} else {
println!("✗ Failed to verify commitment: {}", commitment);
}
}
}
Batch Queries
Always use batch query methods when querying multiple items:
// Bad: O(n) database queries
let mut state_paths = Vec::new();
for commitment in commitments {
state_paths.push(ledger.get_state_path_for_commitment(commitment)?);
}
// Good: O(1) or O(log n) database queries
let state_paths = ledger.get_state_paths_for_commitments(&commitments)?;
Cache Current State
If you need the state root or height multiple times, cache it:
// Cache current state
let state_root = ledger.current_state_root()?;
let height = ledger.current_block_height()?;
// Use cached values
for commitment in commitments {
let state_path = ledger.get_state_path_for_commitment(commitment)?;
assert!(state_path.verify(&state_root, commitment));
}
Use Async for I/O-Bound Operations
If performing many queries, async methods can improve throughput:
#[cfg(feature = "async")]
use futures::future::join_all;
// Query multiple commitments concurrently
let futures: Vec<_> = commitments
.iter()
.map(|c| ledger.get_state_path_for_commitment_async(c))
.collect();
let state_paths = join_all(futures).await;
Next Steps