find.rs
1 // Copyright (c) 2025 ADnet Contributors 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 use alphavm_utilities::flatten_error; 19 20 impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> { 21 /// Returns the block height that contains the given `state root`. 22 pub fn find_block_height_from_state_root(&self, state_root: N::StateRoot) -> Result<Option<u32>> { 23 self.vm.block_store().find_block_height_from_state_root(state_root) 24 } 25 26 /// Returns the block hash that contains the given `transaction ID`. 27 pub fn find_block_hash(&self, transaction_id: &N::TransactionID) -> Result<Option<N::BlockHash>> { 28 self.vm.block_store().find_block_hash(transaction_id) 29 } 30 31 /// Returns the block height that contains the given `solution ID`. 32 pub fn find_block_height_from_solution_id(&self, solution_id: &SolutionID<N>) -> Result<Option<u32>> { 33 self.vm.block_store().find_block_height_from_solution_id(solution_id) 34 } 35 36 /// Returns the latest transaction ID that contains the given `program ID`. 37 pub fn find_latest_transaction_id_from_program_id( 38 &self, 39 program_id: &ProgramID<N>, 40 ) -> Result<Option<N::TransactionID>> { 41 self.vm.transaction_store().find_latest_transaction_id_from_program_id(program_id) 42 } 43 44 /// Returns the transaction ID that contains the given `program ID` and `edition`. 45 pub fn find_transaction_id_from_program_id_and_edition( 46 &self, 47 program_id: &ProgramID<N>, 48 edition: u16, 49 ) -> Result<Option<N::TransactionID>> { 50 self.vm.transaction_store().find_transaction_id_from_program_id_and_edition(program_id, edition) 51 } 52 53 /// Returns the transaction ID that contains the given `transition ID`. 54 pub fn find_transaction_id_from_transition_id( 55 &self, 56 transition_id: &N::TransitionID, 57 ) -> Result<Option<N::TransactionID>> { 58 self.vm.transaction_store().find_transaction_id_from_transition_id(transition_id) 59 } 60 61 /// Returns the transition ID that contains the given `input ID` or `output ID`. 62 pub fn find_transition_id(&self, id: &Field<N>) -> Result<N::TransitionID> { 63 self.vm.transition_store().find_transition_id(id) 64 } 65 66 /// Returns the record ciphertexts that belong to the given view key. 67 #[allow(clippy::type_complexity)] 68 pub fn find_record_ciphertexts<'a>( 69 &'a self, 70 view_key: &'a ViewKey<N>, 71 filter: RecordsFilter<N>, 72 ) -> Result<impl 'a + Iterator<Item = (Field<N>, Cow<'a, Record<N, Ciphertext<N>>>)>> { 73 // Derive the x-coordinate of the address corresponding to the given view key. 74 let address_x_coordinate = view_key.to_address().to_x_coordinate(); 75 // Derive the `sk_tag` from the graph key. 76 let sk_tag = match GraphKey::try_from(view_key) { 77 Ok(graph_key) => graph_key.sk_tag(), 78 Err(e) => bail!("Failed to derive the graph key from the view key: {e}"), 79 }; 80 81 Ok(self.records().flat_map(move |cow| { 82 // Retrieve the commitment and record. 83 let (commitment, record) = match cow { 84 (Cow::Borrowed(commitment), record) => (*commitment, record), 85 (Cow::Owned(commitment), record) => (commitment, record), 86 }; 87 88 // Check ownership before determining whether to decrypt the record. 89 if !record.is_owner_with_address_x_coordinate(view_key, &address_x_coordinate) { 90 return None; 91 } 92 93 // Determine whether to decrypt this record (or not), based on the filter. 94 let commitment = match filter { 95 RecordsFilter::All => Ok(Some(commitment)), 96 RecordsFilter::Spent => Record::<N, Plaintext<N>>::tag(sk_tag, commitment).and_then(|tag| { 97 // Determine if the record is spent. 98 self.contains_tag(&tag).map(|is_spent| match is_spent { 99 true => Some(commitment), 100 false => None, 101 }) 102 }), 103 RecordsFilter::Unspent => Record::<N, Plaintext<N>>::tag(sk_tag, commitment).and_then(|tag| { 104 // Determine if the record is spent. 105 self.contains_tag(&tag).map(|is_spent| match is_spent { 106 true => None, 107 false => Some(commitment), 108 }) 109 }), 110 RecordsFilter::SlowSpent(private_key) => { 111 Record::<N, Plaintext<N>>::serial_number(private_key, commitment).and_then(|serial_number| { 112 // Determine if the record is spent. 113 self.contains_serial_number(&serial_number).map(|is_spent| match is_spent { 114 true => Some(commitment), 115 false => None, 116 }) 117 }) 118 } 119 RecordsFilter::SlowUnspent(private_key) => { 120 Record::<N, Plaintext<N>>::serial_number(private_key, commitment).and_then(|serial_number| { 121 // Determine if the record is spent. 122 self.contains_serial_number(&serial_number).map(|is_spent| match is_spent { 123 true => None, 124 false => Some(commitment), 125 }) 126 }) 127 } 128 }; 129 130 match commitment { 131 Ok(Some(commitment)) => Some((commitment, record)), 132 Ok(None) => None, 133 Err(err) => { 134 warn!("{}", &flatten_error(err.context("Failed to process 'find_record_ciphertexts({filter:?})'"))); 135 None 136 } 137 } 138 })) 139 } 140 141 /// Returns the records that belong to the given view key. 142 #[allow(clippy::type_complexity)] 143 pub fn find_records<'a>( 144 &'a self, 145 view_key: &'a ViewKey<N>, 146 filter: RecordsFilter<N>, 147 ) -> Result<impl 'a + Iterator<Item = (Field<N>, Record<N, Plaintext<N>>)>> { 148 self.find_record_ciphertexts(view_key, filter).map(|iter| { 149 iter.flat_map(|(commitment, record)| match record.decrypt(view_key) { 150 Ok(record) => Some((commitment, record)), 151 Err(err) => { 152 warn!("{}", &flatten_error(err.context("Failed to decrypt record"))); 153 None 154 } 155 }) 156 }) 157 } 158 }