snarkvm_ledger_debug/
find.rs

1// Copyright (C) 2019-2023 Aleo Systems 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// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::*;
16
17impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
18    /// Returns the block height that contains the given `state root`.
19    pub fn find_block_height_from_state_root(&self, state_root: N::StateRoot) -> Result<Option<u32>> {
20        self.vm.block_store().find_block_height_from_state_root(state_root)
21    }
22
23    /// Returns the block hash that contains the given `transaction ID`.
24    pub fn find_block_hash(&self, transaction_id: &N::TransactionID) -> Result<Option<N::BlockHash>> {
25        self.vm.block_store().find_block_hash(transaction_id)
26    }
27
28    /// Returns the block height that contains the given `puzzle commitment`.
29    pub fn find_block_height_from_puzzle_commitment(
30        &self,
31        puzzle_commitment: &PuzzleCommitment<N>,
32    ) -> Result<Option<u32>> {
33        self.vm.block_store().find_block_height_from_puzzle_commitment(puzzle_commitment)
34    }
35
36    /// Returns the transaction ID that contains the given `program ID`.
37    pub fn find_transaction_id_from_program_id(&self, program_id: &ProgramID<N>) -> Result<Option<N::TransactionID>> {
38        self.vm.transaction_store().find_transaction_id_from_program_id(program_id)
39    }
40
41    /// Returns the transaction ID that contains the given `transition ID`.
42    pub fn find_transaction_id_from_transition_id(
43        &self,
44        transition_id: &N::TransitionID,
45    ) -> Result<Option<N::TransactionID>> {
46        self.vm.transaction_store().find_transaction_id_from_transition_id(transition_id)
47    }
48
49    /// Returns the transition ID that contains the given `input ID` or `output ID`.
50    pub fn find_transition_id(&self, id: &Field<N>) -> Result<N::TransitionID> {
51        self.vm.transition_store().find_transition_id(id)
52    }
53
54    /// Returns the record ciphertexts that belong to the given view key.
55    pub fn find_record_ciphertexts<'a>(
56        &'a self,
57        view_key: &'a ViewKey<N>,
58        filter: RecordsFilter<N>,
59    ) -> Result<impl '_ + Iterator<Item = (Field<N>, Cow<'_, Record<N, Ciphertext<N>>>)>> {
60        // Derive the x-coordinate of the address corresponding to the given view key.
61        let address_x_coordinate = view_key.to_address().to_x_coordinate();
62        // Derive the `sk_tag` from the graph key.
63        let sk_tag = match GraphKey::try_from(view_key) {
64            Ok(graph_key) => graph_key.sk_tag(),
65            Err(e) => bail!("Failed to derive the graph key from the view key: {e}"),
66        };
67
68        Ok(self.records().flat_map(move |cow| {
69            // Retrieve the commitment and record.
70            let (commitment, record) = match cow {
71                (Cow::Borrowed(commitment), record) => (*commitment, record),
72                (Cow::Owned(commitment), record) => (commitment, record),
73            };
74
75            // Determine whether to decrypt this record (or not), based on the filter.
76            let commitment = match filter {
77                RecordsFilter::All => Ok(Some(commitment)),
78                RecordsFilter::Spent => Record::<N, Plaintext<N>>::tag(sk_tag, commitment).and_then(|tag| {
79                    // Determine if the record is spent.
80                    self.contains_tag(&tag).map(|is_spent| match is_spent {
81                        true => Some(commitment),
82                        false => None,
83                    })
84                }),
85                RecordsFilter::Unspent => Record::<N, Plaintext<N>>::tag(sk_tag, commitment).and_then(|tag| {
86                    // Determine if the record is spent.
87                    self.contains_tag(&tag).map(|is_spent| match is_spent {
88                        true => None,
89                        false => Some(commitment),
90                    })
91                }),
92                RecordsFilter::SlowSpent(private_key) => {
93                    Record::<N, Plaintext<N>>::serial_number(private_key, commitment).and_then(|serial_number| {
94                        // Determine if the record is spent.
95                        self.contains_serial_number(&serial_number).map(|is_spent| match is_spent {
96                            true => Some(commitment),
97                            false => None,
98                        })
99                    })
100                }
101                RecordsFilter::SlowUnspent(private_key) => {
102                    Record::<N, Plaintext<N>>::serial_number(private_key, commitment).and_then(|serial_number| {
103                        // Determine if the record is spent.
104                        self.contains_serial_number(&serial_number).map(|is_spent| match is_spent {
105                            true => None,
106                            false => Some(commitment),
107                        })
108                    })
109                }
110            };
111
112            match commitment {
113                Ok(Some(commitment)) => {
114                    match record.is_owner_with_address_x_coordinate(view_key, &address_x_coordinate) {
115                        true => Some((commitment, record)),
116                        false => None,
117                    }
118                }
119                Ok(None) => None,
120                Err(e) => {
121                    warn!("Failed to process 'find_record_ciphertexts({:?})': {e}", filter);
122                    None
123                }
124            }
125        }))
126    }
127
128    /// Returns the records that belong to the given view key.
129    pub fn find_records<'a>(
130        &'a self,
131        view_key: &'a ViewKey<N>,
132        filter: RecordsFilter<N>,
133    ) -> Result<impl '_ + Iterator<Item = (Field<N>, Record<N, Plaintext<N>>)>> {
134        self.find_record_ciphertexts(view_key, filter).map(|iter| {
135            iter.flat_map(|(commitment, record)| match record.decrypt(view_key) {
136                Ok(record) => Some((commitment, record)),
137                Err(e) => {
138                    warn!("Failed to decrypt the record: {e}");
139                    None
140                }
141            })
142        })
143    }
144}