get.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 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 impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> { 19 /// Returns the committee for the given `block height`. 20 pub fn get_committee(&self, block_height: u32) -> Result<Option<Committee<N>>> { 21 self.vm.finalize_store().committee_store().get_committee(block_height) 22 } 23 24 /// Returns the committee for the given `round`. 25 pub fn get_committee_for_round(&self, round: u64) -> Result<Option<Committee<N>>> { 26 // Check if the committee is already in the cache. 27 if let Some(committee) = self.committee_cache.lock().get(&round) { 28 return Ok(Some(committee.clone())); 29 } 30 31 match self.vm.finalize_store().committee_store().get_committee_for_round(round)? { 32 // Return the committee if it exists. 33 Some(committee) => { 34 // Insert the committee into the cache. 35 self.committee_cache.lock().push(round, committee.clone()); 36 // Return the committee. 37 Ok(Some(committee)) 38 } 39 // Return the current committee if the round is equivalent. 40 None => { 41 // Retrieve the current committee. 42 let current_committee = self.latest_committee()?; 43 // Return the current committee if the round is equivalent. 44 match current_committee.starting_round() == round { 45 true => Ok(Some(current_committee)), 46 false => Ok(None), 47 } 48 } 49 } 50 } 51 52 /// Returns the committee lookback for the given round. 53 pub fn get_committee_lookback_for_round(&self, round: u64) -> Result<Option<Committee<N>>> { 54 // Get the round number for the previous committee. Note, we subtract 2 from odd rounds, 55 // because committees are updated in even rounds. 56 let previous_round = match round % 2 == 0 { 57 true => round.saturating_sub(1), 58 false => round.saturating_sub(2), 59 }; 60 61 // Get the committee lookback round. 62 let committee_lookback_round = previous_round.saturating_sub(Committee::<N>::COMMITTEE_LOOKBACK_RANGE); 63 64 // Retrieve the committee for the committee lookback round. 65 self.get_committee_for_round(committee_lookback_round) 66 } 67 68 /// Returns the state root that contains the given `block height`. 69 pub fn get_state_root(&self, block_height: u32) -> Result<Option<N::StateRoot>> { 70 self.vm.block_store().get_state_root(block_height) 71 } 72 73 /// Returns a state path for the given commitment. 74 pub fn get_state_path_for_commitment(&self, commitment: &Field<N>) -> Result<StatePath<N>> { 75 self.vm.block_store().get_state_path_for_commitment(commitment) 76 } 77 78 /// Returns a list of state paths for the given list of `commitment`s. 79 pub fn get_state_paths_for_commitments(&self, commitments: &[Field<N>]) -> Result<Vec<StatePath<N>>> { 80 self.vm.block_store().get_state_paths_for_commitments(commitments) 81 } 82 83 /// Returns the epoch hash for the given block height. 84 pub fn get_epoch_hash(&self, block_height: u32) -> Result<N::BlockHash> { 85 // Compute the epoch number from the current block height. 86 let epoch_number = block_height.saturating_div(N::NUM_BLOCKS_PER_EPOCH); 87 // Compute the epoch starting height (a multiple of `NUM_BLOCKS_PER_EPOCH`). 88 let epoch_starting_height = epoch_number.saturating_mul(N::NUM_BLOCKS_PER_EPOCH); 89 // Retrieve the epoch hash, defined as the 'previous block hash' from the epoch starting height. 90 let epoch_hash = self.get_previous_hash(epoch_starting_height)?; 91 // Construct the epoch hash. 92 Ok(epoch_hash) 93 } 94 95 /// Returns the block for the given block height. 96 pub fn get_block(&self, height: u32) -> Result<Block<N>> { 97 match self.try_get_block(height)? { 98 Some(block) => Ok(block), 99 None => bail!("Block {height} does not exist in storage"), 100 } 101 } 102 103 /// Returns the block for the given block height. 104 /// 105 /// This behaves the same as [`Self::get_block`], except that a missing block will cause the function to 106 /// return `Ok(None)` instead of an error. 107 pub fn try_get_block(&self, height: u32) -> Result<Option<Block<N>>> { 108 if height == 0 { 109 return Ok(Some(self.genesis_block.clone())); 110 } 111 112 match self.vm.block_store().get_block_hash(height)? { 113 Some(hash) => self.vm.block_store().get_block(&hash), 114 None => Ok(None), 115 } 116 } 117 118 /// Returns the blocks in the given block range. 119 /// The range is inclusive of the start and exclusive of the end. 120 pub fn get_blocks(&self, heights: Range<u32>) -> Result<Vec<Block<N>>> { 121 cfg_into_iter!(heights).map(|height| self.get_block(height)).collect() 122 } 123 124 /// Returns the block for the given block hash. 125 pub fn get_block_by_hash(&self, block_hash: &N::BlockHash) -> Result<Block<N>> { 126 match self.try_get_block_by_hash(block_hash)? { 127 Some(block) => Ok(block), 128 None => bail!("Block '{block_hash}' does not exist in storage"), 129 } 130 } 131 132 /// Returns the block for the given block hash. 133 /// 134 /// This behaves the same as [`Self::get_block_by_hash`], except that a missing block will cause the function to 135 /// return `Ok(None)` instead of an error. 136 pub fn try_get_block_by_hash(&self, block_hash: &N::BlockHash) -> Result<Option<Block<N>>> { 137 self.vm.block_store().get_block(block_hash) 138 } 139 140 /// Returns the block height for the given block hash. 141 pub fn get_height(&self, block_hash: &N::BlockHash) -> Result<u32> { 142 match self.vm.block_store().get_block_height(block_hash)? { 143 Some(height) => Ok(height), 144 None => bail!("Missing block height for block '{block_hash}'"), 145 } 146 } 147 148 /// Returns the block hash for the given block height. 149 pub fn get_hash(&self, height: u32) -> Result<N::BlockHash> { 150 // If the height is 0, return the genesis block hash. 151 if height == 0 { 152 return Ok(self.genesis_block.hash()); 153 } 154 match self.vm.block_store().get_block_hash(height)? { 155 Some(block_hash) => Ok(block_hash), 156 None => bail!("Missing block hash for block {height}"), 157 } 158 } 159 160 /// Returns the previous block hash for the given block height. 161 pub fn get_previous_hash(&self, height: u32) -> Result<N::BlockHash> { 162 // If the height is 0, return the default block hash. 163 if height == 0 { 164 return Ok(N::BlockHash::default()); 165 } 166 match self.vm.block_store().get_previous_block_hash(height)? { 167 Some(previous_hash) => Ok(previous_hash), 168 None => bail!("Missing previous block hash for block {height}"), 169 } 170 } 171 172 /// Returns the block header for the given block height. 173 pub fn get_header(&self, height: u32) -> Result<Header<N>> { 174 // If the height is 0, return the genesis block header. 175 if height == 0 { 176 return Ok(*self.genesis_block.header()); 177 } 178 // Retrieve the block hash. 179 let block_hash = match self.vm.block_store().get_block_hash(height)? { 180 Some(block_hash) => block_hash, 181 None => bail!("Block {height} does not exist in storage"), 182 }; 183 // Retrieve the block header. 184 match self.vm.block_store().get_block_header(&block_hash)? { 185 Some(header) => Ok(header), 186 None => bail!("Missing block header for block {height}"), 187 } 188 } 189 190 /// Returns the block transactions for the given block height. 191 pub fn get_transactions(&self, height: u32) -> Result<Transactions<N>> { 192 // If the height is 0, return the genesis block transactions. 193 if height == 0 { 194 return Ok(self.genesis_block.transactions().clone()); 195 } 196 // Retrieve the block hash. 197 let Some(block_hash) = self.vm.block_store().get_block_hash(height)? else { 198 bail!("Block {height} does not exist in storage"); 199 }; 200 // Retrieve the block transaction. 201 match self.vm.block_store().get_block_transactions(&block_hash)? { 202 Some(transactions) => Ok(transactions), 203 None => bail!("Missing block transactions for block {height}"), 204 } 205 } 206 207 /// Returns the aborted transaction IDs for the given block height. 208 pub fn get_aborted_transaction_ids(&self, height: u32) -> Result<Vec<N::TransactionID>> { 209 // If the height is 0, return the genesis block aborted transaction IDs. 210 if height == 0 { 211 return Ok(self.genesis_block.aborted_transaction_ids().clone()); 212 } 213 // Retrieve the block hash. 214 let Some(block_hash) = self.vm.block_store().get_block_hash(height)? else { 215 bail!("Block {height} does not exist in storage"); 216 }; 217 // Retrieve the aborted transaction IDs. 218 match self.vm.block_store().get_block_aborted_transaction_ids(&block_hash)? { 219 Some(aborted_transaction_ids) => Ok(aborted_transaction_ids), 220 None => bail!("Missing aborted transaction IDs for block {height}"), 221 } 222 } 223 224 /// Returns the transaction for the given transaction ID. 225 pub fn get_transaction(&self, transaction_id: N::TransactionID) -> Result<Transaction<N>> { 226 // Retrieve the transaction. 227 match self.vm.block_store().get_transaction(&transaction_id)? { 228 Some(transaction) => Ok(transaction), 229 None => bail!("Missing transaction for ID {transaction_id}"), 230 } 231 } 232 233 /// Returns the confirmed transaction for the given transaction ID. 234 pub fn get_confirmed_transaction(&self, transaction_id: N::TransactionID) -> Result<ConfirmedTransaction<N>> { 235 // Retrieve the confirmed transaction. 236 match self.vm.block_store().get_confirmed_transaction(&transaction_id)? { 237 Some(confirmed_transaction) => Ok(confirmed_transaction), 238 None => bail!("Missing confirmed transaction for ID {transaction_id}"), 239 } 240 } 241 242 /// Returns the unconfirmed transaction for the given `transaction ID`. 243 pub fn get_unconfirmed_transaction(&self, transaction_id: &N::TransactionID) -> Result<Transaction<N>> { 244 // Retrieve the unconfirmed transaction. 245 match self.vm.block_store().get_unconfirmed_transaction(transaction_id)? { 246 Some(unconfirmed_transaction) => Ok(unconfirmed_transaction), 247 None => bail!("Missing unconfirmed transaction for ID {transaction_id}"), 248 } 249 } 250 251 /// Returns the latest edition for the given `program ID`. 252 pub fn get_latest_edition_for_program(&self, program_id: &ProgramID<N>) -> Result<u16> { 253 match self.vm.block_store().get_latest_edition_for_program(program_id)? { 254 Some(edition) => Ok(edition), 255 None => bail!("Missing latest edition for program ID {program_id}"), 256 } 257 } 258 259 /// Returns the latest program for the given `program ID`. 260 pub fn get_program(&self, program_id: ProgramID<N>) -> Result<Program<N>> { 261 match self.vm.block_store().get_latest_program(&program_id)? { 262 Some(program) => Ok(program), 263 None => bail!("Missing program for ID {program_id}"), 264 } 265 } 266 267 /// Returns the program for the given `program ID` and `edition`. 268 pub fn get_program_for_edition(&self, program_id: ProgramID<N>, edition: u16) -> Result<Program<N>> { 269 match self.try_get_program_for_edition(&program_id, edition)? { 270 Some(program) => Ok(program), 271 None => bail!("Missing program for ID {program_id} and edition {edition}"), 272 } 273 } 274 275 /// Returns the program for the given `program ID` and `edition`, 276 /// or `None` if no program of this ID and edition exists. 277 pub fn try_get_program_for_edition(&self, program_id: &ProgramID<N>, edition: u16) -> Result<Option<Program<N>>> { 278 self.vm.block_store().get_program_for_edition(program_id, edition) 279 } 280 281 /// Returns the block solutions for the given block height. 282 pub fn get_solutions(&self, height: u32) -> Result<Solutions<N>> { 283 // If the height is 0, return the genesis block solutions. 284 if height == 0 { 285 return Ok(self.genesis_block.solutions().clone()); 286 } 287 // Retrieve the block hash. 288 let block_hash = match self.vm.block_store().get_block_hash(height)? { 289 Some(block_hash) => block_hash, 290 None => bail!("Block {height} does not exist in storage"), 291 }; 292 // Retrieve the block solutions. 293 self.vm.block_store().get_block_solutions(&block_hash) 294 } 295 296 /// Returns the solution for the given solution ID. 297 pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Result<Solution<N>> { 298 self.vm.block_store().get_solution(solution_id) 299 } 300 301 /// Returns the block authority for the given block height. 302 pub fn get_authority(&self, height: u32) -> Result<Authority<N>> { 303 // If the height is 0, return the genesis block authority. 304 if height == 0 { 305 return Ok(self.genesis_block.authority().clone()); 306 } 307 // Retrieve the block hash. 308 let block_hash = match self.vm.block_store().get_block_hash(height)? { 309 Some(block_hash) => block_hash, 310 None => bail!("Block {height} does not exist in storage"), 311 }; 312 // Retrieve the block authority. 313 match self.vm.block_store().get_block_authority(&block_hash)? { 314 Some(authority) => Ok(authority), 315 None => bail!("Missing authority for block {height}"), 316 } 317 } 318 319 /// Returns the batch certificate for the given `certificate ID`. 320 pub fn get_batch_certificate(&self, certificate_id: &Field<N>) -> Result<Option<BatchCertificate<N>>> { 321 self.vm.block_store().get_batch_certificate(certificate_id) 322 } 323 324 /// Returns the delegators for the given validator. 325 pub fn get_delegators_for_validator(&self, validator: &Address<N>) -> Result<Vec<Address<N>>> { 326 // Construct the credits.alpha program ID. 327 let credits_program_id = ProgramID::from_str("credits.alpha")?; 328 // Construct the bonded mapping name. 329 let bonded_mapping = Identifier::from_str("bonded")?; 330 // Construct the bonded mapping key name. 331 let bonded_mapping_key = Identifier::from_str("validator")?; 332 // Get the credits.alpha bonded mapping. 333 let bonded = self.vm.finalize_store().get_mapping_confirmed(credits_program_id, bonded_mapping)?; 334 // Select the delegators for the given validator. 335 cfg_into_iter!(bonded) 336 .filter_map(|(bonded_address, bond_state)| { 337 let Plaintext::Literal(Literal::Address(bonded_address), _) = bonded_address else { 338 return Some(Err(anyhow!("Invalid delegator in finalize storage."))); 339 }; 340 let Value::Plaintext(Plaintext::Struct(bond_state, _)) = bond_state else { 341 return Some(Err(anyhow!("Invalid bond_state in finalize storage."))); 342 }; 343 let Some(mapping_validator) = bond_state.get(&bonded_mapping_key) else { 344 return Some(Err(anyhow!("Invalid bond_state validator in finalize storage."))); 345 }; 346 let Plaintext::Literal(Literal::Address(mapping_validator), _) = mapping_validator else { 347 return Some(Err(anyhow!("Invalid validator in finalize storage."))); 348 }; 349 // Select bonded addresses which: 350 // 1. are bonded to the right validator. 351 // 2. are not themselves the validator. 352 (mapping_validator == validator && bonded_address != *validator).then_some(Ok(bonded_address)) 353 }) 354 .collect::<Result<_>>() 355 } 356 357 /// Returns the amount of microcredits that the given address has bonded. 358 pub fn get_bonded_amount(&self, address: &Address<N>) -> Result<u64> { 359 // Construct the credits.alpha program ID. 360 let credits_program_id = ProgramID::from_str("credits.alpha")?; 361 // Construct the bonded mapping name. 362 let bonded_mapping = Identifier::from_str("bonded")?; 363 // Construct the bonded mapping key name. 364 let bonded_mapping_key = Plaintext::from(Literal::Address(*address)); 365 // Construct the bond_state microcredits key. 366 let microcredits_key = Identifier::from_str("microcredits")?; 367 // Get the bond state for the given staker. 368 let bond_state = 369 self.vm.finalize_store().get_value_confirmed(credits_program_id, bonded_mapping, &bonded_mapping_key)?; 370 // Find the microcredits in the bond state. 371 match bond_state { 372 Some(Value::Plaintext(Plaintext::Struct(bond_state, _))) => match bond_state.get(µcredits_key) { 373 Some(Plaintext::Literal(Literal::U64(amount), _)) => Ok(**amount), 374 _ => bail!("Expected 'microcredits' as a u64 in bond_state struct."), 375 }, 376 // If the address is not bonded, then return 0. 377 None => Ok(0), 378 _ => bail!("Invalid bond_state in finalize storage."), 379 } 380 } 381 }