diff --git a/node/bft/examples/simple_node.rs b/node/bft/examples/simple_node.rs index 9b2d22d748..1af54fcd55 100644 --- a/node/bft/examples/simple_node.rs +++ b/node/bft/examples/simple_node.rs @@ -22,16 +22,21 @@ use snarkos_node_bft::{ BFT, MEMORY_POOL_PORT, }; -use snarkos_node_bft_ledger_service::MockLedgerService; +use snarkos_node_bft_ledger_service::TranslucentLedgerService; use snarkos_node_bft_storage_service::BFTMemoryService; use snarkvm::{ + console::{account::PrivateKey, algorithms::BHP256, types::Address}, ledger::{ block::Transaction, committee::{Committee, MIN_VALIDATOR_STAKE}, narwhal::{BatchHeader, Data}, puzzle::{Solution, SolutionID}, + store::{helpers::memory::ConsensusMemory, ConsensusStore}, + Block, + Ledger, }, - prelude::{Field, Network, Uniform}, + prelude::{Field, Hash, Network, Uniform, VM}, + utilities::{to_bytes_le, FromBytes, TestRng, ToBits, ToBytes}, }; use ::bytes::Bytes; @@ -46,8 +51,14 @@ use axum::{ use axum_extra::response::ErasedJson; use clap::{Parser, ValueEnum}; use indexmap::IndexMap; -use rand::{Rng, SeedableRng}; -use std::{collections::HashMap, net::SocketAddr, path::PathBuf, str::FromStr, sync::Arc}; +use rand::{CryptoRng, Rng, SeedableRng}; +use std::{ + collections::HashMap, + net::SocketAddr, + path::PathBuf, + str::FromStr, + sync::{atomic::AtomicBool, Arc, Mutex, OnceLock}, +}; use tokio::{net::TcpListener, sync::oneshot}; use tracing_subscriber::{ layer::{Layer, SubscriberExt}, @@ -108,8 +119,8 @@ pub async fn start_bft( let (sender, receiver) = init_primary_channels(); // Initialize the components. let (committee, account) = initialize_components(node_id, num_nodes)?; - // Initialize the mock ledger service. - let ledger = Arc::new(MockLedgerService::new(committee)); + // Initialize the translucent ledger service. + let ledger = create_ledger(&account, num_nodes, committee, node_id); // Initialize the storage. let storage = Storage::new( ledger.clone(), @@ -149,8 +160,8 @@ pub async fn start_primary( let (sender, receiver) = init_primary_channels(); // Initialize the components. let (committee, account) = initialize_components(node_id, num_nodes)?; - // Initialize the mock ledger service. - let ledger = Arc::new(MockLedgerService::new(committee)); + // Initialize the translucent ledger service. + let ledger = create_ledger(&account, num_nodes, committee, node_id); // Initialize the storage. let storage = Storage::new( ledger.clone(), @@ -174,6 +185,81 @@ pub async fn start_primary( Ok((primary, sender)) } +/// Initialize the translucent ledger service. +fn create_ledger( + account: &Account, + num_nodes: u16, + committee: Committee, + node_id: u16, +) -> Arc>> { + let gen_key = account.private_key(); + let public_balance_per_validator = + (CurrentNetwork::STARTING_SUPPLY - (num_nodes as u64) * MIN_VALIDATOR_STAKE) / (num_nodes as u64); + let mut balances = IndexMap::, u64>::new(); + for address in committee.members().keys() { + balances.insert(*address, public_balance_per_validator); + } + let mut rng = TestRng::default(); + let gen_ledger = genesis_ledger(*gen_key, committee.clone(), balances.clone(), node_id, &mut rng); + Arc::new(TranslucentLedgerService::new(gen_ledger, Arc::new(AtomicBool::new(false)))) +} + +pub type CurrentLedger = Ledger>; + +fn genesis_cache() -> &'static Mutex, Block>> { + static CACHE: OnceLock, Block>>> = OnceLock::new(); + CACHE.get_or_init(|| Mutex::new(HashMap::new())) +} + +fn genesis_block( + genesis_private_key: PrivateKey, + committee: Committee, + public_balances: IndexMap, u64>, + rng: &mut (impl Rng + CryptoRng), +) -> Block { + // Initialize the store. + let store = ConsensusStore::<_, ConsensusMemory<_>>::open(None).unwrap(); + // Initialize a new VM. + let vm = VM::from(store).unwrap(); + // Initialize the genesis block. + let bonded_balances: IndexMap<_, _> = + committee.members().iter().map(|(address, (amount, _))| (*address, (*address, *address, *amount))).collect(); + vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng).unwrap() +} + +fn genesis_ledger( + genesis_private_key: PrivateKey, + committee: Committee, + public_balances: IndexMap, u64>, + node_id: u16, + rng: &mut (impl Rng + CryptoRng), +) -> CurrentLedger { + let cache_key = + to_bytes_le![genesis_private_key, committee, public_balances.iter().collect::>()].unwrap(); + // Initialize the genesis block on the first call; other callers + // will wait for it on the mutex. + let block = genesis_cache() + .lock() + .unwrap() + .entry(cache_key.clone()) + .or_insert_with(|| { + let hasher = BHP256::::setup("aleo.dev.block").unwrap(); + let file_name = hasher.hash(&cache_key.to_bits_le()).unwrap().to_string() + ".genesis"; + let file_path = std::env::temp_dir().join(file_name); + if file_path.exists() { + let buffer = std::fs::read(file_path).unwrap(); + return Block::from_bytes_le(&buffer).unwrap(); + } + + let block = genesis_block(genesis_private_key, committee, public_balances, rng); + std::fs::write(&file_path, block.to_bytes_le().unwrap()).unwrap(); + block + }) + .clone(); + // Initialize the ledger with the genesis block. + CurrentLedger::load(block, aleo_std::StorageMode::Development(node_id)).unwrap() +} + /// Initializes the components of the node. fn initialize_components(node_id: u16, num_nodes: u16) -> Result<(Committee, Account)> { // Ensure that the node ID is valid.