/ ledger / src / get.rs
get.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm 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.delta program ID.
327          let credits_program_id = ProgramID::from_str("credits.delta")?;
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.delta 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.delta program ID.
360          let credits_program_id = ProgramID::from_str("credits.delta")?;
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(&microcredits_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  }