/ node / bft / tests / common / utils.rs
utils.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphaos 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 crate::common::{primary, CurrentNetwork, TranslucentLedgerService};
 20  use alphaos_account::Account;
 21  use alphaos_node_bft::{
 22      helpers::{PrimarySender, Storage},
 23      Gateway,
 24      Worker,
 25  };
 26  
 27  use alphaos_node_bft::storage_service::BFTMemoryService;
 28  use alphaos_utilities::SimpleStoppable;
 29  use alphastd::StorageMode;
 30  
 31  use alphavm::{
 32      console::account::Address,
 33      ledger::{
 34          committee::Committee,
 35          narwhal::{BatchHeader, Data},
 36          store::helpers::memory::ConsensusMemory,
 37      },
 38      prelude::{
 39          block::Transaction,
 40          committee::MIN_VALIDATOR_STAKE,
 41          puzzle::{Solution, SolutionID},
 42          Field,
 43          Network,
 44          TestRng,
 45          Uniform,
 46      },
 47  };
 48  
 49  use std::{str::FromStr, sync::Arc, time::Duration};
 50  
 51  use ::bytes::Bytes;
 52  use indexmap::IndexMap;
 53  #[cfg(feature = "locktick")]
 54  use locktick::parking_lot::RwLock;
 55  #[cfg(not(feature = "locktick"))]
 56  use parking_lot::RwLock;
 57  use rand::Rng;
 58  use tokio::{sync::oneshot, task::JoinHandle, time::sleep};
 59  use tracing::*;
 60  use tracing_subscriber::{
 61      layer::{Layer, SubscriberExt},
 62      util::SubscriberInitExt,
 63  };
 64  
 65  /// Initializes the logger.
 66  pub fn initialize_logger(verbosity: u8) {
 67      let verbosity_str = match verbosity {
 68          0 => "info",
 69          1 => "debug",
 70          2..=4 => "trace",
 71          _ => "info",
 72      };
 73  
 74      // Filter out undesirable logs. (unfortunately EnvFilter cannot be cloned)
 75      let filter = tracing_subscriber::EnvFilter::from_str(verbosity_str)
 76          .unwrap()
 77          .add_directive("mio=off".parse().unwrap())
 78          .add_directive("tokio_util=off".parse().unwrap())
 79          .add_directive("hyper=off".parse().unwrap())
 80          .add_directive("reqwest=off".parse().unwrap())
 81          .add_directive("want=off".parse().unwrap())
 82          .add_directive("h2=off".parse().unwrap());
 83  
 84      let filter = if verbosity > 3 {
 85          filter.add_directive("alphaos_node_tcp=trace".parse().unwrap())
 86      } else {
 87          filter.add_directive("alphaos_node_tcp=off".parse().unwrap())
 88      };
 89  
 90      // Initialize tracing.
 91      let _ = tracing_subscriber::registry()
 92          .with(tracing_subscriber::fmt::Layer::default().with_target(verbosity > 2).with_filter(filter))
 93          .try_init();
 94  }
 95  
 96  /// Fires *fake* unconfirmed solutions at the node.
 97  pub fn fire_unconfirmed_solutions(
 98      sender: &PrimarySender<CurrentNetwork>,
 99      node_id: u16,
100      interval_ms: u64,
101  ) -> JoinHandle<()> {
102      let tx_unconfirmed_solution = sender.tx_unconfirmed_solution.clone();
103      tokio::task::spawn(async move {
104          // This RNG samples the *same* fake solutions for all nodes.
105          let mut shared_rng = TestRng::fixed(123456789);
106          // This RNG samples *different* fake solutions for each node.
107          let mut unique_rng = TestRng::fixed(node_id as u64);
108  
109          // A closure to generate a solution ID and solution.
110          async fn sample(mut rng: impl Rng) -> (SolutionID<CurrentNetwork>, Data<Solution<CurrentNetwork>>) {
111              // Sample a random fake solution ID.
112              let solution_id = rng.r#gen::<u64>().into();
113              // Sample random fake solution bytes.
114              let mut vec = vec![0u8; 1024];
115              rng.fill_bytes(&mut vec);
116              let solution = Data::Buffer(Bytes::from(vec));
117              // Return the solution ID and solution.
118              (solution_id, solution)
119          }
120  
121          // Initialize a counter.
122          let mut counter = 0;
123  
124          loop {
125              // Sample a random fake solution ID and solution.
126              let (solution_id, solution) =
127                  if counter % 2 == 0 { sample(&mut shared_rng).await } else { sample(&mut unique_rng).await };
128              // Initialize a callback sender and receiver.
129              let (callback, callback_receiver) = oneshot::channel();
130              // Send the fake solution.
131              if let Err(e) = tx_unconfirmed_solution.send((solution_id, solution, callback)).await {
132                  error!("Failed to send unconfirmed solution: {e}");
133              }
134              let _ = callback_receiver.await;
135              // Increment the counter.
136              counter += 1;
137              // Sleep briefly.
138              sleep(Duration::from_millis(interval_ms)).await;
139          }
140      })
141  }
142  
143  /// Fires *fake* unconfirmed transactions at the node.
144  pub fn fire_unconfirmed_transactions(
145      sender: &PrimarySender<CurrentNetwork>,
146      node_id: u16,
147      interval_ms: u64,
148  ) -> JoinHandle<()> {
149      let tx_unconfirmed_transaction = sender.tx_unconfirmed_transaction.clone();
150      tokio::task::spawn(async move {
151          // This RNG samples the *same* fake transactions for all nodes.
152          let mut shared_rng = TestRng::fixed(123456789);
153          // This RNG samples *different* fake transactions for each node.
154          let mut unique_rng = TestRng::fixed(node_id as u64);
155  
156          // A closure to generate an ID and transaction.
157          fn sample(
158              mut rng: impl Rng,
159          ) -> (<CurrentNetwork as Network>::TransactionID, Data<Transaction<CurrentNetwork>>) {
160              // Sample a random fake transaction ID.
161              let id = Field::<CurrentNetwork>::rand(&mut rng).into();
162              // Sample random fake transaction bytes.
163              let mut vec = vec![0u8; 1024];
164              rng.fill_bytes(&mut vec);
165              let transaction = Data::Buffer(Bytes::from(vec));
166              // Return the ID and transaction.
167              (id, transaction)
168          }
169  
170          // Initialize a counter.
171          let mut counter = 0;
172  
173          loop {
174              // Sample a random fake transaction ID and transaction.
175              let (id, transaction) = if counter % 2 == 0 { sample(&mut shared_rng) } else { sample(&mut unique_rng) };
176              // Initialize a callback sender and receiver.
177              let (callback, callback_receiver) = oneshot::channel();
178              // Send the fake transaction.
179              if let Err(e) = tx_unconfirmed_transaction.send((id, transaction, callback)).await {
180                  error!("Failed to send unconfirmed transaction: {e}");
181              }
182              let _ = callback_receiver.await;
183              // Increment the counter.
184              counter += 1;
185              // Sleep briefly.
186              sleep(Duration::from_millis(interval_ms)).await;
187          }
188      })
189  }
190  
191  /// Samples a new ledger with the given number of nodes.
192  pub fn sample_ledger(
193      accounts: &[Account<CurrentNetwork>],
194      committee: &Committee<CurrentNetwork>,
195      rng: &mut TestRng,
196  ) -> Arc<TranslucentLedgerService<CurrentNetwork, ConsensusMemory<CurrentNetwork>>> {
197      let num_nodes = committee.num_members();
198      let bonded_balances: IndexMap<_, _> =
199          committee.members().iter().map(|(address, (amount, _, _))| (*address, (*address, *address, *amount))).collect();
200      let gen_key = *accounts[0].private_key();
201      let public_balance_per_validator =
202          (CurrentNetwork::STARTING_SUPPLY - (num_nodes as u64) * MIN_VALIDATOR_STAKE) / (num_nodes as u64);
203      let mut balances = IndexMap::<Address<CurrentNetwork>, u64>::new();
204      for account in accounts.iter() {
205          balances.insert(account.address(), public_balance_per_validator);
206      }
207  
208      let gen_ledger =
209          primary::genesis_ledger(gen_key, committee.clone(), balances.clone(), bonded_balances.clone(), rng);
210      Arc::new(TranslucentLedgerService::new(gen_ledger, SimpleStoppable::new()))
211  }
212  
213  /// Samples a new storage with the given ledger.
214  pub fn sample_storage<N: Network>(ledger: Arc<TranslucentLedgerService<N, ConsensusMemory<N>>>) -> Storage<N> {
215      Storage::new(ledger, Arc::new(BFTMemoryService::new()), BatchHeader::<N>::MAX_GC_ROUNDS as u64)
216  }
217  
218  /// Samples a new gateway with the given ledger.
219  pub fn sample_gateway<N: Network>(
220      account: Account<N>,
221      storage: Storage<N>,
222      ledger: Arc<TranslucentLedgerService<N, ConsensusMemory<N>>>,
223  ) -> Gateway<N> {
224      // Initialize the gateway.
225      Gateway::new(account, storage, ledger, None, &[], false, StorageMode::new_test(None), None).unwrap()
226  }
227  
228  /// Samples a new worker with the given ledger.
229  pub fn sample_worker<N: Network>(
230      id: u8,
231      account: Account<N>,
232      ledger: Arc<TranslucentLedgerService<N, ConsensusMemory<N>>>,
233  ) -> Worker<N> {
234      // Sample a storage.
235      let storage = sample_storage(ledger.clone());
236      // Sample a gateway.
237      let gateway = sample_gateway(account, storage.clone(), ledger.clone());
238      // Sample a dummy proposed batch.
239      let proposed_batch = Arc::new(RwLock::new(None));
240      // Construct the worker instance.
241      Worker::new(id, Arc::new(gateway.clone()), storage.clone(), ledger, proposed_batch).unwrap()
242  }