Skip to content

Commit

Permalink
v0.1.0-pre.alpha.4
Browse files Browse the repository at this point in the history
  • Loading branch information
rustyspottedcatt committed Feb 8, 2025
1 parent eafcaf8 commit da68353
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nebula"
version = "0.1.0-pre.alpha.3"
version = "0.1.0-pre.alpha.4"
edition = "2024"
authors = ["NEBYTE [email protected]"]
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."
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<img src="https://l7mozmkiwy.ufs.sh/f/HKemhjN71TyOWR3z3yuKt6z8SiwMQpPjTFX1mVLHuAaolWbN" alt="Nebula Logo">
</picture>

# Nebula (Early Alpha Release v0.1.0-pre.alpha.3)
# Nebula (Early Alpha Release v0.1.0-pre.alpha.4)

[![Maintainer](https://img.shields.io/badge/maintainer-NEBYTE-blue)](https://github.com/rustyspottedcatt)
[![Made with Rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg)](https://www.rust-lang.org/)
Expand All @@ -21,6 +21,7 @@ Nebula is a blockchain-based system that replicates ICP’s architecture—inclu
## Table of Contents

- [Features](#features)
- [Whitepaper](#whitepaper)
- [Installation](#installation)
- [Usage](#usage)
- [Wallet Management](#wallet-management)
Expand All @@ -45,6 +46,11 @@ Nebula is a blockchain-based system that replicates ICP’s architecture—inclu
- **Staking** - Secure staking and unstaking of tokens.
- **Canisters** - Modular execution of functions via on-chain canisters.

---
## Whitepaper

[Whitepaper Link](https://whitepapersonline.com/en/whitepaper/nebula-a-decentralized-open-source-blockchain-for-enhanced-governance)

---

## Installation
Expand Down Expand Up @@ -142,14 +148,15 @@ 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<Mutex<T>>, everything is synchronized.
let mut staking_module_clone = staking_module.clone();

tokio::spawn(async move {
run_consensus_loop(
&mut consensus_engine_clone,
&mut staking_module_clone, // staking_module is required because each block calls distribute staking rewards.
&signing_key_clone,
target_cycle,
).await;
});
}); // Target cycle: 0.5s
println!("🚀 Blockchain node is running! Listening for transactions...");
```

Expand Down
37 changes: 37 additions & 0 deletions src/core/consensus/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use chrono::Utc;
use ed25519_dalek::{SigningKey, VerifyingKey};
use bincode;
use hex;
use crate::core::consensus::math::consensus_probability;

pub fn produce_block(
consensus_engine: &mut ConsensusEngine,
Expand All @@ -20,6 +21,8 @@ pub fn produce_block(
let merkle_root = compute_merkle_root(&transactions);

let verifying_key = signing_key.verifying_key();
let validator_address = hex::encode(verifying_key.to_bytes());

let parent_hash = chain_lock.last().map(|blk| hash_block(blk)).unwrap_or([0u8; 32]);

let mut header = BlockHeader {
Expand All @@ -36,6 +39,40 @@ pub fn produce_block(
let block = Block { header, transactions };
chain_lock.push(block.clone());

let neurons_lock = consensus_engine.neurons.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
let mut total_stake: u64 = 0;
let mut validator_stake: u64 = 0;

for neuron in neurons_lock.values() {
total_stake += neuron.staked_amount;
if let Some(ref val_addr) = neuron.validator {
if val_addr == &validator_address {
validator_stake += neuron.staked_amount;
}
}
}

drop(neurons_lock);

if total_stake > 0 {
let prob = consensus_probability(validator_stake, total_stake);
println!(
"Validator {} has stake {} out of total {} => probability: {:.4}",
validator_address, validator_stake, total_stake, prob
);

const BLOCK_REWARD: u64 = 10;
const REWARD_MULTIPLIER: f64 = 1.0;
let reward_float = REWARD_MULTIPLIER * (validator_stake as f64 / total_stake as f64) * (BLOCK_REWARD as f64);
let reward: u64 = reward_float.round() as u64;

let mut ledger_lock = consensus_engine.ledger.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
if let Some(account) = ledger_lock.get_mut(&validator_address) {
account.balance += reward;
println!("Validator {} rewarded with {} tokens", validator_address, reward);
}
}

Ok(block)
}

Expand Down
12 changes: 11 additions & 1 deletion src/core/consensus/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ use crate::core::consensus::validator::select_next_validator;
use ed25519_dalek::{SigningKey, VerifyingKey};
use tokio::time::{sleep, Duration, Instant};
use hex;
use nebula::core::staking::StakingModule;
use crate::core::staking::distribute_rewards;

pub async fn run_consensus_loop(
consensus_engine: &mut ConsensusEngine,
staking_module: &mut StakingModule,
signing_key: &SigningKey,
target_cycle: Duration,
) {
let target_cycle = Duration::from_millis(500);

const REWARD_POOL: u64 = 50;
const ANNUAL_YIELD_PERCENT: f64 = 5.0;

loop {
let cycle_start = Instant::now();

Expand All @@ -26,6 +34,8 @@ pub async fn run_consensus_loop(
block.transactions.len(),
block.header.timestamp
);

distribute_rewards(&mut staking_module.clone(), REWARD_POOL, ANNUAL_YIELD_PERCENT);
}
Err(err) => {
eprintln!("Block production error: {}", err);
Expand Down
23 changes: 23 additions & 0 deletions src/core/consensus/math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pub fn consensus_probability(s_i: u64, s_total: u64) -> f64 {
if s_total == 0 {
0.0
} else {
s_i as f64 / s_total as f64
}
}

pub fn staking_yield(stake: u64, yield_percent: f64) -> u64 {
((stake as f64) * (yield_percent / 100.0)).round() as u64
}

pub fn block_time(lambda: f64) -> f64 {
if lambda == 0.0 { 0.0 } else { 1.0 / lambda }
}

pub fn voting_power(stake: u64, bonus_multiplier: f64) -> u64 {
((stake as f64) * bonus_multiplier).round() as u64
}

pub fn governance_voting_outcome(votes: &[(u64, i64)]) -> i64 {
votes.iter().map(|(v_power, vote_val)| (*v_power as i64) * vote_val).sum()
}
1 change: 1 addition & 0 deletions src/core/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod neuron;
pub mod utils;
pub mod model;
pub mod consensus;
pub mod math;

pub use validator::{ValidatorInfo, select_next_validator, slash};
pub use transaction::{add_transaction, compute_transaction_hash};
Expand Down
20 changes: 19 additions & 1 deletion src/core/governance/governance.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::{BinaryHeap, HashMap};
use std::sync::{Arc, RwLock, Mutex};
use crate::core::types::Neuron;
use crate::core::consensus::math::voting_power;
use crate::core::types::{Neuron, Vote};
use crate::core::governance::proposal::Proposal;

pub struct Governance {
Expand All @@ -21,4 +22,21 @@ impl Governance {
next_id: Arc::new(Mutex::new(1)),
}
}
pub fn compute_voting_outcome(&self, proposal: &Proposal) -> i64 {
let neurons = self.neurons.lock().unwrap();
let mut outcome: i64 = 0;

for (&neuron_id, voting_neuron) in proposal.votes_of_neurons.iter() {
let vote_value = match voting_neuron.vote {
Vote::Yes => 1,
Vote::No => -1,
_ => 0,
};
if let Some(neuron) = neurons.get(&neuron_id) {
let effective_power = voting_power(neuron.staked_amount, neuron.bonus_multiplier);
outcome += (effective_power as i64) * vote_value;
}
}
outcome
}
}
10 changes: 7 additions & 3 deletions src/core/governance/voting.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::{Arc};
use ed25519_dalek::SigningKey;
use nebula::core::consensus::math::voting_power;
use crate::core::types::{Vote, VotingNeuron, VotingStatus};
use crate::core::governance::governance::Governance;

Expand Down Expand Up @@ -44,7 +45,7 @@ pub fn vote(

proposal.votes_of_neurons.insert(neuron_id, voting_neuron);

let effective_stake = (stake as f64 * neuron.bonus_multiplier) as u64;
let effective_stake = voting_power(stake, neuron.bonus_multiplier);
if vote_for {
proposal.tally.yes += effective_stake;
} else {
Expand Down Expand Up @@ -81,7 +82,10 @@ pub fn finalize(
while let Some(mut proposal) = heap.pop() {
if proposal.id == proposal_id {
proposal.status = VotingStatus::Terminated;
finalized = Some(proposal.tally.yes > proposal.tally.no);

let outcome = governance.compute_voting_outcome(&proposal);
finalized = Some(outcome > 0);

break;
} else {
temp.push(proposal);
Expand All @@ -96,4 +100,4 @@ pub fn finalize(
Some(result) => Ok(result),
None => Err("Proposal not found".to_string()),
}
}
}
16 changes: 11 additions & 5 deletions src/core/staking/rewards.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::core::consensus::math::staking_yield;
use crate::core::staking::StakingModule;

pub fn distribute_rewards(
staking_module: &mut StakingModule,
reward_pool: u64
reward_pool: u64,
annual_yield_percent: f64,
) {
let mut neurons = staking_module.neurons.lock().unwrap();
let total_staked: u64 = neurons.values().map(|n| n.staked_amount).sum();
let mut neurons = staking_module.neurons.lock().unwrap_or_else(|e| e.into_inner());
let total_staked = neurons.values().map(|n| n.staked_amount).sum();

if total_staked == 0 {
return;
Expand All @@ -14,8 +16,12 @@ pub fn distribute_rewards(
for neuron in neurons.values_mut() {
let ratio = neuron.staked_amount as f64 / total_staked as f64;
let maturity_bonus = 1.0 + (neuron.maturity as f64 / 100.0);
let reward = ((reward_pool as f64 * ratio) * maturity_bonus) as u64;
neuron.staked_amount += reward;
let pool_reward = (reward_pool as f64 * ratio) * maturity_bonus;

let yield_reward = staking_yield(neuron.staked_amount, annual_yield_percent);
let total_reward = pool_reward.round() as u64 + yield_reward;

neuron.staked_amount += total_reward;
neuron.maturity += 1;
}
}
1 change: 1 addition & 0 deletions src/core/staking/staking_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::core::types::Neuron;

#[derive(Clone)]
pub struct StakingModule {
pub neurons: Arc<Mutex<HashMap<u64, Neuron>>>,
}
Expand Down
5 changes: 2 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,15 @@ async fn main() {
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();
let mut staking_module_clone = staking_module.clone();

tokio::spawn(async move {
run_consensus_loop(
&mut consensus_engine_clone,
&mut staking_module_clone,
&signing_key_clone,
target_cycle,
).await;
});

Expand Down

0 comments on commit da68353

Please sign in to comment.