find.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphavm library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 use super::*; 20 21 use alphavm_utilities::flatten_error; 22 23 impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> { 24 /// Returns the block height that contains the given `state root`. 25 pub fn find_block_height_from_state_root(&self, state_root: N::StateRoot) -> Result<Option<u32>> { 26 self.vm.block_store().find_block_height_from_state_root(state_root) 27 } 28 29 /// Returns the block hash that contains the given `transaction ID`. 30 pub fn find_block_hash(&self, transaction_id: &N::TransactionID) -> Result<Option<N::BlockHash>> { 31 self.vm.block_store().find_block_hash(transaction_id) 32 } 33 34 /// Returns the block height that contains the given `solution ID`. 35 pub fn find_block_height_from_solution_id(&self, solution_id: &SolutionID<N>) -> Result<Option<u32>> { 36 self.vm.block_store().find_block_height_from_solution_id(solution_id) 37 } 38 39 /// Returns the latest transaction ID that contains the given `program ID`. 40 pub fn find_latest_transaction_id_from_program_id( 41 &self, 42 program_id: &ProgramID<N>, 43 ) -> Result<Option<N::TransactionID>> { 44 self.vm.transaction_store().find_latest_transaction_id_from_program_id(program_id) 45 } 46 47 /// Returns the transaction ID that contains the given `program ID` and `edition`. 48 pub fn find_transaction_id_from_program_id_and_edition( 49 &self, 50 program_id: &ProgramID<N>, 51 edition: u16, 52 ) -> Result<Option<N::TransactionID>> { 53 self.vm.transaction_store().find_transaction_id_from_program_id_and_edition(program_id, edition) 54 } 55 56 /// Returns the transaction ID that contains the given `transition ID`. 57 pub fn find_transaction_id_from_transition_id( 58 &self, 59 transition_id: &N::TransitionID, 60 ) -> Result<Option<N::TransactionID>> { 61 self.vm.transaction_store().find_transaction_id_from_transition_id(transition_id) 62 } 63 64 /// Returns the transition ID that contains the given `input ID` or `output ID`. 65 pub fn find_transition_id(&self, id: &Field<N>) -> Result<N::TransitionID> { 66 self.vm.transition_store().find_transition_id(id) 67 } 68 69 /// Returns the record ciphertexts that belong to the given view key. 70 #[allow(clippy::type_complexity)] 71 pub fn find_record_ciphertexts<'a>( 72 &'a self, 73 view_key: &'a ViewKey<N>, 74 filter: RecordsFilter<N>, 75 ) -> Result<impl 'a + Iterator<Item = (Field<N>, Cow<'a, Record<N, Ciphertext<N>>>)>> { 76 // Derive the x-coordinate of the address corresponding to the given view key. 77 let address_x_coordinate = view_key.to_address().to_x_coordinate(); 78 // Derive the `sk_tag` from the graph key. 79 let sk_tag = match GraphKey::try_from(view_key) { 80 Ok(graph_key) => graph_key.sk_tag(), 81 Err(e) => bail!("Failed to derive the graph key from the view key: {e}"), 82 }; 83 84 Ok(self.records().flat_map(move |cow| { 85 // Retrieve the commitment and record. 86 let (commitment, record) = match cow { 87 (Cow::Borrowed(commitment), record) => (*commitment, record), 88 (Cow::Owned(commitment), record) => (commitment, record), 89 }; 90 91 // Check ownership before determining whether to decrypt the record. 92 if !record.is_owner_with_address_x_coordinate(view_key, &address_x_coordinate) { 93 return None; 94 } 95 96 // Determine whether to decrypt this record (or not), based on the filter. 97 let commitment = match filter { 98 RecordsFilter::All => Ok(Some(commitment)), 99 RecordsFilter::Spent => Record::<N, Plaintext<N>>::tag(sk_tag, commitment).and_then(|tag| { 100 // Determine if the record is spent. 101 self.contains_tag(&tag).map(|is_spent| match is_spent { 102 true => Some(commitment), 103 false => None, 104 }) 105 }), 106 RecordsFilter::Unspent => Record::<N, Plaintext<N>>::tag(sk_tag, commitment).and_then(|tag| { 107 // Determine if the record is spent. 108 self.contains_tag(&tag).map(|is_spent| match is_spent { 109 true => None, 110 false => Some(commitment), 111 }) 112 }), 113 RecordsFilter::SlowSpent(private_key) => { 114 Record::<N, Plaintext<N>>::serial_number(private_key, commitment).and_then(|serial_number| { 115 // Determine if the record is spent. 116 self.contains_serial_number(&serial_number).map(|is_spent| match is_spent { 117 true => Some(commitment), 118 false => None, 119 }) 120 }) 121 } 122 RecordsFilter::SlowUnspent(private_key) => { 123 Record::<N, Plaintext<N>>::serial_number(private_key, commitment).and_then(|serial_number| { 124 // Determine if the record is spent. 125 self.contains_serial_number(&serial_number).map(|is_spent| match is_spent { 126 true => None, 127 false => Some(commitment), 128 }) 129 }) 130 } 131 }; 132 133 match commitment { 134 Ok(Some(commitment)) => Some((commitment, record)), 135 Ok(None) => None, 136 Err(err) => { 137 warn!("{}", &flatten_error(err.context("Failed to process 'find_record_ciphertexts({filter:?})'"))); 138 None 139 } 140 } 141 })) 142 } 143 144 /// Returns the records that belong to the given view key. 145 #[allow(clippy::type_complexity)] 146 pub fn find_records<'a>( 147 &'a self, 148 view_key: &'a ViewKey<N>, 149 filter: RecordsFilter<N>, 150 ) -> Result<impl 'a + Iterator<Item = (Field<N>, Record<N, Plaintext<N>>)>> { 151 self.find_record_ciphertexts(view_key, filter).map(|iter| { 152 iter.flat_map(|(commitment, record)| match record.decrypt(view_key) { 153 Ok(record) => Some((commitment, record)), 154 Err(err) => { 155 warn!("{}", &flatten_error(err.context("Failed to decrypt record"))); 156 None 157 } 158 }) 159 }) 160 } 161 }