514 lines
15 KiB
Rust
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);
|
|
}
|
|
}
|