mock.rs
1 // Copyright (c) 2025 ADnet Contributors 2 // This file is part of the AlphaOS 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 crate::{LedgerService, fmt_id}; 17 use alphavm::{ 18 ledger::{ 19 Block, 20 PendingBlock, 21 Transaction, 22 committee::Committee, 23 narwhal::{BatchCertificate, Data, Subdag, Transmission, TransmissionID}, 24 puzzle::{Solution, SolutionID}, 25 }, 26 prelude::{Address, ConsensusVersion, Field, Network, Result, Zero, bail, consensus_config_value, ensure}, 27 }; 28 29 use indexmap::IndexMap; 30 #[cfg(feature = "locktick")] 31 use locktick::parking_lot::Mutex; 32 #[cfg(not(feature = "locktick"))] 33 use parking_lot::Mutex; 34 use std::{collections::BTreeMap, ops::Range}; 35 use tracing::*; 36 37 /// A mock ledger service that always returns `false`. 38 #[derive(Debug)] 39 pub struct MockLedgerService<N: Network> { 40 committee: Committee<N>, 41 height_to_round_and_hash: Mutex<BTreeMap<u32, (u64, N::BlockHash)>>, 42 } 43 44 impl<N: Network> MockLedgerService<N> { 45 /// Initializes a new mock ledger service. 46 pub fn new(committee: Committee<N>) -> Self { 47 Self { committee, height_to_round_and_hash: Default::default() } 48 } 49 50 /// Initializes a new mock ledger service at the specified height. 51 pub fn new_at_height(committee: Committee<N>, height: u32) -> Self { 52 let mut height_to_hash = BTreeMap::new(); 53 for i in 0..=height { 54 height_to_hash.insert(i, (i as u64 * 2, Field::<N>::from_u32(i).into())); 55 } 56 Self { committee, height_to_round_and_hash: Mutex::new(height_to_hash) } 57 } 58 } 59 60 #[async_trait] 61 impl<N: Network> LedgerService<N> for MockLedgerService<N> { 62 /// Returns the latest round in the ledger. 63 fn latest_round(&self) -> u64 { 64 *self.height_to_round_and_hash.lock().keys().last().unwrap_or(&0) as u64 65 } 66 67 /// Returns the latest block height in the canonical ledger. 68 fn latest_block_height(&self) -> u32 { 69 self.height_to_round_and_hash.lock().last_key_value().map(|(height, _)| *height).unwrap_or(0) 70 } 71 72 /// Returns the latest block in the ledger. 73 fn latest_block(&self) -> Block<N> { 74 unreachable!("MockLedgerService does not support latest_block") 75 } 76 77 /// Returns the latest restrictions ID in the ledger. 78 fn latest_restrictions_id(&self) -> Field<N> { 79 Field::zero() 80 } 81 82 /// Returns the latest cached leader and its associated round. 83 fn latest_leader(&self) -> Option<(u64, Address<N>)> { 84 None 85 } 86 87 /// Updates the latest cached leader and its associated round. 88 fn update_latest_leader(&self, _round: u64, _leader: Address<N>) {} 89 90 /// Returns `true` if the given block height exists in the canonical ledger. 91 fn contains_block_height(&self, height: u32) -> bool { 92 self.height_to_round_and_hash.lock().contains_key(&height) 93 } 94 95 /// Returns the canonical block height for the given block hash, if it exists. 96 fn get_block_height(&self, hash: &N::BlockHash) -> Result<u32> { 97 match self 98 .height_to_round_and_hash 99 .lock() 100 .iter() 101 .find_map(|(height, (_, h))| if h == hash { Some(*height) } else { None }) 102 { 103 Some(height) => Ok(height), 104 None => bail!("Missing block {hash}"), 105 } 106 } 107 108 /// Returns the canonical block hash for the given block height, if it exists. 109 fn get_block_hash(&self, height: u32) -> Result<N::BlockHash> { 110 match self.height_to_round_and_hash.lock().get(&height).cloned() { 111 Some((_, hash)) => Ok(hash), 112 None => bail!("Missing block {height}"), 113 } 114 } 115 116 /// Returns the block round for the given block height, if it exists. 117 fn get_block_round(&self, height: u32) -> Result<u64> { 118 match self 119 .height_to_round_and_hash 120 .lock() 121 .iter() 122 .find_map(|(h, (round, _))| if *h == height { Some(*round) } else { None }) 123 { 124 Some(round) => Ok(round), 125 None => bail!("Missing block {height}"), 126 } 127 } 128 129 /// Returns the block for the given block height. 130 fn get_block(&self, _height: u32) -> Result<Block<N>> { 131 unreachable!("MockLedgerService does not support get_block") 132 } 133 134 /// Returns the blocks in the given block range. 135 /// The range is inclusive of the start and exclusive of the end. 136 fn get_blocks(&self, _heights: Range<u32>) -> Result<Vec<Block<N>>> { 137 unreachable!("MockLedgerService does not support get_blocks") 138 } 139 140 /// Returns the solution for the given solution ID. 141 fn get_solution(&self, _solution_id: &SolutionID<N>) -> Result<Solution<N>> { 142 unreachable!("MockLedgerService does not support get_solution") 143 } 144 145 /// Returns the unconfirmed transaction for the given transaction ID. 146 fn get_unconfirmed_transaction(&self, _transaction_id: N::TransactionID) -> Result<Transaction<N>> { 147 unreachable!("MockLedgerService does not support get_unconfirmed_transaction") 148 } 149 150 /// Returns the batch certificate for the given batch certificate ID. 151 fn get_batch_certificate(&self, _certificate_id: &Field<N>) -> Result<BatchCertificate<N>> { 152 unreachable!("MockLedgerService does not support get_batch_certificate") 153 } 154 155 /// Returns the current committee. 156 fn current_committee(&self) -> Result<Committee<N>> { 157 Ok(self.committee.clone()) 158 } 159 160 /// Returns the committee for the given round. 161 fn get_committee_for_round(&self, _round: u64) -> Result<Committee<N>> { 162 Ok(self.committee.clone()) 163 } 164 165 /// Returns the committee lookback for the given round. 166 fn get_committee_lookback_for_round(&self, _round: u64) -> Result<Committee<N>> { 167 Ok(self.committee.clone()) 168 } 169 170 /// Returns `false` for all queries. 171 fn contains_certificate(&self, certificate_id: &Field<N>) -> Result<bool> { 172 trace!("[MockLedgerService] Contains certificate ID {} - false", fmt_id(certificate_id)); 173 Ok(false) 174 } 175 176 /// Returns `false` for all queries. 177 fn contains_transmission(&self, transmission_id: &TransmissionID<N>) -> Result<bool> { 178 trace!( 179 "[MockLedgerService] Contains transmission ID {}.{} - false", 180 fmt_id(transmission_id), 181 fmt_id(transmission_id.checksum().unwrap_or_default()) 182 ); 183 Ok(false) 184 } 185 186 /// Ensures that the given transmission is not a fee and matches the given transmission ID. 187 fn ensure_transmission_is_well_formed( 188 &self, 189 transmission_id: TransmissionID<N>, 190 _transmission: &mut Transmission<N>, 191 ) -> Result<()> { 192 trace!( 193 "[MockLedgerService] Ensure transmission ID matches {}.{} - Ok", 194 fmt_id(transmission_id), 195 fmt_id(transmission_id.checksum().unwrap_or_default()) 196 ); 197 Ok(()) 198 } 199 200 /// Checks the given solution is well-formed. 201 async fn check_solution_basic(&self, solution_id: SolutionID<N>, _solution: Data<Solution<N>>) -> Result<()> { 202 trace!("[MockLedgerService] Check solution basic {:?} - Ok", fmt_id(solution_id)); 203 Ok(()) 204 } 205 206 /// Checks the given transaction is well-formed and unique. 207 async fn check_transaction_basic( 208 &self, 209 transaction_id: N::TransactionID, 210 _transaction: Transaction<N>, 211 ) -> Result<()> { 212 trace!("[MockLedgerService] Check transaction basic {:?} - Ok", fmt_id(transaction_id)); 213 Ok(()) 214 } 215 216 fn check_block_subdag(&self, _block: Block<N>, _prefix: &[PendingBlock<N>]) -> Result<PendingBlock<N>> { 217 unimplemented!(); 218 } 219 220 fn check_block_content(&self, _block: PendingBlock<N>) -> Result<Block<N>> { 221 unimplemented!(); 222 } 223 224 /// Checks the given block is valid next block. 225 fn check_next_block(&self, _block: &Block<N>) -> Result<()> { 226 Ok(()) 227 } 228 229 /// Returns a candidate for the next block in the ledger, using a committed subdag and its transmissions. 230 #[cfg(feature = "ledger-write")] 231 fn prepare_advance_to_next_quorum_block( 232 &self, 233 _subdag: Subdag<N>, 234 _transmissions: IndexMap<TransmissionID<N>, Transmission<N>>, 235 ) -> Result<Block<N>> { 236 unreachable!("MockLedgerService does not support prepare_advance_to_next_quorum_block") 237 } 238 239 /// Adds the given block as the next block in the ledger. 240 #[cfg(feature = "ledger-write")] 241 fn advance_to_next_block(&self, block: &Block<N>) -> Result<()> { 242 ensure!( 243 block.height() == self.latest_block_height() + 1, 244 "Tried to advance to block {} from block {}", 245 block.height(), 246 self.latest_block_height() 247 ); 248 self.height_to_round_and_hash.lock().insert(block.height(), (block.round(), block.hash())); 249 Ok(()) 250 } 251 252 /// Returns the spent cost for a transaction in microcredits. 253 fn transaction_spend_in_microcredits( 254 &self, 255 _transaction: &Transaction<N>, 256 consensus_version: ConsensusVersion, 257 ) -> Result<u64> { 258 let height = N::CONSENSUS_HEIGHT(consensus_version).unwrap(); 259 Ok(consensus_config_value!(N, TRANSACTION_SPEND_LIMIT, height).unwrap()) 260 } 261 }