synor/crates/synor-consensus/src/genesis.rs
2026-01-08 05:22:24 +05:30

514 lines
15 KiB
Rust

//! Genesis block and chain configuration for Synor.
//!
//! This module defines the genesis block for different networks
//! and provides chain configuration parameters.
use synor_types::{
block::BlockBody,
transaction::{Outpoint, ScriptPubKey, ScriptType, SubnetworkId},
Amount, Block, BlockHeader, BlueScore, Hash256, Network, Timestamp, Transaction, TxInput,
TxOutput,
};
/// Chain configuration parameters.
#[derive(Clone, Debug)]
pub struct ChainConfig {
/// Network identifier.
pub network: Network,
/// Genesis block.
pub genesis: Block,
/// Genesis hash.
pub genesis_hash: Hash256,
/// Target block time in milliseconds.
pub target_block_time_ms: u64,
/// GHOSTDAG K parameter.
pub ghostdag_k: u64,
/// Maximum block parents.
pub max_block_parents: u64,
/// Maximum block mass.
pub max_block_mass: u64,
/// Coinbase maturity.
pub coinbase_maturity: u64,
/// Finality depth.
pub finality_depth: u64,
/// Pruning depth.
pub pruning_depth: u64,
/// Initial difficulty bits.
pub initial_difficulty: u32,
/// Halving interval in blocks.
pub halving_interval: u64,
/// Initial block reward in sompi.
pub initial_reward: u64,
/// DNS seeds.
pub dns_seeds: Vec<String>,
/// Bootstrap nodes.
pub bootstrap_nodes: Vec<String>,
/// BIP32 coin type.
pub bip32_coin_type: u32,
/// Address prefix (for bech32).
pub address_prefix: String,
}
impl ChainConfig {
/// Creates mainnet configuration.
pub fn mainnet() -> Self {
let genesis = create_mainnet_genesis();
let genesis_hash = genesis.header.block_id();
ChainConfig {
network: Network::Mainnet,
genesis,
genesis_hash,
target_block_time_ms: 100, // 10 bps
ghostdag_k: 18,
max_block_parents: 64,
max_block_mass: 500_000,
coinbase_maturity: 100,
finality_depth: 86_400, // ~24 hours
pruning_depth: 288_000, // ~8 hours of data
initial_difficulty: 0x1d00ffff, // Bitcoin-style initial difficulty
halving_interval: 315_360_000, // ~1 year at 10 bps
initial_reward: 500 * 100_000_000, // 500 SYNOR
dns_seeds: vec![
"seed1.synor.cc".to_string(),
"seed2.synor.cc".to_string(),
"seed3.synor.cc".to_string(),
],
bootstrap_nodes: vec![],
bip32_coin_type: 0x5359, // "SY" in hex
address_prefix: "synor".to_string(),
}
}
/// Creates testnet configuration.
pub fn testnet() -> Self {
let genesis = create_testnet_genesis();
let genesis_hash = genesis.header.block_id();
ChainConfig {
network: Network::Testnet,
genesis,
genesis_hash,
target_block_time_ms: 100,
ghostdag_k: 18,
max_block_parents: 64,
max_block_mass: 500_000,
coinbase_maturity: 10, // Lower for testing
finality_depth: 1000, // Lower for testing
pruning_depth: 5000,
initial_difficulty: 0x1f00ffff, // Lower difficulty
halving_interval: 10_000, // Quick halvings for testing
initial_reward: 500 * 100_000_000,
dns_seeds: vec![
"testnet-seed1.synor.cc".to_string(),
"testnet-seed2.synor.cc".to_string(),
],
bootstrap_nodes: vec![],
bip32_coin_type: 0x5359,
address_prefix: "tsynor".to_string(),
}
}
/// Creates devnet configuration.
pub fn devnet() -> Self {
let genesis = create_devnet_genesis();
let genesis_hash = genesis.header.block_id();
ChainConfig {
network: Network::Devnet,
genesis,
genesis_hash,
target_block_time_ms: 1000, // 1 second blocks for dev
ghostdag_k: 3, // Smaller K for dev
max_block_parents: 10,
max_block_mass: 100_000,
coinbase_maturity: 1,
finality_depth: 10,
pruning_depth: 100,
initial_difficulty: 0x207fffff, // Minimum difficulty
halving_interval: 100,
initial_reward: 1000 * 100_000_000, // Higher reward for dev
dns_seeds: vec![],
bootstrap_nodes: vec!["/ip4/127.0.0.1/tcp/16511".to_string()],
bip32_coin_type: 0x5359,
address_prefix: "dsynor".to_string(),
}
}
/// Returns the configuration for a network.
pub fn for_network(network: Network) -> Self {
match network {
Network::Mainnet => Self::mainnet(),
Network::Testnet => Self::testnet(),
Network::Devnet => Self::devnet(),
}
}
/// Validates a block against chain rules.
pub fn validate_block_header(
&self,
header: &BlockHeader,
_parent_blue_score: u64,
) -> Result<(), GenesisError> {
// Check timestamp is reasonable
let now_ms = Timestamp::now().as_millis();
let max_future = 2 * 60 * 60 * 1000; // 2 hours
if header.timestamp.as_millis() > now_ms + max_future {
return Err(GenesisError::TimestampTooFar);
}
// Check version
if header.version == 0 {
return Err(GenesisError::InvalidVersion);
}
// Check number of parents
if header.parents.len() > self.max_block_parents as usize {
return Err(GenesisError::TooManyParents);
}
Ok(())
}
}
/// Genesis-related errors.
#[derive(Debug, thiserror::Error)]
pub enum GenesisError {
#[error("Invalid genesis block")]
InvalidGenesis,
#[error("Genesis hash mismatch")]
HashMismatch,
#[error("Invalid block version")]
InvalidVersion,
#[error("Block timestamp too far in future")]
TimestampTooFar,
#[error("Too many parent blocks")]
TooManyParents,
}
/// Creates the mainnet genesis block.
fn create_mainnet_genesis() -> Block {
// Genesis timestamp: 2025-01-01 00:00:00 UTC
let genesis_timestamp = Timestamp::from_millis(1735689600000);
// Genesis message embedded in coinbase
let genesis_message =
b"Synor Genesis - The Dawn of Quantum-Secure Decentralized Computing - 2025";
// Create coinbase transaction
let coinbase_tx = create_coinbase_transaction(
genesis_message,
Amount::from_synor(500),
&genesis_allocation_mainnet(),
);
let txid = coinbase_tx.txid();
// Create genesis header
let header = BlockHeader {
version: 1,
parents: vec![], // Genesis has no parents
merkle_root: txid,
accepted_id_merkle_root: Hash256::ZERO,
utxo_commitment: Hash256::ZERO,
timestamp: genesis_timestamp,
bits: 0x1d00ffff,
nonce: 0,
daa_score: 0,
blue_work: Hash256::ZERO,
blue_score: BlueScore::new(0),
pruning_point: Hash256::ZERO,
};
let body = BlockBody::new(vec![coinbase_tx]);
Block::new(header, body)
}
/// Creates the testnet genesis block.
fn create_testnet_genesis() -> Block {
let genesis_timestamp = Timestamp::from_millis(1735689600000);
let genesis_message = b"Synor Testnet Genesis - Testing the Future";
let coinbase_tx = create_coinbase_transaction(
genesis_message,
Amount::from_synor(500),
&genesis_allocation_testnet(),
);
let txid = coinbase_tx.txid();
let header = BlockHeader {
version: 1,
parents: vec![],
merkle_root: txid,
accepted_id_merkle_root: Hash256::ZERO,
utxo_commitment: Hash256::ZERO,
timestamp: genesis_timestamp,
bits: 0x1f00ffff,
nonce: 0,
daa_score: 0,
blue_work: Hash256::ZERO,
blue_score: BlueScore::new(0),
pruning_point: Hash256::ZERO,
};
let body = BlockBody::new(vec![coinbase_tx]);
Block::new(header, body)
}
/// Creates the devnet genesis block.
fn create_devnet_genesis() -> Block {
let genesis_timestamp = Timestamp::now();
let genesis_message = b"Synor Devnet - Local Development";
let coinbase_tx = create_coinbase_transaction(
genesis_message,
Amount::from_synor(1000),
&genesis_allocation_devnet(),
);
let txid = coinbase_tx.txid();
let header = BlockHeader {
version: 1,
parents: vec![],
merkle_root: txid,
accepted_id_merkle_root: Hash256::ZERO,
utxo_commitment: Hash256::ZERO,
timestamp: genesis_timestamp,
bits: 0x207fffff,
nonce: 0,
daa_score: 0,
blue_work: Hash256::ZERO,
blue_score: BlueScore::new(0),
pruning_point: Hash256::ZERO,
};
let body = BlockBody::new(vec![coinbase_tx]);
Block::new(header, body)
}
/// Genesis allocation entry.
#[derive(Clone, Debug)]
pub struct GenesisAllocation {
/// Recipient address (as string for genesis, actual address derived later).
pub address_hash: Hash256,
/// Amount to allocate.
pub amount: Amount,
/// Description/purpose.
pub description: String,
}
/// Returns mainnet genesis allocations.
fn genesis_allocation_mainnet() -> Vec<GenesisAllocation> {
vec![
// Development fund (15%)
GenesisAllocation {
address_hash: Hash256::from([0x01; 32]),
amount: Amount::from_synor(10_500_000),
description: "Development Fund".to_string(),
},
// Ecosystem grants (10%)
GenesisAllocation {
address_hash: Hash256::from([0x02; 32]),
amount: Amount::from_synor(7_000_000),
description: "Ecosystem Grants".to_string(),
},
// Team (10%, vested)
GenesisAllocation {
address_hash: Hash256::from([0x03; 32]),
amount: Amount::from_synor(7_000_000),
description: "Team (4yr vest, 1yr cliff)".to_string(),
},
// Reserve (5%)
GenesisAllocation {
address_hash: Hash256::from([0x04; 32]),
amount: Amount::from_synor(3_500_000),
description: "Reserve".to_string(),
},
// Initial staking rewards pool (10%)
GenesisAllocation {
address_hash: Hash256::from([0x05; 32]),
amount: Amount::from_synor(7_000_000),
description: "Staking Rewards Pool".to_string(),
},
// Note: Remaining 50% (35M) is for community/public distribution via mining
]
}
/// Returns testnet genesis allocations.
fn genesis_allocation_testnet() -> Vec<GenesisAllocation> {
vec![GenesisAllocation {
address_hash: Hash256::from([0x10; 32]),
amount: Amount::from_synor(1_000_000),
description: "Testnet Faucet".to_string(),
}]
}
/// Returns devnet genesis allocations.
fn genesis_allocation_devnet() -> Vec<GenesisAllocation> {
vec![GenesisAllocation {
address_hash: Hash256::from([0x20; 32]),
amount: Amount::from_synor(100_000_000),
description: "Dev Account".to_string(),
}]
}
/// Creates a coinbase transaction for genesis.
fn create_coinbase_transaction(
message: &[u8],
block_reward: Amount,
allocations: &[GenesisAllocation],
) -> Transaction {
// Coinbase input with genesis message
let input = TxInput {
previous_output: Outpoint::null(),
signature_script: message.to_vec(),
sequence: u64::MAX,
};
// Outputs for allocations
let mut outputs: Vec<TxOutput> = allocations
.iter()
.map(|alloc| TxOutput {
amount: alloc.amount,
script_pubkey: ScriptPubKey {
script_type: ScriptType::P2PKH,
data: alloc.address_hash.as_bytes().to_vec(),
},
})
.collect();
// Add block reward output (to miner - for genesis, burned or to fund)
outputs.push(TxOutput {
amount: block_reward,
script_pubkey: ScriptPubKey::op_return(&[0x00; 32]), // OP_RETURN style burn for genesis
});
Transaction {
version: 1,
inputs: vec![input],
outputs,
lock_time: 0,
subnetwork_id: SubnetworkId::COINBASE,
gas: 0,
payload: vec![],
}
}
/// Checkpoint for chain validation.
#[derive(Clone, Debug)]
pub struct Checkpoint {
/// Blue score at checkpoint.
pub blue_score: u64,
/// Block hash at checkpoint.
pub hash: Hash256,
/// Timestamp for reference.
pub timestamp: u64,
}
/// Returns hardcoded checkpoints for mainnet.
pub fn mainnet_checkpoints() -> Vec<Checkpoint> {
vec![
// Genesis
Checkpoint {
blue_score: 0,
hash: ChainConfig::mainnet().genesis_hash,
timestamp: 1735689600000,
},
// Add more checkpoints as network grows
]
}
/// Returns hardcoded checkpoints for testnet.
pub fn testnet_checkpoints() -> Vec<Checkpoint> {
vec![Checkpoint {
blue_score: 0,
hash: ChainConfig::testnet().genesis_hash,
timestamp: 1735689600000,
}]
}
/// Network magic bytes for message framing.
pub fn network_magic(network: Network) -> [u8; 4] {
match network {
Network::Mainnet => [0x53, 0x59, 0x4E, 0x4F], // "SYNO"
Network::Testnet => [0x54, 0x53, 0x59, 0x4E], // "TSYN"
Network::Devnet => [0x44, 0x53, 0x59, 0x4E], // "DSYN"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mainnet_genesis() {
let config = ChainConfig::mainnet();
assert_eq!(config.network, Network::Mainnet);
assert_eq!(config.genesis.body.transactions.len(), 1);
assert_eq!(config.genesis.header.parents.len(), 0);
assert_eq!(config.genesis.header.blue_score.value(), 0);
}
#[test]
fn test_testnet_genesis() {
let config = ChainConfig::testnet();
assert_eq!(config.network, Network::Testnet);
// Higher bits value = lower difficulty (easier), so testnet < devnet means testnet is harder
assert!(config.initial_difficulty < ChainConfig::devnet().initial_difficulty);
}
#[test]
fn test_devnet_genesis() {
let config = ChainConfig::devnet();
assert_eq!(config.network, Network::Devnet);
assert_eq!(config.coinbase_maturity, 1);
}
#[test]
fn test_genesis_hash_consistency() {
let config1 = ChainConfig::mainnet();
let config2 = ChainConfig::mainnet();
// Genesis hash should be deterministic
assert_eq!(config1.genesis_hash, config2.genesis_hash);
}
#[test]
fn test_network_magic() {
assert_eq!(network_magic(Network::Mainnet), [0x53, 0x59, 0x4E, 0x4F]);
assert_eq!(network_magic(Network::Testnet), [0x54, 0x53, 0x59, 0x4E]);
}
#[test]
fn test_genesis_allocations() {
let allocations = genesis_allocation_mainnet();
// Should have 5 allocations
assert_eq!(allocations.len(), 5);
// Total should be 35M (50% of 70M supply, rest is mined)
let total: u64 = allocations.iter().map(|a| a.amount.as_sompi()).sum();
assert_eq!(total, 35_000_000 * 100_000_000);
}
#[test]
fn test_config_for_network() {
let mainnet = ChainConfig::for_network(Network::Mainnet);
assert_eq!(mainnet.network, Network::Mainnet);
let testnet = ChainConfig::for_network(Network::Testnet);
assert_eq!(testnet.network, Network::Testnet);
}
}