Skip to main content

miden_node_store/state/view/
state_witnesses.rs

1//! State witness query.
2
3use std::collections::BTreeMap;
4
5use miden_protocol::account::AccountId;
6use miden_protocol::block::account_tree::AccountWitness;
7use miden_protocol::block::nullifier_tree::NullifierWitness;
8use miden_protocol::note::Nullifier;
9
10use super::StateView;
11
12/// State witnesses for the requested accounts and nullifiers.
13#[derive(Clone, Debug)]
14pub struct StateWitnesses {
15    pub account_witnesses: BTreeMap<AccountId, AccountWitness>,
16    pub nullifier_witnesses: BTreeMap<Nullifier, NullifierWitness>,
17}
18
19impl StateView {
20    /// Returns state witnesses for the requested accounts and nullifiers.
21    pub fn get_state_witnesses(
22        &self,
23        account_ids: &[AccountId],
24        nullifiers: &[Nullifier],
25    ) -> StateWitnesses {
26        self.with_inner_read_blocking(|inner| {
27            // Fetch witnesses for all accounts.
28            let account_witnesses = account_ids
29                .iter()
30                .copied()
31                .map(|account_id| (account_id, inner.account_tree.open_latest(account_id)))
32                .collect::<BTreeMap<AccountId, AccountWitness>>();
33
34            // Fetch all nullifier witnesses. Block proposal checks whether each nullifier is spent.
35            let nullifier_witnesses: BTreeMap<Nullifier, NullifierWitness> = nullifiers
36                .iter()
37                .copied()
38                .map(|nullifier| (nullifier, inner.nullifier_tree.open(&nullifier)))
39                .collect();
40
41            StateWitnesses { account_witnesses, nullifier_witnesses }
42        })
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use miden_node_utils::fee::{test_fee_params, test_protocol_config};
49    use miden_protocol::Word;
50    use miden_protocol::block::ValidatorConfig;
51    use miden_protocol::note::Nullifier;
52    use miden_protocol::testing::random_secret_key::random_secret_key;
53
54    use crate::GenesisState;
55    use crate::state::State;
56
57    #[tokio::test(flavor = "multi_thread")]
58    async fn state_witnesses_include_requested_nullifiers() {
59        let data_directory = tempfile::tempdir().expect("tempdir should be created");
60        bootstrap_store(data_directory.path());
61        let (state, _block_writer, _proof_writer) = State::for_tests(data_directory.path()).await;
62
63        let nullifier = Nullifier::from_raw(Word::from([1_u32, 2, 3, 4]));
64        let witnesses = state.view().get_state_witnesses(&[], &[nullifier]);
65
66        assert!(witnesses.account_witnesses.is_empty());
67        assert!(witnesses.nullifier_witnesses.contains_key(&nullifier));
68    }
69
70    fn bootstrap_store(path: &std::path::Path) {
71        let signer = random_secret_key();
72        let genesis_state = GenesisState::new(
73            vec![],
74            test_fee_params(),
75            1,
76            ValidatorConfig::new(vec![signer.public_key()], 1)
77                .expect("validator config should be valid"),
78            test_protocol_config(),
79        );
80        let genesis_block = genesis_state.into_block().expect("genesis block should be created");
81
82        State::bootstrap(genesis_block, path).expect("store should bootstrap");
83    }
84}