From 2a8bf4c865f62546d802c157e83c1b688d21b596 Mon Sep 17 00:00:00 2001 From: rustyspottedcatt Date: Sat, 8 Feb 2025 14:33:42 +0100 Subject: [PATCH] Structural Changes & Bug fixing for v0.1.0-pre.alpha.3 --- .gitignore | 4 +- Cargo.toml | 34 +++-- README.md | 213 ++++++++++------------------- src/core/api/v1/transaction.rs | 21 ++- src/core/canister/canister.rs | 10 ++ src/core/consensus/block.rs | 14 +- src/core/consensus/consensus.rs | 50 +++++++ src/core/consensus/mod.rs | 1 + src/core/consensus/model.rs | 14 +- src/core/consensus/neuron.rs | 17 ++- src/core/consensus/transaction.rs | 17 ++- src/core/consensus/validator.rs | 60 +++++++- src/core/governance/governance.rs | 8 +- src/core/nervous/neuron_handler.rs | 6 +- src/main.rs | 157 ++++++++++----------- tests/staking.rs | 62 --------- tests/transactions.rs | 46 ------- 17 files changed, 356 insertions(+), 378 deletions(-) create mode 100644 src/core/consensus/consensus.rs delete mode 100644 tests/staking.rs delete mode 100644 tests/transactions.rs diff --git a/.gitignore b/.gitignore index ee28d79..526f114 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target /.idea -/nebula_db \ No newline at end of file +/nebula_db +/.cargo +/*.tar.gz \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 067ff78..fe23ee6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,12 @@ [package] -name = "nebulacrypto" +name = "nebula" version = "0.1.0-pre.alpha.3" edition = "2024" +authors = ["NEBYTE contact@nebula-protocol.com"] +description = "Nebula is a blockchain-based system that replicates ICP’s architecture—including neurons, governance, canisters, transactions, and staking. It uses ed25519-dalek for key management and transaction signing." +license = "AGPL-3" +readme = "README.md" +repository = "https://github.com/NEBYTE/nebula" [dependencies] tokio = { version = "1", features = ["rt-multi-thread", "macros", "full"] } @@ -13,14 +18,27 @@ chrono = { version = "0.4.39", features = ["serde"]} sha2 = "0.10.8" hex = "0.4.3" +[package.metadata.deb] +maintainer = "NEBYTE contact@nebula-protocol.com" +copyright = "2025, NEBYTE" +license-file = ["LICENSE"] +depends = "libssl-dev" +section = "utils" +priority = "optional" +assets = [ + ["target/release/nebula", "/usr/bin/", "755"], +] + +[[bin]] +name = "nebula" +path = "src/main.rs" + [lib] name = "nebula" path = "src/lib.rs" -[[test]] -name = "transactions" -path = "tests/transactions.rs" - -[[test]] -name = "staking" -path = "tests/staking.rs" \ No newline at end of file +[profile.release] +strip = true +opt-level = "z" +lto = true +panic = "abort" diff --git a/README.md b/README.md index e7d5ef6..e342e39 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ > **NOTE:** Nebula is still under heavy development. Expect bugs and frequent API changes. -Nebula is a blockchain-based system that replicates ICP’s architecture—including neurons, governance, canisters, transactions, and staking. It uses **ed25519-dalek** for key management and transaction signing. +Nebula is a blockchain-based system that replicates ICP’s architecture—including neurons, governance, canisters, transactions, and staking. It utilizes **ed25519-dalek** for key management and transaction signing. --- @@ -22,7 +22,8 @@ Nebula is a blockchain-based system that replicates ICP’s architecture—inclu - [Installation](#installation) - [Usage](#usage) - [Wallet Management](#wallet-management) - - [Transaction Processing (Direct and via Canisters)](#transaction-processing) + - [Transaction Processing](#transaction-processing) + - [Canister Transactions](#canister-transactions) - [Block Production](#block-production) - [Neuron Management](#neuron-management) - [Staking](#staking) @@ -34,13 +35,13 @@ Nebula is a blockchain-based system that replicates ICP’s architecture—inclu ## Features -- **Wallet Management:** Generate wallets with private keys, public keys, and blockchain-compatible addresses. -- **Transaction Processing:** Build, sign, and submit transactions with dynamic fee and index calculation. -- **Consensus Engine:** Validator selection, block production, and transaction verification. -- **Governance:** Neuron-based proposals and voting. -- **Nervous System:** Neuron creation, locking/unlocking, and stake delegation. -- **Staking:** Secure staking and unstaking of tokens. -- **Canisters:** Wrap functionality (e.g., transaction submission, staking) into canisters for modularity and secure isolation. +- **Wallet Management** - Create and manage wallets with private/public keys and blockchain-compatible addresses. +- **Transaction Processing** - Build, sign, and submit transactions securely. +- **Consensus Engine** - Validator selection, block production, and transaction verification. +- **Governance** - Neuron-based proposals and voting. +- **Nervous System** - Neuron creation, locking/unlocking, and stake delegation. +- **Staking** - Secure staking and unstaking of tokens. +- **Canisters** - Modular execution of functions via on-chain canisters. --- @@ -76,183 +77,120 @@ cargo run ### Wallet Management -Create a wallet using the built-in function. The wallet returns a private key, a public key (as a VerifyingKey), and an address. - ```rust use crate::core::api::v1::wallet::create_wallet; -let (signing_key, public_key, address) = create_wallet(); -println!("Wallet created: {:x?}", address); // Shareable address -println!("Public Key: {:?}", public_key); // Safe to share -println!("Private Key: {:?}", signing_key); // DO NOT SHARE! +// Create a new wallet +let (signing_key, public_key, sender_address) = create_wallet(); +println!("Sender Wallet created: {:x?}", sender_address); + +let (_receiver_signing, receiver_public, receiver_address) = create_wallet(); +println!("Receiver Wallet created: {:x?}", receiver_address); ``` -### Transaction Processing +--- -#### Direct Transaction Creation +### Transaction Processing -The following example shows how to build, sign, and submit a transaction directly using the consensus engine (without a canister). +#### Direct Transaction ```rust -use crate::core::api::v1::transaction::{build_transaction, finalize_transaction, submit_transaction}; +use crate::core::api::v1::transaction::{build_transaction, finalize_transaction}; use crate::core::types::TransactionType; -// Assume consensus_engine is already initialized and sender/receiver ledger accounts exist. -let amount = 50; let mut tx = build_transaction( &mut consensus_engine, - sender_address, // sender's address - receiver_address, // receiver's address - amount, - 0, // memo - 0, // nrc_memo - TransactionType::Transfer -); -finalize_transaction(&mut tx, &signing_key)?; // Signs the transaction -submit_transaction(&mut consensus_engine, tx)?; // Submits to the mempool + sender_address.clone(), + receiver_address.clone(), + 50, // Amount + 0, // Memo + 0, // NRC Memo + TransactionType::Transfer, +); + +// above assumes ledger were made with the sufficient balance for the sender address. + +finalize_transaction(&mut tx, &signing_key).expect("Failed to finalize transaction"); ``` -#### Transaction Submission via Canisters +--- -Nebula allows you to wrap functionality in a canister. In the example below, we create a canister, initialize ledger accounts for sender and receiver, build and sign a transaction, then submit it via the canister. +### Canister Transactions ```rust -use crate::core::api::v1::transaction::{submit_transaction, build_transaction, finalize_transaction}; -use crate::core::api::v1::wallet::create_wallet; use crate::core::canister::canister::{Canister, CanisterFunctionPayload}; use crate::core::canister::registry::CanisterRegistry; -use crate::core::consensus::{ValidatorInfo}; -use crate::core::consensus::model::ConsensusEngine; -use crate::core::nervous::NervousSystem; -use crate::core::types::TransactionType; -#[tokio::main] -async fn main() { - // Create wallet for sender. - let (signing_key, public_key, address) = create_wallet(); - println!("Wallet created: {:x?}", address); - - // Create and register a canister. - let mut canister_registry = CanisterRegistry::new(); - let canister_id = "my_canister".to_string(); - let canister = Canister::new(canister_id.clone(), address.clone()); - println!("Canister ID: {}", canister.canister_id); - canister_registry.register_canister(&canister_id, canister); - let mut registered_canister = match canister_registry.get_canister(&canister_id) { - Some(c) => c, - None => { - eprintln!("Canister '{}' not found", canister_id); - return; - } - }; - - // Use canister-based API to submit a transaction. - { - // Create a wallet for the receiver. - let (_signing_key1, public_key1, address1) = create_wallet(); - - // Initialize the Nervous System and Consensus Engine. - let nervous_system = NervousSystem::new(); - let validators = vec![ValidatorInfo { address: address.clone(), active: true }]; - let mut consensus_engine = ConsensusEngine::new(validators, nervous_system.neurons.clone()); - - // Initialize ledger accounts for sender and receiver. - consensus_engine.init_ledger(address.clone(), public_key, 100) - .expect("Failed to initialize sender ledger"); - consensus_engine.init_ledger(address1.clone(), public_key1, 0) - .expect("Failed to initialize receiver ledger"); - - let amount = 50; - // Build and sign the transaction. - let mut tx = build_transaction( - &mut consensus_engine, - address.clone(), - address1.clone(), - amount, - 0, - 0, - TransactionType::Transfer - ); - finalize_transaction(&mut tx, &signing_key) - .expect("Failed to sign the transaction"); - - // Create a transfer payload for the canister. - let transfer_payload = CanisterFunctionPayload::Transfer { - consensus_engine: &mut consensus_engine, - tx, - }; - - // Execute the transaction via the canister. - match registered_canister.execute_function(transfer_payload) { - Ok(msg) => println!("Successfully sent transfer: {}", msg), - Err(err) => eprintln!("Transfer failed: {}", err), - } - } -} +// Register a canister +let mut canister_registry = CanisterRegistry::new(); +let canister = Canister::new("my_canister".to_string(), sender_address.clone()); +println!("Canister created with ID: {}", canister.canister_id); + +canister_registry.register_canister(&canister.canister_id, canister.clone()); ``` -### Block Production +--- -After transactions are in the mempool, you can produce a block: +### Block Production ```rust -use crate::core::consensus::block::produce_block; - -let block = produce_block(&mut consensus_engine, &signing_key)?; -println!("Block produced with {} transactions", block.transactions.len()); -println!("Block timestamp: {}", block.header.timestamp); +use crate::core::consensus::consensus::run_consensus_loop; +use std::time::Duration; + +let target_cycle = Duration::from_secs(1 / 2); // 0.5s +let signing_key_clone = signing_key.clone(); +let mut consensus_engine_clone = consensus_engine.clone(); // consensus_engine uses Arc>, everything is synchronized. + +tokio::spawn(async move { + run_consensus_loop( + &mut consensus_engine_clone, + &signing_key_clone, + target_cycle, + ).await; +}); +println!("🚀 Blockchain node is running! Listening for transactions..."); ``` -### Neuron Management +--- -Create a neuron for governance: +### Neuron Management ```rust -use crate::core::nervous::neuron_handler::create_neuron; +use crate::core::nervous::{create_neuron, NervousSystem}; -let neuron_id = create_neuron(&nervous_system, &signing_key, "Test Neuron".to_string(), 30)?; -println!("Neuron created with id: {}", neuron_id); +let mut nervous_system = NervousSystem::new(); +let neuron_id = create_neuron(&mut nervous_system, &signing_key, "John Doe".to_string(), 365) + .expect("Failed to create neuron"); +println!("Created Neuron with ID: {}", neuron_id); ``` -### Staking +--- -Stake tokens to a neuron: +### Staking ```rust -use crate::core::staking::staking_handler::{stake, unstake}; +use crate::core::staking::{stake, StakingModule}; -// init consensus_engine beforehand -let mut staking_module = core::staking::staking_module::StakingModule::new(nervous_system.neurons.clone()); -stake(&mut staking_module, &mut consensus_engine, &signing_key, neuron_id, 50)?; -println!("Staked 50 tokens to neuron {}", neuron_id); +let mut staking_module = StakingModule::new(nervous_system.neurons.clone()); +stake(&mut staking_module, &mut consensus_engine, &signing_key, neuron_id, 500) + .expect("Failed to stake 500 tokens"); +println!("Staked 500 tokens to neuron {}", neuron_id); ``` -### Governance and Voting +--- -Submit a proposal and vote: +### Governance and Voting ```rust -use crate::core::governance::{proposal_handler::propose, voting::{vote, finalize}}; - -let governance = core::governance::Governance::new(nervous_system.neurons.clone()); -let proposal_id = propose(&governance, "Increase block size".to_string(), &signing_key, neuron_id)?; -println!("Proposal created with id: {}", proposal_id); +use crate::core::governance::Governance; -match vote(&governance, &signing_key, neuron_id, proposal_id, true, 10) { - Ok(_) => println!("Voted on proposal {}", proposal_id), - Err(e) => println!("Voting failed: {}", e), -} - -let proposal_result = finalize(&governance, proposal_id)?; -println!("Proposal finalized with result: {}", proposal_result); +let mut governance_module = Governance::new(nervous_system.neurons.clone()); ``` --- ## Dependencies -Add the following to your `Cargo.toml`: - ```toml [dependencies] tokio = { version = "1", features = ["rt-multi-thread", "macros", "full"] } @@ -269,4 +207,5 @@ hex = "0.4" ## License -Distributed under the [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/) license. \ No newline at end of file +Distributed under the [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/) license. + diff --git a/src/core/api/v1/transaction.rs b/src/core/api/v1/transaction.rs index c82aa08..2509a77 100644 --- a/src/core/api/v1/transaction.rs +++ b/src/core/api/v1/transaction.rs @@ -17,12 +17,14 @@ pub fn build_transaction( tx_type: TransactionType, ) -> Transaction { let fee = amount / 100; - let index = consensus - .chain - .iter() - .map(|chain| chain.transactions.len()) - .sum::() as u32 - + consensus.mempool.len() as u32; + + let chain_guard = consensus.chain.lock().unwrap(); + let total_chain_txs: usize = chain_guard.iter().map(|block| block.transactions.len()).sum(); + + let mempool_guard = consensus.mempool.lock().unwrap(); + let mempool_len = mempool_guard.len(); + + let index = total_chain_txs as u32 + mempool_len as u32; Transaction { hash: String::new(), @@ -54,6 +56,13 @@ pub fn finalize_transaction(tx: &mut Transaction, signing_key: &SigningKey) -> R Ok(()) } +pub fn cancel_transaction(canister: &mut Canister, consensus_engine: &mut ConsensusEngine, tx_hash: String) -> Result { + canister.execute_function(CanisterFunctionPayload::CancelTransfer { + consensus_engine, + tx_hash, + }) +} + pub fn submit_transaction( canister: &mut Canister, consensus_engine: &mut ConsensusEngine, diff --git a/src/core/canister/canister.rs b/src/core/canister/canister.rs index 8d89224..a8e2140 100644 --- a/src/core/canister/canister.rs +++ b/src/core/canister/canister.rs @@ -8,6 +8,7 @@ use crate::core::governance::{ }; use crate::core::nervous::*; use crate::core::consensus::model::ConsensusEngine; +use crate::core::consensus::transaction::cancel_transaction; use std::collections::HashMap; use std::sync::{Arc, Mutex}; @@ -19,6 +20,7 @@ pub enum CanisterFunctionPayload<'a> { consensus_engine: &'a mut ConsensusEngine, tx: Transaction, }, + Stake { staking_module: &'a mut StakingModule, consensus_engine: &'a mut ConsensusEngine, @@ -71,6 +73,10 @@ pub enum CanisterFunctionPayload<'a> { governance: &'a mut Governance, proposal_id: u64, }, + CancelTransfer { + consensus_engine: &'a mut ConsensusEngine, + tx_hash: String, + } } #[derive(Clone)] @@ -110,6 +116,10 @@ impl Canister { tx.from, tx.to, tx.amount )) } + CanisterFunctionPayload::CancelTransfer { consensus_engine, tx_hash } => { + cancel_transaction(consensus_engine, tx_hash.clone())?; + Ok("Transacted cancelled!".to_string()) + } CanisterFunctionPayload::Stake { staking_module, consensus_engine, diff --git a/src/core/consensus/block.rs b/src/core/consensus/block.rs index 646b931..48651ed 100644 --- a/src/core/consensus/block.rs +++ b/src/core/consensus/block.rs @@ -13,11 +13,14 @@ pub fn produce_block( consensus_engine: &mut ConsensusEngine, signing_key: &SigningKey ) -> Result { - let transactions = consensus_engine.mempool.drain(..).collect::>(); + let mut mempool_lock = consensus_engine.mempool.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + let mut chain_lock = consensus_engine.chain.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + + let transactions = mempool_lock.drain(..).collect::>(); let merkle_root = compute_merkle_root(&transactions); let verifying_key = signing_key.verifying_key(); - let parent_hash = consensus_engine.chain.last().map(|blk| hash_block(blk)).unwrap_or([0u8; 32]); + let parent_hash = chain_lock.last().map(|blk| hash_block(blk)).unwrap_or([0u8; 32]); let mut header = BlockHeader { parent_hash, @@ -31,7 +34,7 @@ pub fn produce_block( header.signature = sign_data(signing_key, &signable); let block = Block { header, transactions }; - consensus_engine.chain.push(block.clone()); + chain_lock.push(block.clone()); Ok(block) } @@ -50,7 +53,9 @@ pub fn validate_block( let pubkey = VerifyingKey::from_bytes(&pubkey_array) .map_err(|e| format!("Failed to create VerifyingKey: {}", e))?; - if !consensus_engine.validators.iter().any(|v| v.address == block.header.validator && v.active) { + let validators_lock = consensus_engine.validators.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + + if !validators_lock.iter().any(|v| v.address == block.header.validator && v.active) { return Err("Block validator is not active".into()); } @@ -71,7 +76,6 @@ pub fn validate_block( Ok(()) } - pub fn compute_merkle_root( transactions: &[Transaction] ) -> [u8; 32] { diff --git a/src/core/consensus/consensus.rs b/src/core/consensus/consensus.rs new file mode 100644 index 0000000..e02886a --- /dev/null +++ b/src/core/consensus/consensus.rs @@ -0,0 +1,50 @@ +use crate::core::consensus::block::produce_block; +use crate::core::consensus::model::ConsensusEngine; +use crate::core::consensus::validator::select_next_validator; + +use ed25519_dalek::{SigningKey, VerifyingKey}; +use tokio::time::{sleep, Duration, Instant}; +use hex; +pub async fn run_consensus_loop( + consensus_engine: &mut ConsensusEngine, + signing_key: &SigningKey, + target_cycle: Duration, +) { + loop { + let cycle_start = Instant::now(); + + if let Some(next_validator) = select_next_validator(consensus_engine) { + let verifying_key = VerifyingKey::from(signing_key); + let my_address = hex::encode(verifying_key.to_bytes()); + + + if next_validator == my_address { + match produce_block(consensus_engine, signing_key) { + Ok(block) => { + println!( + "Block produced: {} transactions, timestamp: {}", + block.transactions.len(), + block.header.timestamp + ); + } + Err(err) => { + eprintln!("Block production error: {}", err); + } + } + } else { + println!("This node is NOT the selected validator. Skipping block production."); + } + } else { + println!("No active validators found."); + } + + let elapsed = cycle_start.elapsed(); + let sleep_duration = if elapsed < target_cycle { + target_cycle - elapsed + } else { + Duration::from_secs(1) + }; + + sleep(sleep_duration).await; + } +} diff --git a/src/core/consensus/mod.rs b/src/core/consensus/mod.rs index 75cad22..820c0f2 100644 --- a/src/core/consensus/mod.rs +++ b/src/core/consensus/mod.rs @@ -4,6 +4,7 @@ pub mod block; pub mod neuron; pub mod utils; pub mod model; +pub mod consensus; pub use validator::{ValidatorInfo, select_next_validator, slash}; pub use transaction::{add_transaction, compute_transaction_hash}; diff --git a/src/core/consensus/model.rs b/src/core/consensus/model.rs index 90d482f..df61c7e 100644 --- a/src/core/consensus/model.rs +++ b/src/core/consensus/model.rs @@ -10,21 +10,23 @@ pub struct Account { pub public_key: VerifyingKey, pub balance: u64, } + +#[derive(Clone)] pub struct ConsensusEngine { - pub validators: Vec, + pub validators: Arc>>, pub neurons: Arc>>, - pub mempool: Vec, - pub chain: Vec, + pub mempool:Arc>>, + pub chain: Arc>>, pub ledger: Arc>>, } impl ConsensusEngine { - pub fn new(validators: Vec, neurons: Arc>>) -> Self { + pub fn new(validators: Arc>>, neurons: Arc>>) -> Self { Self { validators, neurons, - mempool: Vec::new(), - chain: Vec::new(), + mempool: Arc::new(Mutex::new(Vec::new())), + chain: Arc::new(Mutex::new(Vec::new())), ledger: Arc::new(Mutex::new(HashMap::new())) } } diff --git a/src/core/consensus/neuron.rs b/src/core/consensus/neuron.rs index 2862d49..f47a13d 100644 --- a/src/core/consensus/neuron.rs +++ b/src/core/consensus/neuron.rs @@ -1,5 +1,5 @@ use crate::core::consensus::model::ConsensusEngine; -use crate::core::types::{Address}; +use crate::core::types::Address; pub fn delegate_stake( consensus_engine: &mut ConsensusEngine, @@ -8,16 +8,19 @@ pub fn delegate_stake( ) -> Result<(), String> { let mut neurons = consensus_engine.neurons.lock().unwrap(); - let validator_list: Vec
= neurons.values() - .filter_map(|n| n.validator.clone()) - .collect(); - let neuron = neurons.get_mut(&neuron_id).ok_or("Neuron not found")?; - if !validator_list.contains(&validator) { + let validators_lock = consensus_engine.validators.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + + let is_valid_validator = validators_lock + .iter() + .any(|v| v.address == validator); + + if !is_valid_validator { return Err("Validator not found or inactive".into()); } - neuron.validator = Some(validator); + neuron.validator = Some(validator.clone()); + Ok(()) } diff --git a/src/core/consensus/transaction.rs b/src/core/consensus/transaction.rs index 93dbfe3..9cb6ab4 100644 --- a/src/core/consensus/transaction.rs +++ b/src/core/consensus/transaction.rs @@ -87,10 +87,25 @@ pub fn add_transaction( }; ledger.insert(tx.to.clone(), updated_receiver); - consensus_engine.mempool.push(tx); + let mut mempool_lock = consensus_engine.mempool.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + mempool_lock.push(tx); + Ok(()) } +pub fn cancel_transaction(consensus_engine: &mut ConsensusEngine, tx_hash: String) -> Result<(), String> { + let mut mempool_lock = consensus_engine.mempool.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + let pos = mempool_lock.iter().position(|tx| tx.hash == tx_hash); + + if let Some(index) = pos { + mempool_lock.remove(index); + Ok(()) + } else { + Err("Transaction not found in mempool".to_string()) + } +} + + pub fn compute_transaction_hash( tx: &Transaction ) -> Result { diff --git a/src/core/consensus/validator.rs b/src/core/consensus/validator.rs index 76d8948..cae839a 100644 --- a/src/core/consensus/validator.rs +++ b/src/core/consensus/validator.rs @@ -1,11 +1,15 @@ +use std::sync::{Arc, Mutex}; use crate::core::types::{Address}; use sha2::{Digest, Sha256}; use chrono::Utc; +use rand::Rng; use crate::core::consensus::model::ConsensusEngine; +use crate::core::nervous::NervousSystem; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ValidatorInfo { pub address: Address, + pub neuron_id: u64, pub active: bool, } pub fn slash( @@ -16,6 +20,8 @@ pub fn slash( let penalty = amount * 2; let mut neurons = consensus_engine.neurons.lock().unwrap(); + let mut validators_lock = consensus_engine.validators.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + for neuron in neurons.values_mut() { if neuron.validator == Some(validator.clone()) { if neuron.staked_amount > penalty { @@ -26,20 +32,66 @@ pub fn slash( } } - if let Some(v) = consensus_engine.validators.iter_mut().find(|v| v.address == validator) { + if let Some(v) = validators_lock.iter_mut().find(|v| v.address == validator) { v.active = false; } } +pub fn build_validator(nervous_system: &mut NervousSystem, neuron_id: u64) -> Result { + let neurons_lock = nervous_system.neurons.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + let neuron = neurons_lock.get(&neuron_id).ok_or("Neuron not exist")?; + + let validator = ValidatorInfo { + address: neuron.address.clone(), + neuron_id, + active: false, + }; + + Ok(validator) +} + +pub fn wrap_validator(validator_info: ValidatorInfo) -> Arc>> { + Arc::new(Mutex::new(vec![validator_info])) +} + +pub fn register_validator(consensus_engine: &mut ConsensusEngine, neuron_id: u64) -> Result<(), String> { + let neurons_lock = consensus_engine.neurons.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + let neuron = neurons_lock.get(&neuron_id).ok_or("Neuron not exist")?; + + let mut validators_lock = consensus_engine.validators.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + + if validators_lock.iter().any(|v| v.address == neuron.address) { + return Err("Neuron already exists".into()); + } + + if neuron.staked_amount < 100 { + return Err("Neuron does not have enough stake".into()); + } + + validators_lock.push(ValidatorInfo { + address: neuron.address.clone(), + neuron_id, + active: true, + }); + + Ok(()) +} pub fn select_next_validator( consensus_engine: &mut ConsensusEngine, ) -> Option
{ let now = Utc::now().timestamp_nanos_opt().ok_or("Couldn't fetch timestamp for UTC"); let neurons_lock = consensus_engine.neurons.lock().map_err(|_| "Mutex poisoned").ok()?; + let validators_lock = consensus_engine.validators.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + let stake_weighted: Vec<(Address, u64)> = neurons_lock .values() - .filter_map(|neuron| neuron.validator.clone().map(|v| (v, neuron.staked_amount))) + .filter_map(|neuron| { + neuron.validator.as_ref().and_then(|validator_address| { + validators_lock.iter().find(|v| v.address == *validator_address) + .map(|validator| (validator.address.clone(), neuron.staked_amount)) + }) + }) .collect(); if stake_weighted.is_empty() { @@ -47,7 +99,7 @@ pub fn select_next_validator( } let total_stake: u64 = stake_weighted.iter().map(|(_, stake)| stake).sum(); - let seed = now.unwrap().wrapping_add(total_stake as i64); + let seed = now.unwrap().wrapping_add(rand::thread_rng().gen_range(0..total_stake as i64)); let hash = Sha256::digest(&seed.to_be_bytes()); let roll = u64::from_be_bytes(hash[0..8].try_into().unwrap()) % total_stake; diff --git a/src/core/governance/governance.rs b/src/core/governance/governance.rs index 1177d3f..2c888ae 100644 --- a/src/core/governance/governance.rs +++ b/src/core/governance/governance.rs @@ -6,8 +6,8 @@ use crate::core::governance::proposal::Proposal; pub struct Governance { pub proposals: Arc>>, pub neurons: Arc>>, - pub total_voting_power: u128, - pub daily_voting_rewards: u128, + pub total_voting_power: Arc>, + pub daily_voting_rewards: Arc>, pub next_id: Arc>, } @@ -16,8 +16,8 @@ impl Governance { Self { proposals: Arc::new(RwLock::new(BinaryHeap::new())), neurons, - total_voting_power: 500_000_000, - daily_voting_rewards: 90_500, + total_voting_power: Arc::new(Mutex::new(500_000_000)), + daily_voting_rewards: Arc::new(Mutex::new(90_500)), next_id: Arc::new(Mutex::new(1)), } } diff --git a/src/core/nervous/neuron_handler.rs b/src/core/nervous/neuron_handler.rs index d6195c8..5a344fe 100644 --- a/src/core/nervous/neuron_handler.rs +++ b/src/core/nervous/neuron_handler.rs @@ -5,7 +5,7 @@ use crate::core::types::{Neuron, NeuronStatus}; use crate::core::nervous::nervous_system::NervousSystem; pub fn create_neuron( - nervous_system: &NervousSystem, + nervous_system: &mut NervousSystem, caller: &SigningKey, name: String, dissolve_days: i64 @@ -48,7 +48,7 @@ pub fn create_neuron( } pub fn get_neuron( - nervous_system: &NervousSystem, + nervous_system: &mut NervousSystem, neuron_id: u64 ) -> Option { let neurons = nervous_system.neurons.lock().unwrap(); @@ -56,7 +56,7 @@ pub fn get_neuron( } pub fn list_neurons( - nervous_system: &NervousSystem + nervous_system: &mut NervousSystem ) -> Vec { let neurons = nervous_system.neurons.lock().unwrap(); neurons.values().cloned().collect() diff --git a/src/main.rs b/src/main.rs index 878079f..e8eb7fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,18 @@ -use crate::core::api::v1::transaction::{submit_transaction, build_transaction, finalize_transaction}; +use std::time::Duration; +use ed25519_dalek::SigningKey; +use rand::rngs::OsRng; +use tokio::task; +use crate::core::api::v1::transaction::{build_transaction, finalize_transaction}; use crate::core::api::v1::wallet::create_wallet; use crate::core::canister::canister::{Canister, CanisterFunctionPayload}; use crate::core::canister::registry::CanisterRegistry; -use crate::core::consensus::{ValidatorInfo}; +use crate::core::consensus::{delegate_stake, ValidatorInfo}; +use crate::core::consensus::consensus::run_consensus_loop; use crate::core::consensus::model::ConsensusEngine; -use crate::core::nervous::NervousSystem; +use crate::core::consensus::validator::{build_validator, register_validator, wrap_validator}; +use crate::core::governance::Governance; +use crate::core::nervous::{create_neuron, NervousSystem}; +use crate::core::staking::{stake, StakingModule}; use crate::core::types::TransactionType; pub mod core { @@ -26,103 +34,76 @@ async fn main() { let (_receiver_signing, receiver_public, receiver_address) = create_wallet(); println!("Receiver Wallet created: {:x?}", receiver_address); - let nervous_system = NervousSystem::new(); - let validators = vec![ValidatorInfo { address: sender_address.clone(), active: true }]; - let mut consensus_engine = ConsensusEngine::new(validators, nervous_system.neurons.clone()); + let mut nervous_system = NervousSystem::new(); + let mut staking_module = StakingModule::new(nervous_system.neurons.clone()); - consensus_engine - .init_ledger(sender_address.clone(), public_key, 100) - .expect("Failed to initialize sender ledger"); - consensus_engine - .init_ledger(receiver_address.clone(), receiver_public, 0) - .expect("Failed to initialize receiver ledger"); + let neuron_id = create_neuron(&mut nervous_system, &signing_key, "John Doe".to_string(), 365).unwrap(); + println!("Created Neuron with ID: {}", neuron_id); + let mut built_validator = build_validator(&mut nervous_system, neuron_id.clone()).unwrap(); + built_validator.active = true; + + let wrapped_validators = wrap_validator(built_validator); + + let mut consensus_engine = ConsensusEngine::new(wrapped_validators, nervous_system.neurons.clone()); + let mut governance_module = Governance::new(nervous_system.neurons.clone()); let mut canister_registry = CanisterRegistry::new(); - let canister_id = "my_canister".to_string(); - let canister = Canister::new(canister_id.clone(), sender_address.clone()); + let sender_ledger_wallet = consensus_engine.init_ledger(sender_address.clone(), public_key, 1000); + let receiver_ledger_wallet = consensus_engine.init_ledger(receiver_address.clone(), public_key, 1000); + + stake(&mut staking_module, &mut consensus_engine, &signing_key, neuron_id, 500).expect("Failed to stake 500 tokens"); + delegate_stake(&mut consensus_engine, neuron_id, sender_address.clone()).expect("Failed to delegate stake to neuron"); + + let target_cycle = Duration::from_secs(1/2); + + let signing_key_clone = signing_key.clone(); + let mut consensus_engine_clone = consensus_engine.clone(); + + tokio::spawn(async move { + run_consensus_loop( + &mut consensus_engine_clone, + &signing_key_clone, + target_cycle, + ).await; + }); + + println!("🚀 Blockchain node is running! Listening for transactions..."); + + let canister = Canister::new("my_canister".to_string(), sender_address.clone()); println!("Canister created with ID: {}", canister.canister_id); - canister_registry.register_canister(&canister_id, canister); - let mut registered_canister = match canister_registry.get_canister(&canister_id) { + canister_registry.register_canister(&canister.canister_id, canister.clone()); + + let mut registered_canister = match canister_registry.get_canister(&canister.canister_id) { Some(c) => c, None => { - eprintln!("Canister '{}' not found", canister_id); + eprintln!("Canister '{}' not found", &canister.canister_id); return; } }; - { - let amount = 50; - - let mut tx = build_transaction( - &mut consensus_engine, - sender_address.clone(), - receiver_address.clone(), - amount, - 0, - 0, - TransactionType::Transfer, - ); - finalize_transaction(&mut tx, &signing_key).expect("Failed to sign the transaction"); - - let transfer_payload = CanisterFunctionPayload::Transfer { - consensus_engine: &mut consensus_engine, - tx, - }; - - match registered_canister.execute_function(transfer_payload) { - Ok(msg) => println!("Successfully sent transfer: {}", msg), - Err(err) => eprintln!("Transfer failed: {}", err), - } - } - - { - let block = crate::core::consensus::block::produce_block(&mut consensus_engine, &signing_key) - .expect("Failed to produce block"); + let mut tx = build_transaction( + &mut consensus_engine, + sender_address.clone(), + receiver_address.clone(), + 50, + 0, + 0, + TransactionType::Transfer, + ); + + finalize_transaction(&mut tx, &signing_key).expect("Failed to finalize transaction"); + + let transfer_payload = CanisterFunctionPayload::Transfer { + consensus_engine: &mut consensus_engine, + tx, + }; - println!("Block produced with {} transaction(s)", block.transactions.len()); - println!("Block timestamp: {}", block.header.timestamp); + match registered_canister.execute_function(transfer_payload) { + Ok(msg) => println!("Successfully sent transfer: {}", msg), + Err(err) => eprintln!("Transfer failed: {}", err), } - { - let neuron_id = crate::core::nervous::neuron_handler::create_neuron( - &nervous_system, - &signing_key, - "Test Neuron".to_string(), - 30, - ) - .expect("Failed to create neuron"); - println!("Neuron created with id: {}", neuron_id); - - let mut staking_module = - crate::core::staking::staking_module::StakingModule::new(nervous_system.neurons.clone()); - crate::core::staking::staking_handler::stake(&mut staking_module, &mut consensus_engine, &signing_key, neuron_id, 50) - .expect("Failed to stake tokens"); - println!("Staked 50 tokens to neuron {}", neuron_id); - - let governance = crate::core::governance::Governance::new(nervous_system.neurons.clone()); - let proposal_id = crate::core::governance::proposal_handler::propose( - &governance, - "Increase block size".to_string(), - &signing_key, - neuron_id, - ) - .expect("Failed to create proposal"); - println!("Proposal created with id: {}", proposal_id); - match crate::core::governance::voting::vote( - &governance, - &signing_key, - neuron_id, - proposal_id, - true, - 10, - ) { - Ok(_) => println!("Voted on proposal {}", proposal_id), - Err(e) => println!("Voting failed: {}", e), - } - let proposal_result = - crate::core::governance::voting::finalize(&governance, proposal_id).expect("Failed to finalize proposal"); - println!("Proposal finalized with result: {}", proposal_result); - } -} + tokio::signal::ctrl_c().await.expect("Failed to listen for shutdown signal"); +} \ No newline at end of file diff --git a/tests/staking.rs b/tests/staking.rs deleted file mode 100644 index dfd856a..0000000 --- a/tests/staking.rs +++ /dev/null @@ -1,62 +0,0 @@ -use nebula::core::staking::staking_module::StakingModule; -use nebula::core::staking::staking_handler::stake; -use nebula::core::consensus::model::ConsensusEngine; -use nebula::core::consensus::ValidatorInfo; -use nebula::core::nervous::NervousSystem; -use nebula::core::api::v1::wallet::create_wallet; -use nebula::core::nervous::neuron_handler::create_neuron; - -#[test] -fn test_stake() { - let nervous_system = NervousSystem::new(); - - let (signing_key, public_key, sender_address) = create_wallet(); - let validators = vec![ValidatorInfo { - address: sender_address.clone(), - active: true - }]; - - let mut consensus_engine = ConsensusEngine::new(validators, nervous_system.neurons.clone()); - consensus_engine - .init_ledger(sender_address.clone(), public_key, 100) - .expect("Failed to initialize sender ledger"); - - let neuron_id = create_neuron( - &nervous_system, - &signing_key, - "TestNeuron".to_string(), - 30, - ) - .expect("Failed to create neuron"); - - let mut staking_module = StakingModule::new(nervous_system.neurons.clone()); - - let amount_to_stake = 50; - let result = stake( - &mut staking_module, - &mut consensus_engine, - &signing_key, - neuron_id, - amount_to_stake, - ); - assert!(result.is_ok(), "Staking failed: {:?}", result.err()); - - let neurons_lock = nervous_system.neurons.lock().unwrap(); - let updated_neuron = neurons_lock - .get(&neuron_id) - .expect("Neuron not found"); - assert_eq!( - updated_neuron.staked_amount, - amount_to_stake, - "Stake amount incorrect" - ); - - let sender_balance = consensus_engine - .get_balance(&sender_address); - - assert_eq!( - sender_balance, - 100 - amount_to_stake, - "Ledger balance incorrect after staking" - ); -} diff --git a/tests/transactions.rs b/tests/transactions.rs deleted file mode 100644 index 177ee94..0000000 --- a/tests/transactions.rs +++ /dev/null @@ -1,46 +0,0 @@ -use nebula::core::api::v1::transaction::{build_transaction, finalize_transaction, submit_transaction}; -use nebula::core::api::v1::wallet::create_wallet; -use nebula::core::consensus::model::ConsensusEngine; -use nebula::core::types::TransactionType; -use nebula::core::canister::canister::Canister; -use nebula::core::nervous::NervousSystem; - -#[test] -fn test_full_transaction_flow() { - let nervous_system = NervousSystem::new(); - - let validators = vec![]; - let mut consensus_engine = ConsensusEngine::new(validators, nervous_system.neurons.clone()); - - let (signing_key, public_key, address) = create_wallet(); - let (recipient_signing_key, recipient_public_key, recipient_address) = create_wallet(); - - let amount = 100; - - let canister_id = "test_canister".to_string(); - let mut canister = Canister::new(canister_id, address.clone()); - - consensus_engine.init_ledger(address.clone(), public_key, amount); - consensus_engine.init_ledger(recipient_address.clone(), recipient_public_key, amount); // Making sure recipient wallet exists in ledger - - let mut tx = build_transaction( - &mut consensus_engine, - address.clone(), - recipient_address.clone(), - amount, - 0, - 0, - TransactionType::Transfer, - ); - - finalize_transaction(&mut tx, &signing_key).expect("Finalization failed"); - - submit_transaction(&mut canister, &mut consensus_engine, tx) - .expect("Transaction submission failed"); - - assert_eq!( - consensus_engine.mempool.len(), - 1, - "Mempool should contain 1 transaction" - ); -}