diff --git a/apps/cli/src/client.rs b/apps/cli/src/client.rs index 51764b0..940d43b 100644 --- a/apps/cli/src/client.rs +++ b/apps/cli/src/client.rs @@ -29,12 +29,7 @@ impl RpcClient { "params": params }); - let response = self - .client - .post(&self.url) - .json(&request) - .send() - .await?; + let response = self.client.post(&self.url).json(&request).send().await?; let rpc_response: RpcResponse = response.json().await?; @@ -68,7 +63,8 @@ impl RpcClient { /// Gets a block by hash. pub async fn get_block(&self, hash: &str, include_txs: bool) -> Result { - self.call("synor_getBlock", json!([hash, include_txs])).await + self.call("synor_getBlock", json!([hash, include_txs])) + .await } /// Gets block header. @@ -235,7 +231,11 @@ impl RpcClient { } /// Gets contract storage value. - pub async fn get_contract_storage(&self, contract_id: &str, key: &str) -> Result { + pub async fn get_contract_storage( + &self, + contract_id: &str, + key: &str, + ) -> Result { self.call( "synor_getStorageAt", json!({ diff --git a/apps/cli/src/commands/address.rs b/apps/cli/src/commands/address.rs index 876b99c..2e6b3dd 100644 --- a/apps/cli/src/commands/address.rs +++ b/apps/cli/src/commands/address.rs @@ -22,12 +22,20 @@ pub async fn validate(address: &str, format: OutputFormat) -> Result<()> { OutputFormat::Text => { if validation.is_valid { output::print_success("Address is valid"); - output::print_kv("Network", validation.network.as_deref().unwrap_or("unknown")); - output::print_kv("Type", validation.address_type.as_deref().unwrap_or("unknown")); + output::print_kv( + "Network", + validation.network.as_deref().unwrap_or("unknown"), + ); + output::print_kv( + "Type", + validation.address_type.as_deref().unwrap_or("unknown"), + ); } else { output::print_error(&format!( "Invalid address: {}", - validation.error.unwrap_or_else(|| "unknown error".to_string()) + validation + .error + .unwrap_or_else(|| "unknown error".to_string()) )); } } diff --git a/apps/cli/src/commands/block.rs b/apps/cli/src/commands/block.rs index 7da47af..b1f0647 100644 --- a/apps/cli/src/commands/block.rs +++ b/apps/cli/src/commands/block.rs @@ -35,7 +35,10 @@ pub async fn get_block(client: &RpcClient, id: &str, format: OutputFormat) -> Re output::print_header("Block"); output::print_kv("Hash", &block.hash); output::print_kv("Version", &block.header.version.to_string()); - output::print_kv("Timestamp", &output::format_timestamp(block.header.timestamp)); + output::print_kv( + "Timestamp", + &output::format_timestamp(block.header.timestamp), + ); output::print_kv("Blue Score", &block.header.blue_score.to_string()); output::print_kv("Bits", &format!("0x{:08x}", block.header.bits)); output::print_kv("Nonce", &block.header.nonce.to_string()); diff --git a/apps/cli/src/commands/contract.rs b/apps/cli/src/commands/contract.rs index df2b3e5..b2abbb7 100644 --- a/apps/cli/src/commands/contract.rs +++ b/apps/cli/src/commands/contract.rs @@ -18,24 +18,55 @@ pub async fn handle( format: OutputFormat, ) -> Result<()> { match cmd { - ContractCommands::Deploy { wasm, deployer, args, gas } => { - deploy(client, wasm, &deployer, args.as_deref(), Some(gas), format).await - } - ContractCommands::Call { contract_id, method, caller, args, value, gas } => { - call(client, &contract_id, &method, &caller, args.as_deref(), value, Some(gas), format).await - } - ContractCommands::Code { contract_id } => { - code(client, &contract_id, format).await + ContractCommands::Deploy { + wasm, + deployer, + args, + gas, + } => deploy(client, wasm, &deployer, args.as_deref(), Some(gas), format).await, + ContractCommands::Call { + contract_id, + method, + caller, + args, + value, + gas, + } => { + call( + client, + &contract_id, + &method, + &caller, + args.as_deref(), + value, + Some(gas), + format, + ) + .await } + ContractCommands::Code { contract_id } => code(client, &contract_id, format).await, ContractCommands::Storage { contract_id, key } => { storage(client, &contract_id, &key, format).await } - ContractCommands::EstimateGas { contract_id, method, caller, args, value } => { - estimate_gas(client, &contract_id, &method, &caller, args.as_deref(), value, format).await - } - ContractCommands::Info { contract_id } => { - info(client, &contract_id, format).await + ContractCommands::EstimateGas { + contract_id, + method, + caller, + args, + value, + } => { + estimate_gas( + client, + &contract_id, + &method, + &caller, + args.as_deref(), + value, + format, + ) + .await } + ContractCommands::Info { contract_id } => info(client, &contract_id, format).await, } } @@ -52,23 +83,31 @@ async fn deploy( let wasm_bytes = fs::read(&wasm_path)?; let wasm_hex = hex::encode(&wasm_bytes); - output::print_info(&format!("Deploying contract ({} bytes)...", wasm_bytes.len())); + output::print_info(&format!( + "Deploying contract ({} bytes)...", + wasm_bytes.len() + )); let args_hex = args.unwrap_or(""); let spinner = output::create_spinner("Deploying contract..."); - let result = client.deploy_contract(&wasm_hex, args_hex, deployer, gas_limit).await?; + let result = client + .deploy_contract(&wasm_hex, args_hex, deployer, gas_limit) + .await?; spinner.finish_and_clear(); if let Some(error) = &result.error { match format { OutputFormat::Json => { - println!("{}", serde_json::to_string_pretty(&serde_json::json!({ - "success": false, - "error": error - }))?); + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "success": false, + "error": error + }))? + ); } OutputFormat::Text => { output::print_error(&format!("Deployment failed: {}", error)); @@ -79,12 +118,15 @@ async fn deploy( match format { OutputFormat::Json => { - println!("{}", serde_json::to_string_pretty(&serde_json::json!({ - "success": true, - "contractId": result.contract_id, - "address": result.address, - "gasUsed": result.gas_used, - }))?); + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "success": true, + "contractId": result.contract_id, + "address": result.address, + "gasUsed": result.gas_used, + }))? + ); } OutputFormat::Text => { output::print_success("Contract deployed!"); @@ -110,15 +152,20 @@ async fn call( ) -> Result<()> { let args_hex = args.unwrap_or(""); - let result = client.call_contract(contract_id, method, args_hex, caller, value, gas_limit).await?; + let result = client + .call_contract(contract_id, method, args_hex, caller, value, gas_limit) + .await?; if let Some(error) = &result.error { match format { OutputFormat::Json => { - output::print_value(&serde_json::json!({ - "success": false, - "error": error - }), format); + output::print_value( + &serde_json::json!({ + "success": false, + "error": error + }), + format, + ); } OutputFormat::Text => { output::print_error(&format!("Contract call failed: {}", error)); @@ -129,12 +176,15 @@ async fn call( match format { OutputFormat::Json => { - output::print_value(&serde_json::json!({ - "success": result.success, - "data": result.data, - "gasUsed": result.gas_used, - "logs": result.logs - }), format); + output::print_value( + &serde_json::json!({ + "success": result.success, + "data": result.data, + "gasUsed": result.gas_used, + "logs": result.logs + }), + format, + ); } OutputFormat::Text => { output::print_header("Contract Call Result"); @@ -170,9 +220,12 @@ async fn code(client: &RpcClient, contract_id: &str, format: OutputFormat) -> Re if let Some(error) = &result.error { match format { OutputFormat::Json => { - println!("{}", serde_json::to_string_pretty(&serde_json::json!({ - "error": error - }))?); + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "error": error + }))? + ); } OutputFormat::Text => { output::print_error(&format!("Failed to get code: {}", error)); @@ -208,15 +261,23 @@ async fn code(client: &RpcClient, contract_id: &str, format: OutputFormat) -> Re } /// Get contract storage. -async fn storage(client: &RpcClient, contract_id: &str, key: &str, format: OutputFormat) -> Result<()> { +async fn storage( + client: &RpcClient, + contract_id: &str, + key: &str, + format: OutputFormat, +) -> Result<()> { let result = client.get_contract_storage(contract_id, key).await?; if let Some(error) = &result.error { match format { OutputFormat::Json => { - println!("{}", serde_json::to_string_pretty(&serde_json::json!({ - "error": error - }))?); + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "error": error + }))? + ); } OutputFormat::Text => { output::print_error(&format!("Failed to get storage: {}", error)); @@ -258,14 +319,19 @@ async fn estimate_gas( ) -> Result<()> { let args_hex = args.unwrap_or(""); - let result = client.estimate_gas(contract_id, method, args_hex, caller, value).await?; + let result = client + .estimate_gas(contract_id, method, args_hex, caller, value) + .await?; if let Some(error) = &result.error { match format { OutputFormat::Json => { - println!("{}", serde_json::to_string_pretty(&serde_json::json!({ - "error": error - }))?); + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "error": error + }))? + ); } OutputFormat::Text => { output::print_error(&format!("Failed to estimate gas: {}", error)); @@ -300,9 +366,12 @@ async fn info(client: &RpcClient, contract_id: &str, format: OutputFormat) -> Re if let Some(error) = &result.error { match format { OutputFormat::Json => { - println!("{}", serde_json::to_string_pretty(&serde_json::json!({ - "error": error - }))?); + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "error": error + }))? + ); } OutputFormat::Text => { output::print_error(&format!("Failed to get contract info: {}", error)); @@ -313,13 +382,16 @@ async fn info(client: &RpcClient, contract_id: &str, format: OutputFormat) -> Re match format { OutputFormat::Json => { - println!("{}", serde_json::to_string_pretty(&serde_json::json!({ - "contractId": contract_id, - "codeHash": result.code_hash, - "deployer": result.deployer, - "deployedAt": result.deployed_at, - "deployedHeight": result.deployed_height, - }))?); + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "contractId": contract_id, + "codeHash": result.code_hash, + "deployer": result.deployer, + "deployedAt": result.deployed_at, + "deployedHeight": result.deployed_height, + }))? + ); } OutputFormat::Text => { output::print_header("Contract Info"); diff --git a/apps/cli/src/commands/governance.rs b/apps/cli/src/commands/governance.rs index b24f7f9..f51fb89 100644 --- a/apps/cli/src/commands/governance.rs +++ b/apps/cli/src/commands/governance.rs @@ -53,7 +53,17 @@ pub async fn handle( voter, choice, reason, - } => vote(client, &proposal_id, &voter, &choice, reason.as_deref(), format).await, + } => { + vote( + client, + &proposal_id, + &voter, + &choice, + reason.as_deref(), + format, + ) + .await + } GovernanceCommands::Execute { proposal_id, executor, @@ -75,15 +85,9 @@ async fn info(client: &RpcClient, format: OutputFormat) -> Result<()> { } OutputFormat::Text => { output::print_header("Governance Info"); - output::print_kv( - "Proposal Threshold", - &format_synor(info.proposal_threshold), - ); + output::print_kv("Proposal Threshold", &format_synor(info.proposal_threshold)); output::print_kv("Quorum", &format!("{}%", info.quorum_bps as f64 / 100.0)); - output::print_kv( - "Voting Period", - &format_blocks(info.voting_period_blocks), - ); + output::print_kv("Voting Period", &format_blocks(info.voting_period_blocks)); output::print_kv( "Execution Delay", &format_blocks(info.execution_delay_blocks), @@ -125,11 +129,7 @@ async fn stats(client: &RpcClient, format: OutputFormat) -> Result<()> { } /// List proposals. -async fn proposals( - client: &RpcClient, - state: Option<&str>, - format: OutputFormat, -) -> Result<()> { +async fn proposals(client: &RpcClient, state: Option<&str>, format: OutputFormat) -> Result<()> { let proposals = match state { Some(s) => client.get_proposals_by_state(s).await?, None => client.get_active_proposals().await?, @@ -171,7 +171,11 @@ async fn proposals( println!( " Participation: {:.2}% | Quorum: {}", proposal.participation_rate, - if proposal.has_quorum { "Reached" } else { "Not reached" } + if proposal.has_quorum { + "Reached" + } else { + "Not reached" + } ); if let Some(remaining) = proposal.time_remaining_blocks { println!(" Time Remaining: {}", format_blocks(remaining)); @@ -197,10 +201,16 @@ async fn proposal(client: &RpcClient, id: &str, format: OutputFormat) -> Result< println!("{}", serde_json::to_string_pretty(&proposal)?); } OutputFormat::Text => { - output::print_header(&format!("Proposal #{}: {}", proposal.number, proposal.title)); + output::print_header(&format!( + "Proposal #{}: {}", + proposal.number, proposal.title + )); println!(); output::print_kv("ID", &proposal.id); - output::print_kv("State", &format!("{} {}", state_emoji(&proposal.state), proposal.state)); + output::print_kv( + "State", + &format!("{} {}", state_emoji(&proposal.state), proposal.state), + ); output::print_kv("Type", &proposal.proposal_type); output::print_kv("Proposer", &proposal.proposer); println!(); @@ -212,8 +222,14 @@ async fn proposal(client: &RpcClient, id: &str, format: OutputFormat) -> Result< println!(); println!("Timeline:"); output::print_kv(" Created", &format!("Block {}", proposal.created_at_block)); - output::print_kv(" Voting Starts", &format!("Block {}", proposal.voting_starts_block)); - output::print_kv(" Voting Ends", &format!("Block {}", proposal.voting_ends_block)); + output::print_kv( + " Voting Starts", + &format!("Block {}", proposal.voting_starts_block), + ); + output::print_kv( + " Voting Ends", + &format!("Block {}", proposal.voting_ends_block), + ); output::print_kv( " Execution Allowed", &format!("Block {}", proposal.execution_allowed_block), @@ -226,7 +242,10 @@ async fn proposal(client: &RpcClient, id: &str, format: OutputFormat) -> Result< } else { 0.0 }; - output::print_kv(" Yes", &format!("{} ({:.1}%)", format_synor(proposal.yes_votes), yes_pct)); + output::print_kv( + " Yes", + &format!("{} ({:.1}%)", format_synor(proposal.yes_votes), yes_pct), + ); output::print_kv(" No", &format_synor(proposal.no_votes)); output::print_kv(" Abstain", &format_synor(proposal.abstain_votes)); output::print_kv(" Total Voters", &proposal.votes.len().to_string()); @@ -276,8 +295,10 @@ async fn create_proposal( // Build proposal params based on type let params = match proposal_type { "treasury_spend" | "ecosystem_grant" => { - let recipient = recipient.ok_or_else(|| anyhow::anyhow!("--recipient required for treasury proposals"))?; - let amount = amount.ok_or_else(|| anyhow::anyhow!("--amount required for treasury proposals"))?; + let recipient = recipient + .ok_or_else(|| anyhow::anyhow!("--recipient required for treasury proposals"))?; + let amount = amount + .ok_or_else(|| anyhow::anyhow!("--amount required for treasury proposals"))?; json!({ "recipient": recipient, "amount": amount @@ -423,11 +444,18 @@ async fn treasury(client: &RpcClient, format: OutputFormat) -> Result<()> { println!("Pools:"); for pool in &pools { println!(); - let status = if pool.frozen { "🔒 FROZEN" } else { "✅ Active" }; + let status = if pool.frozen { + "🔒 FROZEN" + } else { + "✅ Active" + }; println!(" {} [{}]", pool.name, status); println!(" ID: {}", pool.id); println!(" Balance: {}", format_synor(pool.balance)); - println!(" Total Deposited: {}", format_synor(pool.total_deposited)); + println!( + " Total Deposited: {}", + format_synor(pool.total_deposited) + ); println!(" Total Spent: {}", format_synor(pool.total_spent)); } } @@ -445,7 +473,11 @@ async fn treasury_pool(client: &RpcClient, id: &str, format: OutputFormat) -> Re println!("{}", serde_json::to_string_pretty(&pool)?); } OutputFormat::Text => { - let status = if pool.frozen { "🔒 FROZEN" } else { "✅ Active" }; + let status = if pool.frozen { + "🔒 FROZEN" + } else { + "✅ Active" + }; output::print_header(&format!("{} [{}]", pool.name, status)); output::print_kv("ID", &pool.id); output::print_kv("Balance", &format_synor(pool.balance)); @@ -466,7 +498,9 @@ fn format_synor(amount: u64) -> String { if frac == 0 { format!("{} SYNOR", whole) } else { - format!("{}.{:08} SYNOR", whole, frac).trim_end_matches('0').to_string() + format!("{}.{:08} SYNOR", whole, frac) + .trim_end_matches('0') + .to_string() } } diff --git a/apps/cli/src/commands/mining.rs b/apps/cli/src/commands/mining.rs index 813759c..a73d601 100644 --- a/apps/cli/src/commands/mining.rs +++ b/apps/cli/src/commands/mining.rs @@ -7,11 +7,7 @@ use crate::output::{self, OutputFormat}; use crate::MiningCommands; /// Handle mining commands. -pub async fn handle( - client: &RpcClient, - cmd: MiningCommands, - format: OutputFormat, -) -> Result<()> { +pub async fn handle(client: &RpcClient, cmd: MiningCommands, format: OutputFormat) -> Result<()> { match cmd { MiningCommands::Info => info(client, format).await, MiningCommands::Template { address } => template(client, &address, format).await, @@ -32,7 +28,10 @@ async fn info(client: &RpcClient, format: OutputFormat) -> Result<()> { output::print_header("Mining Information"); output::print_kv("Blocks", &info.blocks.to_string()); output::print_kv("Difficulty", &format!("{:.6}", info.difficulty)); - output::print_kv("Network Hashrate", &output::format_hashrate(info.network_hashrate)); + output::print_kv( + "Network Hashrate", + &output::format_hashrate(info.network_hashrate), + ); if let Some(pool_hr) = info.pool_hashrate { output::print_kv("Pool Hashrate", &output::format_hashrate(pool_hr)); } @@ -54,7 +53,10 @@ async fn template(client: &RpcClient, address: &str, format: OutputFormat) -> Re output::print_header("Block Template"); output::print_kv("Version", &template.header.version.to_string()); output::print_kv("Parents", &template.header.parents.len().to_string()); - output::print_kv("Timestamp", &output::format_timestamp(template.header.timestamp)); + output::print_kv( + "Timestamp", + &output::format_timestamp(template.header.timestamp), + ); output::print_kv("Bits", &format!("0x{:08x}", template.header.bits)); output::print_kv("Blue Score", &template.header.blue_score.to_string()); output::print_kv("Target", &output::format_hash(&template.target)); @@ -105,7 +107,10 @@ async fn hashrate(client: &RpcClient, format: OutputFormat) -> Result<()> { println!("{}", serde_json::to_string_pretty(&json)?); } OutputFormat::Text => { - output::print_kv("Network Hashrate", &output::format_hashrate(info.network_hashrate)); + output::print_kv( + "Network Hashrate", + &output::format_hashrate(info.network_hashrate), + ); output::print_kv("Difficulty", &format!("{:.6}", info.difficulty)); } } diff --git a/apps/cli/src/commands/tx.rs b/apps/cli/src/commands/tx.rs index 7760862..6cfb1d2 100644 --- a/apps/cli/src/commands/tx.rs +++ b/apps/cli/src/commands/tx.rs @@ -3,8 +3,8 @@ use anyhow::Result; use dialoguer::Password; use synor_types::{ - Address, Amount, Hash256, transaction::{Outpoint, ScriptPubKey, ScriptType, Transaction, TxInput, TxOutput}, + Address, Amount, Hash256, }; use crate::client::RpcClient; @@ -122,7 +122,15 @@ pub async fn send( // Build transaction let change = selected_amount - total_needed; - let tx_hex = build_transaction(&wallet, &selected_utxos, to, amount_sompi, &from_addr.address, change, &password)?; + let tx_hex = build_transaction( + &wallet, + &selected_utxos, + to, + amount_sompi, + &from_addr.address, + change, + &password, + )?; // Submit transaction let tx_hash = client.submit_transaction(&tx_hex).await?; @@ -219,8 +227,8 @@ fn build_transaction( password: &str, ) -> Result { // Parse destination address - let to_address = Address::from_str(to) - .map_err(|e| anyhow::anyhow!("Invalid destination address: {}", e))?; + let to_address = + Address::from_str(to).map_err(|e| anyhow::anyhow!("Invalid destination address: {}", e))?; // Parse change address let change_address = Address::from_str(change_addr) @@ -236,10 +244,7 @@ fn build_transaction( let mut txid_array = [0u8; 32]; txid_array.copy_from_slice(&txid_bytes); - let outpoint = Outpoint::new( - Hash256::from_bytes(txid_array), - utxo.outpoint.index, - ); + let outpoint = Outpoint::new(Hash256::from_bytes(txid_array), utxo.outpoint.index); // Empty signature script initially - will be filled after signing inputs.push(TxInput::new(outpoint, Vec::new())); diff --git a/apps/cli/src/commands/wallet.rs b/apps/cli/src/commands/wallet.rs index 4c904dd..df0d4f8 100644 --- a/apps/cli/src/commands/wallet.rs +++ b/apps/cli/src/commands/wallet.rs @@ -12,11 +12,7 @@ use crate::wallet::Wallet; use crate::WalletCommands; /// Handle wallet commands. -pub async fn handle( - config: &CliConfig, - cmd: WalletCommands, - format: OutputFormat, -) -> Result<()> { +pub async fn handle(config: &CliConfig, cmd: WalletCommands, format: OutputFormat) -> Result<()> { match cmd { WalletCommands::Create { name } => create(config, &name, format).await, WalletCommands::Import { name } => import(config, &name, format).await, @@ -72,7 +68,10 @@ async fn create(config: &CliConfig, name: &str, format: OutputFormat) -> Result< output::print_kv("Network", &wallet.network); output::print_kv( "Address", - wallet.default_address().map(|a| a.address.as_str()).unwrap_or("none"), + wallet + .default_address() + .map(|a| a.address.as_str()) + .unwrap_or("none"), ); output::print_kv("Key Type", "Hybrid (Ed25519 + Dilithium)"); @@ -134,7 +133,10 @@ async fn import(config: &CliConfig, name: &str, format: OutputFormat) -> Result< output::print_kv("Name", &wallet.name); output::print_kv( "Address", - wallet.default_address().map(|a| a.address.as_str()).unwrap_or("none"), + wallet + .default_address() + .map(|a| a.address.as_str()) + .unwrap_or("none"), ); } } @@ -162,26 +164,26 @@ async fn export(config: &CliConfig, name: &str, format: OutputFormat) -> Result< // Note: We can't export the mnemonic from the derived seed // The user should have written down the mnemonic during creation match wallet.export_seed_phrase(&password) { - Ok(seed_phrase) => { - match format { - OutputFormat::Json => { - let result = serde_json::json!({ - "name": wallet.name, - "seed_phrase": seed_phrase, - }); - println!("{}", serde_json::to_string_pretty(&result)?); - } - OutputFormat::Text => { - output::print_warning("Keep this seed phrase secret and safe!"); - println!(); - println!(" {}", seed_phrase); - println!(); - } + Ok(seed_phrase) => match format { + OutputFormat::Json => { + let result = serde_json::json!({ + "name": wallet.name, + "seed_phrase": seed_phrase, + }); + println!("{}", serde_json::to_string_pretty(&result)?); } - } + OutputFormat::Text => { + output::print_warning("Keep this seed phrase secret and safe!"); + println!(); + println!(" {}", seed_phrase); + println!(); + } + }, Err(e) => { output::print_warning(&format!("{}", e)); - output::print_info("Please use the mnemonic phrase you wrote down during wallet creation."); + output::print_info( + "Please use the mnemonic phrase you wrote down during wallet creation.", + ); } } @@ -235,7 +237,10 @@ async fn info(config: &CliConfig, name: &str, format: OutputFormat) -> Result<() output::print_kv("Network", &wallet.network); output::print_kv("Key Type", "Hybrid (Ed25519 + Dilithium)"); output::print_kv("Addresses", &wallet.addresses.len().to_string()); - output::print_kv("Created", &output::format_timestamp(wallet.created_at * 1000)); + output::print_kv( + "Created", + &output::format_timestamp(wallet.created_at * 1000), + ); if let Some(default) = wallet.default_address() { output::print_kv("Default Address", &default.address); diff --git a/apps/cli/src/main.rs b/apps/cli/src/main.rs index 5c93375..48af009 100644 --- a/apps/cli/src/main.rs +++ b/apps/cli/src/main.rs @@ -23,7 +23,12 @@ use crate::config::CliConfig; #[command(version, about = "Synor blockchain CLI", long_about = None)] struct Cli { /// RPC server URL - #[arg(short, long, env = "SYNOR_RPC_URL", default_value = "http://127.0.0.1:16110")] + #[arg( + short, + long, + env = "SYNOR_RPC_URL", + default_value = "http://127.0.0.1:16110" + )] rpc: String, /// Configuration file path @@ -472,39 +477,29 @@ async fn main() { Commands::Balance { address } => { commands::wallet::balance(&client, &config, address.as_deref(), output).await } - Commands::Utxos { address } => { - commands::wallet::utxos(&client, &address, output).await - } + Commands::Utxos { address } => commands::wallet::utxos(&client, &address, output).await, // Address commands Commands::ValidateAddress { address } => { commands::address::validate(&address, output).await } - Commands::DecodeAddress { address } => { - commands::address::decode(&address, output).await - } + Commands::DecodeAddress { address } => commands::address::decode(&address, output).await, // Mining commands Commands::Mining(cmd) => commands::mining::handle(&client, cmd, output).await, // Contract commands - Commands::Contract(cmd) => { - commands::contract::handle(&client, &config, cmd, output).await - } + Commands::Contract(cmd) => commands::contract::handle(&client, &config, cmd, output).await, // Governance commands - Commands::Governance(cmd) => { - commands::governance::handle(&client, cmd, output).await - } + Commands::Governance(cmd) => commands::governance::handle(&client, cmd, output).await, // Network commands Commands::AddPeer { address } => { commands::network::add_peer(&client, &address, output).await } Commands::BanPeer { peer } => commands::network::ban_peer(&client, &peer, output).await, - Commands::UnbanPeer { peer } => { - commands::network::unban_peer(&client, &peer, output).await - } + Commands::UnbanPeer { peer } => commands::network::unban_peer(&client, &peer, output).await, }; if let Err(e) = result { diff --git a/apps/cli/src/wallet.rs b/apps/cli/src/wallet.rs index 727d7a5..eaa8275 100644 --- a/apps/cli/src/wallet.rs +++ b/apps/cli/src/wallet.rs @@ -190,7 +190,11 @@ impl Wallet { } /// Generates a new address. - pub fn new_address(&mut self, label: Option, password: &str) -> anyhow::Result<&WalletAddress> { + pub fn new_address( + &mut self, + label: Option, + password: &str, + ) -> anyhow::Result<&WalletAddress> { let seed = self .encrypted_seed .as_ref() diff --git a/apps/explorer-web/.vite/deps_temp_a70ee4eb/package.json b/apps/explorer-web/.vite/deps_temp_a70ee4eb/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/apps/explorer-web/.vite/deps_temp_a70ee4eb/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/apps/explorer/src/main.rs b/apps/explorer/src/main.rs index 18eac35..459e049 100644 --- a/apps/explorer/src/main.rs +++ b/apps/explorer/src/main.rs @@ -12,6 +12,7 @@ use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; +use axum::http::{HeaderValue, Method}; use axum::{ extract::{Path, Query, State}, http::StatusCode, @@ -21,10 +22,9 @@ use axum::{ }; use moka::future::Cache; use serde::{Deserialize, Serialize}; -use tower_http::cors::{Any, CorsLayer}; -use axum::http::{HeaderValue, Method}; -use tower_http::trace::TraceLayer; use tower_http::compression::CompressionLayer; +use tower_http::cors::{Any, CorsLayer}; +use tower_http::trace::TraceLayer; use tracing::{error, info}; // ==================== Configuration ==================== @@ -297,8 +297,12 @@ pub struct PaginationParams { pub limit: usize, } -fn default_page() -> usize { 1 } -fn default_limit() -> usize { 25 } +fn default_page() -> usize { + 1 +} +fn default_limit() -> usize { + 25 +} /// Paginated response. #[derive(Clone, Debug, Serialize)] @@ -435,14 +439,19 @@ async fn health(State(state): State>) -> impl IntoResponse { StatusCode::SERVICE_UNAVAILABLE }; - (status, Json(Health { - healthy: rpc_ok, - rpc_connected: rpc_ok, - })) + ( + status, + Json(Health { + healthy: rpc_ok, + rpc_connected: rpc_ok, + }), + ) } /// Get network statistics. -async fn get_stats(State(state): State>) -> Result, ApiError> { +async fn get_stats( + State(state): State>, +) -> Result, ApiError> { // Check cache first if let Some(stats) = state.stats_cache.get("network_stats").await { return Ok(Json(stats)); @@ -532,7 +541,10 @@ async fn get_stats(State(state): State>) -> Result = state - .rpc_call("synor_getUtxosByAddresses", GetUtxosParams { - addresses: vec![address.clone()], - }) + .rpc_call( + "synor_getUtxosByAddresses", + GetUtxosParams { + addresses: vec![address.clone()], + }, + ) .await?; // Get balance @@ -727,9 +748,12 @@ async fn get_address( } let balance: BalanceResult = state - .rpc_call("synor_getBalanceByAddress", GetBalanceParams { - address: address.clone(), - }) + .rpc_call( + "synor_getBalanceByAddress", + GetBalanceParams { + address: address.clone(), + }, + ) .await?; let info = AddressInfo { @@ -737,8 +761,8 @@ async fn get_address( balance: balance.balance, balance_human: format_synor(balance.balance), utxo_count: utxos.len(), - total_received: 0, // Would need historical data - total_sent: 0, // Would need historical data + total_received: 0, // Would need historical data + total_sent: 0, // Would need historical data transaction_count: 0, // Would need indexing }; @@ -756,9 +780,12 @@ async fn get_address_utxos( } let utxos: Vec = state - .rpc_call("synor_getUtxosByAddresses", GetUtxosParams { - addresses: vec![address], - }) + .rpc_call( + "synor_getUtxosByAddresses", + GetUtxosParams { + addresses: vec![address], + }, + ) .await?; Ok(Json(utxos)) @@ -820,7 +847,7 @@ async fn get_dag( hash: header.hash.clone(), short_hash: header.hash.chars().take(8).collect(), blue_score: header.blue_score, - is_blue: true, // Would need verbose data + is_blue: true, // Would need verbose data is_chain_block: true, // Would need verbose data timestamp: header.timestamp, tx_count: 0, // Unknown from header @@ -852,10 +879,13 @@ async fn get_mempool( } let entries: Vec = state - .rpc_call("synor_getMempoolEntries", GetMempoolParams { - include_orphan_pool: false, - filter_tx_in_addresses: false, - }) + .rpc_call( + "synor_getMempoolEntries", + GetMempoolParams { + include_orphan_pool: false, + filter_tx_in_addresses: false, + }, + ) .await?; let total = entries.len(); @@ -912,10 +942,13 @@ async fn search( } let block_result: Result = state - .rpc_call("synor_getBlock", GetBlockParams { - hash: query.to_string(), - include_txs: false, - }) + .rpc_call( + "synor_getBlock", + GetBlockParams { + hash: query.to_string(), + include_txs: false, + }, + ) .await; if block_result.is_ok() { @@ -933,9 +966,12 @@ async fn search( } let tx_result: Result = state - .rpc_call("synor_getTransaction", GetTxParams { - tx_id: query.to_string(), - }) + .rpc_call( + "synor_getTransaction", + GetTxParams { + tx_id: query.to_string(), + }, + ) .await; if tx_result.is_ok() { @@ -984,9 +1020,15 @@ fn convert_rpc_block(rpc: synor_rpc::RpcBlock) -> ExplorerBlock { .map(convert_rpc_transaction) .collect(), ), - children_hashes: verbose.map(|v| v.children_hashes.clone()).unwrap_or_default(), - merge_set_blues: verbose.map(|v| v.merge_set_blues_hashes.clone()).unwrap_or_default(), - merge_set_reds: verbose.map(|v| v.merge_set_reds_hashes.clone()).unwrap_or_default(), + children_hashes: verbose + .map(|v| v.children_hashes.clone()) + .unwrap_or_default(), + merge_set_blues: verbose + .map(|v| v.merge_set_blues_hashes.clone()) + .unwrap_or_default(), + merge_set_reds: verbose + .map(|v| v.merge_set_reds_hashes.clone()) + .unwrap_or_default(), } } @@ -995,7 +1037,11 @@ fn convert_rpc_transaction(rpc: synor_rpc::RpcTransaction) -> ExplorerTransactio let verbose = rpc.verbose_data.as_ref(); let is_coinbase = rpc.inputs.is_empty() - || rpc.inputs.first().map(|i| i.previous_outpoint.transaction_id.chars().all(|c| c == '0')).unwrap_or(false); + || rpc + .inputs + .first() + .map(|i| i.previous_outpoint.transaction_id.chars().all(|c| c == '0')) + .unwrap_or(false); let total_output: u64 = rpc.outputs.iter().map(|o| o.value).sum(); let total_input: u64 = rpc @@ -1004,10 +1050,17 @@ fn convert_rpc_transaction(rpc: synor_rpc::RpcTransaction) -> ExplorerTransactio .filter_map(|i| i.verbose_data.as_ref().map(|v| v.value)) .sum(); - let fee = if is_coinbase { 0 } else { total_input.saturating_sub(total_output) }; + let fee = if is_coinbase { + 0 + } else { + total_input.saturating_sub(total_output) + }; ExplorerTransaction { - id: verbose.as_ref().map(|v| v.transaction_id.clone()).unwrap_or_default(), + id: verbose + .as_ref() + .map(|v| v.transaction_id.clone()) + .unwrap_or_default(), hash: verbose.as_ref().map(|v| v.hash.clone()).unwrap_or_default(), version: rpc.version, inputs: rpc diff --git a/apps/faucet/src/main.rs b/apps/faucet/src/main.rs index 6647d43..c281d83 100644 --- a/apps/faucet/src/main.rs +++ b/apps/faucet/src/main.rs @@ -8,6 +8,7 @@ use std::net::SocketAddr; use std::sync::Arc; use std::time::{Duration, Instant}; +use axum::http::{HeaderValue, Method}; use axum::{ extract::{ConnectInfo, State}, http::StatusCode, @@ -15,11 +16,10 @@ use axum::{ routing::{get, post}, Json, Router, }; -use governor::{Quota, RateLimiter, state::keyed::DashMapStateStore}; +use governor::{state::keyed::DashMapStateStore, Quota, RateLimiter}; use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; use tower_http::cors::{Any, CorsLayer}; -use axum::http::{HeaderValue, Method}; use tower_http::trace::TraceLayer; use tracing::{info, warn}; @@ -47,7 +47,7 @@ impl Default for FaucetConfig { FaucetConfig { rpc_url: "http://localhost:17110".to_string(), dispense_amount: 10_00000000, // 10 SYNOR - cooldown_seconds: 3600, // 1 hour + cooldown_seconds: 3600, // 1 hour rate_limit_per_minute: 10, listen_addr: "0.0.0.0:8080".parse().unwrap(), wallet_key: None, @@ -210,7 +210,8 @@ async fn main() -> anyhow::Result<()> { // Create rate limiter (using NonZeroU32 for quota) let quota = Quota::per_minute( - std::num::NonZeroU32::new(config.rate_limit_per_minute).unwrap_or(std::num::NonZeroU32::new(10).unwrap()) + std::num::NonZeroU32::new(config.rate_limit_per_minute) + .unwrap_or(std::num::NonZeroU32::new(10).unwrap()), ); let rate_limiter = RateLimiter::keyed(quota); @@ -639,10 +640,7 @@ async fn send_tokens(state: &FaucetState, address: &str) -> anyhow::Result bool { /// Formats a hash for display. pub fn format_hash(hash: &[u8]) -> String { if hash.len() >= 8 { - format!("{}...{}", hex::encode(&hash[..4]), hex::encode(&hash[hash.len() - 4..])) + format!( + "{}...{}", + hex::encode(&hash[..4]), + hex::encode(&hash[hash.len() - 4..]) + ) } else { hex::encode(hash) } diff --git a/apps/synord/src/config.rs b/apps/synord/src/config.rs index 23c1cd5..7ef3c3d 100644 --- a/apps/synord/src/config.rs +++ b/apps/synord/src/config.rs @@ -115,12 +115,7 @@ impl NodeConfig { } /// Sets mining configuration. - pub fn with_mining( - mut self, - enabled: bool, - coinbase: Option, - threads: usize, - ) -> Self { + pub fn with_mining(mut self, enabled: bool, coinbase: Option, threads: usize) -> Self { if enabled { self.mining.enabled = true; } @@ -448,7 +443,7 @@ impl Default for ConsensusConfig { fn default() -> Self { ConsensusConfig { ghostdag_k: 18, - merge_depth: 3600, // ~1 hour + merge_depth: 3600, // ~1 hour finality_depth: 86400, // ~24 hours target_time_ms: 1000, difficulty_window: 2641, diff --git a/apps/synord/src/main.rs b/apps/synord/src/main.rs index b05c940..cd74b1a 100644 --- a/apps/synord/src/main.rs +++ b/apps/synord/src/main.rs @@ -210,19 +210,14 @@ async fn main() { fn init_logging(level: &str, json: bool) { use tracing_subscriber::{fmt, prelude::*, EnvFilter}; - let filter = EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new(level)); + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level)); let subscriber = tracing_subscriber::registry().with(filter); if json { - subscriber - .with(fmt::layer().json()) - .init(); + subscriber.with(fmt::layer().json()).init(); } else { - subscriber - .with(fmt::layer().with_target(true)) - .init(); + subscriber.with(fmt::layer().with_target(true)).init(); } } @@ -277,11 +272,7 @@ async fn run_node( } /// Initialize a new node with genesis block. -async fn init_node( - data_dir: Option, - network: String, - force: bool, -) -> anyhow::Result<()> { +async fn init_node(data_dir: Option, network: String, force: bool) -> anyhow::Result<()> { use synor_consensus::genesis::ChainConfig; use synor_storage::{BlockBody, ChainState}; use synor_types::{BlockId, Network}; @@ -302,7 +293,10 @@ async fn init_node( "mainnet" => Network::Mainnet, "testnet" => Network::Testnet, "devnet" => Network::Devnet, - _ => anyhow::bail!("Unknown network: {}. Use mainnet, testnet, or devnet.", network), + _ => anyhow::bail!( + "Unknown network: {}. Use mainnet, testnet, or devnet.", + network + ), }; info!(network = %network, "Initializing node..."); @@ -323,8 +317,7 @@ async fn init_node( std::fs::create_dir_all(data_dir.join("keys"))?; // Create and save node config - let config = NodeConfig::for_network(&network)? - .with_data_dir(Some(data_dir.clone())); + let config = NodeConfig::for_network(&network)?.with_data_dir(Some(data_dir.clone())); let config_path = data_dir.join("synord.toml"); config.save(&config_path)?; @@ -343,7 +336,10 @@ async fn init_node( // Store genesis block body let genesis_hash = chain_config.genesis_hash; let body = BlockBody { - transaction_ids: chain_config.genesis.body.transactions + transaction_ids: chain_config + .genesis + .body + .transactions .iter() .map(|tx| tx.txid()) .collect(), @@ -401,10 +397,19 @@ async fn init_node( println!(" Genesis: {}", hex::encode(genesis_hash.as_bytes())); println!(); println!("Chain parameters:"); - println!(" Block time: {} ms", chain_config.target_block_time_ms); + println!( + " Block time: {} ms", + chain_config.target_block_time_ms + ); println!(" GHOSTDAG K: {}", chain_config.ghostdag_k); - println!(" Initial reward: {} SYNOR", chain_config.initial_reward / 100_000_000); - println!(" Halving interval: {} blocks", chain_config.halving_interval); + println!( + " Initial reward: {} SYNOR", + chain_config.initial_reward / 100_000_000 + ); + println!( + " Halving interval: {} blocks", + chain_config.halving_interval + ); println!(); println!("To start the node:"); println!(" synord run --network {}", network); @@ -481,7 +486,10 @@ async fn import_blocks( // Store the block if let Err(e) = storage.put_block(&block_data).await { - error!(hash = hex::encode(&block_data.hash[..8]), "Failed to store block: {}", e); + error!( + hash = hex::encode(&block_data.hash[..8]), + "Failed to store block: {}", e + ); errors += 1; } else { imported += 1; @@ -656,7 +664,9 @@ async fn wait_for_shutdown() { #[cfg(windows)] { - tokio::signal::ctrl_c().await.expect("Failed to listen for Ctrl+C"); + tokio::signal::ctrl_c() + .await + .expect("Failed to listen for Ctrl+C"); info!("Received Ctrl+C"); } } diff --git a/apps/synord/src/node.rs b/apps/synord/src/node.rs index f9fe285..0e93c4a 100644 --- a/apps/synord/src/node.rs +++ b/apps/synord/src/node.rs @@ -207,10 +207,7 @@ impl SynorNode { // Start miner if enabled if let Some(ref miner) = self.miner { miner.start().await?; - info!( - threads = self.config.mining.threads, - "Miner started" - ); + info!(threads = self.config.mining.threads, "Miner started"); } // Update state diff --git a/apps/synord/src/services/consensus.rs b/apps/synord/src/services/consensus.rs index d7517aa..1f456ff 100644 --- a/apps/synord/src/services/consensus.rs +++ b/apps/synord/src/services/consensus.rs @@ -7,8 +7,8 @@ use tokio::sync::{broadcast, RwLock}; use tracing::{debug, info}; use synor_consensus::{ - BlockValidator, DaaParams, DifficultyManager, RewardCalculator, - TransactionValidator, UtxoSet, ValidationError, + BlockValidator, DaaParams, DifficultyManager, RewardCalculator, TransactionValidator, UtxoSet, + ValidationError, }; use synor_types::{ block::{Block, BlockHeader}, @@ -288,10 +288,15 @@ impl ConsensusService { } // Calculate expected reward - let expected_reward = self.reward_calculator.calculate_subsidy(block.header.daa_score); + let expected_reward = self + .reward_calculator + .calculate_subsidy(block.header.daa_score); // Validate the full block (including transactions) - if let Err(e) = self.block_validator.validate_block(block, &self.utxo_set, expected_reward) { + if let Err(e) = self + .block_validator + .validate_block(block, &self.utxo_set, expected_reward) + { return BlockValidation::Invalid { reason: format!("Invalid block: {}", e), }; @@ -442,7 +447,10 @@ impl ConsensusService { // For non-coinbase transactions, validate against UTXO set if !tx.is_coinbase() { let current_daa = *self.daa_score.read().await; - if let Err(e) = self.tx_validator.validate_against_utxos(tx, &self.utxo_set, current_daa) { + if let Err(e) = + self.tx_validator + .validate_against_utxos(tx, &self.utxo_set, current_daa) + { // Check if this is a double-spend conflict if matches!(e, ValidationError::UtxoNotFound(_)) { return TxValidation::Conflict; diff --git a/apps/synord/src/services/contract.rs b/apps/synord/src/services/contract.rs index a3603d6..0d830da 100644 --- a/apps/synord/src/services/contract.rs +++ b/apps/synord/src/services/contract.rs @@ -247,7 +247,8 @@ impl ContractService { call_data.extend_from_slice(&args); // Create execution context - let call_context = CallContext::new(vm_contract_id, caller.clone(), value, call_data.clone()); + let call_context = + CallContext::new(vm_contract_id, caller.clone(), value, call_data.clone()); let context = ExecutionContext::new( synor_vm::context::BlockInfo { @@ -353,7 +354,10 @@ impl ContractService { } /// Gets contract metadata. - pub async fn get_contract(&self, contract_id: &[u8; 32]) -> anyhow::Result> { + pub async fn get_contract( + &self, + contract_id: &[u8; 32], + ) -> anyhow::Result> { let store = self.contract_store.read().await; let store = store .as_ref() diff --git a/apps/synord/src/services/governance.rs b/apps/synord/src/services/governance.rs index d80ae21..4c970e1 100644 --- a/apps/synord/src/services/governance.rs +++ b/apps/synord/src/services/governance.rs @@ -8,8 +8,8 @@ use tokio::sync::{broadcast, RwLock}; use tracing::{debug, info, warn}; use synor_governance::{ - DaoStats, GovernanceConfig, Proposal, ProposalId, ProposalState, ProposalSummary, - ProposalType, Treasury, TreasuryPoolId, VoteChoice, VotingConfig, DAO, + DaoStats, GovernanceConfig, Proposal, ProposalId, ProposalState, ProposalSummary, ProposalType, + Treasury, TreasuryPoolId, VoteChoice, VotingConfig, DAO, }; use synor_types::Address; @@ -247,7 +247,10 @@ impl GovernanceService { } let current_block = state.current_block; - let proposal = state.dao.execute(proposal_id, executor, current_block)?.clone(); + let proposal = state + .dao + .execute(proposal_id, executor, current_block)? + .clone(); info!( proposal_id = %hex::encode(proposal_id.as_bytes()), @@ -256,7 +259,8 @@ impl GovernanceService { ); // Handle proposal execution based on type - self.handle_proposal_execution(&proposal, &mut state).await?; + self.handle_proposal_execution(&proposal, &mut state) + .await?; Ok(proposal) } @@ -268,7 +272,11 @@ impl GovernanceService { state: &mut GovernanceState, ) -> Result<(), GovernanceError> { match &proposal.proposal_type { - ProposalType::TreasurySpend { recipient, amount, reason } => { + ProposalType::TreasurySpend { + recipient, + amount, + reason, + } => { info!( recipient = %recipient, amount = amount, @@ -278,7 +286,11 @@ impl GovernanceService { // In production, this would create a spending request // For now, we log the action } - ProposalType::ParameterChange { parameter, old_value, new_value } => { + ProposalType::ParameterChange { + parameter, + old_value, + new_value, + } => { info!( parameter = %parameter, old_value = %old_value, @@ -287,7 +299,11 @@ impl GovernanceService { ); // In production, this would update chain parameters } - ProposalType::CouncilChange { action, member, role } => { + ProposalType::CouncilChange { + action, + member, + role, + } => { info!( action = ?action, member = %member, @@ -341,7 +357,10 @@ impl GovernanceService { } /// Gets a proposal by ID. - pub async fn get_proposal(&self, proposal_id: &ProposalId) -> Result { + pub async fn get_proposal( + &self, + proposal_id: &ProposalId, + ) -> Result { let state = self.state.read().await; if !state.initialized { diff --git a/apps/synord/src/services/mempool.rs b/apps/synord/src/services/mempool.rs index 58cd651..58bfcf5 100644 --- a/apps/synord/src/services/mempool.rs +++ b/apps/synord/src/services/mempool.rs @@ -104,7 +104,8 @@ impl MempoolService { let mut block_rx = self.consensus.subscribe_blocks(); let handle = tokio::spawn(async move { - let mut cleanup_interval = tokio::time::interval(Duration::from_secs(CLEANUP_INTERVAL_SECS)); + let mut cleanup_interval = + tokio::time::interval(Duration::from_secs(CLEANUP_INTERVAL_SECS)); cleanup_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); info!("Mempool cleanup task started"); @@ -232,7 +233,10 @@ impl MempoolService { *current_size += tx_size; } - debug!(hash = hex::encode(&hash[..8]), "Transaction added to mempool"); + debug!( + hash = hex::encode(&hash[..8]), + "Transaction added to mempool" + ); let _ = self.tx_added.send(hash); Ok(()) @@ -245,7 +249,10 @@ impl MempoolService { let mut current_size = self.current_size.write().await; *current_size = current_size.saturating_sub(tx.data.len()); - debug!(hash = hex::encode(&hash[..8]), "Transaction removed from mempool"); + debug!( + hash = hex::encode(&hash[..8]), + "Transaction removed from mempool" + ); let _ = self.tx_removed.send(*hash); Some(tx) @@ -285,7 +292,11 @@ impl MempoolService { // Sort by fee rate descending let mut sorted: Vec<_> = txs.values().cloned().collect(); - sorted.sort_by(|a, b| b.fee_rate.partial_cmp(&a.fee_rate).unwrap_or(std::cmp::Ordering::Equal)); + sorted.sort_by(|a, b| { + b.fee_rate + .partial_cmp(&a.fee_rate) + .unwrap_or(std::cmp::Ordering::Equal) + }); // Select transactions up to max mass let mut selected = Vec::new(); @@ -308,7 +319,11 @@ impl MempoolService { // Sort by fee rate ascending (evict lowest first) let mut sorted: Vec<_> = txs.values().cloned().collect(); - sorted.sort_by(|a, b| a.fee_rate.partial_cmp(&b.fee_rate).unwrap_or(std::cmp::Ordering::Equal)); + sorted.sort_by(|a, b| { + a.fee_rate + .partial_cmp(&b.fee_rate) + .unwrap_or(std::cmp::Ordering::Equal) + }); let mut freed = 0usize; let mut to_remove = Vec::new(); diff --git a/apps/synord/src/services/miner.rs b/apps/synord/src/services/miner.rs index 92c73c0..b634901 100644 --- a/apps/synord/src/services/miner.rs +++ b/apps/synord/src/services/miner.rs @@ -7,7 +7,9 @@ use tokio::sync::{broadcast, mpsc, RwLock}; use tracing::{debug, error, info, warn}; use synor_mining::{ - BlockMiner, BlockTemplate as MiningBlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, MinerCommand, MinerConfig, MinerEvent, MiningResult, MiningStats as CrateMiningStats, TemplateTransaction, + BlockMiner, BlockTemplate as MiningBlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, + MinerCommand, MinerConfig, MinerEvent, MiningResult, MiningStats as CrateMiningStats, + TemplateTransaction, }; use synor_types::{Address, Hash256, Network}; @@ -118,9 +120,11 @@ impl MinerService { }; // Parse coinbase address if provided - let coinbase_address = config.mining.coinbase_address.as_ref().and_then(|addr_str| { - addr_str.parse::
().ok() - }); + let coinbase_address = config + .mining + .coinbase_address + .as_ref() + .and_then(|addr_str| addr_str.parse::
().ok()); // Determine network from config let network = match config.network.as_str() { @@ -261,7 +265,10 @@ impl MinerService { /// Updates the mining template. async fn update_template(&self) -> anyhow::Result<()> { let template = self.build_template().await?; - let _ = self.cmd_tx.send(MinerCommand::NewTemplate(Arc::new(template))).await; + let _ = self + .cmd_tx + .send(MinerCommand::NewTemplate(Arc::new(template))) + .await; Ok(()) } @@ -301,12 +308,16 @@ impl MinerService { /// Sets coinbase address. pub async fn set_coinbase_address(&self, address: String) -> anyhow::Result<()> { - let parsed: Address = address.parse() + let parsed: Address = address + .parse() .map_err(|e| anyhow::anyhow!("Invalid address: {}", e))?; // Update miner config let new_config = MinerConfig::solo(parsed, self.threads); - let _ = self.cmd_tx.send(MinerCommand::UpdateConfig(new_config)).await; + let _ = self + .cmd_tx + .send(MinerCommand::UpdateConfig(new_config)) + .await; info!(address = %address, "Updated coinbase address"); Ok(()) @@ -314,7 +325,9 @@ impl MinerService { /// Builds a block template for mining. async fn build_template(&self) -> anyhow::Result { - let coinbase_address = self.coinbase_address.clone() + let coinbase_address = self + .coinbase_address + .clone() .ok_or_else(|| anyhow::anyhow!("No coinbase address set"))?; // Get transactions from mempool @@ -363,7 +376,8 @@ impl MinerService { builder = builder.add_transaction(template_tx); } - let template = builder.build(template_id) + let template = builder + .build(template_id) .map_err(|e| anyhow::anyhow!("Failed to build template: {}", e))?; debug!( @@ -416,7 +430,9 @@ impl MinerService { ); // Get the template that was mined - let template = self.miner.current_template() + let template = self + .miner + .current_template() .ok_or_else(|| anyhow::anyhow!("No current template"))?; // Build full block from template and mining result @@ -493,7 +509,9 @@ impl MinerService { // Get hash from block header for notification let hash = if block.len() >= 32 { let mut h = [0u8; 32]; - h.copy_from_slice(&blake3::hash(&block[..96.min(block.len())]).as_bytes()[..32]); + h.copy_from_slice( + &blake3::hash(&block[..96.min(block.len())]).as_bytes()[..32], + ); h } else { [0u8; 32] diff --git a/apps/synord/src/services/network.rs b/apps/synord/src/services/network.rs index 392b67a..3e8219e 100644 --- a/apps/synord/src/services/network.rs +++ b/apps/synord/src/services/network.rs @@ -50,7 +50,10 @@ pub enum NetworkMessage { /// Block response. Blocks { data: Vec> }, /// Headers request. - GetHeaders { locator: Vec<[u8; 32]>, stop: [u8; 32] }, + GetHeaders { + locator: Vec<[u8; 32]>, + stop: [u8; 32], + }, /// Headers response. Headers { headers: Vec> }, } @@ -112,11 +115,11 @@ impl NetworkService { }; // Parse listen address - let listen_addr_parsed: Multiaddr = config - .p2p - .listen_addr - .parse() - .unwrap_or_else(|_| format!("/ip4/0.0.0.0/tcp/{}", synor_network::DEFAULT_PORT).parse().unwrap()); + let listen_addr_parsed: Multiaddr = config.p2p.listen_addr.parse().unwrap_or_else(|_| { + format!("/ip4/0.0.0.0/tcp/{}", synor_network::DEFAULT_PORT) + .parse() + .unwrap() + }); // Parse seed/bootstrap peers let bootstrap_peers: Vec = config @@ -364,7 +367,7 @@ impl NetworkService { } NetworkMessage::TxAnnounce { hash } => { let announcement = TransactionAnnouncement::id_only( - synor_types::TransactionId::from_bytes(hash) + synor_types::TransactionId::from_bytes(hash), ); if let Err(e) = handle.broadcast_transaction(announcement).await { warn!("Failed to broadcast transaction: {}", e); @@ -406,11 +409,7 @@ impl NetworkService { } /// Requests blocks from a peer. - pub async fn request_blocks( - &self, - peer_id: &str, - hashes: Vec<[u8; 32]>, - ) -> anyhow::Result<()> { + pub async fn request_blocks(&self, peer_id: &str, hashes: Vec<[u8; 32]>) -> anyhow::Result<()> { let handle_guard = self.handle.read().await; if let Some(ref handle) = *handle_guard { let peer: PeerId = peer_id diff --git a/apps/synord/src/services/rpc.rs b/apps/synord/src/services/rpc.rs index 8866bc1..41bc693 100644 --- a/apps/synord/src/services/rpc.rs +++ b/apps/synord/src/services/rpc.rs @@ -9,7 +9,7 @@ use tokio::sync::RwLock; use tracing::{info, warn}; use synor_network::SyncState; -use synor_types::{BlockHeader, block::BlockBody}; +use synor_types::{block::BlockBody, BlockHeader}; use crate::config::NodeConfig; use crate::services::{ @@ -109,7 +109,9 @@ impl RpcService { // Start HTTP server if self.http_enabled { - let http_addr: SocketAddr = self.http_addr.parse() + let http_addr: SocketAddr = self + .http_addr + .parse() .map_err(|e| anyhow::anyhow!("Invalid HTTP address: {}", e))?; info!(addr = %http_addr, "Starting HTTP RPC server"); @@ -119,7 +121,8 @@ impl RpcService { .await .map_err(|e| anyhow::anyhow!("Failed to start HTTP server: {}", e))?; - let local_addr = server.local_addr() + let local_addr = server + .local_addr() .map_err(|e| anyhow::anyhow!("Failed to get local address: {}", e))?; info!(addr = %local_addr, "HTTP RPC server started"); @@ -129,7 +132,9 @@ impl RpcService { // Start WebSocket server if self.ws_enabled { - let ws_addr: SocketAddr = self.ws_addr.parse() + let ws_addr: SocketAddr = self + .ws_addr + .parse() .map_err(|e| anyhow::anyhow!("Invalid WebSocket address: {}", e))?; info!(addr = %ws_addr, "Starting WebSocket RPC server"); @@ -139,7 +144,8 @@ impl RpcService { .await .map_err(|e| anyhow::anyhow!("Failed to start WebSocket server: {}", e))?; - let local_addr = server.local_addr() + let local_addr = server + .local_addr() .map_err(|e| anyhow::anyhow!("Failed to get local address: {}", e))?; info!(addr = %local_addr, "WebSocket RPC server started"); @@ -319,7 +325,10 @@ impl RpcService { let mempool_size = ctx.mempool.count().await; // Check actual sync status from network service - let synced = ctx.network.sync_status().await + let synced = ctx + .network + .sync_status() + .await .map(|status| matches!(status.state, SyncState::Synced | SyncState::Idle)) .unwrap_or(false); @@ -343,16 +352,19 @@ impl RpcService { // synor_getPeerInfo module.register_async_method("synor_getPeerInfo", |_, ctx| async move { let peers = ctx.network.peers().await; - let peer_info: Vec = peers.iter().map(|p| { - serde_json::json!({ - "id": p.id, - "address": p.address.map(|a| a.to_string()).unwrap_or_default(), - "isInbound": p.inbound, - "version": p.version, - "userAgent": p.user_agent, - "latencyMs": p.latency_ms + let peer_info: Vec = peers + .iter() + .map(|p| { + serde_json::json!({ + "id": p.id, + "address": p.address.map(|a| a.to_string()).unwrap_or_default(), + "isInbound": p.inbound, + "version": p.version, + "userAgent": p.user_agent, + "latencyMs": p.latency_ms + }) }) - }).collect(); + .collect(); serde_json::json!({"peers": peer_info}) })?; @@ -418,7 +430,9 @@ impl RpcService { let bytecode = match hex::decode(¶ms.bytecode) { Ok(b) => b, - Err(e) => return serde_json::json!({"error": format!("Invalid bytecode hex: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid bytecode hex: {}", e)}) + } }; let init_args = if params.init_args.is_empty() { @@ -426,21 +440,27 @@ impl RpcService { } else { match hex::decode(¶ms.init_args) { Ok(a) => a, - Err(e) => return serde_json::json!({"error": format!("Invalid init_args hex: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid init_args hex: {}", e)}) + } } }; let block_height = ctx.consensus.current_height().await; let timestamp = current_timestamp(); - match ctx.contract.deploy( - bytecode, - init_args, - ¶ms.deployer, - params.gas_limit, - block_height, - timestamp, - ).await { + match ctx + .contract + .deploy( + bytecode, + init_args, + ¶ms.deployer, + params.gas_limit, + block_height, + timestamp, + ) + .await + { Ok(result) => serde_json::json!({ "contractId": hex::encode(&result.contract_id), "address": hex::encode(&result.address), @@ -448,7 +468,7 @@ impl RpcService { }), Err(e) => serde_json::json!({ "error": e.to_string() - }) + }), } })?; @@ -474,7 +494,9 @@ impl RpcService { let contract_id = match hex_to_hash(¶ms.contract_id) { Ok(id) => id, - Err(e) => return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}) + } }; let args = if params.args.is_empty() { @@ -482,23 +504,29 @@ impl RpcService { } else { match hex::decode(¶ms.args) { Ok(a) => a, - Err(e) => return serde_json::json!({"error": format!("Invalid args hex: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid args hex: {}", e)}) + } } }; let block_height = ctx.consensus.current_height().await; let timestamp = current_timestamp(); - match ctx.contract.call( - &contract_id, - ¶ms.method, - args, - ¶ms.caller, - params.value, - params.gas_limit, - block_height, - timestamp, - ).await { + match ctx + .contract + .call( + &contract_id, + ¶ms.method, + args, + ¶ms.caller, + params.value, + params.gas_limit, + block_height, + timestamp, + ) + .await + { Ok(result) => { let logs: Vec = result.logs.iter().map(|log| { serde_json::json!({ @@ -514,10 +542,10 @@ impl RpcService { "gasUsed": result.gas_used, "logs": logs }) - }, + } Err(e) => serde_json::json!({ "error": e.to_string() - }) + }), } })?; @@ -541,7 +569,9 @@ impl RpcService { let contract_id = match hex_to_hash(¶ms.contract_id) { Ok(id) => id, - Err(e) => return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}) + } }; let args = if params.args.is_empty() { @@ -549,28 +579,34 @@ impl RpcService { } else { match hex::decode(¶ms.args) { Ok(a) => a, - Err(e) => return serde_json::json!({"error": format!("Invalid args hex: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid args hex: {}", e)}) + } } }; let block_height = ctx.consensus.current_height().await; let timestamp = current_timestamp(); - match ctx.contract.estimate_gas( - &contract_id, - ¶ms.method, - args, - ¶ms.caller, - params.value, - block_height, - timestamp, - ).await { + match ctx + .contract + .estimate_gas( + &contract_id, + ¶ms.method, + args, + ¶ms.caller, + params.value, + block_height, + timestamp, + ) + .await + { Ok(gas) => serde_json::json!({ "estimatedGas": gas }), Err(e) => serde_json::json!({ "error": e.to_string() - }) + }), } })?; @@ -588,7 +624,9 @@ impl RpcService { let contract_id = match hex_to_hash(¶ms.contract_id) { Ok(id) => id, - Err(e) => return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}) + } }; match ctx.contract.get_code(&contract_id).await { @@ -600,7 +638,7 @@ impl RpcService { }), Err(e) => serde_json::json!({ "error": e.to_string() - }) + }), } })?; @@ -619,7 +657,9 @@ impl RpcService { let contract_id = match hex_to_hash(¶ms.contract_id) { Ok(id) => id, - Err(e) => return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}) + } }; let key = match hex_to_hash(¶ms.key) { @@ -636,7 +676,7 @@ impl RpcService { }), Err(e) => serde_json::json!({ "error": e.to_string() - }) + }), } })?; @@ -654,7 +694,9 @@ impl RpcService { let contract_id = match hex_to_hash(¶ms.contract_id) { Ok(id) => id, - Err(e) => return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}), + Err(e) => { + return serde_json::json!({"error": format!("Invalid contract_id: {}", e)}) + } }; match ctx.contract.get_contract(&contract_id).await { @@ -669,7 +711,7 @@ impl RpcService { }), Err(e) => serde_json::json!({ "error": e.to_string() - }) + }), } })?; @@ -705,11 +747,7 @@ impl RpcService { blue_work: String::new(), pruning_point: None, }, - transactions: if include_txs { - vec![] - } else { - vec![] - }, + transactions: if include_txs { vec![] } else { vec![] }, verbose_data: None, })) } else { @@ -845,8 +883,7 @@ impl RpcService { pruning_point: None, }, transactions: vec![], - target: "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - .to_string(), + target: "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string(), is_synced: true, }) } diff --git a/apps/synord/src/services/storage.rs b/apps/synord/src/services/storage.rs index be665d8..2e0be02 100644 --- a/apps/synord/src/services/storage.rs +++ b/apps/synord/src/services/storage.rs @@ -7,10 +7,9 @@ use tokio::sync::RwLock; use tracing::{debug, info, warn}; use synor_storage::{ - cf, Database, DatabaseConfig, - BlockBody, BlockStore, ChainState, GhostdagStore, HeaderStore, - MetadataStore, RelationsStore, StoredGhostdagData, StoredRelations, - StoredUtxo, TransactionStore, UtxoStore, + cf, BlockBody, BlockStore, ChainState, Database, DatabaseConfig, GhostdagStore, HeaderStore, + MetadataStore, RelationsStore, StoredGhostdagData, StoredRelations, StoredUtxo, + TransactionStore, UtxoStore, }; use synor_types::{BlockHeader, BlockId, Hash256, Transaction, TransactionId}; @@ -168,15 +167,23 @@ impl StorageService { /// Stores a block header. pub async fn put_header(&self, header: &BlockHeader) -> anyhow::Result<()> { let store = self.header_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.put(header).map_err(|e| anyhow::anyhow!("Failed to store header: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .put(header) + .map_err(|e| anyhow::anyhow!("Failed to store header: {}", e)) } /// Gets a block header by hash. pub async fn get_header(&self, hash: &Hash256) -> anyhow::Result> { let store = self.header_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get(hash).map_err(|e| anyhow::anyhow!("Failed to get header: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get(hash) + .map_err(|e| anyhow::anyhow!("Failed to get header: {}", e)) } /// Checks if a header exists. @@ -192,15 +199,23 @@ impl StorageService { /// Gets header by height. pub async fn get_header_by_height(&self, height: u64) -> anyhow::Result> { let store = self.header_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_by_height(height).map_err(|e| anyhow::anyhow!("Failed to get header by height: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_by_height(height) + .map_err(|e| anyhow::anyhow!("Failed to get header by height: {}", e)) } /// Indexes a header by height. pub async fn index_header_by_height(&self, height: u64, hash: &Hash256) -> anyhow::Result<()> { let store = self.header_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.index_by_height(height, hash).map_err(|e| anyhow::anyhow!("Failed to index header: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .index_by_height(height, hash) + .map_err(|e| anyhow::anyhow!("Failed to index header: {}", e)) } // ==================== Block Body Operations ==================== @@ -208,15 +223,23 @@ impl StorageService { /// Stores a block body. pub async fn put_block_body(&self, hash: &Hash256, body: &BlockBody) -> anyhow::Result<()> { let store = self.block_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.put(hash, body).map_err(|e| anyhow::anyhow!("Failed to store block body: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .put(hash, body) + .map_err(|e| anyhow::anyhow!("Failed to store block body: {}", e)) } /// Gets a block body by hash. pub async fn get_block_body(&self, hash: &Hash256) -> anyhow::Result> { let store = self.block_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get(hash).map_err(|e| anyhow::anyhow!("Failed to get block body: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get(hash) + .map_err(|e| anyhow::anyhow!("Failed to get block body: {}", e)) } /// Checks if block exists. @@ -233,7 +256,9 @@ impl StorageService { pub async fn put_block(&self, block: &BlockData) -> anyhow::Result<()> { debug!(hash = hex::encode(&block.hash[..8]), "Storing block"); let db = self.database.read().await; - let db = db.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + let db = db + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; // Store header bytes db.put(cf::HEADERS, &block.hash, &block.header) @@ -249,11 +274,15 @@ impl StorageService { /// Legacy method: Gets a block by hash (raw bytes). pub async fn get_block(&self, hash: &[u8; 32]) -> anyhow::Result> { let db = self.database.read().await; - let db = db.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + let db = db + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - let header = db.get(cf::HEADERS, hash) + let header = db + .get(cf::HEADERS, hash) .map_err(|e| anyhow::anyhow!("Failed to get header: {}", e))?; - let body = db.get(cf::BLOCKS, hash) + let body = db + .get(cf::BLOCKS, hash) .map_err(|e| anyhow::anyhow!("Failed to get body: {}", e))?; match (header, body) { @@ -271,15 +300,26 @@ impl StorageService { /// Stores a transaction. pub async fn put_transaction(&self, tx: &Transaction) -> anyhow::Result<()> { let store = self.tx_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.put(tx).map_err(|e| anyhow::anyhow!("Failed to store transaction: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .put(tx) + .map_err(|e| anyhow::anyhow!("Failed to store transaction: {}", e)) } /// Gets a transaction by ID. - pub async fn get_transaction(&self, txid: &TransactionId) -> anyhow::Result> { + pub async fn get_transaction( + &self, + txid: &TransactionId, + ) -> anyhow::Result> { let store = self.tx_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get(txid).map_err(|e| anyhow::anyhow!("Failed to get transaction: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get(txid) + .map_err(|e| anyhow::anyhow!("Failed to get transaction: {}", e)) } /// Checks if a transaction exists. @@ -295,24 +335,45 @@ impl StorageService { // ==================== UTXO Operations ==================== /// Gets a UTXO. - pub async fn get_utxo(&self, txid: &TransactionId, index: u32) -> anyhow::Result> { + pub async fn get_utxo( + &self, + txid: &TransactionId, + index: u32, + ) -> anyhow::Result> { let store = self.utxo_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get(txid, index).map_err(|e| anyhow::anyhow!("Failed to get UTXO: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get(txid, index) + .map_err(|e| anyhow::anyhow!("Failed to get UTXO: {}", e)) } /// Stores a UTXO. - pub async fn put_utxo(&self, txid: &TransactionId, index: u32, utxo: &StoredUtxo) -> anyhow::Result<()> { + pub async fn put_utxo( + &self, + txid: &TransactionId, + index: u32, + utxo: &StoredUtxo, + ) -> anyhow::Result<()> { let store = self.utxo_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.put(txid, index, utxo).map_err(|e| anyhow::anyhow!("Failed to store UTXO: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .put(txid, index, utxo) + .map_err(|e| anyhow::anyhow!("Failed to store UTXO: {}", e)) } /// Deletes a UTXO (marks as spent). pub async fn delete_utxo(&self, txid: &TransactionId, index: u32) -> anyhow::Result<()> { let store = self.utxo_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.delete(txid, index).map_err(|e| anyhow::anyhow!("Failed to delete UTXO: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .delete(txid, index) + .map_err(|e| anyhow::anyhow!("Failed to delete UTXO: {}", e)) } /// Checks if a UTXO exists (is unspent). @@ -326,77 +387,134 @@ impl StorageService { } /// Gets all UTXOs for a transaction. - pub async fn get_utxos_by_tx(&self, txid: &TransactionId) -> anyhow::Result> { + pub async fn get_utxos_by_tx( + &self, + txid: &TransactionId, + ) -> anyhow::Result> { let store = self.utxo_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_by_tx(txid).map_err(|e| anyhow::anyhow!("Failed to get UTXOs: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_by_tx(txid) + .map_err(|e| anyhow::anyhow!("Failed to get UTXOs: {}", e)) } // ==================== DAG Relations Operations ==================== /// Stores DAG relations for a block. - pub async fn put_relations(&self, block_id: &BlockId, relations: &StoredRelations) -> anyhow::Result<()> { + pub async fn put_relations( + &self, + block_id: &BlockId, + relations: &StoredRelations, + ) -> anyhow::Result<()> { let store = self.relations_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.put(block_id, relations).map_err(|e| anyhow::anyhow!("Failed to store relations: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .put(block_id, relations) + .map_err(|e| anyhow::anyhow!("Failed to store relations: {}", e)) } /// Gets DAG relations for a block. - pub async fn get_relations(&self, block_id: &BlockId) -> anyhow::Result> { + pub async fn get_relations( + &self, + block_id: &BlockId, + ) -> anyhow::Result> { let store = self.relations_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get(block_id).map_err(|e| anyhow::anyhow!("Failed to get relations: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get(block_id) + .map_err(|e| anyhow::anyhow!("Failed to get relations: {}", e)) } /// Gets parents of a block. pub async fn get_parents(&self, block_id: &BlockId) -> anyhow::Result> { let store = self.relations_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_parents(block_id).map_err(|e| anyhow::anyhow!("Failed to get parents: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_parents(block_id) + .map_err(|e| anyhow::anyhow!("Failed to get parents: {}", e)) } /// Gets children of a block. pub async fn get_children(&self, block_id: &BlockId) -> anyhow::Result> { let store = self.relations_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_children(block_id).map_err(|e| anyhow::anyhow!("Failed to get children: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_children(block_id) + .map_err(|e| anyhow::anyhow!("Failed to get children: {}", e)) } /// Adds a child to a block's relations. pub async fn add_child(&self, parent_id: &BlockId, child_id: BlockId) -> anyhow::Result<()> { let store = self.relations_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.add_child(parent_id, child_id).map_err(|e| anyhow::anyhow!("Failed to add child: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .add_child(parent_id, child_id) + .map_err(|e| anyhow::anyhow!("Failed to add child: {}", e)) } // ==================== GHOSTDAG Operations ==================== /// Stores GHOSTDAG data for a block. - pub async fn put_ghostdag(&self, block_id: &BlockId, data: &StoredGhostdagData) -> anyhow::Result<()> { + pub async fn put_ghostdag( + &self, + block_id: &BlockId, + data: &StoredGhostdagData, + ) -> anyhow::Result<()> { let store = self.ghostdag_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.put(block_id, data).map_err(|e| anyhow::anyhow!("Failed to store GHOSTDAG data: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .put(block_id, data) + .map_err(|e| anyhow::anyhow!("Failed to store GHOSTDAG data: {}", e)) } /// Gets GHOSTDAG data for a block. - pub async fn get_ghostdag(&self, block_id: &BlockId) -> anyhow::Result> { + pub async fn get_ghostdag( + &self, + block_id: &BlockId, + ) -> anyhow::Result> { let store = self.ghostdag_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get(block_id).map_err(|e| anyhow::anyhow!("Failed to get GHOSTDAG data: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get(block_id) + .map_err(|e| anyhow::anyhow!("Failed to get GHOSTDAG data: {}", e)) } /// Gets the blue score of a block. pub async fn get_blue_score(&self, block_id: &BlockId) -> anyhow::Result> { let store = self.ghostdag_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_blue_score(block_id).map_err(|e| anyhow::anyhow!("Failed to get blue score: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_blue_score(block_id) + .map_err(|e| anyhow::anyhow!("Failed to get blue score: {}", e)) } /// Gets the selected parent of a block. pub async fn get_selected_parent(&self, block_id: &BlockId) -> anyhow::Result> { let store = self.ghostdag_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_selected_parent(block_id).map_err(|e| anyhow::anyhow!("Failed to get selected parent: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_selected_parent(block_id) + .map_err(|e| anyhow::anyhow!("Failed to get selected parent: {}", e)) } // ==================== Metadata Operations ==================== @@ -404,15 +522,23 @@ impl StorageService { /// Gets current DAG tips. pub async fn get_tips(&self) -> anyhow::Result> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_tips().map_err(|e| anyhow::anyhow!("Failed to get tips: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_tips() + .map_err(|e| anyhow::anyhow!("Failed to get tips: {}", e)) } /// Sets current DAG tips. pub async fn set_tips(&self, tips: &[BlockId]) -> anyhow::Result<()> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.set_tips(tips).map_err(|e| anyhow::anyhow!("Failed to set tips: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .set_tips(tips) + .map_err(|e| anyhow::anyhow!("Failed to set tips: {}", e)) } /// Gets the current chain tip (first tip, for legacy compatibility). @@ -433,29 +559,45 @@ impl StorageService { /// Gets the genesis block ID. pub async fn get_genesis(&self) -> anyhow::Result> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_genesis().map_err(|e| anyhow::anyhow!("Failed to get genesis: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_genesis() + .map_err(|e| anyhow::anyhow!("Failed to get genesis: {}", e)) } /// Sets the genesis block ID. pub async fn set_genesis(&self, genesis: &BlockId) -> anyhow::Result<()> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.set_genesis(genesis).map_err(|e| anyhow::anyhow!("Failed to set genesis: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .set_genesis(genesis) + .map_err(|e| anyhow::anyhow!("Failed to set genesis: {}", e)) } /// Gets the chain state. pub async fn get_chain_state(&self) -> anyhow::Result> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_chain_state().map_err(|e| anyhow::anyhow!("Failed to get chain state: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_chain_state() + .map_err(|e| anyhow::anyhow!("Failed to get chain state: {}", e)) } /// Sets the chain state. pub async fn set_chain_state(&self, state: &ChainState) -> anyhow::Result<()> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.set_chain_state(state).map_err(|e| anyhow::anyhow!("Failed to set chain state: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .set_chain_state(state) + .map_err(|e| anyhow::anyhow!("Failed to set chain state: {}", e)) } /// Gets current height from chain state. @@ -470,15 +612,23 @@ impl StorageService { /// Gets the pruning point. pub async fn get_pruning_point(&self) -> anyhow::Result> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.get_pruning_point().map_err(|e| anyhow::anyhow!("Failed to get pruning point: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .get_pruning_point() + .map_err(|e| anyhow::anyhow!("Failed to get pruning point: {}", e)) } /// Sets the pruning point. pub async fn set_pruning_point(&self, point: &BlockId) -> anyhow::Result<()> { let store = self.metadata_store.read().await; - let store = store.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - store.set_pruning_point(point).map_err(|e| anyhow::anyhow!("Failed to set pruning point: {}", e)) + let store = store + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + store + .set_pruning_point(point) + .map_err(|e| anyhow::anyhow!("Failed to set pruning point: {}", e)) } // ==================== Contract Storage ==================== @@ -490,7 +640,9 @@ impl StorageService { key: &[u8; 32], ) -> anyhow::Result>> { let db = self.database.read().await; - let db = db.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + let db = db + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; // Create composite key: contract_address || storage_key let mut composite_key = Vec::with_capacity(64); @@ -510,7 +662,9 @@ impl StorageService { value: &[u8], ) -> anyhow::Result<()> { let db = self.database.read().await; - let db = db.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + let db = db + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; let mut composite_key = Vec::with_capacity(64); composite_key.extend_from_slice(contract); @@ -526,15 +680,21 @@ impl StorageService { pub async fn compact(&self) -> anyhow::Result<()> { info!("Compacting database"); let db = self.database.read().await; - let db = db.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - db.compact().map_err(|e| anyhow::anyhow!("Failed to compact database: {}", e)) + let db = db + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + db.compact() + .map_err(|e| anyhow::anyhow!("Failed to compact database: {}", e)) } /// Flushes pending writes to disk. pub async fn flush(&self) -> anyhow::Result<()> { let db = self.database.read().await; - let db = db.as_ref().ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; - db.flush().map_err(|e| anyhow::anyhow!("Failed to flush database: {}", e)) + let db = db + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?; + db.flush() + .map_err(|e| anyhow::anyhow!("Failed to flush database: {}", e)) } /// Gets database statistics. @@ -545,7 +705,9 @@ impl StorageService { let headers_size = db.cf_size(cf::HEADERS).unwrap_or(0); let blocks_size = db.cf_size(cf::BLOCKS).unwrap_or(0); let utxos_size = db.cf_size(cf::UTXOS).unwrap_or(0); - let total_size = headers_size + blocks_size + utxos_size + let total_size = headers_size + + blocks_size + + utxos_size + db.cf_size(cf::TRANSACTIONS).unwrap_or(0) + db.cf_size(cf::RELATIONS).unwrap_or(0) + db.cf_size(cf::GHOSTDAG).unwrap_or(0) @@ -559,7 +721,7 @@ impl StorageService { blocks_count, utxo_count, disk_usage_bytes: total_size, - cache_hits: 0, // Would need cache instrumentation + cache_hits: 0, // Would need cache instrumentation cache_misses: 0, } } else { diff --git a/apps/synord/tests/fork_resolution.rs b/apps/synord/tests/fork_resolution.rs index 327dc69..7d2e184 100644 --- a/apps/synord/tests/fork_resolution.rs +++ b/apps/synord/tests/fork_resolution.rs @@ -169,7 +169,11 @@ async fn test_selected_parent_chain() { for (i, node) in network.nodes.iter().enumerate() { let consensus = node.consensus(); let chain: Vec<[u8; 32]> = consensus.get_selected_chain(10).await; - info!(node = i, chain_length = chain.len(), "Selected parent chain"); + info!( + node = i, + chain_length = chain.len(), + "Selected parent chain" + ); // Chain should be consistent across nodes in same network for (j, block) in chain.iter().enumerate() { @@ -551,8 +555,16 @@ async fn test_partition_and_recovery() { // In real test, we'd need fresh config for same ports // For now, just verify nodes didn't crash - assert_eq!(node1.state().await, NodeState::Running, "Node 1 should survive partition"); - assert_eq!(node2.state().await, NodeState::Running, "Node 2 should survive partition"); + assert_eq!( + node1.state().await, + NodeState::Running, + "Node 1 should survive partition" + ); + assert_eq!( + node2.state().await, + NodeState::Running, + "Node 2 should survive partition" + ); node2.stop().await.unwrap(); node1.stop().await.unwrap(); @@ -594,11 +606,7 @@ async fn test_difficulty_adjustment() { let difficulty = consensus.current_difficulty().await; let _target = consensus.get_current_target().await; - info!( - node = i, - difficulty_bits = difficulty, - "Difficulty info" - ); + info!(node = i, difficulty_bits = difficulty, "Difficulty info"); // Difficulty should be set // Target is the hash threshold for valid blocks @@ -653,7 +661,10 @@ async fn test_block_accepted_subscription() { // Check if any blocks are received (unlikely in test without mining) match tokio::time::timeout(Duration::from_millis(500), rx.recv()).await { Ok(Ok(hash)) => { - info!(block = hex::encode(&hash[..8]), "Received block notification"); + info!( + block = hex::encode(&hash[..8]), + "Received block notification" + ); } Ok(Err(_)) => { info!("Block channel closed"); diff --git a/apps/synord/tests/multi_node_network.rs b/apps/synord/tests/multi_node_network.rs index 46d7925..1a3d0f4 100644 --- a/apps/synord/tests/multi_node_network.rs +++ b/apps/synord/tests/multi_node_network.rs @@ -188,7 +188,11 @@ async fn test_three_node_mesh() { info!(total_connections = total, "Network mesh formed"); // In a 3-node mesh, we expect 2-4 total connections (each connection counted twice) - assert!(total >= 2, "Expected at least 2 total connections, got {}", total); + assert!( + total >= 2, + "Expected at least 2 total connections, got {}", + total + ); network.stop_all().await.unwrap(); } @@ -532,7 +536,11 @@ async fn test_simultaneous_node_start() { let net = node.network(); let count = net.peer_count().await; total_connections += count; - info!(node = i, peers = count, "Peer count after simultaneous start"); + info!( + node = i, + peers = count, + "Peer count after simultaneous start" + ); } info!( diff --git a/apps/synord/tests/node_lifecycle.rs b/apps/synord/tests/node_lifecycle.rs index 43dc949..afbfdad 100644 --- a/apps/synord/tests/node_lifecycle.rs +++ b/apps/synord/tests/node_lifecycle.rs @@ -119,7 +119,11 @@ async fn test_node_creation() { assert!(result.is_ok(), "Node creation timed out"); let node_result = result.unwrap(); - assert!(node_result.is_ok(), "Node creation failed: {:?}", node_result.err()); + assert!( + node_result.is_ok(), + "Node creation failed: {:?}", + node_result.err() + ); } #[tokio::test] @@ -185,7 +189,7 @@ async fn test_node_services_accessible() { // Services should be accessible even before start // These return &Arc directly, not Option - let _ = node.storage(); // Storage is always created + let _ = node.storage(); // Storage is always created let _ = node.network(); let _ = node.consensus(); let _ = node.mempool(); diff --git a/apps/synord/tests/reorg_tests.rs b/apps/synord/tests/reorg_tests.rs index 573a6f0..f25afb5 100644 --- a/apps/synord/tests/reorg_tests.rs +++ b/apps/synord/tests/reorg_tests.rs @@ -250,11 +250,19 @@ async fn test_selected_chain_update() { // Log the chain blocks for (i, block) in chain0.iter().enumerate().take(5) { - info!(position = i, block = hex::encode(&block[..8]), "Node 0 chain"); + info!( + position = i, + block = hex::encode(&block[..8]), + "Node 0 chain" + ); } for (i, block) in chain1.iter().enumerate().take(5) { - info!(position = i, block = hex::encode(&block[..8]), "Node 1 chain"); + info!( + position = i, + block = hex::encode(&block[..8]), + "Node 1 chain" + ); } // Chains should be similar after sync diff --git a/apps/synord/tests/sync_protocol.rs b/apps/synord/tests/sync_protocol.rs index 09b4659..c20f348 100644 --- a/apps/synord/tests/sync_protocol.rs +++ b/apps/synord/tests/sync_protocol.rs @@ -274,7 +274,9 @@ async fn test_block_request_response() { if !peers.is_empty() { // Attempt to request blocks (would need actual block hashes) let test_hashes = vec![[0u8; 32]]; // Dummy hash - let result = network_service.request_blocks(&peers[0].id, test_hashes).await; + let result = network_service + .request_blocks(&peers[0].id, test_hashes) + .await; info!(result = ?result.is_ok(), "Block request result"); // Error expected for non-existent hash, but API should work } diff --git a/crates/synor-compiler/src/metadata.rs b/crates/synor-compiler/src/metadata.rs index 1bd323b..308e6e5 100644 --- a/crates/synor-compiler/src/metadata.rs +++ b/crates/synor-compiler/src/metadata.rs @@ -159,14 +159,12 @@ impl ContractAbi { /// Serializes to JSON. pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(self) - .map_err(|e| CompilerError::EncodingError(e.to_string())) + serde_json::to_string_pretty(self).map_err(|e| CompilerError::EncodingError(e.to_string())) } /// Deserializes from JSON. pub fn from_json(json: &str) -> Result { - serde_json::from_str(json) - .map_err(|e| CompilerError::ParseError(e.to_string())) + serde_json::from_str(json).map_err(|e| CompilerError::ParseError(e.to_string())) } } @@ -358,7 +356,9 @@ pub enum TypeInfo { Bytes, /// Fixed-size bytes. - FixedBytes { size: usize }, + FixedBytes { + size: usize, + }, /// Address. Address, @@ -367,22 +367,36 @@ pub enum TypeInfo { Hash256, /// Array. - Array { element: Box }, + Array { + element: Box, + }, /// Fixed-size array. - FixedArray { element: Box, size: usize }, + FixedArray { + element: Box, + size: usize, + }, /// Optional value. - Option { inner: Box }, + Option { + inner: Box, + }, /// Tuple. - Tuple { elements: Vec }, + Tuple { + elements: Vec, + }, /// Struct. - Struct { name: String, fields: Vec<(String, TypeInfo)> }, + Struct { + name: String, + fields: Vec<(String, TypeInfo)>, + }, /// Unknown type. - Unknown { name: String }, + Unknown { + name: String, + }, } impl TypeInfo { @@ -493,7 +507,9 @@ fn parse_producers_section(metadata: &mut ContractMetadata, data: &[u8]) { if data_str.contains("rustc") { // Extract rustc version if present if let Some(start) = data_str.find("rustc") { - let end = data_str[start..].find('\0').unwrap_or(data_str.len() - start); + let end = data_str[start..] + .find('\0') + .unwrap_or(data_str.len() - start); let version = &data_str[start..start + end]; metadata.rust_version = Some(version.to_string()); } @@ -540,7 +556,8 @@ pub fn extract_abi(wasm: &[u8]) -> Result { match payload { Payload::TypeSection(reader) => { for rec_group in reader { - let rec_group = rec_group.map_err(|e| CompilerError::ParseError(e.to_string()))?; + let rec_group = + rec_group.map_err(|e| CompilerError::ParseError(e.to_string()))?; for subtype in rec_group.into_types() { if let wasmparser::CompositeType::Func(func_type) = subtype.composite_type { function_types.push(FunctionType { @@ -562,7 +579,8 @@ pub fn extract_abi(wasm: &[u8]) -> Result { } Payload::FunctionSection(reader) => { for type_idx in reader { - let type_idx = type_idx.map_err(|e| CompilerError::ParseError(e.to_string()))?; + let type_idx = + type_idx.map_err(|e| CompilerError::ParseError(e.to_string()))?; function_type_indices.push(type_idx); } } @@ -773,13 +791,13 @@ mod tests { assert_eq!(TypeInfo::U64.type_name(), "u64"); assert_eq!(TypeInfo::Address.type_name(), "Address"); assert_eq!( - TypeInfo::Array { element: Box::new(TypeInfo::U8) }.type_name(), + TypeInfo::Array { + element: Box::new(TypeInfo::U8) + } + .type_name(), "Vec" ); - assert_eq!( - TypeInfo::FixedBytes { size: 32 }.type_name(), - "[u8; 32]" - ); + assert_eq!(TypeInfo::FixedBytes { size: 32 }.type_name(), "[u8; 32]"); } #[test] diff --git a/crates/synor-compiler/src/optimizer.rs b/crates/synor-compiler/src/optimizer.rs index f01e059..803f4db 100644 --- a/crates/synor-compiler/src/optimizer.rs +++ b/crates/synor-compiler/src/optimizer.rs @@ -260,13 +260,14 @@ impl Optimizer { } // Run wasm-opt - debug!("Running wasm-opt with flags: {:?}", self.level.wasm_opt_flags()); - let output = cmd - .output() - .map_err(|e| CompilerError::ExternalToolError { - tool: "wasm-opt".into(), - message: e.to_string(), - })?; + debug!( + "Running wasm-opt with flags: {:?}", + self.level.wasm_opt_flags() + ); + let output = cmd.output().map_err(|e| CompilerError::ExternalToolError { + tool: "wasm-opt".into(), + message: e.to_string(), + })?; // Check result if !output.status.success() { @@ -388,11 +389,7 @@ mod tests { #[test] fn test_optimizer_creation() { - let optimizer = Optimizer::new( - OptimizationLevel::Size, - StripOptions::default(), - None, - ); + let optimizer = Optimizer::new(OptimizationLevel::Size, StripOptions::default(), None); assert_eq!(optimizer.level, OptimizationLevel::Size); } diff --git a/crates/synor-compiler/src/validator.rs b/crates/synor-compiler/src/validator.rs index cef06ed..6535ffc 100644 --- a/crates/synor-compiler/src/validator.rs +++ b/crates/synor-compiler/src/validator.rs @@ -316,7 +316,8 @@ impl Validator { } Payload::ImportSection(reader) => { for import in reader { - let import = import.map_err(|e| ValidationError::ParseError(e.to_string()))?; + let import = + import.map_err(|e| ValidationError::ParseError(e.to_string()))?; let kind = match import.ty { wasmparser::TypeRef::Func(type_idx) => ImportKind::Function(type_idx), @@ -339,7 +340,8 @@ impl Validator { } Payload::MemorySection(reader) => { for memory in reader { - let memory = memory.map_err(|e| ValidationError::ParseError(e.to_string()))?; + let memory = + memory.map_err(|e| ValidationError::ParseError(e.to_string()))?; memories.push(MemoryInfo { min_pages: memory.initial, max_pages: memory.maximum, @@ -350,7 +352,8 @@ impl Validator { } Payload::GlobalSection(reader) => { for global in reader { - let global = global.map_err(|e| ValidationError::ParseError(e.to_string()))?; + let global = + global.map_err(|e| ValidationError::ParseError(e.to_string()))?; if global.ty.mutable { has_mutable_globals = true; } @@ -358,7 +361,8 @@ impl Validator { } Payload::ExportSection(reader) => { for export in reader { - let export = export.map_err(|e| ValidationError::ParseError(e.to_string()))?; + let export = + export.map_err(|e| ValidationError::ParseError(e.to_string()))?; let kind = match export.kind { wasmparser::ExternalKind::Func => ExportKind::Function, @@ -404,10 +408,14 @@ impl Validator { // Validate entry points (warn if missing, don't error) if !exports.iter().any(|e| e.name == "__synor_init") { - result.warnings.push("Missing __synor_init export - contract cannot be initialized".into()); + result + .warnings + .push("Missing __synor_init export - contract cannot be initialized".into()); } if !exports.iter().any(|e| e.name == "__synor_call") { - result.warnings.push("Missing __synor_call export - contract cannot be called".into()); + result + .warnings + .push("Missing __synor_call export - contract cannot be called".into()); } // Validate entry point signatures @@ -427,12 +435,19 @@ impl Validator { debug!("Validating {} memories", memories.len()); if memories.is_empty() { // Check if memory is imported - let has_imported_memory = result.imports.iter().any(|i| matches!(i.kind, ImportKind::Memory)); + let has_imported_memory = result + .imports + .iter() + .any(|i| matches!(i.kind, ImportKind::Memory)); if !has_imported_memory { - errors.push(ValidationError::MemoryError("No memory defined or imported".into())); + errors.push(ValidationError::MemoryError( + "No memory defined or imported".into(), + )); } } else if memories.len() > 1 { - errors.push(ValidationError::MemoryError("Multiple memories not supported".into())); + errors.push(ValidationError::MemoryError( + "Multiple memories not supported".into(), + )); } else { let mem = &memories[0]; @@ -445,9 +460,9 @@ impl Validator { ))); } } else { - result.warnings.push( - "Memory has no maximum limit - recommended to set max_pages".into(), - ); + result + .warnings + .push("Memory has no maximum limit - recommended to set max_pages".into()); } // Check for shared memory (threads) diff --git a/crates/synor-consensus/benches/consensus_bench.rs b/crates/synor-consensus/benches/consensus_bench.rs index 722c286..be0a039 100644 --- a/crates/synor-consensus/benches/consensus_bench.rs +++ b/crates/synor-consensus/benches/consensus_bench.rs @@ -30,10 +30,7 @@ fn make_outpoint(tx_n: u64, index: u32) -> Outpoint { /// Creates a P2PKH output. fn make_p2pkh_output(amount: u64) -> TxOutput { - TxOutput::new( - Amount::from_sompi(amount), - ScriptPubKey::p2pkh(&[0u8; 32]), - ) + TxOutput::new(Amount::from_sompi(amount), ScriptPubKey::p2pkh(&[0u8; 32])) } /// Creates a UTXO entry. @@ -235,9 +232,7 @@ fn utxo_lookup(c: &mut Criterion) { group.bench_with_input( BenchmarkId::from_parameter(set_size), &(utxo_set, target), - |b, (set, target)| { - b.iter(|| black_box(set.get(target))) - }, + |b, (set, target)| b.iter(|| black_box(set.get(target))), ); } @@ -254,9 +249,7 @@ fn utxo_contains(c: &mut Criterion) { group.bench_with_input( BenchmarkId::from_parameter(set_size), &(utxo_set, target), - |b, (set, target)| { - b.iter(|| black_box(set.contains(target))) - }, + |b, (set, target)| b.iter(|| black_box(set.contains(target))), ); } @@ -328,7 +321,10 @@ fn utxo_diff_apply(c: &mut Criterion) { // Add new entries for i in n..(n + n / 2) { - diff.add(make_outpoint(i as u64, 0), make_utxo_entry(500_000_000, 100, false)); + diff.add( + make_outpoint(i as u64, 0), + make_utxo_entry(500_000_000, 100, false), + ); } (set, diff) @@ -357,14 +353,20 @@ fn utxo_diff_merge(c: &mut Criterion) { let mut diff2 = UtxoDiff::new(); for i in 0..n { - diff1.add(make_outpoint(i as u64, 0), make_utxo_entry(100_000_000, 100, false)); + diff1.add( + make_outpoint(i as u64, 0), + make_utxo_entry(100_000_000, 100, false), + ); } for i in 0..(n / 2) { diff2.remove(make_outpoint(i as u64, 0)); } for i in n..(n * 2) { - diff2.add(make_outpoint(i as u64, 0), make_utxo_entry(200_000_000, 100, false)); + diff2.add( + make_outpoint(i as u64, 0), + make_utxo_entry(200_000_000, 100, false), + ); } (diff1, diff2) @@ -400,9 +402,7 @@ fn utxo_create_tx_diff(c: &mut Criterion) { group.bench_with_input( BenchmarkId::from_parameter(input_count), &(utxo_set, tx), - |b, (set, tx)| { - b.iter(|| black_box(set.create_transaction_diff(tx, 1000))) - }, + |b, (set, tx)| b.iter(|| black_box(set.create_transaction_diff(tx, 1000))), ); } @@ -434,9 +434,7 @@ fn batch_tx_validation(c: &mut Criterion) { let validator = TransactionValidator::new(); for batch_size in [10, 50, 100] { - let transactions: Vec<_> = (0..batch_size) - .map(|_| make_transaction(2, 2)) - .collect(); + let transactions: Vec<_> = (0..batch_size).map(|_| make_transaction(2, 2)).collect(); group.throughput(Throughput::Elements(batch_size as u64)); group.bench_with_input( @@ -486,11 +484,7 @@ criterion_group!( utxo_create_tx_diff, ); -criterion_group!( - misc_benches, - utxo_get_balance, - batch_tx_validation, -); +criterion_group!(misc_benches, utxo_get_balance, batch_tx_validation,); criterion_main!( tx_validation_benches, diff --git a/crates/synor-consensus/src/genesis.rs b/crates/synor-consensus/src/genesis.rs index af7556e..cfd097e 100644 --- a/crates/synor-consensus/src/genesis.rs +++ b/crates/synor-consensus/src/genesis.rs @@ -6,8 +6,8 @@ use synor_types::{ block::BlockBody, transaction::{Outpoint, ScriptPubKey, ScriptType, SubnetworkId}, - Amount, Block, BlockHeader, BlueScore, Hash256, Network, Timestamp, - Transaction, TxInput, TxOutput, + Amount, Block, BlockHeader, BlueScore, Hash256, Network, Timestamp, Transaction, TxInput, + TxOutput, }; /// Chain configuration parameters. @@ -59,23 +59,23 @@ impl ChainConfig { network: Network::Mainnet, genesis, genesis_hash, - target_block_time_ms: 100, // 10 bps + 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 + 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 + bip32_coin_type: 0x5359, // "SY" in hex address_prefix: "synor".to_string(), } } @@ -93,11 +93,11 @@ impl ChainConfig { ghostdag_k: 18, max_block_parents: 64, max_block_mass: 500_000, - coinbase_maturity: 10, // Lower for testing - finality_depth: 1000, // Lower for testing + 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_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(), @@ -118,20 +118,18 @@ impl ChainConfig { network: Network::Devnet, genesis, genesis_hash, - target_block_time_ms: 1000, // 1 second blocks for dev - ghostdag_k: 3, // Smaller K for dev + 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 + initial_difficulty: 0x207fffff, // Minimum difficulty halving_interval: 100, - initial_reward: 1000 * 100_000_000, // Higher reward for dev + 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(), - ], + bootstrap_nodes: vec!["/ip4/127.0.0.1/tcp/16511".to_string()], bip32_coin_type: 0x5359, address_prefix: "dsynor".to_string(), } @@ -147,7 +145,11 @@ impl ChainConfig { } /// Validates a block against chain rules. - pub fn validate_block_header(&self, header: &BlockHeader, _parent_blue_score: u64) -> Result<(), GenesisError> { + 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 @@ -195,7 +197,8 @@ fn create_mainnet_genesis() -> Block { 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"; + let genesis_message = + b"Synor Genesis - The Dawn of Quantum-Secure Decentralized Computing - 2025"; // Create coinbase transaction let coinbase_tx = create_coinbase_transaction( @@ -209,7 +212,7 @@ fn create_mainnet_genesis() -> Block { // Create genesis header let header = BlockHeader { version: 1, - parents: vec![], // Genesis has no parents + parents: vec![], // Genesis has no parents merkle_root: txid, accepted_id_merkle_root: Hash256::ZERO, utxo_commitment: Hash256::ZERO, @@ -342,24 +345,20 @@ fn genesis_allocation_mainnet() -> Vec { /// Returns testnet genesis allocations. fn genesis_allocation_testnet() -> Vec { - vec![ - GenesisAllocation { - address_hash: Hash256::from([0x10; 32]), - amount: Amount::from_synor(1_000_000), - description: "Testnet Faucet".to_string(), - }, - ] + 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 { - vec![ - GenesisAllocation { - address_hash: Hash256::from([0x20; 32]), - amount: Amount::from_synor(100_000_000), - description: "Dev Account".to_string(), - }, - ] + 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. @@ -430,13 +429,11 @@ pub fn mainnet_checkpoints() -> Vec { /// Returns hardcoded checkpoints for testnet. pub fn testnet_checkpoints() -> Vec { - vec![ - Checkpoint { - blue_score: 0, - hash: ChainConfig::testnet().genesis_hash, - timestamp: 1735689600000, - }, - ] + vec![Checkpoint { + blue_score: 0, + hash: ChainConfig::testnet().genesis_hash, + timestamp: 1735689600000, + }] } /// Network magic bytes for message framing. diff --git a/crates/synor-consensus/src/rewards.rs b/crates/synor-consensus/src/rewards.rs index e6eefa6..e58ece8 100644 --- a/crates/synor-consensus/src/rewards.rs +++ b/crates/synor-consensus/src/rewards.rs @@ -220,7 +220,10 @@ impl FeeDistribution { let burn = Amount::from_sompi(fee_sompi * 10 / 100); let stakers = Amount::from_sompi(fee_sompi * 60 / 100); let community = Amount::from_sompi(fee_sompi * 20 / 100); - let miner = fees.saturating_sub(burn).saturating_sub(stakers).saturating_sub(community); + let miner = fees + .saturating_sub(burn) + .saturating_sub(stakers) + .saturating_sub(community); FeeDistribution { burn, diff --git a/crates/synor-consensus/src/utxo.rs b/crates/synor-consensus/src/utxo.rs index f21ff23..e4cc74e 100644 --- a/crates/synor-consensus/src/utxo.rs +++ b/crates/synor-consensus/src/utxo.rs @@ -144,7 +144,10 @@ impl UtxoSet { /// Creates a UTXO set from existing entries. pub fn from_entries(entries: HashMap) -> Self { - let total: Amount = entries.values().map(|e| e.amount()).fold(Amount::ZERO, |a, b| a.saturating_add(b)); + let total: Amount = entries + .values() + .map(|e| e.amount()) + .fold(Amount::ZERO, |a, b| a.saturating_add(b)); let count = entries.len(); UtxoSet { utxos: RwLock::new(entries), @@ -278,7 +281,11 @@ impl UtxoSet { } /// Gets all UTXOs for a given address. - pub fn get_by_address(&self, address: &Address, network: synor_types::Network) -> Vec<(Outpoint, UtxoEntry)> { + pub fn get_by_address( + &self, + address: &Address, + network: synor_types::Network, + ) -> Vec<(Outpoint, UtxoEntry)> { let utxos = self.utxos.read(); let mut result = Vec::new(); @@ -410,7 +417,11 @@ impl VirtualUtxoState { } /// Updates the virtual block and applies necessary diffs. - pub fn update_virtual(&self, new_virtual: Hash256, chain_diffs: Vec) -> Result<(), UtxoError> { + pub fn update_virtual( + &self, + new_virtual: Hash256, + chain_diffs: Vec, + ) -> Result<(), UtxoError> { // Apply all diffs in order for diff in chain_diffs { self.base.apply_diff(&diff)?; @@ -470,10 +481,7 @@ mod tests { fn make_entry(amount: u64) -> UtxoEntry { UtxoEntry::new( - TxOutput::new( - Amount::from_sompi(amount), - ScriptPubKey::p2pkh(&[0u8; 32]), - ), + TxOutput::new(Amount::from_sompi(amount), ScriptPubKey::p2pkh(&[0u8; 32])), 100, false, ) @@ -481,10 +489,7 @@ mod tests { fn make_coinbase_entry(amount: u64, daa_score: u64) -> UtxoEntry { UtxoEntry::new( - TxOutput::new( - Amount::from_sompi(amount), - ScriptPubKey::p2pkh(&[0u8; 32]), - ), + TxOutput::new(Amount::from_sompi(amount), ScriptPubKey::p2pkh(&[0u8; 32])), daa_score, true, ) diff --git a/crates/synor-consensus/src/validation.rs b/crates/synor-consensus/src/validation.rs index 79382ba..4cdb461 100644 --- a/crates/synor-consensus/src/validation.rs +++ b/crates/synor-consensus/src/validation.rs @@ -182,7 +182,11 @@ impl TransactionValidator { } /// Verifies an input's signature script against the UTXO. - fn verify_input_script(&self, input: &TxInput, utxo: &UtxoEntry) -> Result<(), ValidationError> { + fn verify_input_script( + &self, + input: &TxInput, + utxo: &UtxoEntry, + ) -> Result<(), ValidationError> { // Simplified verification - in production would: // 1. Parse the signature script // 2. Execute against the UTXO's script pubkey @@ -282,15 +286,16 @@ impl BlockValidator { } /// Validates proof of work. - pub fn validate_pow(&self, header: &BlockHeader, target: Hash256) -> Result<(), ValidationError> { + pub fn validate_pow( + &self, + header: &BlockHeader, + target: Hash256, + ) -> Result<(), ValidationError> { let hash = header.block_id(); // Check hash is below target if hash > target { - return Err(ValidationError::InsufficientPow { - hash, - target, - }); + return Err(ValidationError::InsufficientPow { hash, target }); } Ok(()) @@ -340,9 +345,11 @@ impl BlockValidator { // Validate against UTXOs (skip coinbase) if i > 0 { - let fee = self - .tx_validator - .validate_against_utxos(tx, utxo_set, block.header.daa_score)?; + let fee = self.tx_validator.validate_against_utxos( + tx, + utxo_set, + block.header.daa_score, + )?; total_fees = total_fees.saturating_add(fee); } @@ -468,7 +475,10 @@ pub enum ValidationError { InvalidCoinbaseAmount { output: Amount, max: Amount }, #[error("Invalid merkle root: expected {expected}, computed {computed}")] - InvalidMerkleRoot { expected: Hash256, computed: Hash256 }, + InvalidMerkleRoot { + expected: Hash256, + computed: Hash256, + }, #[error("UTXO error: {0}")] UtxoError(#[from] UtxoError), @@ -481,10 +491,7 @@ mod tests { use synor_types::TxOutput; fn make_p2pkh_output(amount: u64) -> TxOutput { - TxOutput::new( - Amount::from_sompi(amount), - ScriptPubKey::p2pkh(&[0u8; 32]), - ) + TxOutput::new(Amount::from_sompi(amount), ScriptPubKey::p2pkh(&[0u8; 32])) } #[test] diff --git a/crates/synor-contract-test/src/environment.rs b/crates/synor-contract-test/src/environment.rs index 807d41c..cbe31ef 100644 --- a/crates/synor-contract-test/src/environment.rs +++ b/crates/synor-contract-test/src/environment.rs @@ -163,7 +163,12 @@ impl TestEnvironment { } // Create execution context - let call = CallContext::new(contract_id, deployer_addr.clone(), value, init_params.to_vec()); + let call = CallContext::new( + contract_id, + deployer_addr.clone(), + value, + init_params.to_vec(), + ); let mut storage = MemoryStorage::new(); // Copy existing storage for this contract if any diff --git a/crates/synor-contract-test/src/mock_storage.rs b/crates/synor-contract-test/src/mock_storage.rs index 85d30ea..7ab3921 100644 --- a/crates/synor-contract-test/src/mock_storage.rs +++ b/crates/synor-contract-test/src/mock_storage.rs @@ -100,11 +100,7 @@ impl MockStorage { /// Gets the number of storage entries for a contract. pub fn len(&self, contract: &ContractId) -> usize { - self.data - .read() - .get(contract) - .map(|m| m.len()) - .unwrap_or(0) + self.data.read().get(contract).map(|m| m.len()).unwrap_or(0) } /// Checks if a contract has any storage. @@ -362,7 +358,10 @@ impl StorageSnapshot { } /// Gets storage entries for a contract. - pub fn get_contract(&self, contract: &ContractId) -> Option<&HashMap> { + pub fn get_contract( + &self, + contract: &ContractId, + ) -> Option<&HashMap> { self.data.get(contract) } } diff --git a/crates/synor-contract-test/src/test_account.rs b/crates/synor-contract-test/src/test_account.rs index a6b8937..2d56853 100644 --- a/crates/synor-contract-test/src/test_account.rs +++ b/crates/synor-contract-test/src/test_account.rs @@ -262,8 +262,7 @@ impl TestAccountBuilder { /// Adds a new account with a specific balance. pub fn add_account(mut self, balance: u64) -> Self { - let account = TestAccount::from_index(self.accounts.len() as u64) - .set_balance(balance); + let account = TestAccount::from_index(self.accounts.len() as u64).set_balance(balance); self.accounts.push(account); self } @@ -271,8 +270,7 @@ impl TestAccountBuilder { /// Adds multiple accounts with the same balance. pub fn add_accounts(mut self, count: usize, balance: u64) -> Self { for _ in 0..count { - let account = TestAccount::from_index(self.accounts.len() as u64) - .set_balance(balance); + let account = TestAccount::from_index(self.accounts.len() as u64).set_balance(balance); self.accounts.push(account); } self diff --git a/crates/synor-crypto/benches/crypto_bench.rs b/crates/synor-crypto/benches/crypto_bench.rs index 40e87b2..4b684fe 100644 --- a/crates/synor-crypto/benches/crypto_bench.rs +++ b/crates/synor-crypto/benches/crypto_bench.rs @@ -238,11 +238,7 @@ criterion_group!( criterion_group!(hash_benches, blake3_hash_benchmark, blake3_hash_large,); -criterion_group!( - misc_benches, - address_derivation_benchmark, - signature_sizes, -); +criterion_group!(misc_benches, address_derivation_benchmark, signature_sizes,); criterion_main!( keygen_benches, diff --git a/crates/synor-crypto/src/kdf.rs b/crates/synor-crypto/src/kdf.rs index 6527016..198ed09 100644 --- a/crates/synor-crypto/src/kdf.rs +++ b/crates/synor-crypto/src/kdf.rs @@ -58,13 +58,19 @@ pub fn derive_ed25519_key(master_seed: &[u8], account: u32) -> Result Result { +pub fn derive_dilithium_seed( + master_seed: &[u8], + account: u32, +) -> Result { let info = [domain::DILITHIUM_KEY, &account.to_be_bytes()].concat(); derive_key(master_seed, b"", &info, 32) } /// Derives a 32-byte encryption key. -pub fn derive_encryption_key(master_seed: &[u8], purpose: &[u8]) -> Result { +pub fn derive_encryption_key( + master_seed: &[u8], + purpose: &[u8], +) -> Result { let info = [domain::ENCRYPTION_KEY, purpose].concat(); derive_key(master_seed, b"", &info, 32) } @@ -81,8 +87,8 @@ pub fn derive_child_key( return Err(DeriveKeyError::InvalidKeyLength); } - let mut hmac = Hmac::::new_from_slice(chain_code) - .map_err(|_| DeriveKeyError::HmacError)?; + let mut hmac = + Hmac::::new_from_slice(chain_code).map_err(|_| DeriveKeyError::HmacError)?; // For hardened keys (index >= 0x80000000), use parent key // For normal keys, use parent public key (not implemented here for simplicity) diff --git a/crates/synor-crypto/src/keypair.rs b/crates/synor-crypto/src/keypair.rs index c3a40ed..f91788c 100644 --- a/crates/synor-crypto/src/keypair.rs +++ b/crates/synor-crypto/src/keypair.rs @@ -3,9 +3,7 @@ //! Provides hybrid keypairs combining Ed25519 and Dilithium3 for quantum resistance. use crate::{mnemonic::Mnemonic, signature::HybridSignature, Address, Hash256, Network}; -use ed25519_dalek::{ - Signer, SigningKey as Ed25519SigningKey, VerifyingKey as Ed25519VerifyingKey, -}; +use ed25519_dalek::{Signer, SigningKey as Ed25519SigningKey, VerifyingKey as Ed25519VerifyingKey}; use pqcrypto_dilithium::dilithium3; use pqcrypto_traits::sign::{PublicKey as PqPublicKey, SignedMessage as PqSignedMessage}; use rand::rngs::OsRng; @@ -80,7 +78,7 @@ impl Dilithium3Keypair { /// Creates a keypair from seed bytes using deterministic key generation. pub fn from_seed(seed: &[u8; 32]) -> Self { // Use SHAKE256 to expand seed to required size - use sha3::{Shake256, digest::Update}; + use sha3::{digest::Update, Shake256}; let mut hasher = Shake256::default(); hasher.update(b"synor-dilithium3-keygen"); @@ -170,7 +168,8 @@ impl PublicKey { let ed_verifying_key = Ed25519VerifyingKey::from_bytes(&self.ed25519) .map_err(|_| KeypairError::InvalidPublicKey)?; - let ed_sig_array = signature.ed25519_array() + let ed_sig_array = signature + .ed25519_array() .ok_or(KeypairError::InvalidSignature)?; let ed_sig = ed25519_dalek::Signature::from_bytes(&ed_sig_array); ed_verifying_key diff --git a/crates/synor-crypto/src/lib.rs b/crates/synor-crypto/src/lib.rs index d7064f5..89a728b 100644 --- a/crates/synor-crypto/src/lib.rs +++ b/crates/synor-crypto/src/lib.rs @@ -87,15 +87,15 @@ #![allow(dead_code)] -pub mod keypair; -pub mod signature; -pub mod mnemonic; pub mod kdf; +pub mod keypair; +pub mod mnemonic; +pub mod signature; -pub use keypair::{HybridKeypair, Ed25519Keypair, PublicKey, SecretKey}; -pub use signature::{HybridSignature, Signature, SignatureError}; -pub use mnemonic::{Mnemonic, MnemonicError}; pub use kdf::{derive_key, DeriveKeyError}; +pub use keypair::{Ed25519Keypair, HybridKeypair, PublicKey, SecretKey}; +pub use mnemonic::{Mnemonic, MnemonicError}; +pub use signature::{HybridSignature, Signature, SignatureError}; /// Re-export common types pub use synor_types::{Address, Hash256, Network}; diff --git a/crates/synor-crypto/src/mnemonic.rs b/crates/synor-crypto/src/mnemonic.rs index 9b387d8..3cfe42b 100644 --- a/crates/synor-crypto/src/mnemonic.rs +++ b/crates/synor-crypto/src/mnemonic.rs @@ -94,7 +94,13 @@ impl std::fmt::Display for Mnemonic { // Only show first and last word for security let words: Vec<&str> = self.words(); if words.len() >= 2 { - write!(f, "{} ... {} ({} words)", words[0], words[words.len() - 1], words.len()) + write!( + f, + "{} ... {} ({} words)", + words[0], + words[words.len() - 1], + words.len() + ) } else { write!(f, "[invalid mnemonic]") } diff --git a/crates/synor-dag/benches/ghostdag_bench.rs b/crates/synor-dag/benches/ghostdag_bench.rs index 71f7e20..ccd4b54 100644 --- a/crates/synor-dag/benches/ghostdag_bench.rs +++ b/crates/synor-dag/benches/ghostdag_bench.rs @@ -11,9 +11,7 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::sync::Arc; -use synor_dag::{ - BlockDag, BlockId, GhostdagManager, ReachabilityStore, GHOSTDAG_K, -}; +use synor_dag::{BlockDag, BlockId, GhostdagManager, ReachabilityStore, GHOSTDAG_K}; use synor_types::Hash256; /// Helper to create deterministic block IDs. @@ -46,7 +44,8 @@ fn build_linear_chain( let block = make_block_id(i as u64); let parent = blocks[i - 1]; - dag.insert_block(block, vec![parent], i as u64 * 100).unwrap(); + dag.insert_block(block, vec![parent], i as u64 * 100) + .unwrap(); reachability.add_block(block, parent, &[parent]).unwrap(); ghostdag.add_block(block, &[parent]).unwrap(); blocks.push(block); @@ -77,8 +76,11 @@ fn build_wide_dag( let parents: Vec<_> = layers[d - 1].iter().copied().collect(); let selected_parent = parents[0]; - dag.insert_block(block, parents.clone(), block_num * 100).unwrap(); - reachability.add_block(block, selected_parent, &parents).unwrap(); + dag.insert_block(block, parents.clone(), block_num * 100) + .unwrap(); + reachability + .add_block(block, selected_parent, &parents) + .unwrap(); ghostdag.add_block(block, &parents).unwrap(); layer.push(block); @@ -113,8 +115,11 @@ fn block_insertion_linear(c: &mut Criterion) { let new_block = make_block_id(n as u64); let parent = *blocks.last().unwrap(); - dag.insert_block(new_block, vec![parent], n as u64 * 100).unwrap(); - reachability.add_block(new_block, parent, &[parent]).unwrap(); + dag.insert_block(new_block, vec![parent], n as u64 * 100) + .unwrap(); + reachability + .add_block(new_block, parent, &[parent]) + .unwrap(); black_box(ghostdag.add_block(new_block, &[parent]).unwrap()) }, criterion::BatchSize::SmallInput, @@ -146,8 +151,11 @@ fn block_insertion_wide(c: &mut Criterion) { let parents: Vec<_> = layers.last().unwrap().iter().copied().collect(); let selected_parent = parents[0]; - dag.insert_block(new_block, parents.clone(), 99999 * 100).unwrap(); - reachability.add_block(new_block, selected_parent, &parents).unwrap(); + dag.insert_block(new_block, parents.clone(), 99999 * 100) + .unwrap(); + reachability + .add_block(new_block, selected_parent, &parents) + .unwrap(); black_box(ghostdag.add_block(new_block, &parents).unwrap()) }, criterion::BatchSize::SmallInput, @@ -269,8 +277,11 @@ fn merge_set_calculation(c: &mut Criterion) { let parents: Vec<_> = layers.last().unwrap().iter().copied().collect(); let selected_parent = parents[0]; - dag.insert_block(new_block, parents.clone(), 99999 * 100).unwrap(); - reachability.add_block(new_block, selected_parent, &parents).unwrap(); + dag.insert_block(new_block, parents.clone(), 99999 * 100) + .unwrap(); + reachability + .add_block(new_block, selected_parent, &parents) + .unwrap(); let data = ghostdag.add_block(new_block, &parents).unwrap(); black_box(data.merge_set_size()) }, @@ -294,7 +305,8 @@ fn k_cluster_validation(c: &mut Criterion) { let genesis = make_block_id(0); let dag = Arc::new(BlockDag::new(genesis, 0)); let reachability = Arc::new(ReachabilityStore::new(genesis)); - let ghostdag = GhostdagManager::with_k(dag.clone(), reachability.clone(), k_val); + let ghostdag = + GhostdagManager::with_k(dag.clone(), reachability.clone(), k_val); build_wide_dag(5, k_val as usize, &dag, &reachability, &ghostdag); (dag, reachability, ghostdag) }, @@ -354,9 +366,7 @@ fn dag_tips_query(c: &mut Criterion) { let (dag, reachability, ghostdag) = setup_dag(); build_wide_dag(10, 4, &dag, &reachability, &ghostdag); - c.bench_function("dag_get_tips", |b| { - b.iter(|| black_box(dag.tips())) - }); + c.bench_function("dag_get_tips", |b| b.iter(|| black_box(dag.tips()))); } // ==================== Criterion Groups ==================== @@ -367,16 +377,9 @@ criterion_group!( block_insertion_wide, ); -criterion_group!( - score_benches, - blue_score_calculation, - blue_score_batch, -); +criterion_group!(score_benches, blue_score_calculation, blue_score_batch,); -criterion_group!( - chain_benches, - selected_chain_traversal, -); +criterion_group!(chain_benches, selected_chain_traversal,); criterion_group!( reachability_benches, @@ -384,11 +387,7 @@ criterion_group!( reachability_anticone_check, ); -criterion_group!( - merge_benches, - merge_set_calculation, - k_cluster_validation, -); +criterion_group!(merge_benches, merge_set_calculation, k_cluster_validation,); criterion_group!( data_access_benches, diff --git a/crates/synor-dag/src/dag.rs b/crates/synor-dag/src/dag.rs index 0e359aa..362bec0 100644 --- a/crates/synor-dag/src/dag.rs +++ b/crates/synor-dag/src/dag.rs @@ -449,8 +449,7 @@ mod tests { let dag = BlockDag::new(genesis_id, 0); let block1 = make_block_id(1); - dag.insert_block(block1, vec![genesis_id], 100) - .unwrap(); + dag.insert_block(block1, vec![genesis_id], 100).unwrap(); assert_eq!(dag.len(), 2); assert!(dag.contains(&block1)); @@ -471,8 +470,7 @@ mod tests { // Add a child block let block1 = make_block_id(1); - dag.insert_block(block1, vec![genesis_id], 100) - .unwrap(); + dag.insert_block(block1, vec![genesis_id], 100).unwrap(); // Now only block1 is a tip assert_eq!(dag.tips().len(), 1); @@ -481,8 +479,7 @@ mod tests { // Add another block with genesis as parent (parallel mining) let block2 = make_block_id(2); - dag.insert_block(block2, vec![genesis_id], 100) - .unwrap(); + dag.insert_block(block2, vec![genesis_id], 100).unwrap(); // Now we have two tips assert_eq!(dag.tips().len(), 2); @@ -498,15 +495,12 @@ mod tests { let block1 = make_block_id(1); let block2 = make_block_id(2); - dag.insert_block(block1, vec![genesis_id], 100) - .unwrap(); - dag.insert_block(block2, vec![genesis_id], 100) - .unwrap(); + dag.insert_block(block1, vec![genesis_id], 100).unwrap(); + dag.insert_block(block2, vec![genesis_id], 100).unwrap(); // Block with multiple parents let block3 = make_block_id(3); - dag.insert_block(block3, vec![block1, block2], 200) - .unwrap(); + dag.insert_block(block3, vec![block1, block2], 200).unwrap(); let parents = dag.get_parents(&block3).unwrap(); assert_eq!(parents.len(), 2); @@ -523,12 +517,9 @@ mod tests { let block2 = make_block_id(2); let block3 = make_block_id(3); - dag.insert_block(block1, vec![genesis_id], 100) - .unwrap(); - dag.insert_block(block2, vec![block1], 200) - .unwrap(); - dag.insert_block(block3, vec![block2], 300) - .unwrap(); + dag.insert_block(block1, vec![genesis_id], 100).unwrap(); + dag.insert_block(block2, vec![block1], 200).unwrap(); + dag.insert_block(block3, vec![block2], 300).unwrap(); let ancestors = dag.get_ancestors(&block3, 10); assert!(ancestors.contains(&block2)); @@ -542,8 +533,7 @@ mod tests { let dag = BlockDag::new(genesis_id, 0); let block1 = make_block_id(1); - dag.insert_block(block1, vec![genesis_id], 100) - .unwrap(); + dag.insert_block(block1, vec![genesis_id], 100).unwrap(); // Try to insert same block again let result = dag.insert_block(block1, vec![genesis_id], 100); @@ -571,12 +561,9 @@ mod tests { let block2 = make_block_id(2); let block3 = make_block_id(3); - dag.insert_block(block1, vec![genesis_id], 100) - .unwrap(); - dag.insert_block(block2, vec![block1], 200) - .unwrap(); - dag.insert_block(block3, vec![block2], 300) - .unwrap(); + dag.insert_block(block1, vec![genesis_id], 100).unwrap(); + dag.insert_block(block2, vec![block1], 200).unwrap(); + dag.insert_block(block3, vec![block2], 300).unwrap(); let order = dag.iter_topological(); diff --git a/crates/synor-dag/src/ghostdag.rs b/crates/synor-dag/src/ghostdag.rs index 9fca557..a82f701 100644 --- a/crates/synor-dag/src/ghostdag.rs +++ b/crates/synor-dag/src/ghostdag.rs @@ -264,8 +264,9 @@ impl GhostdagManager { ) -> Result<(Vec, Vec, HashMap), GhostdagError> { let mut blues: Vec = Vec::with_capacity(merge_set.len()); let mut reds: Vec = Vec::new(); - let mut blues_anticone_sizes: HashMap = - HashMap::with_capacity(merge_set.len() + selected_parent_data.blues_anticone_sizes.len()); + let mut blues_anticone_sizes: HashMap = HashMap::with_capacity( + merge_set.len() + selected_parent_data.blues_anticone_sizes.len(), + ); // Initialize with selected parent's blues anticone sizes blues_anticone_sizes.extend(selected_parent_data.blues_anticone_sizes.iter()); @@ -378,7 +379,8 @@ impl GhostdagManager { new_blue: &BlockId, current_blues: &[BlockId], ) -> Result { - let (_, anticone_blues) = self.calculate_blues_anticone_optimized(new_blue, current_blues)?; + let (_, anticone_blues) = + self.calculate_blues_anticone_optimized(new_blue, current_blues)?; // Build a temporary anticone sizes map for the check let mut temp_sizes = HashMap::new(); for blue in current_blues { @@ -483,7 +485,11 @@ mod tests { Hash256::from_bytes(bytes) } - fn setup_test_dag() -> (std::sync::Arc, std::sync::Arc, GhostdagManager) { + fn setup_test_dag() -> ( + std::sync::Arc, + std::sync::Arc, + GhostdagManager, + ) { let genesis = make_block_id(0); let dag = std::sync::Arc::new(BlockDag::new(genesis, 0)); let reachability = std::sync::Arc::new(ReachabilityStore::new(genesis)); @@ -550,7 +556,9 @@ mod tests { // Now create a block with both branches as parents let block4 = make_block_id(4); dag.insert_block(block4, vec![block3, block2], 300).unwrap(); - reachability.add_block(block4, block3, &[block3, block2]).unwrap(); + reachability + .add_block(block4, block3, &[block3, block2]) + .unwrap(); let data4 = ghostdag.add_block(block4, &[block3, block2]).unwrap(); // Block3 should be selected parent (higher chain) @@ -565,7 +573,8 @@ mod tests { let mut current = genesis; for i in 1..=5 { let block = make_block_id(i); - dag.insert_block(block, vec![current], i as u64 * 100).unwrap(); + dag.insert_block(block, vec![current], i as u64 * 100) + .unwrap(); reachability.add_block(block, current, &[current]).unwrap(); let data = ghostdag.add_block(block, &[current]).unwrap(); diff --git a/crates/synor-dag/src/lib.rs b/crates/synor-dag/src/lib.rs index c5a81f2..11e8a35 100644 --- a/crates/synor-dag/src/lib.rs +++ b/crates/synor-dag/src/lib.rs @@ -23,16 +23,16 @@ #![allow(dead_code)] pub mod dag; -pub mod reachability; pub mod ghostdag; pub mod ordering; pub mod pruning; +pub mod reachability; -pub use dag::{BlockDag, DagError, BlockRelations}; -pub use reachability::{ReachabilityStore, ReachabilityError}; -pub use ghostdag::{GhostdagManager, GhostdagData, GhostdagError}; +pub use dag::{BlockDag, BlockRelations, DagError}; +pub use ghostdag::{GhostdagData, GhostdagError, GhostdagManager}; pub use ordering::{BlockOrdering, OrderedBlock}; -pub use pruning::{PruningManager, PruningConfig}; +pub use pruning::{PruningConfig, PruningManager}; +pub use reachability::{ReachabilityError, ReachabilityStore}; use synor_types::Hash256; diff --git a/crates/synor-dag/src/ordering.rs b/crates/synor-dag/src/ordering.rs index bbc46e7..523d494 100644 --- a/crates/synor-dag/src/ordering.rs +++ b/crates/synor-dag/src/ordering.rs @@ -8,10 +8,7 @@ //! 3. Within the merge set, blues come before reds //! 4. Within blues/reds, ordering is by blue score then hash -use crate::{ - ghostdag::GhostdagManager, - BlockId, BlueScore, -}; +use crate::{ghostdag::GhostdagManager, BlockId, BlueScore}; use std::cmp::Ordering; use thiserror::Error; @@ -122,11 +119,7 @@ impl BlockOrdering { // Add sorted merge blocks for (block_id, blue_score, is_blue) in merge_blocks { ordering.push(OrderedBlock::new( - block_id, - position, - blue_score, - is_blue, - true, // Is a merge block + block_id, position, blue_score, is_blue, true, // Is a merge block )); position += 1; } @@ -138,7 +131,11 @@ impl BlockOrdering { /// Gets only the blocks that need to be processed between two points. /// /// Returns blocks that are in `to`'s past but not in `from`'s past. - pub fn get_diff(&self, from: &BlockId, to: &BlockId) -> Result, OrderingError> { + pub fn get_diff( + &self, + from: &BlockId, + to: &BlockId, + ) -> Result, OrderingError> { let from_ordering = self.get_ordering(from)?; let to_ordering = self.get_ordering(to)?; @@ -164,14 +161,17 @@ impl BlockOrdering { match (pos_a, pos_b) { (Some(pa), Some(pb)) => Ok(pa < pb), - (Some(_), None) => Ok(true), // A is in past of B + (Some(_), None) => Ok(true), // A is in past of B (None, Some(_)) => Ok(false), // A is not in past of B (None, None) => Err(OrderingError::BlocksNotRelated(*a, *b)), } } /// Gets the merge set at a specific block in canonical order. - pub fn get_merge_set_ordered(&self, block_id: &BlockId) -> Result, OrderingError> { + pub fn get_merge_set_ordered( + &self, + block_id: &BlockId, + ) -> Result, OrderingError> { let data = self.ghostdag.get_data(block_id)?; let mut merge_blocks = Vec::new(); let mut position = 0u64; @@ -191,15 +191,13 @@ impl BlockOrdering { } // Sort by blue score and hash - merge_blocks.sort_by(|a, b| { - match (a.is_blue, b.is_blue) { - (true, false) => Ordering::Less, - (false, true) => Ordering::Greater, - _ => match b.blue_score.cmp(&a.blue_score) { - Ordering::Equal => a.id.cmp(&b.id), - other => other, - }, - } + merge_blocks.sort_by(|a, b| match (a.is_blue, b.is_blue) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => match b.blue_score.cmp(&a.blue_score) { + Ordering::Equal => a.id.cmp(&b.id), + other => other, + }, }); // Update positions after sorting @@ -333,10 +331,8 @@ mod tests { let block1 = make_block_id(1); let block2 = make_block_id(2); - dag.insert_block(block1, vec![genesis], 100) - .unwrap(); - dag.insert_block(block2, vec![block1], 200) - .unwrap(); + dag.insert_block(block1, vec![genesis], 100).unwrap(); + dag.insert_block(block2, vec![block1], 200).unwrap(); reachability.add_block(block1, genesis, &[genesis]).unwrap(); reachability.add_block(block2, block1, &[block1]).unwrap(); @@ -361,10 +357,8 @@ mod tests { let block1 = make_block_id(1); let block2 = make_block_id(2); - dag.insert_block(block1, vec![genesis], 100) - .unwrap(); - dag.insert_block(block2, vec![block1], 200) - .unwrap(); + dag.insert_block(block1, vec![genesis], 100).unwrap(); + dag.insert_block(block2, vec![block1], 200).unwrap(); reachability.add_block(block1, genesis, &[genesis]).unwrap(); reachability.add_block(block2, block1, &[block1]).unwrap(); @@ -383,8 +377,7 @@ mod tests { let (dag, reachability, ghostdag, ordering) = setup_test(); let block1 = make_block_id(1); - dag.insert_block(block1, vec![genesis], 100) - .unwrap(); + dag.insert_block(block1, vec![genesis], 100).unwrap(); reachability.add_block(block1, genesis, &[genesis]).unwrap(); ghostdag.add_block(block1, &[genesis]).unwrap(); diff --git a/crates/synor-dag/src/reachability.rs b/crates/synor-dag/src/reachability.rs index 65cd1cc..83e3cc0 100644 --- a/crates/synor-dag/src/reachability.rs +++ b/crates/synor-dag/src/reachability.rs @@ -114,7 +114,11 @@ impl ReachabilityStore { /// Checks if block `ancestor` is in the past of block `descendant`. /// Returns true if ancestor is an ancestor of descendant. - pub fn is_ancestor(&self, ancestor: &BlockId, descendant: &BlockId) -> Result { + pub fn is_ancestor( + &self, + ancestor: &BlockId, + descendant: &BlockId, + ) -> Result { if ancestor == descendant { return Ok(true); // A block is its own ancestor } @@ -132,7 +136,11 @@ impl ReachabilityStore { } /// Checks if two blocks are in each other's anticone (neither is ancestor of other). - pub fn is_anticone(&self, block_a: &BlockId, block_b: &BlockId) -> Result { + pub fn is_anticone( + &self, + block_a: &BlockId, + block_b: &BlockId, + ) -> Result { if block_a == block_b { return Ok(false); } @@ -171,11 +179,8 @@ impl ReachabilityStore { let (new_interval, parent_needs_realloc) = self.allocate_interval(&parent_data, &data)?; // Create new block's data - let new_data = ReachabilityData::new( - new_interval, - Some(selected_parent), - parent_data.height + 1, - ); + let new_data = + ReachabilityData::new(new_interval, Some(selected_parent), parent_data.height + 1); // Update parent's tree children if let Some(parent) = data.get_mut(&selected_parent) { diff --git a/crates/synor-governance/src/dao.rs b/crates/synor-governance/src/dao.rs index 709feca..a6a9e0b 100644 --- a/crates/synor-governance/src/dao.rs +++ b/crates/synor-governance/src/dao.rs @@ -32,8 +32,8 @@ //! ``` use borsh::{BorshDeserialize, BorshSerialize}; -use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use synor_types::{Address, Hash256}; use thiserror::Error; @@ -145,10 +145,7 @@ pub enum ProposalType { options: Vec, }, /// Custom proposal with arbitrary data. - Custom { - action_type: String, - data: Vec, - }, + Custom { action_type: String, data: Vec }, } /// Council member action. @@ -160,7 +157,9 @@ pub enum CouncilAction { } /// State of a proposal. -#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] pub enum ProposalState { /// Proposal is pending (voting hasn't started). Pending, @@ -183,13 +182,18 @@ impl ProposalState { pub fn is_final(&self) -> bool { matches!( self, - ProposalState::Executed | ProposalState::Cancelled | ProposalState::Expired | ProposalState::Defeated + ProposalState::Executed + | ProposalState::Cancelled + | ProposalState::Expired + | ProposalState::Defeated ) } } /// Vote choice. -#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] pub enum VoteChoice { /// Vote in favor. Yes, @@ -401,7 +405,9 @@ impl Proposal { match self.state { ProposalState::Pending => Some(self.voting_starts_block.saturating_sub(current_block)), ProposalState::Active => Some(self.voting_ends_block.saturating_sub(current_block)), - ProposalState::Passed => Some(self.execution_allowed_block.saturating_sub(current_block)), + ProposalState::Passed => { + Some(self.execution_allowed_block.saturating_sub(current_block)) + } _ => None, } } @@ -581,7 +587,8 @@ impl DAO { current_block: u64, reason: Option, ) -> Result<(), DaoError> { - let proposal = self.proposals + let proposal = self + .proposals .get_mut(proposal_id) .ok_or(DaoError::ProposalNotFound)?; @@ -644,7 +651,8 @@ impl DAO { _executor: &Address, current_block: u64, ) -> Result<&Proposal, DaoError> { - let proposal = self.proposals + let proposal = self + .proposals .get_mut(proposal_id) .ok_or(DaoError::ProposalNotFound)?; @@ -683,7 +691,8 @@ impl DAO { proposal_id: &ProposalId, canceller: &Address, ) -> Result<(), DaoError> { - let proposal = self.proposals + let proposal = self + .proposals .get_mut(proposal_id) .ok_or(DaoError::ProposalNotFound)?; @@ -713,11 +722,7 @@ impl DAO { pub fn get_by_proposer(&self, proposer: &Address) -> Vec<&Proposal> { self.by_proposer .get(proposer) - .map(|ids| { - ids.iter() - .filter_map(|id| self.proposals.get(id)) - .collect() - }) + .map(|ids| ids.iter().filter_map(|id| self.proposals.get(id)).collect()) .unwrap_or_default() } @@ -726,8 +731,8 @@ impl DAO { self.proposals .values() .filter(|p| { - p.state == ProposalState::Active || - (p.state == ProposalState::Pending && current_block >= p.voting_starts_block) + p.state == ProposalState::Active + || (p.state == ProposalState::Pending && current_block >= p.voting_starts_block) }) .collect() } @@ -753,10 +758,22 @@ impl DAO { DaoStats { total_proposals: proposals.len() as u64, - active_proposals: proposals.iter().filter(|p| p.state == ProposalState::Active).count() as u64, - passed_proposals: proposals.iter().filter(|p| p.state == ProposalState::Passed || p.state == ProposalState::Executed).count() as u64, - defeated_proposals: proposals.iter().filter(|p| p.state == ProposalState::Defeated).count() as u64, - executed_proposals: proposals.iter().filter(|p| p.state == ProposalState::Executed).count() as u64, + active_proposals: proposals + .iter() + .filter(|p| p.state == ProposalState::Active) + .count() as u64, + passed_proposals: proposals + .iter() + .filter(|p| p.state == ProposalState::Passed || p.state == ProposalState::Executed) + .count() as u64, + defeated_proposals: proposals + .iter() + .filter(|p| p.state == ProposalState::Defeated) + .count() as u64, + executed_proposals: proposals + .iter() + .filter(|p| p.state == ProposalState::Executed) + .count() as u64, total_votes_cast: proposals.iter().map(|p| p.votes.len()).sum::() as u64, council_members: self.council.len(), } @@ -797,19 +814,21 @@ mod tests { let mut dao = DAO::new(VotingConfig::fast()); let proposer = test_address(1); - let id = dao.create_proposal( - proposer, - 1_000_000 * 100_000_000, // 1M tokens - ProposalType::Signaling { - title: "Test".to_string(), - description: "Test proposal".to_string(), - options: vec!["Yes".to_string(), "No".to_string()], - }, - "Test Proposal".to_string(), - "This is a test".to_string(), - 100, - 70_000_000 * 100_000_000, - ).unwrap(); + let id = dao + .create_proposal( + proposer, + 1_000_000 * 100_000_000, // 1M tokens + ProposalType::Signaling { + title: "Test".to_string(), + description: "Test proposal".to_string(), + options: vec!["Yes".to_string(), "No".to_string()], + }, + "Test Proposal".to_string(), + "This is a test".to_string(), + 100, + 70_000_000 * 100_000_000, + ) + .unwrap(); let proposal = dao.get_proposal(&id).unwrap(); assert_eq!(proposal.state, ProposalState::Pending); @@ -845,27 +864,38 @@ mod tests { let voter2 = test_address(3); // Create proposal - let id = dao.create_proposal( - proposer, - 1_000_000 * 100_000_000, - ProposalType::Signaling { - title: "Test".to_string(), - description: "Test".to_string(), - options: vec![], - }, - "Test".to_string(), - "Test".to_string(), - 0, - 70_000_000 * 100_000_000, - ).unwrap(); + let id = dao + .create_proposal( + proposer, + 1_000_000 * 100_000_000, + ProposalType::Signaling { + title: "Test".to_string(), + description: "Test".to_string(), + options: vec![], + }, + "Test".to_string(), + "Test".to_string(), + 0, + 70_000_000 * 100_000_000, + ) + .unwrap(); // Try voting before start let result = dao.vote(&id, voter1.clone(), 100_000, VoteChoice::Yes, 5, None); assert!(matches!(result, Err(DaoError::VotingNotStarted))); // Vote after start - dao.vote(&id, voter1.clone(), 1_000_000_000, VoteChoice::Yes, 15, None).unwrap(); - dao.vote(&id, voter2, 500_000_000, VoteChoice::No, 16, None).unwrap(); + dao.vote( + &id, + voter1.clone(), + 1_000_000_000, + VoteChoice::Yes, + 15, + None, + ) + .unwrap(); + dao.vote(&id, voter2, 500_000_000, VoteChoice::No, 16, None) + .unwrap(); let proposal = dao.get_proposal(&id).unwrap(); assert_eq!(proposal.votes.len(), 2); @@ -888,22 +918,25 @@ mod tests { let mut dao = DAO::new(VotingConfig::fast()); let proposer = test_address(1); - let id = dao.create_proposal( - proposer.clone(), - 1_000_000 * 100_000_000, - ProposalType::Signaling { - title: "Test".to_string(), - description: "Test".to_string(), - options: vec![], - }, - "Test".to_string(), - "Test".to_string(), - 0, - 100 * 100_000_000, // Small total supply for easy quorum - ).unwrap(); + let id = dao + .create_proposal( + proposer.clone(), + 1_000_000 * 100_000_000, + ProposalType::Signaling { + title: "Test".to_string(), + description: "Test".to_string(), + options: vec![], + }, + "Test".to_string(), + "Test".to_string(), + 0, + 100 * 100_000_000, // Small total supply for easy quorum + ) + .unwrap(); // Vote with more than quorum - dao.vote(&id, proposer, 10 * 100_000_000, VoteChoice::Yes, 15, None).unwrap(); + dao.vote(&id, proposer, 10 * 100_000_000, VoteChoice::Yes, 15, None) + .unwrap(); // Update state after voting ends dao.update_all_states(200); @@ -920,19 +953,21 @@ mod tests { dao.set_guardian(guardian.clone()); - let id = dao.create_proposal( - proposer, - 1_000_000 * 100_000_000, - ProposalType::Signaling { - title: "Test".to_string(), - description: "Test".to_string(), - options: vec![], - }, - "Test".to_string(), - "Test".to_string(), - 0, - 70_000_000 * 100_000_000, - ).unwrap(); + let id = dao + .create_proposal( + proposer, + 1_000_000 * 100_000_000, + ProposalType::Signaling { + title: "Test".to_string(), + description: "Test".to_string(), + options: vec![], + }, + "Test".to_string(), + "Test".to_string(), + 0, + 70_000_000 * 100_000_000, + ) + .unwrap(); // Guardian cancels dao.cancel(&id, &guardian).unwrap(); diff --git a/crates/synor-governance/src/lib.rs b/crates/synor-governance/src/lib.rs index 02220f4..51cd279 100644 --- a/crates/synor-governance/src/lib.rs +++ b/crates/synor-governance/src/lib.rs @@ -36,8 +36,8 @@ pub mod treasury; pub mod vesting; pub use dao::{ - DaoStats, Proposal, ProposalId, ProposalState, ProposalSummary, ProposalType, Vote, - VoteChoice, VotingConfig, VotingPower, DAO, + DaoStats, Proposal, ProposalId, ProposalState, ProposalSummary, ProposalType, Vote, VoteChoice, + VotingConfig, VotingPower, DAO, }; pub use multisig::{ MultisigConfig, MultisigTransaction, MultisigTxId, MultisigTxState, MultisigWallet, diff --git a/crates/synor-governance/src/multisig.rs b/crates/synor-governance/src/multisig.rs index b66f736..4878935 100644 --- a/crates/synor-governance/src/multisig.rs +++ b/crates/synor-governance/src/multisig.rs @@ -32,8 +32,8 @@ //! ``` use borsh::{BorshDeserialize, BorshSerialize}; -use std::collections::{HashMap, HashSet}; use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, HashSet}; use synor_types::{Address, Hash256}; use thiserror::Error; @@ -81,7 +81,11 @@ impl MultisigConfig { } /// Creates a 2-of-3 multisig. - pub fn two_of_three(signer1: Address, signer2: Address, signer3: Address) -> Result { + pub fn two_of_three( + signer1: Address, + signer2: Address, + signer3: Address, + ) -> Result { Self::new(2, vec![signer1, signer2, signer3]) } @@ -113,7 +117,9 @@ impl MultisigConfig { } /// State of a multisig transaction. -#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] pub enum MultisigTxState { /// Transaction is pending signatures. Pending, @@ -131,26 +137,15 @@ pub enum MultisigTxState { #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] pub enum MultisigTxType { /// Transfer tokens to an address. - Transfer { - to: Address, - amount: u64, - }, + Transfer { to: Address, amount: u64 }, /// Add a new signer. - AddSigner { - new_signer: Address, - }, + AddSigner { new_signer: Address }, /// Remove a signer. - RemoveSigner { - signer: Address, - }, + RemoveSigner { signer: Address }, /// Change the threshold. - ChangeThreshold { - new_threshold: u32, - }, + ChangeThreshold { new_threshold: u32 }, /// Execute a raw transaction. - RawTransaction { - tx_data: Vec, - }, + RawTransaction { tx_data: Vec }, /// Create a vesting contract. CreateVesting { beneficiary: Address, @@ -160,14 +155,9 @@ pub enum MultisigTxType { description: String, }, /// Approve a DAO proposal. - ApproveProposal { - proposal_id: Hash256, - }, + ApproveProposal { proposal_id: Hash256 }, /// Custom action with arbitrary data. - Custom { - action_type: String, - data: Vec, - }, + Custom { action_type: String, data: Vec }, } /// A pending multisig transaction. @@ -394,7 +384,8 @@ impl MultisigWallet { return Err(MultisigError::NotAuthorized); } - let tx = self.pending_transactions + let tx = self + .pending_transactions .get_mut(tx_id) .ok_or(MultisigError::TransactionNotFound)?; @@ -438,7 +429,8 @@ impl MultisigWallet { return Err(MultisigError::NotAuthorized); } - let tx = self.pending_transactions + let tx = self + .pending_transactions .get_mut(tx_id) .ok_or(MultisigError::TransactionNotFound)?; @@ -517,7 +509,8 @@ impl MultisigWallet { canceller: &Address, ) -> Result<(), MultisigError> { // Only proposer can cancel - let tx = self.pending_transactions + let tx = self + .pending_transactions .get_mut(tx_id) .ok_or(MultisigError::TransactionNotFound)?; @@ -564,7 +557,8 @@ impl MultisigWallet { /// Cleans up expired transactions. pub fn cleanup_expired(&mut self, current_time: u64) -> usize { - let expired: Vec<_> = self.pending_transactions + let expired: Vec<_> = self + .pending_transactions .iter() .filter(|(_, tx)| tx.is_expired(current_time) && tx.state == MultisigTxState::Pending) .map(|(id, _)| *id) @@ -591,7 +585,10 @@ impl MultisigWallet { threshold: self.config.threshold, balance: self.balance, pending_count: pending.len(), - ready_count: pending.iter().filter(|tx| tx.state == MultisigTxState::Ready).count(), + ready_count: pending + .iter() + .filter(|tx| tx.state == MultisigTxState::Ready) + .count(), total_executed: self.executed_transactions.len(), } } @@ -662,20 +659,20 @@ impl MultisigManager { pub fn wallets_for_signer(&self, signer: &Address) -> Vec<&MultisigWallet> { self.by_signer .get(signer) - .map(|ids| { - ids.iter() - .filter_map(|id| self.wallets.get(id)) - .collect() - }) + .map(|ids| ids.iter().filter_map(|id| self.wallets.get(id)).collect()) .unwrap_or_default() } /// Gets all transactions awaiting a signer across all wallets. - pub fn awaiting_signature(&self, signer: &Address) -> Vec<(&MultisigWallet, &MultisigTransaction)> { + pub fn awaiting_signature( + &self, + signer: &Address, + ) -> Vec<(&MultisigWallet, &MultisigTransaction)> { self.wallets_for_signer(signer) .into_iter() .flat_map(|wallet| { - wallet.awaiting_signature(signer) + wallet + .awaiting_signature(signer) .into_iter() .map(move |tx| (wallet, tx)) }) @@ -707,11 +704,9 @@ mod tests { #[test] fn test_multisig_config() { - let config = MultisigConfig::two_of_three( - test_address(1), - test_address(2), - test_address(3), - ).unwrap(); + let config = + MultisigConfig::two_of_three(test_address(1), test_address(2), test_address(3)) + .unwrap(); assert_eq!(config.threshold, 2); assert_eq!(config.signer_count(), 3); @@ -742,22 +737,25 @@ mod tests { let signer3 = test_address(3); let recipient = test_address(10); - let config = MultisigConfig::two_of_three( - signer1.clone(), - signer2.clone(), - signer3.clone(), - ).unwrap(); + let config = + MultisigConfig::two_of_three(signer1.clone(), signer2.clone(), signer3.clone()) + .unwrap(); let mut wallet = MultisigWallet::new("Test Wallet".to_string(), config, 0); wallet.deposit(1_000_000); // Propose a transfer - let tx_id = wallet.propose( - MultisigTxType::Transfer { to: recipient, amount: 100_000 }, - &signer1, - 100, - "Pay developer".to_string(), - ).unwrap(); + let tx_id = wallet + .propose( + MultisigTxType::Transfer { + to: recipient, + amount: 100_000, + }, + &signer1, + 100, + "Pay developer".to_string(), + ) + .unwrap(); // Check proposer auto-signed let tx = wallet.get_transaction(&tx_id).unwrap(); @@ -780,21 +778,24 @@ mod tests { let signer2 = test_address(2); let signer3 = test_address(3); - let config = MultisigConfig::two_of_three( - signer1.clone(), - signer2.clone(), - signer3.clone(), - ).unwrap(); + let config = + MultisigConfig::two_of_three(signer1.clone(), signer2.clone(), signer3.clone()) + .unwrap(); let mut wallet = MultisigWallet::new("Test".to_string(), config, 0); wallet.deposit(1_000_000); - let tx_id = wallet.propose( - MultisigTxType::Transfer { to: test_address(10), amount: 100 }, - &signer1, - 100, - "Test".to_string(), - ).unwrap(); + let tx_id = wallet + .propose( + MultisigTxType::Transfer { + to: test_address(10), + amount: 100, + }, + &signer1, + 100, + "Test".to_string(), + ) + .unwrap(); // Try to execute with only 1 signature let result = wallet.execute(&tx_id, &signer1, 101); diff --git a/crates/synor-governance/src/treasury.rs b/crates/synor-governance/src/treasury.rs index 7d9c0d4..8f56b84 100644 --- a/crates/synor-governance/src/treasury.rs +++ b/crates/synor-governance/src/treasury.rs @@ -7,8 +7,8 @@ //! - Automated fund streams use borsh::{BorshDeserialize, BorshSerialize}; -use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use synor_types::{Address, Hash256}; use crate::multisig::{MultisigTxId, MultisigWallet}; @@ -16,7 +16,16 @@ use crate::ProposalId; /// Unique identifier for a treasury pool. #[derive( - Clone, Copy, Debug, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, Serialize, Deserialize, + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, )] pub struct TreasuryPoolId(pub [u8; 32]); @@ -156,9 +165,9 @@ impl SpendingLimit { /// High security: 1% per tx, 5% per week. pub fn high_security(total_balance: u64) -> Self { SpendingLimit::new( - total_balance / 100, // 1% per tx - total_balance / 20, // 5% per week - 7 * 24 * 60 * 60, // 1 week + total_balance / 100, // 1% per tx + total_balance / 20, // 5% per week + 7 * 24 * 60 * 60, // 1 week ) .with_cooling_period(24 * 60 * 60) // 1 day cooling } @@ -166,9 +175,9 @@ impl SpendingLimit { /// Medium security: 5% per tx, 20% per month. pub fn medium_security(total_balance: u64) -> Self { SpendingLimit::new( - total_balance / 20, // 5% per tx - total_balance / 5, // 20% per month - 30 * 24 * 60 * 60, // 1 month + total_balance / 20, // 5% per tx + total_balance / 5, // 20% per month + 30 * 24 * 60 * 60, // 1 month ) .with_cooling_period(6 * 60 * 60) // 6 hour cooling } @@ -176,9 +185,9 @@ impl SpendingLimit { /// Low security: 10% per tx, 50% per month. pub fn low_security(total_balance: u64) -> Self { SpendingLimit::new( - total_balance / 10, // 10% per tx - total_balance / 2, // 50% per month - 30 * 24 * 60 * 60, // 1 month + total_balance / 10, // 10% per tx + total_balance / 2, // 50% per month + 30 * 24 * 60 * 60, // 1 month ) } } @@ -336,8 +345,8 @@ impl TreasuryConfig { approvals: ApprovalRequirements { multisig_required: true, dao_threshold: Some(50_000 * 100_000_000), // 50k SYNOR needs DAO - council_required: true, // Council must approve team allocations - min_delay: 7 * 24 * 60 * 60, // 7 day delay + council_required: true, // Council must approve team allocations + min_delay: 7 * 24 * 60 * 60, // 7 day delay }, allow_deposits: false, // No external deposits frozen: false, @@ -497,21 +506,29 @@ impl TreasuryPool { } // Check spending limits - self.config.spending_limit.can_spend(amount, timestamp) + self.config + .spending_limit + .can_spend(amount, timestamp) .map_err(TreasuryError::SpendingLimitExceeded)?; // Determine initial state based on requirements - let initial_state = if self.config.approvals.multisig_required && self.multisig_wallet.is_some() { - SpendingRequestState::PendingMultisig - } else if self.config.approvals.dao_threshold.map_or(false, |t| amount >= t) { - SpendingRequestState::PendingDao - } else if self.config.approvals.council_required { - SpendingRequestState::PendingCouncil - } else { - SpendingRequestState::Timelocked { - execute_after: timestamp + self.config.approvals.min_delay, - } - }; + let initial_state = + if self.config.approvals.multisig_required && self.multisig_wallet.is_some() { + SpendingRequestState::PendingMultisig + } else if self + .config + .approvals + .dao_threshold + .map_or(false, |t| amount >= t) + { + SpendingRequestState::PendingDao + } else if self.config.approvals.council_required { + SpendingRequestState::PendingCouncil + } else { + SpendingRequestState::Timelocked { + execute_after: timestamp + self.config.approvals.min_delay, + } + }; let request = SpendingRequest { id, @@ -538,7 +555,9 @@ impl TreasuryPool { multisig_tx: MultisigTxId, timestamp: u64, ) -> Result<(), TreasuryError> { - let request = self.pending_requests.get_mut(request_id) + let request = self + .pending_requests + .get_mut(request_id) .ok_or(TreasuryError::RequestNotFound)?; if request.state != SpendingRequestState::PendingMultisig { @@ -548,7 +567,10 @@ impl TreasuryPool { request.multisig_tx = Some(multisig_tx); // Move to next approval stage - let needs_dao = self.config.approvals.dao_threshold + let needs_dao = self + .config + .approvals + .dao_threshold .map_or(false, |t| request.amount >= t); if needs_dao { @@ -571,7 +593,9 @@ impl TreasuryPool { proposal_id: ProposalId, timestamp: u64, ) -> Result<(), TreasuryError> { - let request = self.pending_requests.get_mut(request_id) + let request = self + .pending_requests + .get_mut(request_id) .ok_or(TreasuryError::RequestNotFound)?; if request.state != SpendingRequestState::PendingDao { @@ -599,7 +623,9 @@ impl TreasuryPool { required_approvals: usize, timestamp: u64, ) -> Result<(), TreasuryError> { - let request = self.pending_requests.get_mut(request_id) + let request = self + .pending_requests + .get_mut(request_id) .ok_or(TreasuryError::RequestNotFound)?; if request.state != SpendingRequestState::PendingCouncil { @@ -625,7 +651,9 @@ impl TreasuryPool { request_id: &Hash256, timestamp: u64, ) -> Result { - let request = self.pending_requests.get(request_id) + let request = self + .pending_requests + .get(request_id) .ok_or(TreasuryError::RequestNotFound)?; // Check if ready @@ -654,7 +682,9 @@ impl TreasuryPool { // Execute self.balance -= amount; self.total_spent += amount; - self.config.spending_limit.record_spending(amount, timestamp); + self.config + .spending_limit + .record_spending(amount, timestamp); // Update request state and remove from pending let mut executed_request = self.pending_requests.remove(request_id).unwrap(); @@ -669,7 +699,9 @@ impl TreasuryPool { request_id: &Hash256, canceller: &Address, ) -> Result<(), TreasuryError> { - let request = self.pending_requests.get_mut(request_id) + let request = self + .pending_requests + .get_mut(request_id) .ok_or(TreasuryError::RequestNotFound)?; // Only proposer can cancel @@ -710,14 +742,7 @@ impl TreasuryPool { // Reserve the amount self.balance -= amount; - let stream = FundingStream::new( - id, - recipient, - amount, - start_time, - duration, - description, - ); + let stream = FundingStream::new(id, recipient, amount, start_time, duration, description); self.streams.insert(id, stream); Ok(self.streams.get(&id).unwrap()) @@ -730,7 +755,9 @@ impl TreasuryPool { claimer: &Address, timestamp: u64, ) -> Result { - let stream = self.streams.get_mut(stream_id) + let stream = self + .streams + .get_mut(stream_id) .ok_or(TreasuryError::StreamNotFound)?; if &stream.recipient != claimer { @@ -745,7 +772,9 @@ impl TreasuryPool { /// Cancels a funding stream (returns unvested to pool). pub fn cancel_stream(&mut self, stream_id: &Hash256) -> Result { - let stream = self.streams.get_mut(stream_id) + let stream = self + .streams + .get_mut(stream_id) .ok_or(TreasuryError::StreamNotFound)?; let remaining = stream.total_amount - stream.claimed_amount; @@ -842,7 +871,9 @@ impl Treasury { ) -> Result<(), TreasuryError> { let wallet_id = wallet.id; - let pool = self.pools.get_mut(pool_id) + let pool = self + .pools + .get_mut(pool_id) .ok_or(TreasuryError::PoolNotFound)?; pool.set_multisig(wallet_id); @@ -985,8 +1016,15 @@ impl std::fmt::Display for SpendingError { Self::CoolingPeriod { remaining } => { write!(f, "Cooling period: {} seconds remaining", remaining) } - Self::ExceedsPeriodLimit { requested, remaining } => { - write!(f, "Requested {} exceeds remaining period allowance of {}", requested, remaining) + Self::ExceedsPeriodLimit { + requested, + remaining, + } => { + write!( + f, + "Requested {} exceeds remaining period allowance of {}", + requested, remaining + ) } } } @@ -1025,8 +1063,15 @@ impl std::fmt::Display for TreasuryError { Self::PoolNotFound => write!(f, "Treasury pool not found"), Self::PoolFrozen => write!(f, "Treasury pool is frozen"), Self::DepositsNotAllowed => write!(f, "Deposits not allowed for this pool"), - Self::InsufficientBalance { requested, available } => { - write!(f, "Insufficient balance: {} requested, {} available", requested, available) + Self::InsufficientBalance { + requested, + available, + } => { + write!( + f, + "Insufficient balance: {} requested, {} available", + requested, available + ) } Self::SpendingLimitExceeded(e) => write!(f, "Spending limit exceeded: {}", e), Self::RequestNotFound => write!(f, "Spending request not found"), @@ -1049,7 +1094,11 @@ mod tests { fn test_address(n: u8) -> Address { let mut bytes = [0u8; 32]; bytes[0] = n; - Address::from_parts(synor_types::Network::Devnet, synor_types::address::AddressType::P2PKH, bytes) + Address::from_parts( + synor_types::Network::Devnet, + synor_types::address::AddressType::P2PKH, + bytes, + ) } fn test_hash(n: u8) -> Hash256 { @@ -1061,9 +1110,9 @@ mod tests { #[test] fn test_spending_limit() { let mut limit = SpendingLimit::new( - 1000, // 1000 per tx - 5000, // 5000 per period - 3600, // 1 hour period + 1000, // 1000 per tx + 5000, // 5000 per period + 3600, // 1 hour period ); // Should allow first spend @@ -1088,13 +1137,7 @@ mod tests { fn test_treasury_pool() { let pool_id = TreasuryPoolId::new([1u8; 32]); let config = TreasuryConfig::ecosystem(1_000_000); - let mut pool = TreasuryPool::new( - pool_id, - "Test Pool".to_string(), - config, - 1_000_000, - 0, - ); + let mut pool = TreasuryPool::new(pool_id, "Test Pool".to_string(), config, 1_000_000, 0); // Create spending request let request_id = test_hash(1); @@ -1108,7 +1151,8 @@ mod tests { "Test payment".to_string(), proposer, 100, - ).unwrap(); + ) + .unwrap(); assert!(pool.pending_requests.contains_key(&request_id)); } diff --git a/crates/synor-governance/src/vesting.rs b/crates/synor-governance/src/vesting.rs index df4bee6..105e77d 100644 --- a/crates/synor-governance/src/vesting.rs +++ b/crates/synor-governance/src/vesting.rs @@ -105,7 +105,8 @@ impl VestingSchedule { // Linear vesting between cliff and end // vested = total * elapsed / vesting_duration - let vested = (self.total_amount as u128 * elapsed as u128 / self.vesting_duration as u128) as u64; + let vested = + (self.total_amount as u128 * elapsed as u128 / self.vesting_duration as u128) as u64; vested } @@ -131,7 +132,9 @@ impl VestingSchedule { } /// Current state of a vesting contract. -#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] pub enum VestingState { /// Vesting is active. Active, @@ -302,7 +305,9 @@ impl VestingContract { /// Returns the unvested amount. pub fn unvested_amount(&self, timestamp: u64) -> u64 { - self.schedule.total_amount.saturating_sub(self.vested_amount(timestamp)) + self.schedule + .total_amount + .saturating_sub(self.vested_amount(timestamp)) } /// Claims available tokens. @@ -429,7 +434,8 @@ impl VestingManager { schedule: VestingSchedule, description: String, ) -> Result { - let contract = VestingContract::new(beneficiary.clone(), grantor.clone(), schedule, description)?; + let contract = + VestingContract::new(beneficiary.clone(), grantor.clone(), schedule, description)?; let id = contract.id; // Index by beneficiary @@ -462,11 +468,7 @@ impl VestingManager { pub fn get_by_beneficiary(&self, beneficiary: &Address) -> Vec<&VestingContract> { self.by_beneficiary .get(beneficiary) - .map(|ids| { - ids.iter() - .filter_map(|id| self.contracts.get(id)) - .collect() - }) + .map(|ids| ids.iter().filter_map(|id| self.contracts.get(id)).collect()) .unwrap_or_default() } @@ -474,22 +476,28 @@ impl VestingManager { pub fn get_by_grantor(&self, grantor: &Address) -> Vec<&VestingContract> { self.by_grantor .get(grantor) - .map(|ids| { - ids.iter() - .filter_map(|id| self.contracts.get(id)) - .collect() - }) + .map(|ids| ids.iter().filter_map(|id| self.contracts.get(id)).collect()) .unwrap_or_default() } /// Claims tokens from a vesting contract. - pub fn claim(&mut self, id: &Hash256, caller: &Address, timestamp: u64) -> Result { + pub fn claim( + &mut self, + id: &Hash256, + caller: &Address, + timestamp: u64, + ) -> Result { let contract = self.contracts.get_mut(id).ok_or(VestingError::NotFound)?; contract.claim(caller, timestamp) } /// Revokes a vesting contract. - pub fn revoke(&mut self, id: &Hash256, caller: &Address, timestamp: u64) -> Result { + pub fn revoke( + &mut self, + id: &Hash256, + caller: &Address, + timestamp: u64, + ) -> Result { let contract = self.contracts.get_mut(id).ok_or(VestingError::NotFound)?; contract.revoke(caller, timestamp) } @@ -523,9 +531,18 @@ impl VestingManager { let contracts: Vec<_> = self.contracts.values().collect(); VestingStats { total_contracts: contracts.len(), - active_contracts: contracts.iter().filter(|c| c.state == VestingState::Active).count(), - revoked_contracts: contracts.iter().filter(|c| c.state == VestingState::Revoked).count(), - completed_contracts: contracts.iter().filter(|c| c.state == VestingState::Completed).count(), + active_contracts: contracts + .iter() + .filter(|c| c.state == VestingState::Active) + .count(), + revoked_contracts: contracts + .iter() + .filter(|c| c.state == VestingState::Revoked) + .count(), + completed_contracts: contracts + .iter() + .filter(|c| c.state == VestingState::Completed) + .count(), total_amount: contracts.iter().map(|c| c.schedule.total_amount).sum(), total_claimed: contracts.iter().map(|c| c.claimed_amount).sum(), } @@ -556,7 +573,11 @@ mod tests { fn test_address(n: u8) -> Address { let mut bytes = [0u8; 32]; bytes[0] = n; - Address::from_parts(synor_types::Network::Devnet, synor_types::address::AddressType::P2PKH, bytes) + Address::from_parts( + synor_types::Network::Devnet, + synor_types::address::AddressType::P2PKH, + bytes, + ) } #[test] @@ -591,7 +612,8 @@ mod tests { grantor, VestingSchedule::new(1_000_000, 0, 12, start_time, true), // 1 year, no cliff "Test vesting".to_string(), - ).unwrap(); + ) + .unwrap(); // After 6 months, should have ~50% vested let six_months = 6 * 30 * 24 * 60 * 60; @@ -617,7 +639,8 @@ mod tests { grantor.clone(), VestingSchedule::new(1_000_000, 0, 12, 0, true), "Revocable".to_string(), - ).unwrap(); + ) + .unwrap(); // Revoke at 6 months let six_months = 6 * 30 * 24 * 60 * 60; @@ -637,7 +660,8 @@ mod tests { grantor.clone(), VestingSchedule::new(1_000_000, 0, 12, 0, false), // non-revocable "Non-revocable".to_string(), - ).unwrap(); + ) + .unwrap(); let result = contract.revoke(&grantor, 0); assert!(matches!(result, Err(VestingError::NotRevocable))); @@ -649,12 +673,14 @@ mod tests { let beneficiary = test_address(1); let grantor = test_address(2); - let id = manager.create_vesting( - beneficiary.clone(), - grantor, - VestingSchedule::new(1_000_000, 0, 12, 0, true), - "Test".to_string(), - ).unwrap(); + let id = manager + .create_vesting( + beneficiary.clone(), + grantor, + VestingSchedule::new(1_000_000, 0, 12, 0, true), + "Test".to_string(), + ) + .unwrap(); assert!(manager.get(&id).is_some()); assert_eq!(manager.get_by_beneficiary(&beneficiary).len(), 1); diff --git a/crates/synor-mining/benches/kheavyhash.rs b/crates/synor-mining/benches/kheavyhash.rs index 64bac97..fe233bb 100644 --- a/crates/synor-mining/benches/kheavyhash.rs +++ b/crates/synor-mining/benches/kheavyhash.rs @@ -9,9 +9,7 @@ //! //! Run with: cargo bench -p synor-mining -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use synor_mining::kheavyhash::{HashrateBenchmark, KHeavyHash, ParallelMiner}; use synor_mining::matrix::{gf_mul, HeavyMatrix, OptimizedMatrix}; use synor_mining::Target; @@ -141,17 +139,13 @@ fn bench_mining_throughput(c: &mut Criterion) { for count in [100, 1000, 10000] { group.throughput(Throughput::Elements(count)); - group.bench_with_input( - BenchmarkId::from_parameter(count), - &count, - |b, &count| { - b.iter(|| { - for nonce in 0..count { - black_box(hasher.finalize(&pre_hash, nonce)); - } - }) - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &count| { + b.iter(|| { + for nonce in 0..count { + black_box(hasher.finalize(&pre_hash, nonce)); + } + }) + }); } group.finish(); @@ -168,9 +162,7 @@ fn bench_mining_with_target(c: &mut Criterion) { group.bench_function("mine_easy_target", |b| { b.iter_batched( || 0u64, - |start_nonce| { - black_box(hasher.mine(&header, &target, start_nonce, 10000)) - }, + |start_nonce| black_box(hasher.mine(&header, &target, start_nonce, 10000)), criterion::BatchSize::SmallInput, ) }); @@ -212,13 +204,9 @@ fn bench_parallel_miner_creation(c: &mut Criterion) { let mut group = c.benchmark_group("parallel_miner_create"); for threads in [1, 2, 4, 8] { - group.bench_with_input( - BenchmarkId::from_parameter(threads), - &threads, - |b, &t| { - b.iter(|| black_box(ParallelMiner::new(t))) - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(threads), &threads, |b, &t| { + b.iter(|| black_box(ParallelMiner::new(t))) + }); } group.finish(); @@ -296,11 +284,7 @@ fn bench_matrix_memory(c: &mut Criterion) { // ==================== Criterion Groups ==================== -criterion_group!( - gf_benches, - bench_gf_mul, - bench_gf_mul_single, -); +criterion_group!(gf_benches, bench_gf_mul, bench_gf_mul_single,); criterion_group!( matrix_benches, @@ -332,10 +316,7 @@ criterion_group!( bench_verify, ); -criterion_group!( - parallel_benches, - bench_parallel_miner_creation, -); +criterion_group!(parallel_benches, bench_parallel_miner_creation,); criterion_main!( gf_benches, diff --git a/crates/synor-mining/benches/mining_bench.rs b/crates/synor-mining/benches/mining_bench.rs index 48fc384..580ce15 100644 --- a/crates/synor-mining/benches/mining_bench.rs +++ b/crates/synor-mining/benches/mining_bench.rs @@ -9,14 +9,14 @@ //! //! Run with: cargo bench -p synor-mining --bench mining_bench -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use synor_mining::{ kheavyhash::{KHeavyHash, ParallelMiner}, matrix::{GfTables, HeavyMatrix, OptimizedMatrix}, miner::{BlockMiner, MinerConfig, ParallelBlockMiner}, - template::{BlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, CoinbaseData, TemplateTransaction}, + template::{ + BlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, CoinbaseData, TemplateTransaction, + }, Target, }; use synor_types::{Address, Hash256, Network}; @@ -146,9 +146,7 @@ fn matrix_initialization(c: &mut Criterion) { b.iter(|| black_box(OptimizedMatrix::new())) }); - group.bench_function("gf_tables_new", |b| { - b.iter(|| black_box(GfTables::new())) - }); + group.bench_function("gf_tables_new", |b| b.iter(|| black_box(GfTables::new()))); // Custom seed initialization for seed_idx in [0u8, 42u8, 255u8] { @@ -156,9 +154,7 @@ fn matrix_initialization(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("matrix_from_seed", seed_idx), &seed, - |b, s| { - b.iter(|| black_box(HeavyMatrix::from_seed(s))) - }, + |b, s| b.iter(|| black_box(HeavyMatrix::from_seed(s))), ); } @@ -232,27 +228,15 @@ fn nonce_search_rate(c: &mut Criterion) { for range in [1000u64, 5000, 10000] { let target = Target::from_bytes([0x00; 32]); // Impossible target group.throughput(Throughput::Elements(range)); - group.bench_with_input( - BenchmarkId::new("range_search", range), - &range, - |b, &r| { - b.iter(|| black_box(hasher.mine(&header, &target, 0, r))) - }, - ); + group.bench_with_input(BenchmarkId::new("range_search", range), &range, |b, &r| { + b.iter(|| black_box(hasher.mine(&header, &target, 0, r))) + }); } // Search with progress callback group.bench_function("search_with_callback", |b| { let target = Target::max(); - b.iter(|| { - hasher.mine_with_callback( - &header, - &target, - 0, - 5000, - |_tried, _nonce| true, - ) - }) + b.iter(|| hasher.mine_with_callback(&header, &target, 0, 5000, |_tried, _nonce| true)) }); group.finish(); @@ -265,14 +249,10 @@ fn parallel_nonce_search(c: &mut Criterion) { let target = Target::max(); for threads in [1, 2, 4] { - group.bench_with_input( - BenchmarkId::new("threads", threads), - &threads, - |b, &t| { - let miner = ParallelMiner::new(t); - b.iter(|| black_box(miner.mine(&header, &target, 0))) - }, - ); + group.bench_with_input(BenchmarkId::new("threads", threads), &threads, |b, &t| { + let miner = ParallelMiner::new(t); + b.iter(|| black_box(miner.mine(&header, &target, 0))) + }); } group.finish(); @@ -291,9 +271,8 @@ fn block_template_creation(c: &mut Criterion) { b.iter_batched( || { // Setup: create transactions - let txs: Vec = (0..count) - .map(|i| test_template_tx(i as u64)) - .collect(); + let txs: Vec = + (0..count).map(|i| test_template_tx(i as u64)).collect(); txs }, |txs| { @@ -331,17 +310,13 @@ fn template_header_creation(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("header_for_mining", tx_count), &template, - |b, tmpl| { - b.iter(|| black_box(tmpl.header_for_mining())) - }, + |b, tmpl| b.iter(|| black_box(tmpl.header_for_mining())), ); group.bench_with_input( BenchmarkId::new("header_with_extra_nonce", tx_count), &template, - |b, tmpl| { - b.iter(|| black_box(tmpl.header_with_extra_nonce(12345))) - }, + |b, tmpl| b.iter(|| black_box(tmpl.header_with_extra_nonce(12345))), ); } @@ -357,17 +332,13 @@ fn template_validation(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("validate", tx_count), &template, - |b, tmpl| { - b.iter(|| black_box(tmpl.validate())) - }, + |b, tmpl| b.iter(|| black_box(tmpl.validate())), ); group.bench_with_input( BenchmarkId::new("get_target", tx_count), &template, - |b, tmpl| { - b.iter(|| black_box(tmpl.get_target())) - }, + |b, tmpl| b.iter(|| black_box(tmpl.get_target())), ); } @@ -378,9 +349,7 @@ fn coinbase_building(c: &mut Criterion) { let mut group = c.benchmark_group("coinbase_building"); group.bench_function("coinbase_simple", |b| { - b.iter(|| { - black_box(CoinbaseBuilder::new(test_address(), 1000).build()) - }) + b.iter(|| black_box(CoinbaseBuilder::new(test_address(), 1000).build())) }); group.bench_function("coinbase_with_data", |b| { @@ -390,7 +359,7 @@ fn coinbase_building(c: &mut Criterion) { .extra_data(b"Synor Mining Pool v1.0.0".to_vec()) .reward(500_00000000) .fees(1_00000000) - .build() + .build(), ) }) }); @@ -407,7 +376,7 @@ fn coinbase_building(c: &mut Criterion) { CoinbaseBuilder::new(test_address(), 1000) .extra_data(data.clone()) .reward(500_00000000) - .build() + .build(), ) }) }, @@ -477,9 +446,7 @@ fn target_operations(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("from_bits", format!("{:08x}", bits)), &bits, - |b, &bit| { - b.iter(|| black_box(Target::from_bits(bit))) - }, + |b, &bit| b.iter(|| black_box(Target::from_bits(bit))), ); } @@ -607,11 +574,7 @@ criterion_group!( matrix_memory_patterns, ); -criterion_group!( - nonce_benches, - nonce_search_rate, - parallel_nonce_search, -); +criterion_group!(nonce_benches, nonce_search_rate, parallel_nonce_search,); criterion_group!( template_benches, @@ -628,16 +591,9 @@ criterion_group!( parallel_block_miner_creation, ); -criterion_group!( - misc_benches, - target_operations, - mining_stats_operations, -); +criterion_group!(misc_benches, target_operations, mining_stats_operations,); -criterion_group!( - e2e_benches, - end_to_end_mining, -); +criterion_group!(e2e_benches, end_to_end_mining,); criterion_main!( throughput_benches, diff --git a/crates/synor-mining/src/kheavyhash.rs b/crates/synor-mining/src/kheavyhash.rs index 83928ae..ddfe04d 100644 --- a/crates/synor-mining/src/kheavyhash.rs +++ b/crates/synor-mining/src/kheavyhash.rs @@ -251,12 +251,7 @@ impl ParallelMiner { /// Mines using all threads. /// /// Each thread gets a different nonce range to search. - pub fn mine( - &self, - header: &[u8], - target: &Target, - start_nonce: u64, - ) -> Option { + pub fn mine(&self, header: &[u8], target: &Target, start_nonce: u64) -> Option { use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; diff --git a/crates/synor-mining/src/lib.rs b/crates/synor-mining/src/lib.rs index 127a00a..005533c 100644 --- a/crates/synor-mining/src/lib.rs +++ b/crates/synor-mining/src/lib.rs @@ -43,9 +43,13 @@ pub mod template; pub use kheavyhash::{KHeavyHash, PowHash}; pub use matrix::HeavyMatrix; -pub use miner::{BlockMiner, MinerCommand, MinerConfig, MinerEvent, MiningResult, ParallelBlockMiner}; +pub use miner::{ + BlockMiner, MinerCommand, MinerConfig, MinerEvent, MiningResult, ParallelBlockMiner, +}; pub use stratum::{StratumClient, StratumJob, StratumServer}; -pub use template::{BlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, CoinbaseData, TemplateTransaction}; +pub use template::{ + BlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, CoinbaseData, TemplateTransaction, +}; use synor_types::{Address, Hash256}; @@ -297,27 +301,24 @@ mod tests { #[test] fn test_target_comparison() { let target = Target::from_bytes([ - 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, ]); // Hash with more leading zeros should pass let easy_hash = Hash256::from_bytes([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, ]); assert!(target.is_met_by(&easy_hash)); // Hash with fewer leading zeros should fail let hard_hash = Hash256::from_bytes([ - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]); assert!(!target.is_met_by(&hard_hash)); } diff --git a/crates/synor-mining/src/matrix.rs b/crates/synor-mining/src/matrix.rs index 12e5534..eb7bb9b 100644 --- a/crates/synor-mining/src/matrix.rs +++ b/crates/synor-mining/src/matrix.rs @@ -3,7 +3,6 @@ //! The matrix is a 64x64 matrix where each element is in GF(2^8). //! Matrix multiplication is performed using Galois field arithmetic. - /// The irreducible polynomial for GF(2^8): x^8 + x^4 + x^3 + x + 1 const GF_POLY: u16 = 0x11B; diff --git a/crates/synor-mining/src/stratum.rs b/crates/synor-mining/src/stratum.rs index 31c9546..c4a227a 100644 --- a/crates/synor-mining/src/stratum.rs +++ b/crates/synor-mining/src/stratum.rs @@ -301,7 +301,8 @@ impl StratumServer { }; // Reconstruct header: header_hash + extra_nonce1 + extra_nonce2 - let mut header_data = Vec::with_capacity(header_hash.len() + extra_nonce1.len() + extra_nonce2.len()); + let mut header_data = + Vec::with_capacity(header_hash.len() + extra_nonce1.len() + extra_nonce2.len()); header_data.extend_from_slice(&header_hash); header_data.extend_from_slice(&extra_nonce1); header_data.extend_from_slice(&extra_nonce2); @@ -442,19 +443,13 @@ impl StratumServer { }; let id = request.get("id").and_then(|v| v.as_u64()).unwrap_or(0); - let method = request - .get("method") - .and_then(|v| v.as_str()) - .unwrap_or(""); + let method = request.get("method").and_then(|v| v.as_str()).unwrap_or(""); let response = match method { "mining.subscribe" => { let extra_nonce = self.allocate_extra_nonce(); - let result = serde_json::json!([ - [["mining.notify", "1"]], - extra_nonce, - 8 - ]); + let result = + serde_json::json!([[["mining.notify", "1"]], extra_nonce, 8]); StratumResponse { id, result: Some(result), @@ -463,7 +458,8 @@ impl StratumServer { } "mining.authorize" => { let params = request.get("params").cloned().unwrap_or_default(); - let worker = params.get(0).and_then(|v| v.as_str()).unwrap_or("unknown"); + let worker = + params.get(0).and_then(|v| v.as_str()).unwrap_or("unknown"); _worker_name = Some(worker.to_string()); authorized = true; self.register_worker(worker.to_string(), self.allocate_extra_nonce()); @@ -485,10 +481,26 @@ impl StratumServer { // Parse submission let params = request.get("params").cloned().unwrap_or_default(); let submission = ShareSubmission { - worker: params.get(0).and_then(|v| v.as_str()).unwrap_or("").to_string(), - job_id: params.get(1).and_then(|v| v.as_str()).unwrap_or("").to_string(), - extra_nonce2: params.get(2).and_then(|v| v.as_str()).unwrap_or("").to_string(), - nonce: params.get(3).and_then(|v| v.as_str()).unwrap_or("").to_string(), + worker: params + .get(0) + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + job_id: params + .get(1) + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + extra_nonce2: params + .get(2) + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + nonce: params + .get(3) + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), timestamp: current_timestamp(), }; @@ -680,7 +692,10 @@ mod tests { hashrate: 1000.0, }; - assert_eq!(info.shares_valid + info.shares_invalid, info.shares_submitted); + assert_eq!( + info.shares_valid + info.shares_invalid, + info.shares_submitted + ); } #[test] @@ -699,7 +714,7 @@ mod tests { // Create a job with easy targets let job = StratumJob { job_id: "1".to_string(), - header_hash: "00".repeat(32), // 32 zero bytes + header_hash: "00".repeat(32), // 32 zero bytes share_target: "ff".repeat(32), // All 0xff = easy target block_target: "00".repeat(32), // All 0x00 = impossible target timestamp: 1234567890, @@ -745,7 +760,11 @@ mod tests { timestamp: 1234567890, }; let result = server.validate_share(&valid_submission); - assert!(matches!(result, ShareResult::ValidShare), "Expected ValidShare, got {:?}", result); + assert!( + matches!(result, ShareResult::ValidShare), + "Expected ValidShare, got {:?}", + result + ); // Test duplicate share let duplicate_submission = ShareSubmission { diff --git a/crates/synor-mining/src/template.rs b/crates/synor-mining/src/template.rs index e172410..e931b2f 100644 --- a/crates/synor-mining/src/template.rs +++ b/crates/synor-mining/src/template.rs @@ -239,13 +239,17 @@ impl BlockTemplateBuilder { /// Builds the block template. pub fn build(mut self, template_id: u64) -> Result { - let selected = self.selected_parent.or_else(|| self.parents.first().copied()) + let selected = self + .selected_parent + .or_else(|| self.parents.first().copied()) .ok_or_else(|| MiningError::InvalidTemplate("No parents".into()))?; // Build header data first before taking coinbase let header_data = self.build_header(&selected)?; - let coinbase = self.coinbase.take() + let coinbase = self + .coinbase + .take() .ok_or_else(|| MiningError::InvalidTemplate("No coinbase".into()))?; // Calculate fees @@ -321,7 +325,11 @@ impl BlockTemplateBuilder { } // Collect all txids - let txids: Vec<&[u8; 32]> = self.transactions.iter().map(|tx| tx.txid.as_bytes()).collect(); + let txids: Vec<&[u8; 32]> = self + .transactions + .iter() + .map(|tx| tx.txid.as_bytes()) + .collect(); // Simple merkle tree merkle_root(&txids) diff --git a/crates/synor-network/src/behaviour.rs b/crates/synor-network/src/behaviour.rs index ef64beb..de298b0 100644 --- a/crates/synor-network/src/behaviour.rs +++ b/crates/synor-network/src/behaviour.rs @@ -7,8 +7,7 @@ use libp2p::{ gossipsub::{self, IdentTopic, MessageAuthenticity, MessageId, ValidationMode}, identify, kad::{self, store::MemoryStore}, - mdns, - ping, + mdns, ping, request_response::{self, ProtocolSupport}, swarm::NetworkBehaviour, PeerId, StreamProtocol, @@ -35,7 +34,10 @@ pub struct SynorBehaviour { impl SynorBehaviour { /// Creates a new behaviour with the given configuration. - pub fn new(keypair: &libp2p::identity::Keypair, config: &NetworkConfig) -> Result> { + pub fn new( + keypair: &libp2p::identity::Keypair, + config: &NetworkConfig, + ) -> Result> { let local_peer_id = keypair.public().to_peer_id(); // GossipSub configuration @@ -68,21 +70,15 @@ impl SynorBehaviour { // Request-response configuration let request_response = request_response::Behaviour::new( - [( - StreamProtocol::new(SYNOR_PROTOCOL), - ProtocolSupport::Full, - )], - request_response::Config::default() - .with_request_timeout(config.sync.request_timeout), + [(StreamProtocol::new(SYNOR_PROTOCOL), ProtocolSupport::Full)], + request_response::Config::default().with_request_timeout(config.sync.request_timeout), ); // Identify configuration - let identify_config = identify::Config::new( - "/synor/id/1.0.0".to_string(), - keypair.public(), - ) - .with_agent_version(crate::user_agent()) - .with_push_listen_addr_updates(true); + let identify_config = + identify::Config::new("/synor/id/1.0.0".to_string(), keypair.public()) + .with_agent_version(crate::user_agent()) + .with_push_listen_addr_updates(true); let identify = identify::Behaviour::new(identify_config); @@ -94,10 +90,7 @@ impl SynorBehaviour { ); // mDNS configuration - let mdns = mdns::tokio::Behaviour::new( - mdns::Config::default(), - local_peer_id, - )?; + let mdns = mdns::tokio::Behaviour::new(mdns::Config::default(), local_peer_id)?; Ok(SynorBehaviour { gossipsub, @@ -135,22 +128,35 @@ impl SynorBehaviour { } /// Publishes a block announcement. - pub fn publish_block(&mut self, data: Vec) -> Result { + pub fn publish_block( + &mut self, + data: Vec, + ) -> Result { self.publish(topics::BLOCKS, data) } /// Publishes a transaction announcement. - pub fn publish_transaction(&mut self, data: Vec) -> Result { + pub fn publish_transaction( + &mut self, + data: Vec, + ) -> Result { self.publish(topics::TRANSACTIONS, data) } /// Publishes a header announcement. - pub fn publish_header(&mut self, data: Vec) -> Result { + pub fn publish_header( + &mut self, + data: Vec, + ) -> Result { self.publish(topics::HEADERS, data) } /// Sends a request to a peer. - pub fn send_request(&mut self, peer: &PeerId, request: SynorRequest) -> request_response::OutboundRequestId { + pub fn send_request( + &mut self, + peer: &PeerId, + request: SynorRequest, + ) -> request_response::OutboundRequestId { self.request_response.send_request(peer, request) } @@ -191,10 +197,7 @@ impl SynorBehaviour { /// Returns peers subscribed to a topic. pub fn topic_peers(&self, topic: &str) -> Vec { let topic = IdentTopic::new(topic); - self.gossipsub - .mesh_peers(&topic.hash()) - .cloned() - .collect() + self.gossipsub.mesh_peers(&topic.hash()).cloned().collect() } } diff --git a/crates/synor-network/src/config.rs b/crates/synor-network/src/config.rs index 8b19d9d..be68e2b 100644 --- a/crates/synor-network/src/config.rs +++ b/crates/synor-network/src/config.rs @@ -40,7 +40,9 @@ impl Default for NetworkConfig { NetworkConfig { chain_id: ChainId::Mainnet, listen_addresses: vec![ - format!("/ip4/0.0.0.0/tcp/{}", DEFAULT_PORT).parse().unwrap(), + format!("/ip4/0.0.0.0/tcp/{}", DEFAULT_PORT) + .parse() + .unwrap(), format!("/ip6/::/tcp/{}", DEFAULT_PORT).parse().unwrap(), ], bootstrap_peers: Vec::new(), @@ -74,11 +76,9 @@ impl NetworkConfig { NetworkConfig { chain_id: ChainId::Testnet, bootstrap_peers: testnet_bootstrap_peers(), - listen_addresses: vec![ - format!("/ip4/0.0.0.0/tcp/{}", DEFAULT_PORT + 1000) - .parse() - .unwrap(), - ], + listen_addresses: vec![format!("/ip4/0.0.0.0/tcp/{}", DEFAULT_PORT + 1000) + .parse() + .unwrap()], ..Default::default() } } @@ -229,10 +229,7 @@ fn testnet_bootstrap_peers() -> Vec { // "/dns4/testnet-seed3.synor.cc/tcp/17511/p2p/12D3KooW...", ]; - seeds - .iter() - .filter_map(|s| s.parse().ok()) - .collect() + seeds.iter().filter_map(|s| s.parse().ok()).collect() } /// Returns devnet bootstrap peers for local development. diff --git a/crates/synor-network/src/eclipse.rs b/crates/synor-network/src/eclipse.rs index b0d3456..e3c72d1 100644 --- a/crates/synor-network/src/eclipse.rs +++ b/crates/synor-network/src/eclipse.rs @@ -41,8 +41,8 @@ impl Default for EclipseConfig { min_outbound: 8, anchor_count: 4, rotation_interval: Duration::from_secs(3600), // 1 hour - rotation_percentage: 0.1, // 10% - trial_period: Duration::from_secs(300), // 5 minutes + rotation_percentage: 0.1, // 10% + trial_period: Duration::from_secs(300), // 5 minutes max_trial_peers: 5, } } @@ -414,7 +414,9 @@ mod tests { // Different subnet should be allowed let different_ip = Some(IpAddr::V4(Ipv4Addr::new(10, 1, 1, 1))); - assert!(protection.check_connection(&peer3, different_ip, false).is_ok()); + assert!(protection + .check_connection(&peer3, different_ip, false) + .is_ok()); } #[test] diff --git a/crates/synor-network/src/lib.rs b/crates/synor-network/src/lib.rs index 6d7579d..4f47e51 100644 --- a/crates/synor-network/src/lib.rs +++ b/crates/synor-network/src/lib.rs @@ -41,7 +41,9 @@ pub use behaviour::SynorBehaviour; pub use config::NetworkConfig; pub use eclipse::{EclipseConfig, EclipseProtection, EclipseStats, PeerType}; pub use message::{BlockAnnouncement, Message, TransactionAnnouncement}; -pub use partition::{PartitionConfig, PartitionDetector, PartitionReason, PartitionStats, PartitionStatus}; +pub use partition::{ + PartitionConfig, PartitionDetector, PartitionReason, PartitionStats, PartitionStatus, +}; pub use peer::{PeerInfo, PeerManager, PeerState}; pub use protocol::{SynorProtocol, SynorRequest, SynorResponse}; pub use rate_limit::{ diff --git a/crates/synor-network/src/message.rs b/crates/synor-network/src/message.rs index 0846362..5321158 100644 --- a/crates/synor-network/src/message.rs +++ b/crates/synor-network/src/message.rs @@ -278,7 +278,9 @@ impl Headers { /// Creates an empty headers response. pub fn empty() -> Self { - Headers { headers: Vec::new() } + Headers { + headers: Vec::new(), + } } } diff --git a/crates/synor-network/src/partition.rs b/crates/synor-network/src/partition.rs index 19ef943..10fc5dd 100644 --- a/crates/synor-network/src/partition.rs +++ b/crates/synor-network/src/partition.rs @@ -119,15 +119,24 @@ pub enum PartitionReason { BlockProductionStalled { duration_secs: u64 }, /// Block production rate significantly lower than expected. /// Rates are stored as millibps (blocks per 1000 seconds) for precision. - LowBlockRate { current_rate_millibps: u32, expected_rate_millibps: u32 }, + LowBlockRate { + current_rate_millibps: u32, + expected_rate_millibps: u32, + }, /// Our tips don't match peer tips. /// `matching_peers_pct` is 0-100. - TipDivergence { matching_peers_pct: u8, threshold_pct: u8 }, + TipDivergence { + matching_peers_pct: u8, + threshold_pct: u8, + }, /// Tip is too old. /// `age_secs` and `max_age_secs` are in seconds. StaleTip { age_secs: u64, max_age_secs: u64 }, /// Protocol version mismatch with majority. - ProtocolVersionSkew { our_version: u32, majority_version: u32 }, + ProtocolVersionSkew { + our_version: u32, + majority_version: u32, + }, /// Most peers have higher blue scores, we may be on a minority fork. BehindNetwork { our_score: u64, network_score: u64 }, /// All connections are inbound (potential eclipse attack). @@ -141,40 +150,59 @@ impl PartitionReason { pub fn description(&self) -> String { match self { PartitionReason::InsufficientPeers { current, required } => { - format!("Only {} peers connected, need at least {}", current, required) + format!( + "Only {} peers connected, need at least {}", + current, required + ) } PartitionReason::InsufficientOutbound { current, required } => { - format!("Only {} outbound peers, need at least {}", current, required) + format!( + "Only {} outbound peers, need at least {}", + current, required + ) } PartitionReason::LowSubnetDiversity { current, required } => { - format!("Only {} unique subnets, need at least {}", current, required) + format!( + "Only {} unique subnets, need at least {}", + current, required + ) } PartitionReason::SubnetConcentration { subnet, percentage } => { format!( "Subnet {:X} has {}% of peers, max allowed 50%", - subnet, - percentage + subnet, percentage ) } PartitionReason::BlockProductionStalled { duration_secs } => { format!("No new blocks for {} seconds", duration_secs) } - PartitionReason::LowBlockRate { current_rate_millibps, expected_rate_millibps } => { + PartitionReason::LowBlockRate { + current_rate_millibps, + expected_rate_millibps, + } => { format!( "Block rate {:.2}/s, expected {:.2}/s", *current_rate_millibps as f64 / 1000.0, *expected_rate_millibps as f64 / 1000.0 ) } - PartitionReason::TipDivergence { matching_peers_pct, threshold_pct } => { + PartitionReason::TipDivergence { + matching_peers_pct, + threshold_pct, + } => { format!( "Only {}% of peers agree on tips, need {}%", - matching_peers_pct, - threshold_pct + matching_peers_pct, threshold_pct ) } - PartitionReason::StaleTip { age_secs, max_age_secs } => { - format!("Best tip is {} seconds old, max allowed {} seconds", age_secs, max_age_secs) + PartitionReason::StaleTip { + age_secs, + max_age_secs, + } => { + format!( + "Best tip is {} seconds old, max allowed {} seconds", + age_secs, max_age_secs + ) } PartitionReason::ProtocolVersionSkew { our_version, @@ -404,12 +432,7 @@ impl PartitionDetector { } /// Records a peer connection. - pub fn record_peer_connected( - &self, - peer_id: PeerId, - ip: Option, - is_outbound: bool, - ) { + pub fn record_peer_connected(&self, peer_id: PeerId, ip: Option, is_outbound: bool) { let info = PeerPartitionInfo::new(peer_id, ip, is_outbound); self.peers.write().insert(peer_id, info); @@ -743,7 +766,9 @@ impl PartitionDetector { // Any critical reason means we're partitioned let mut all_reasons = critical_reasons; all_reasons.extend(warning_reasons); - PartitionStatus::Partitioned { reasons: all_reasons } + PartitionStatus::Partitioned { + reasons: all_reasons, + } } else if !warning_reasons.is_empty() { PartitionStatus::Degraded { reasons: warning_reasons, @@ -801,11 +826,7 @@ impl PartitionDetector { let (status_str, warning_count, critical_count) = match &status { PartitionStatus::Connected => ("Connected".to_string(), 0, 0), - PartitionStatus::Degraded { reasons } => ( - "Degraded".to_string(), - reasons.len(), - 0, - ), + PartitionStatus::Degraded { reasons } => ("Degraded".to_string(), reasons.len(), 0), PartitionStatus::Partitioned { reasons } => { let critical = reasons.iter().filter(|r| r.is_critical()).count(); ( @@ -966,10 +987,9 @@ mod tests { match status { PartitionStatus::Degraded { reasons } => { - assert!(reasons.iter().any(|r| matches!( - r, - PartitionReason::InsufficientPeers { .. } - ))); + assert!(reasons + .iter() + .any(|r| matches!(r, PartitionReason::InsufficientPeers { .. }))); } _ => panic!("Expected degraded status"), } @@ -993,10 +1013,9 @@ mod tests { match status { PartitionStatus::Partitioned { reasons } => { - assert!(reasons.iter().any(|r| matches!( - r, - PartitionReason::NoOutboundConnections - ))); + assert!(reasons + .iter() + .any(|r| matches!(r, PartitionReason::NoOutboundConnections))); } _ => panic!("Expected partitioned status"), } @@ -1098,9 +1117,7 @@ mod tests { required: 3, }, PartitionReason::NoOutboundConnections, - PartitionReason::BlockProductionStalled { - duration_secs: 60, - }, + PartitionReason::BlockProductionStalled { duration_secs: 60 }, ]; for reason in reasons { diff --git a/crates/synor-network/src/peer.rs b/crates/synor-network/src/peer.rs index ca661e3..217cd1f 100644 --- a/crates/synor-network/src/peer.rs +++ b/crates/synor-network/src/peer.rs @@ -334,10 +334,9 @@ impl PeerManager { // Ban after releasing the peers lock to avoid deadlock if should_ban { - self.banned.write().insert( - *peer_id, - Instant::now() + self.ban_duration, - ); + self.banned + .write() + .insert(*peer_id, Instant::now() + self.ban_duration); } true @@ -436,8 +435,14 @@ impl PeerManager { PeerStats { total: peers.len(), active: active.len(), - inbound: active.iter().filter(|p| p.direction == Direction::Inbound).count(), - outbound: active.iter().filter(|p| p.direction == Direction::Outbound).count(), + inbound: active + .iter() + .filter(|p| p.direction == Direction::Inbound) + .count(), + outbound: active + .iter() + .filter(|p| p.direction == Direction::Outbound) + .count(), banned: self.banned.read().len(), avg_latency: active .iter() diff --git a/crates/synor-network/src/protocol.rs b/crates/synor-network/src/protocol.rs index 1ab721d..01270ef 100644 --- a/crates/synor-network/src/protocol.rs +++ b/crates/synor-network/src/protocol.rs @@ -269,13 +269,9 @@ impl Codec for SynorCodec { } /// Creates a request-response behaviour for the Synor protocol. -pub fn create_request_response_behaviour( -) -> request_response::Behaviour { +pub fn create_request_response_behaviour() -> request_response::Behaviour { request_response::Behaviour::new( - [( - StreamProtocol::new(SYNOR_PROTOCOL), - ProtocolSupport::Full, - )], + [(StreamProtocol::new(SYNOR_PROTOCOL), ProtocolSupport::Full)], request_response::Config::default(), ) } diff --git a/crates/synor-network/src/rate_limit.rs b/crates/synor-network/src/rate_limit.rs index c4c1db9..3a0c2af 100644 --- a/crates/synor-network/src/rate_limit.rs +++ b/crates/synor-network/src/rate_limit.rs @@ -371,7 +371,9 @@ impl PerPeerLimiter { pub fn cleanup(&self, connected_peers: &[PeerId]) { let connected: std::collections::HashSet<_> = connected_peers.iter().collect(); self.peers.write().retain(|id, _| connected.contains(id)); - self.cooldowns.write().retain(|id, _| connected.contains(id)); + self.cooldowns + .write() + .retain(|id, _| connected.contains(id)); } /// Returns the number of tracked peers. @@ -522,7 +524,7 @@ mod tests { }; let limiter = PerPeerLimiter::with_violation_config( config, - 3, // max_violations + 3, // max_violations Duration::from_millis(100), // short cooldown for test ); diff --git a/crates/synor-network/src/ratelimit.rs b/crates/synor-network/src/ratelimit.rs index 1c0ab0e..a39f457 100644 --- a/crates/synor-network/src/ratelimit.rs +++ b/crates/synor-network/src/ratelimit.rs @@ -224,7 +224,10 @@ impl RateLimiter { /// Returns the number of requests made by a peer in the current window. pub fn request_count(&self, peer_id: &PeerId) -> u32 { let peers = self.peers.read(); - peers.get(peer_id).map(|s| s.requests.len() as u32).unwrap_or(0) + peers + .get(peer_id) + .map(|s| s.requests.len() as u32) + .unwrap_or(0) } /// Returns the number of violations for a peer. diff --git a/crates/synor-network/src/reputation.rs b/crates/synor-network/src/reputation.rs index 203e0cb..8c2cb54 100644 --- a/crates/synor-network/src/reputation.rs +++ b/crates/synor-network/src/reputation.rs @@ -178,7 +178,10 @@ impl PeerReputation { pub fn record_success(&mut self) { self.successes += 1; self.last_seen = Instant::now(); - self.score = self.score.saturating_add(Self::SUCCESS_BONUS).min(Self::MAX_SCORE); + self.score = self + .score + .saturating_add(Self::SUCCESS_BONUS) + .min(Self::MAX_SCORE); } /// Records a violation and returns true if the peer should be banned. @@ -602,7 +605,10 @@ mod tests { rep.record_success(); assert_eq!(rep.successes, 1); - assert_eq!(rep.score, PeerReputation::INITIAL_SCORE + PeerReputation::SUCCESS_BONUS); + assert_eq!( + rep.score, + PeerReputation::INITIAL_SCORE + PeerReputation::SUCCESS_BONUS + ); } #[test] @@ -690,7 +696,10 @@ mod tests { manager.record_success(&peer_id); let score = manager.get_score(&peer_id).unwrap(); - assert_eq!(score, PeerReputation::INITIAL_SCORE + PeerReputation::SUCCESS_BONUS); + assert_eq!( + score, + PeerReputation::INITIAL_SCORE + PeerReputation::SUCCESS_BONUS + ); } #[test] diff --git a/crates/synor-network/src/service.rs b/crates/synor-network/src/service.rs index 0eb8697..233254a 100644 --- a/crates/synor-network/src/service.rs +++ b/crates/synor-network/src/service.rs @@ -3,7 +3,7 @@ use crate::behaviour::SynorBehaviour; use crate::config::NetworkConfig; use crate::message::{BlockAnnouncement, TransactionAnnouncement}; -use crate::peer::{Direction, PeerInfo, PeerManager, reputation}; +use crate::peer::{reputation, Direction, PeerInfo, PeerManager}; use crate::protocol::{SynorRequest, SynorResponse}; use crate::rate_limit::{PerPeerLimiter, RateLimitConfig as TokenBucketConfig}; use crate::ratelimit::{RateLimitResult, RateLimiters}; @@ -12,14 +12,8 @@ use crate::sync::{SyncManager, SyncStatus}; use crate::topics; use futures::StreamExt; use libp2p::{ - gossipsub, - identify, - kad, - mdns, - ping, - request_response, - swarm::SwarmEvent, - Multiaddr, PeerId, Swarm, SwarmBuilder, + gossipsub, identify, kad, mdns, ping, request_response, swarm::SwarmEvent, Multiaddr, PeerId, + Swarm, SwarmBuilder, }; use parking_lot::RwLock; use std::sync::Arc; @@ -125,7 +119,10 @@ impl NetworkHandle { } /// Broadcasts a block to the network. - pub async fn broadcast_block(&self, announcement: BlockAnnouncement) -> Result<(), NetworkError> { + pub async fn broadcast_block( + &self, + announcement: BlockAnnouncement, + ) -> Result<(), NetworkError> { self.command_tx .send(NetworkCommand::BroadcastBlock(announcement)) .await @@ -133,7 +130,10 @@ impl NetworkHandle { } /// Broadcasts a transaction to the network. - pub async fn broadcast_transaction(&self, announcement: TransactionAnnouncement) -> Result<(), NetworkError> { + pub async fn broadcast_transaction( + &self, + announcement: TransactionAnnouncement, + ) -> Result<(), NetworkError> { self.command_tx .send(NetworkCommand::BroadcastTransaction(announcement)) .await @@ -278,10 +278,8 @@ pub struct NetworkService { /// Event sender. event_tx: broadcast::Sender, /// Pending requests. - pending_requests: RwLock>, + pending_requests: + RwLock>, } struct PendingRequest { @@ -290,7 +288,9 @@ struct PendingRequest { impl NetworkService { /// Creates a new network service. - pub async fn new(config: NetworkConfig) -> Result<(Self, NetworkHandle), Box> { + pub async fn new( + config: NetworkConfig, + ) -> Result<(Self, NetworkHandle), Box> { // Generate keypair let local_keypair = libp2p::identity::Keypair::generate_ed25519(); let local_peer_id = PeerId::from(local_keypair.public()); @@ -310,9 +310,7 @@ impl NetworkService { .with_behaviour(|key| { SynorBehaviour::new(key, &config).expect("Behaviour creation failed") })? - .with_swarm_config(|cfg| { - cfg.with_idle_connection_timeout(config.idle_timeout) - }) + .with_swarm_config(|cfg| cfg.with_idle_connection_timeout(config.idle_timeout)) .build(); // Create peer, sync, and reputation managers @@ -397,13 +395,18 @@ impl NetworkService { } /// Handles a swarm event. - async fn handle_swarm_event(&mut self, event: SwarmEvent) { + async fn handle_swarm_event( + &mut self, + event: SwarmEvent, + ) { match event { SwarmEvent::NewListenAddr { address, .. } => { info!("Listening on {}", address); } - SwarmEvent::ConnectionEstablished { peer_id, endpoint, .. } => { + SwarmEvent::ConnectionEstablished { + peer_id, endpoint, .. + } => { // Check if peer is banned via reputation system if self.reputation_manager.is_banned(&peer_id) { warn!("Rejecting connection from banned peer: {}", peer_id); @@ -458,22 +461,26 @@ impl NetworkService { message_id: _, message, }) => { - self.handle_gossip_message(propagation_source, message).await; + self.handle_gossip_message(propagation_source, message) + .await; } SynorBehaviourEvent::RequestResponse(request_response::Event::Message { peer, message, - }) => { - match message { - request_response::Message::Request { request, channel, .. } => { - self.handle_request(peer, request, channel).await; - } - request_response::Message::Response { request_id, response } => { - self.handle_response(peer, request_id, response).await; - } + }) => match message { + request_response::Message::Request { + request, channel, .. + } => { + self.handle_request(peer, request, channel).await; } - } + request_response::Message::Response { + request_id, + response, + } => { + self.handle_response(peer, request_id, response).await; + } + }, SynorBehaviourEvent::RequestResponse(request_response::Event::OutboundFailure { peer, @@ -484,7 +491,9 @@ impl NetworkService { self.sync_manager.on_request_failed(request_id); self.peer_manager.update_peer(&peer, |p| p.record_failure()); // Record violation in reputation system (no response from peer) - let auto_banned = self.reputation_manager.record_violation(&peer, ViolationType::NoResponse); + let auto_banned = self + .reputation_manager + .record_violation(&peer, ViolationType::NoResponse); if auto_banned { warn!("Auto-banned peer {} for repeated failures", peer); let _ = self.swarm.disconnect_peer_id(peer); @@ -508,25 +517,25 @@ impl NetworkService { } } - SynorBehaviourEvent::Ping(ping::Event { peer, result, .. }) => { - match result { - Ok(rtt) => { - trace!("Ping to {} succeeded: {:?}", peer, rtt); - self.peer_manager.update_peer(&peer, |p| { - p.latency = Some(rtt); - }); - } - Err(e) => { - debug!("Ping to {} failed: {}", peer, e); - } + SynorBehaviourEvent::Ping(ping::Event { peer, result, .. }) => match result { + Ok(rtt) => { + trace!("Ping to {} succeeded: {:?}", peer, rtt); + self.peer_manager.update_peer(&peer, |p| { + p.latency = Some(rtt); + }); } - } + Err(e) => { + debug!("Ping to {} failed: {}", peer, e); + } + }, SynorBehaviourEvent::Mdns(mdns::Event::Discovered(peers)) => { for (peer_id, addr) in peers { debug!("mDNS discovered peer: {} at {}", peer_id, addr); if self.peer_manager.can_connect_outbound() { - self.swarm.behaviour_mut().add_address(&peer_id, addr.clone()); + self.swarm + .behaviour_mut() + .add_address(&peer_id, addr.clone()); if let Err(e) = self.swarm.dial(addr) { debug!("Failed to dial discovered peer: {}", e); } @@ -540,19 +549,19 @@ impl NetworkService { } } - SynorBehaviourEvent::Kademlia(kad::Event::OutboundQueryProgressed { result, .. }) => { - match result { - kad::QueryResult::GetClosestPeers(Ok(ok)) => { - for peer in ok.peers { - debug!("Kademlia found peer: {:?}", peer); - } + SynorBehaviourEvent::Kademlia(kad::Event::OutboundQueryProgressed { + result, .. + }) => match result { + kad::QueryResult::GetClosestPeers(Ok(ok)) => { + for peer in ok.peers { + debug!("Kademlia found peer: {:?}", peer); } - kad::QueryResult::Bootstrap(Ok(_)) => { - info!("Kademlia bootstrap complete"); - } - _ => {} } - } + kad::QueryResult::Bootstrap(Ok(_)) => { + info!("Kademlia bootstrap complete"); + } + _ => {} + }, _ => {} } @@ -565,14 +574,24 @@ impl NetworkService { match topic { t if t == topics::BLOCKS => { if let Ok(announcement) = borsh::from_slice::(&message.data) { - debug!("Received block announcement from {}: {}", source, announcement.hash); + debug!( + "Received block announcement from {}: {}", + source, announcement.hash + ); let _ = self.event_tx.send(NetworkEvent::NewBlock(announcement)); } } t if t == topics::TRANSACTIONS => { - if let Ok(announcement) = borsh::from_slice::(&message.data) { - debug!("Received tx announcement from {}: {}", source, announcement.txid); - let _ = self.event_tx.send(NetworkEvent::NewTransaction(announcement)); + if let Ok(announcement) = + borsh::from_slice::(&message.data) + { + debug!( + "Received tx announcement from {}: {}", + source, announcement.txid + ); + let _ = self + .event_tx + .send(NetworkEvent::NewTransaction(announcement)); } } _ => { @@ -614,7 +633,9 @@ impl NetworkService { p.add_reputation(reputation::INVALID_DATA); }); // Record spam violation in reputation system (may auto-ban) - let auto_banned = self.reputation_manager.record_violation(&peer, ViolationType::Spam); + let auto_banned = self + .reputation_manager + .record_violation(&peer, ViolationType::Spam); if auto_banned { warn!("Auto-banned peer {} for repeated spam violations", peer); let _ = self.swarm.disconnect_peer_id(peer); @@ -640,14 +661,15 @@ impl NetworkService { p.add_reputation(reputation::TIMEOUT); }); // Record spam violation in reputation system - let auto_banned = self.reputation_manager.record_violation(&peer, ViolationType::Spam); + let auto_banned = self + .reputation_manager + .record_violation(&peer, ViolationType::Spam); if auto_banned { warn!("Auto-banned peer {} for spam violations", peer); let _ = self.swarm.disconnect_peer_id(peer); } - let response = SynorResponse::Error( - "Rate limited. Too many requests per second.".to_string() - ); + let response = + SynorResponse::Error("Rate limited. Too many requests per second.".to_string()); if let Err(e) = self.swarm.behaviour_mut().send_response(channel, response) { warn!("Failed to send rate limit response to {}: {:?}", peer, e); } @@ -688,7 +710,8 @@ impl NetworkService { p.add_reputation(reputation::TIMEOUT); }); // Record slow response violation - self.reputation_manager.record_violation(&peer, ViolationType::SlowResponse); + self.reputation_manager + .record_violation(&peer, ViolationType::SlowResponse); let response = SynorResponse::Error(format!( "Rate limited. Retry after {} seconds.", retry_after.as_secs() @@ -710,9 +733,14 @@ impl NetworkService { p.add_reputation(reputation::INVALID_DATA); }); // Record spam violation (may auto-ban) - let auto_banned = self.reputation_manager.record_violation(&peer, ViolationType::Spam); + let auto_banned = self + .reputation_manager + .record_violation(&peer, ViolationType::Spam); if auto_banned { - warn!("Auto-banned peer {} for repeated rate limit violations", peer); + warn!( + "Auto-banned peer {} for repeated rate limit violations", + peer + ); let _ = self.swarm.disconnect_peer_id(peer); } let response = SynorResponse::Error(format!( @@ -794,7 +822,8 @@ impl NetworkService { let _ = self.event_tx.send(NetworkEvent::HeadersReceived(headers)); } SynorResponse::Blocks(blocks) => { - self.sync_manager.on_blocks_response(request_id, blocks.clone()); + self.sync_manager + .on_blocks_response(request_id, blocks.clone()); let _ = self.event_tx.send(NetworkEvent::BlocksReceived(blocks)); } _ => {} @@ -848,17 +877,30 @@ impl NetworkService { } } - NetworkCommand::RequestBlocks { peer, block_ids, response } => { + NetworkCommand::RequestBlocks { + peer, + block_ids, + response, + } => { let request = SynorRequest::GetBlocks(block_ids); let _request_id = self.swarm.behaviour_mut().send_request(&peer, request); // Would need to track the response sender - let _ = response.send(Err(NetworkError::RequestFailed("Not implemented".to_string()))); + let _ = response.send(Err(NetworkError::RequestFailed( + "Not implemented".to_string(), + ))); } - NetworkCommand::RequestHeaders { peer, start, max_count, response } => { + NetworkCommand::RequestHeaders { + peer, + start, + max_count, + response, + } => { let request = SynorRequest::GetHeaders { start, max_count }; let _request_id = self.swarm.behaviour_mut().send_request(&peer, request); - let _ = response.send(Err(NetworkError::RequestFailed("Not implemented".to_string()))); + let _ = response.send(Err(NetworkError::RequestFailed( + "Not implemented".to_string(), + ))); } NetworkCommand::GetPeerCount(response) => { diff --git a/crates/synor-network/src/sync.rs b/crates/synor-network/src/sync.rs index 16eade1..5f7c2bb 100644 --- a/crates/synor-network/src/sync.rs +++ b/crates/synor-network/src/sync.rs @@ -188,11 +188,7 @@ pub struct SyncManager { impl SyncManager { /// Creates a new sync manager. - pub fn new( - config: SyncConfig, - peer_manager: Arc, - genesis_hash: Hash256, - ) -> Self { + pub fn new(config: SyncConfig, peer_manager: Arc, genesis_hash: Hash256) -> Self { SyncManager { config, status: RwLock::new(SyncStatus::default()), diff --git a/crates/synor-rpc/src/api.rs b/crates/synor-rpc/src/api.rs index e0b67c8..8b052f8 100644 --- a/crates/synor-rpc/src/api.rs +++ b/crates/synor-rpc/src/api.rs @@ -174,7 +174,8 @@ pub trait UtxoApi { /// Gets balances by addresses. #[method(name = "getBalancesByAddresses")] - async fn get_balances_by_addresses(&self, addresses: Vec) -> RpcResult>; + async fn get_balances_by_addresses(&self, addresses: Vec) + -> RpcResult>; /// Gets coin supply. #[method(name = "getCoinSupply")] @@ -194,7 +195,10 @@ pub trait MiningApi { /// Submits a mined block. #[method(name = "submitBlock")] - async fn submit_block(&self, request: RpcSubmitBlockRequest) -> RpcResult; + async fn submit_block( + &self, + request: RpcSubmitBlockRequest, + ) -> RpcResult; /// Gets current network hashrate. #[method(name = "getNetworkHashrate")] diff --git a/crates/synor-rpc/src/lib.rs b/crates/synor-rpc/src/lib.rs index 0467884..6ad2693 100644 --- a/crates/synor-rpc/src/lib.rs +++ b/crates/synor-rpc/src/lib.rs @@ -38,13 +38,13 @@ pub mod server; pub mod types; pub use api::{ - BlockApiServer, MiningApiServer, NetworkApiServer, TransactionApiServer, UtxoApiServer, - ContractApiServer, SubscriptionApiServer, + BlockApiServer, ContractApiServer, MiningApiServer, NetworkApiServer, SubscriptionApiServer, + TransactionApiServer, UtxoApiServer, }; pub use pool::{ - ConnectionPool, ConnectionPoolExt, PoolConfig, PoolError, PoolStats, PoolStatsSnapshot, - PooledHttpClient, PooledHttpClientGuard, PooledWsClient, PooledWsClientGuard, - global_pool, init_global_pool, + global_pool, init_global_pool, ConnectionPool, ConnectionPoolExt, PoolConfig, PoolError, + PoolStats, PoolStatsSnapshot, PooledHttpClient, PooledHttpClientGuard, PooledWsClient, + PooledWsClientGuard, }; pub use server::{RpcConfig, RpcServer}; pub use types::*; diff --git a/crates/synor-rpc/src/pool.rs b/crates/synor-rpc/src/pool.rs index f116e20..8abad7b 100644 --- a/crates/synor-rpc/src/pool.rs +++ b/crates/synor-rpc/src/pool.rs @@ -102,7 +102,9 @@ impl PoolStats { PoolStatsSnapshot { connections_created: self.connections_created.load(Ordering::Relaxed), connections_closed: self.connections_closed.load(Ordering::Relaxed), - connections_active: self.connections_created.load(Ordering::Relaxed) + connections_active: self + .connections_created + .load(Ordering::Relaxed) .saturating_sub(self.connections_closed.load(Ordering::Relaxed)), requests_total: self.requests_total.load(Ordering::Relaxed), requests_failed: self.requests_failed.load(Ordering::Relaxed), @@ -289,7 +291,9 @@ impl ConnectionPool { // Try to get an existing client if let Some(client) = entry_lock.http_clients.pop() { - self.stats.connections_reused.fetch_add(1, Ordering::Relaxed); + self.stats + .connections_reused + .fetch_add(1, Ordering::Relaxed); client.touch(); return Ok(client); } @@ -332,7 +336,9 @@ impl ConnectionPool { // Try to get an existing client if let Some(client) = entry_lock.ws_clients.pop() { - self.stats.connections_reused.fetch_add(1, Ordering::Relaxed); + self.stats + .connections_reused + .fetch_add(1, Ordering::Relaxed); client.touch(); return Ok(client); } @@ -355,7 +361,9 @@ impl ConnectionPool { { entry_lock.http_clients.push(client); } else { - self.stats.connections_closed.fetch_add(1, Ordering::Relaxed); + self.stats + .connections_closed + .fetch_add(1, Ordering::Relaxed); } } } @@ -373,7 +381,9 @@ impl ConnectionPool { { entry_lock.ws_clients.push(client); } else { - self.stats.connections_closed.fetch_add(1, Ordering::Relaxed); + self.stats + .connections_closed + .fetch_add(1, Ordering::Relaxed); } } } @@ -394,7 +404,9 @@ impl ConnectionPool { entry_lock.consecutive_failures += 1; if entry_lock.consecutive_failures >= self.config.max_retries { entry_lock.healthy = false; - self.stats.health_check_failures.fetch_add(1, Ordering::Relaxed); + self.stats + .health_check_failures + .fetch_add(1, Ordering::Relaxed); } } } @@ -509,14 +521,17 @@ impl ConnectionPool { consecutive_failures: 0, })); - endpoints.insert(endpoint.to_string(), Mutex::new(EndpointEntry { - http_clients: Vec::new(), - ws_clients: Vec::new(), - semaphore: Arc::new(Semaphore::new(self.config.max_connections_per_endpoint)), - last_health_check: Instant::now(), - healthy: true, - consecutive_failures: 0, - })); + endpoints.insert( + endpoint.to_string(), + Mutex::new(EndpointEntry { + http_clients: Vec::new(), + ws_clients: Vec::new(), + semaphore: Arc::new(Semaphore::new(self.config.max_connections_per_endpoint)), + last_health_check: Instant::now(), + healthy: true, + consecutive_failures: 0, + }), + ); entry } @@ -533,7 +548,9 @@ impl ConnectionPool { match self.try_create_http_client(endpoint).await { Ok(client) => { - self.stats.connections_created.fetch_add(1, Ordering::Relaxed); + self.stats + .connections_created + .fetch_add(1, Ordering::Relaxed); self.mark_healthy(endpoint); return Ok(client); } @@ -545,9 +562,7 @@ impl ConnectionPool { } self.stats.requests_failed.fetch_add(1, Ordering::Relaxed); - Err(last_error.unwrap_or(PoolError::ConnectionFailed( - "Unknown error".to_string(), - ))) + Err(last_error.unwrap_or(PoolError::ConnectionFailed("Unknown error".to_string()))) } /// Creates a new WebSocket client with retries. @@ -562,7 +577,9 @@ impl ConnectionPool { match self.try_create_ws_client(endpoint).await { Ok(client) => { - self.stats.connections_created.fetch_add(1, Ordering::Relaxed); + self.stats + .connections_created + .fetch_add(1, Ordering::Relaxed); self.mark_healthy(endpoint); return Ok(client); } @@ -574,9 +591,7 @@ impl ConnectionPool { } self.stats.requests_failed.fetch_add(1, Ordering::Relaxed); - Err(last_error.unwrap_or(PoolError::ConnectionFailed( - "Unknown error".to_string(), - ))) + Err(last_error.unwrap_or(PoolError::ConnectionFailed("Unknown error".to_string()))) } /// Attempts to create an HTTP client. @@ -599,10 +614,7 @@ impl ConnectionPool { } /// Attempts to create a WebSocket client. - async fn try_create_ws_client( - &self, - endpoint: &str, - ) -> Result, PoolError> { + async fn try_create_ws_client(&self, endpoint: &str) -> Result, PoolError> { let client = WsClientBuilder::default() .request_timeout(self.config.request_timeout) .connection_timeout(self.config.connect_timeout) @@ -674,7 +686,10 @@ impl<'a> PooledHttpClientGuard<'a> { pub fn success(&self) { if let Some(ref client) = self.client { client.touch(); - self.pool.stats.requests_total.fetch_add(1, Ordering::Relaxed); + self.pool + .stats + .requests_total + .fetch_add(1, Ordering::Relaxed); } } @@ -682,14 +697,20 @@ impl<'a> PooledHttpClientGuard<'a> { pub fn failed(&self) { if let Some(ref client) = self.client { self.pool.mark_unhealthy(client.endpoint()); - self.pool.stats.requests_failed.fetch_add(1, Ordering::Relaxed); + self.pool + .stats + .requests_failed + .fetch_add(1, Ordering::Relaxed); } } /// Discards the connection (don't return to pool). pub fn discard(mut self) { if let Some(client) = self.client.take() { - self.pool.stats.connections_closed.fetch_add(1, Ordering::Relaxed); + self.pool + .stats + .connections_closed + .fetch_add(1, Ordering::Relaxed); drop(client); } } @@ -727,7 +748,10 @@ impl<'a> PooledWsClientGuard<'a> { pub fn success(&self) { if let Some(ref client) = self.client { client.touch(); - self.pool.stats.requests_total.fetch_add(1, Ordering::Relaxed); + self.pool + .stats + .requests_total + .fetch_add(1, Ordering::Relaxed); } } @@ -735,14 +759,20 @@ impl<'a> PooledWsClientGuard<'a> { pub fn failed(&self) { if let Some(ref client) = self.client { self.pool.mark_unhealthy(client.endpoint()); - self.pool.stats.requests_failed.fetch_add(1, Ordering::Relaxed); + self.pool + .stats + .requests_failed + .fetch_add(1, Ordering::Relaxed); } } /// Discards the connection (don't return to pool). pub fn discard(mut self) { if let Some(client) = self.client.take() { - self.pool.stats.connections_closed.fetch_add(1, Ordering::Relaxed); + self.pool + .stats + .connections_closed + .fetch_add(1, Ordering::Relaxed); drop(client); } } @@ -760,7 +790,10 @@ impl<'a> Drop for PooledWsClientGuard<'a> { #[async_trait::async_trait] pub trait ConnectionPoolExt { /// Acquires an HTTP client and returns a guard. - async fn acquire_http_guard(&self, endpoint: &str) -> Result, PoolError>; + async fn acquire_http_guard( + &self, + endpoint: &str, + ) -> Result, PoolError>; /// Acquires a WebSocket client and returns a guard. async fn acquire_ws_guard(&self, endpoint: &str) -> Result, PoolError>; @@ -768,7 +801,10 @@ pub trait ConnectionPoolExt { #[async_trait::async_trait] impl ConnectionPoolExt for ConnectionPool { - async fn acquire_http_guard(&self, endpoint: &str) -> Result, PoolError> { + async fn acquire_http_guard( + &self, + endpoint: &str, + ) -> Result, PoolError> { let client = self.acquire_http(endpoint).await?; Ok(PooledHttpClientGuard::new(self, client)) } @@ -806,7 +842,10 @@ mod tests { #[test] fn test_pool_config_high_throughput() { let config = PoolConfig::high_throughput(); - assert!(config.max_connections_per_endpoint > PoolConfig::default().max_connections_per_endpoint); + assert!( + config.max_connections_per_endpoint + > PoolConfig::default().max_connections_per_endpoint + ); } #[test] diff --git a/crates/synor-rpc/src/server.rs b/crates/synor-rpc/src/server.rs index 4bfdb10..1746fcb 100644 --- a/crates/synor-rpc/src/server.rs +++ b/crates/synor-rpc/src/server.rs @@ -131,7 +131,9 @@ impl RpcServer { .await .map_err(|e: std::io::Error| RpcServerError::StartFailed(e.to_string()))?; - let addr = server.local_addr().map_err(|e: std::io::Error| RpcServerError::StartFailed(e.to_string()))?; + let addr = server + .local_addr() + .map_err(|e: std::io::Error| RpcServerError::StartFailed(e.to_string()))?; tracing::info!("HTTP RPC server listening on {}", addr); self.http_handle = Some(server.start(module)); @@ -150,7 +152,9 @@ impl RpcServer { .await .map_err(|e: std::io::Error| RpcServerError::StartFailed(e.to_string()))?; - let addr = server.local_addr().map_err(|e: std::io::Error| RpcServerError::StartFailed(e.to_string()))?; + let addr = server + .local_addr() + .map_err(|e: std::io::Error| RpcServerError::StartFailed(e.to_string()))?; tracing::info!("WebSocket RPC server listening on {}", addr); self.ws_handle = Some(server.start(module)); @@ -235,9 +239,9 @@ impl RpcModuleBuilder { F: Fn(jsonrpsee::types::Params<'_>) -> R + Send + Sync + Clone + 'static, R: jsonrpsee::IntoResponse + Send + 'static, { - let _ = self.module.register_method(name, move |params, _| { - callback(params) - }); + let _ = self + .module + .register_method(name, move |params, _| callback(params)); self } diff --git a/crates/synor-sdk/src/error.rs b/crates/synor-sdk/src/error.rs index 4510fca..3bcca3c 100644 --- a/crates/synor-sdk/src/error.rs +++ b/crates/synor-sdk/src/error.rs @@ -131,7 +131,10 @@ impl fmt::Display for Error { Error::Failed(msg) => write!(f, "Failed: {}", msg), Error::InvalidMethod => write!(f, "Invalid method"), Error::InvalidArgs(msg) => write!(f, "Invalid arguments: {}", msg), - Error::InsufficientBalance { required, available } => { + Error::InsufficientBalance { + required, + available, + } => { write!( f, "Insufficient balance: required {}, available {}", diff --git a/crates/synor-sdk/src/host.rs b/crates/synor-sdk/src/host.rs index bc0b43c..5fa4bf6 100644 --- a/crates/synor-sdk/src/host.rs +++ b/crates/synor-sdk/src/host.rs @@ -61,7 +61,13 @@ pub fn storage_read(key: &[u8; 32], out: &mut [u8]) -> i32 { /// Writes to storage. #[cfg(target_arch = "wasm32")] pub fn storage_write(key: &[u8; 32], value: &[u8]) -> i32 { - unsafe { synor_storage_write(key.as_ptr() as i32, value.as_ptr() as i32, value.len() as i32) } + unsafe { + synor_storage_write( + key.as_ptr() as i32, + value.as_ptr() as i32, + value.len() as i32, + ) + } } /// Deletes from storage. Returns 1 if key existed, 0 otherwise. @@ -152,7 +158,13 @@ pub fn get_chain_id() -> u64 { #[cfg(target_arch = "wasm32")] pub fn sha3(data: &[u8]) -> [u8; 32] { let mut out = [0u8; 32]; - unsafe { synor_sha3(data.as_ptr() as i32, data.len() as i32, out.as_mut_ptr() as i32) }; + unsafe { + synor_sha3( + data.as_ptr() as i32, + data.len() as i32, + out.as_mut_ptr() as i32, + ) + }; out } @@ -160,7 +172,13 @@ pub fn sha3(data: &[u8]) -> [u8; 32] { #[cfg(target_arch = "wasm32")] pub fn blake3(data: &[u8]) -> [u8; 32] { let mut out = [0u8; 32]; - unsafe { synor_blake3(data.as_ptr() as i32, data.len() as i32, out.as_mut_ptr() as i32) }; + unsafe { + synor_blake3( + data.as_ptr() as i32, + data.len() as i32, + out.as_mut_ptr() as i32, + ) + }; out } diff --git a/crates/synor-sdk/src/lib.rs b/crates/synor-sdk/src/lib.rs index 493f7f0..be99a0e 100644 --- a/crates/synor-sdk/src/lib.rs +++ b/crates/synor-sdk/src/lib.rs @@ -186,9 +186,7 @@ macro_rules! entry_point { $crate::host::return_data(&data); data.len() as i32 } - Err(e) => { - $crate::host::revert(&e.to_string()) - } + Err(e) => $crate::host::revert(&e.to_string()), } } }; diff --git a/crates/synor-storage/benches/storage_bench.rs b/crates/synor-storage/benches/storage_bench.rs index 7ca2fd8..f80d5d8 100644 --- a/crates/synor-storage/benches/storage_bench.rs +++ b/crates/synor-storage/benches/storage_bench.rs @@ -9,17 +9,16 @@ //! //! Run with: cargo bench -p synor-storage --bench storage_bench -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::sync::Arc; use synor_storage::{ cache::{CacheConfig, LruCache, StorageCache}, - cf, Database, DatabaseConfig, + cf, stores::{ - ChainState, GhostdagStore, HeaderStore, MetadataStore, RelationsStore, - StoredGhostdagData, StoredRelations, StoredUtxo, UtxoStore, + ChainState, GhostdagStore, HeaderStore, MetadataStore, RelationsStore, StoredGhostdagData, + StoredRelations, StoredUtxo, UtxoStore, }, + Database, DatabaseConfig, }; use synor_types::{BlockHeader, BlockId, BlueScore, Hash256, Timestamp, TransactionId}; use tempfile::TempDir; @@ -83,16 +82,22 @@ fn make_ghostdag_data(n: u64) -> StoredGhostdagData { StoredGhostdagData { blue_score: n * 10, selected_parent: make_block_id(n.saturating_sub(1)), - merge_set_blues: (0..5).map(|i| make_block_id(n.saturating_sub(i + 1))).collect(), + merge_set_blues: (0..5) + .map(|i| make_block_id(n.saturating_sub(i + 1))) + .collect(), merge_set_reds: (0..2).map(|i| make_block_id(n + i + 100)).collect(), - blues_anticone_sizes: (0..3).map(|i| (make_block_id(n.saturating_sub(i + 1)), i + 1)).collect(), + blues_anticone_sizes: (0..3) + .map(|i| (make_block_id(n.saturating_sub(i + 1)), i + 1)) + .collect(), } } /// Creates a test relations entry. fn make_relations(n: u64) -> StoredRelations { StoredRelations { - parents: (1..=3).map(|i| make_block_id(n.saturating_sub(i))).collect(), + parents: (1..=3) + .map(|i| make_block_id(n.saturating_sub(i))) + .collect(), children: (1..=2).map(|i| make_block_id(n + i)).collect(), } } @@ -157,25 +162,21 @@ fn header_write_batch(c: &mut Criterion) { let headers: Vec = (0..count).map(|i| make_header(i as u64)).collect(); group.throughput(Throughput::Elements(count as u64)); - group.bench_with_input( - BenchmarkId::from_parameter(count), - &headers, - |b, hdrs| { - b.iter_batched( - || { - let (dir, db) = setup_db(); - let store = HeaderStore::new(db); - (dir, store) - }, - |(_dir, store)| { - for header in hdrs { - store.put(header).unwrap(); - } - }, - criterion::BatchSize::SmallInput, - ) - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(count), &headers, |b, hdrs| { + b.iter_batched( + || { + let (dir, db) = setup_db(); + let store = HeaderStore::new(db); + (dir, store) + }, + |(_dir, store)| { + for header in hdrs { + store.put(header).unwrap(); + } + }, + criterion::BatchSize::SmallInput, + ) + }); } group.finish(); @@ -189,9 +190,7 @@ fn header_read_single(c: &mut Criterion) { let store = populate_headers(&db, 1000); let target_hash = make_header(500).block_id(); - group.bench_function("single", |b| { - b.iter(|| black_box(store.get(&target_hash))) - }); + group.bench_function("single", |b| b.iter(|| black_box(store.get(&target_hash)))); drop(dir); group.finish(); @@ -208,9 +207,7 @@ fn header_read_varying_db_sizes(c: &mut Criterion) { group.bench_with_input( BenchmarkId::from_parameter(db_size), &target_hash, - |b, hash| { - b.iter(|| black_box(store.get(hash))) - }, + |b, hash| b.iter(|| black_box(store.get(hash))), ); drop(dir); @@ -231,13 +228,9 @@ fn header_multi_get(c: &mut Criterion) { .collect(); group.throughput(Throughput::Elements(count as u64)); - group.bench_with_input( - BenchmarkId::from_parameter(count), - &hashes, - |b, h| { - b.iter(|| black_box(store.multi_get(h))) - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(count), &hashes, |b, h| { + b.iter(|| black_box(store.multi_get(h))) + }); } drop(dir); @@ -304,9 +297,7 @@ fn utxo_lookup(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("size", db_size), &target_txid, - |b, txid| { - b.iter(|| black_box(store.get(txid, 0))) - }, + |b, txid| b.iter(|| black_box(store.get(txid, 0))), ); drop(dir); @@ -320,26 +311,22 @@ fn utxo_delete(c: &mut Criterion) { group.throughput(Throughput::Elements(1)); for db_size in [1000, 10000] { - group.bench_with_input( - BenchmarkId::new("size", db_size), - &db_size, - |b, &size| { - b.iter_batched( - || { - let (dir, db) = setup_db(); - let store = populate_utxos(&db, size); - let target_txid = make_txid((size / 2) as u64); - (dir, store, target_txid) - }, - |(dir, store, txid)| { - let result = store.delete(&txid, 0); - drop(dir); - black_box(result) - }, - criterion::BatchSize::SmallInput, - ) - }, - ); + group.bench_with_input(BenchmarkId::new("size", db_size), &db_size, |b, &size| { + b.iter_batched( + || { + let (dir, db) = setup_db(); + let store = populate_utxos(&db, size); + let target_txid = make_txid((size / 2) as u64); + (dir, store, target_txid) + }, + |(dir, store, txid)| { + let result = store.delete(&txid, 0); + drop(dir); + black_box(result) + }, + criterion::BatchSize::SmallInput, + ) + }); } group.finish(); @@ -474,19 +461,15 @@ fn iterator_full_scan(c: &mut Criterion) { let _ = populate_headers(&db, db_size); group.throughput(Throughput::Elements(db_size as u64)); - group.bench_with_input( - BenchmarkId::from_parameter(db_size), - &db, - |b, database| { - b.iter(|| { - let mut count = 0; - for _ in database.iter(cf::HEADERS).unwrap() { - count += 1; - } - black_box(count) - }) - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(db_size), &db, |b, database| { + b.iter(|| { + let mut count = 0; + for _ in database.iter(cf::HEADERS).unwrap() { + count += 1; + } + black_box(count) + }) + }); drop(dir); } @@ -502,7 +485,13 @@ fn iterator_prefix_scan(c: &mut Criterion) { for tx_num in 0..100 { let txid = make_txid(tx_num); for output_idx in 0..10 { - store.put(&txid, output_idx, &make_utxo(tx_num * 10 + output_idx as u64)).unwrap(); + store + .put( + &txid, + output_idx, + &make_utxo(tx_num * 10 + output_idx as u64), + ) + .unwrap(); } } @@ -535,9 +524,7 @@ fn ghostdag_operations(c: &mut Criterion) { // Read let target = make_block_id(500); - group.bench_function("get", |b| { - b.iter(|| black_box(store.get(&target))) - }); + group.bench_function("get", |b| b.iter(|| black_box(store.get(&target)))); group.bench_function("get_blue_score", |b| { b.iter(|| black_box(store.get_blue_score(&target))) @@ -581,9 +568,7 @@ fn relations_operations(c: &mut Criterion) { let target = make_block_id(500); - group.bench_function("get", |b| { - b.iter(|| black_box(store.get(&target))) - }); + group.bench_function("get", |b| b.iter(|| black_box(store.get(&target)))); group.bench_function("get_parents", |b| { b.iter(|| black_box(store.get_parents(&target))) @@ -621,18 +606,14 @@ fn metadata_operations(c: &mut Criterion) { let mut group = c.benchmark_group("metadata_store"); - group.bench_function("get_tips", |b| { - b.iter(|| black_box(store.get_tips())) - }); + group.bench_function("get_tips", |b| b.iter(|| black_box(store.get_tips()))); group.bench_function("set_tips", |b| { let new_tips = vec![make_block_id(200), make_block_id(201)]; b.iter(|| black_box(store.set_tips(&new_tips))) }); - group.bench_function("get_genesis", |b| { - b.iter(|| black_box(store.get_genesis())) - }); + group.bench_function("get_genesis", |b| b.iter(|| black_box(store.get_genesis()))); group.bench_function("get_chain_state", |b| { b.iter(|| black_box(store.get_chain_state())) @@ -747,9 +728,7 @@ fn storage_cache_operations(c: &mut Criterion) { }); // Stats access - group.bench_function("stats", |b| { - b.iter(|| black_box(cache.stats())) - }); + group.bench_function("stats", |b| b.iter(|| black_box(cache.stats()))); // Total entries group.bench_function("total_entries", |b| { @@ -812,17 +791,9 @@ criterion_group!( utxo_get_by_tx, ); -criterion_group!( - batch_benches, - batch_write_mixed, - batch_headers_and_utxos, -); +criterion_group!(batch_benches, batch_write_mixed, batch_headers_and_utxos,); -criterion_group!( - iterator_benches, - iterator_full_scan, - iterator_prefix_scan, -); +criterion_group!(iterator_benches, iterator_full_scan, iterator_prefix_scan,); criterion_group!( store_benches, @@ -837,10 +808,7 @@ criterion_group!( storage_cache_operations, ); -criterion_group!( - db_benches, - database_creation, -); +criterion_group!(db_benches, database_creation,); criterion_main!( header_benches, diff --git a/crates/synor-storage/src/db.rs b/crates/synor-storage/src/db.rs index 1c1a27d..160f2b3 100644 --- a/crates/synor-storage/src/db.rs +++ b/crates/synor-storage/src/db.rs @@ -3,9 +3,7 @@ //! Provides a typed interface to the underlying RocksDB instance. use crate::cf; -use rocksdb::{ - ColumnFamily, ColumnFamilyDescriptor, DBCompressionType, Options, WriteBatch, DB, -}; +use rocksdb::{ColumnFamily, ColumnFamilyDescriptor, DBCompressionType, Options, WriteBatch, DB}; use std::path::Path; use std::sync::Arc; use thiserror::Error; @@ -182,7 +180,10 @@ impl Database { } /// Opens a database in read-only mode. - pub fn open_read_only>(path: P, config: &DatabaseConfig) -> Result { + pub fn open_read_only>( + path: P, + config: &DatabaseConfig, + ) -> Result { let path_str = path.as_ref().to_string_lossy().to_string(); let opts = config.to_options(); @@ -241,7 +242,10 @@ impl Database { } /// Iterates over all keys in a column family. - pub fn iter(&self, cf_name: &str) -> Result, Box<[u8]>)> + '_, DbError> { + pub fn iter( + &self, + cf_name: &str, + ) -> Result, Box<[u8]>)> + '_, DbError> { let cf = self.get_cf(cf_name)?; let iter = self.db.iterator_cf(cf, rocksdb::IteratorMode::Start); Ok(iter.map(|r| r.unwrap())) diff --git a/crates/synor-storage/src/lib.rs b/crates/synor-storage/src/lib.rs index 066d7f2..cd6be86 100644 --- a/crates/synor-storage/src/lib.rs +++ b/crates/synor-storage/src/lib.rs @@ -32,8 +32,8 @@ pub use cache::{CacheConfig, CacheSizeInfo, CacheStats, StorageCache}; pub use db::{Database, DatabaseConfig, DbError}; pub use stores::{ BatchStore, BlockBody, BlockStore, ChainState, ContractStateStore, ContractStore, - GhostdagStore, HeaderStore, MetadataStore, RelationsStore, StoredContract, - StoredGhostdagData, StoredRelations, StoredUtxo, TransactionStore, UtxoStore, + GhostdagStore, HeaderStore, MetadataStore, RelationsStore, StoredContract, StoredGhostdagData, + StoredRelations, StoredUtxo, TransactionStore, UtxoStore, }; /// Column family names. diff --git a/crates/synor-storage/src/stores.rs b/crates/synor-storage/src/stores.rs index e8ca111..0f408cc 100644 --- a/crates/synor-storage/src/stores.rs +++ b/crates/synor-storage/src/stores.rs @@ -39,8 +39,7 @@ impl HeaderStore { /// Stores a header. pub fn put(&self, header: &BlockHeader) -> Result<(), DbError> { let hash = header.block_id(); - let bytes = borsh::to_vec(header) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(header).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::HEADERS, hash.as_bytes(), &bytes) } @@ -133,8 +132,7 @@ impl BlockStore { /// Stores a block body. pub fn put(&self, hash: &Hash256, body: &BlockBody) -> Result<(), DbError> { - let bytes = borsh::to_vec(body) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(body).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::BLOCKS, hash.as_bytes(), &bytes) } @@ -181,8 +179,7 @@ impl TransactionStore { /// Stores a transaction. pub fn put(&self, tx: &Transaction) -> Result<(), DbError> { let txid = tx.txid(); - let bytes = borsh::to_vec(tx) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(tx).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::TRANSACTIONS, txid.as_bytes(), &bytes) } @@ -268,8 +265,7 @@ impl UtxoStore { /// Stores a UTXO. pub fn put(&self, txid: &TransactionId, index: u32, utxo: &StoredUtxo) -> Result<(), DbError> { let key = Self::make_key(txid, index); - let bytes = borsh::to_vec(utxo) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(utxo).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::UTXOS, &key, &bytes) } @@ -352,8 +348,7 @@ impl RelationsStore { /// Stores relations for a block. pub fn put(&self, block_id: &BlockId, relations: &StoredRelations) -> Result<(), DbError> { - let bytes = borsh::to_vec(relations) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(relations).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::RELATIONS, block_id.as_bytes(), &bytes) } @@ -429,8 +424,7 @@ impl GhostdagStore { /// Stores GHOSTDAG data for a block. pub fn put(&self, block_id: &BlockId, data: &StoredGhostdagData) -> Result<(), DbError> { - let bytes = borsh::to_vec(data) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(data).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::GHOSTDAG, block_id.as_bytes(), &bytes) } @@ -500,8 +494,7 @@ impl MetadataStore { /// Sets the current DAG tips. pub fn set_tips(&self, tips: &[BlockId]) -> Result<(), DbError> { - let bytes = borsh::to_vec(tips) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(tips).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::METADATA, Self::KEY_TIPS, &bytes) } @@ -519,8 +512,7 @@ impl MetadataStore { /// Sets the pruning point. pub fn set_pruning_point(&self, point: &BlockId) -> Result<(), DbError> { - let bytes = borsh::to_vec(point) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(point).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::METADATA, Self::KEY_PRUNING_POINT, &bytes) } @@ -538,8 +530,7 @@ impl MetadataStore { /// Sets the chain state. pub fn set_chain_state(&self, state: &ChainState) -> Result<(), DbError> { - let bytes = borsh::to_vec(state) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(state).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::METADATA, Self::KEY_CHAIN_STATE, &bytes) } @@ -557,8 +548,7 @@ impl MetadataStore { /// Sets the genesis block ID. pub fn set_genesis(&self, genesis: &BlockId) -> Result<(), DbError> { - let bytes = borsh::to_vec(genesis) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(genesis).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::METADATA, Self::KEY_GENESIS, &bytes) } @@ -631,8 +621,7 @@ impl ContractStore { /// Stores a contract. pub fn put(&self, contract: &StoredContract) -> Result<(), DbError> { - let bytes = borsh::to_vec(contract) - .map_err(|e| DbError::Serialization(e.to_string()))?; + let bytes = borsh::to_vec(contract).map_err(|e| DbError::Serialization(e.to_string()))?; self.db.put(cf::CONTRACTS, &contract.code_hash, &bytes) } @@ -694,11 +683,7 @@ impl ContractStateStore { } /// Deletes a storage value. - pub fn delete( - &self, - contract_id: &[u8; 32], - storage_key: &[u8; 32], - ) -> Result<(), DbError> { + pub fn delete(&self, contract_id: &[u8; 32], storage_key: &[u8; 32]) -> Result<(), DbError> { let key = Self::make_key(contract_id, storage_key); self.db.delete(cf::CONTRACT_STATE, &key) } diff --git a/crates/synor-types/src/address.rs b/crates/synor-types/src/address.rs index d05a881..89fbb47 100644 --- a/crates/synor-types/src/address.rs +++ b/crates/synor-types/src/address.rs @@ -95,11 +95,7 @@ impl Address { } /// Creates an address from raw components. - pub fn from_parts( - network: Network, - addr_type: AddressType, - payload: [u8; 32], - ) -> Self { + pub fn from_parts(network: Network, addr_type: AddressType, payload: [u8; 32]) -> Self { Address { network, addr_type, @@ -110,8 +106,8 @@ impl Address { /// Parses an address from a Bech32m string. pub fn from_str(s: &str) -> Result { // Decode Bech32m - let (hrp, data) = bech32::decode(s) - .map_err(|e| AddressError::Bech32Error(e.to_string()))?; + let (hrp, data) = + bech32::decode(s).map_err(|e| AddressError::Bech32Error(e.to_string()))?; // Determine network from HRP let network = match hrp.as_str() { @@ -169,10 +165,7 @@ impl Address { /// Returns true if this is a post-quantum address. pub fn is_post_quantum(&self) -> bool { - matches!( - self.addr_type, - AddressType::P2pkhPqc | AddressType::P2shPqc - ) + matches!(self.addr_type, AddressType::P2pkhPqc | AddressType::P2shPqc) } /// Returns a short representation of the address for display. @@ -258,9 +251,8 @@ impl borsh::BorshDeserialize for Address { let mut type_byte = [0u8; 1]; reader.read_exact(&mut type_byte)?; - let addr_type = AddressType::from_byte(type_byte[0]).map_err(|e| { - std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()) - })?; + let addr_type = AddressType::from_byte(type_byte[0]) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?; let mut payload = [0u8; 32]; reader.read_exact(&mut payload)?; @@ -324,8 +316,7 @@ mod tests { let ed_pubkey = [1u8; 32]; let dilithium_pubkey = vec![2u8; 1312]; // Dilithium3 public key size - let addr = - Address::from_hybrid_pubkey(Network::Mainnet, &ed_pubkey, &dilithium_pubkey); + let addr = Address::from_hybrid_pubkey(Network::Mainnet, &ed_pubkey, &dilithium_pubkey); assert!(addr.is_post_quantum()); assert_eq!(addr.addr_type(), AddressType::P2pkhPqc); diff --git a/crates/synor-types/src/block.rs b/crates/synor-types/src/block.rs index 01686aa..b0052a4 100644 --- a/crates/synor-types/src/block.rs +++ b/crates/synor-types/src/block.rs @@ -159,11 +159,7 @@ impl BlockBody { /// Computes the Merkle root of transactions. pub fn merkle_root(&self) -> Hash256 { - let tx_hashes: Vec = self - .transactions - .iter() - .map(|tx| tx.txid()) - .collect(); + let tx_hashes: Vec = self.transactions.iter().map(|tx| tx.txid()).collect(); Hash256::merkle_root(&tx_hashes) } diff --git a/crates/synor-types/src/hash.rs b/crates/synor-types/src/hash.rs index da4dcb1..a7a84a3 100644 --- a/crates/synor-types/src/hash.rs +++ b/crates/synor-types/src/hash.rs @@ -248,10 +248,9 @@ mod tests { #[test] fn test_hash_display() { - let hash = Hash256::from_hex( - "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", - ) - .unwrap(); + let hash = + Hash256::from_hex("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890") + .unwrap(); let display = format!("{}", hash); assert!(display.contains("...")); assert!(display.starts_with("abcdef12")); diff --git a/crates/synor-types/src/lib.rs b/crates/synor-types/src/lib.rs index e370db8..b130208 100644 --- a/crates/synor-types/src/lib.rs +++ b/crates/synor-types/src/lib.rs @@ -16,7 +16,9 @@ pub use hash::{Hash256, HashError}; pub use transaction::{Transaction, TransactionId, TxInput, TxOutput}; /// Network identifier for Synor chains. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, +)] pub enum Network { /// Main production network Mainnet, diff --git a/crates/synor-types/src/transaction.rs b/crates/synor-types/src/transaction.rs index ff3918a..c383f33 100644 --- a/crates/synor-types/src/transaction.rs +++ b/crates/synor-types/src/transaction.rs @@ -13,7 +13,16 @@ pub type TransactionId = Hash256; /// Outpoint - references a specific output of a previous transaction. #[derive( - Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize, + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + BorshSerialize, + BorshDeserialize, )] pub struct Outpoint { /// The transaction ID containing the output. @@ -184,7 +193,16 @@ impl ScriptPubKey { /// Script type enumeration. #[derive( - Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize, + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + BorshSerialize, + BorshDeserialize, )] #[borsh(use_discriminant = true)] #[repr(u8)] @@ -222,10 +240,7 @@ pub struct Transaction { impl Transaction { /// Creates a new transaction. - pub fn new( - inputs: Vec, - outputs: Vec, - ) -> Self { + pub fn new(inputs: Vec, outputs: Vec) -> Self { Transaction { version: 1, inputs, @@ -263,11 +278,9 @@ impl Transaction { /// Computes the total output value. pub fn total_output(&self) -> Amount { - self.outputs - .iter() - .fold(Amount::ZERO, |acc, output| { - acc.saturating_add(output.amount) - }) + self.outputs.iter().fold(Amount::ZERO, |acc, output| { + acc.saturating_add(output.amount) + }) } /// Returns the transaction weight (for fee calculation). @@ -303,7 +316,16 @@ impl fmt::Display for Transaction { /// Subnetwork identifier. #[derive( - Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize, + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + BorshSerialize, + BorshDeserialize, )] pub struct SubnetworkId([u8; 20]); @@ -312,10 +334,12 @@ impl SubnetworkId { pub const NATIVE: SubnetworkId = SubnetworkId([0u8; 20]); /// Coinbase subnetwork. - pub const COINBASE: SubnetworkId = SubnetworkId([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + pub const COINBASE: SubnetworkId = + SubnetworkId([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); /// Registry subnetwork (for registrations). - pub const REGISTRY: SubnetworkId = SubnetworkId([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + pub const REGISTRY: SubnetworkId = + SubnetworkId([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); /// Creates a new subnetwork ID from bytes. pub fn from_bytes(bytes: [u8; 20]) -> Self { @@ -362,10 +386,7 @@ mod tests { #[test] fn test_coinbase_transaction() { - let output = TxOutput::new( - Amount::from_synor(500), - ScriptPubKey::p2pkh(&[0u8; 32]), - ); + let output = TxOutput::new(Amount::from_synor(500), ScriptPubKey::p2pkh(&[0u8; 32])); let tx = Transaction::coinbase(vec![output], b"Synor Genesis".to_vec()); assert!(tx.is_coinbase()); @@ -375,14 +396,8 @@ mod tests { #[test] fn test_transaction_id() { - let input = TxInput::new( - Outpoint::new(Hash256::blake3(b"prev_tx"), 0), - vec![], - ); - let output = TxOutput::new( - Amount::from_synor(10), - ScriptPubKey::p2pkh(&[1u8; 32]), - ); + let input = TxInput::new(Outpoint::new(Hash256::blake3(b"prev_tx"), 0), vec![]); + let output = TxOutput::new(Amount::from_synor(10), ScriptPubKey::p2pkh(&[1u8; 32])); let tx = Transaction::new(vec![input], vec![output]); let txid = tx.txid(); diff --git a/crates/synor-vm/src/engine.rs b/crates/synor-vm/src/engine.rs index 86bd832..187f63e 100644 --- a/crates/synor-vm/src/engine.rs +++ b/crates/synor-vm/src/engine.rs @@ -10,11 +10,9 @@ use std::sync::Arc; use parking_lot::RwLock; use wasmtime::{ - Config, Engine, Instance, Linker, Memory, Module, Store, StoreLimits, - StoreLimitsBuilder, + Config, Engine, Instance, Linker, Memory, Module, Store, StoreLimits, StoreLimitsBuilder, }; - use crate::context::ExecutionContext; use crate::gas::GasMeter; use crate::storage::{ContractStorage, MemoryStorage}; @@ -243,7 +241,11 @@ impl VmEngine { // Read from storage let data = caller.data_mut(); let storage_key = crate::storage::StorageKey::new(key); - match data.context.storage.get(&data.context.call.contract, &storage_key) { + match data + .context + .storage + .get(&data.context.call.contract, &storage_key) + { Some(value) => { let len = value.0.len(); // Write to output buffer diff --git a/crates/synor-vm/src/gas_estimator.rs b/crates/synor-vm/src/gas_estimator.rs index 5a84491..a0c76f6 100644 --- a/crates/synor-vm/src/gas_estimator.rs +++ b/crates/synor-vm/src/gas_estimator.rs @@ -100,7 +100,10 @@ impl GasEstimator { ); // Execute init - match self.engine.execute(&module, "__synor_init", init_params, context, self.max_gas) { + match self + .engine + .execute(&module, "__synor_init", init_params, context, self.max_gas) + { Ok(result) => GasEstimate::success(result.gas_used, result.return_data), Err(e) => { // Try to extract gas used from error @@ -141,7 +144,10 @@ impl GasEstimator { ); // Execute call - match self.engine.execute(module, "__synor_call", &call_data, context, self.max_gas) { + match self + .engine + .execute(module, "__synor_call", &call_data, context, self.max_gas) + { Ok(result) => GasEstimate::success(result.gas_used, result.return_data), Err(e) => { let gas = match &e { @@ -167,7 +173,14 @@ impl GasEstimator { storage: MemoryStorage, ) -> GasEstimate { // First, try with max gas to see if it succeeds - let initial = self.estimate_call(module, method, params, caller.clone(), value, storage.clone()); + let initial = self.estimate_call( + module, + method, + params, + caller.clone(), + value, + storage.clone(), + ); if !initial.success { return initial; @@ -197,7 +210,10 @@ impl GasEstimator { 1, // chain_id ); - match self.engine.execute(module, "__synor_call", &call_data, context, mid) { + match self + .engine + .execute(module, "__synor_call", &call_data, context, mid) + { Ok(_) => high = mid, Err(_) => low = mid + 1, } @@ -334,7 +350,10 @@ mod tests { fn test_calldata_cost() { let data = [0u8, 1, 0, 2, 0, 3]; // 3 zeros, 3 non-zeros let cost = costs::calldata_cost(&data); - assert_eq!(cost, 3 * costs::CALLDATA_ZERO_BYTE + 3 * costs::CALLDATA_BYTE); + assert_eq!( + cost, + 3 * costs::CALLDATA_ZERO_BYTE + 3 * costs::CALLDATA_BYTE + ); } #[test] diff --git a/crates/synor-vm/src/host.rs b/crates/synor-vm/src/host.rs index 9382e03..b3e117f 100644 --- a/crates/synor-vm/src/host.rs +++ b/crates/synor-vm/src/host.rs @@ -83,7 +83,10 @@ impl HostFunctions { // Add refund for deletion ctx.gas.add_refund(ctx.gas.config().storage_delete_refund); - let existed = ctx.storage.delete(&ctx.call.contract, &storage_key).is_some(); + let existed = ctx + .storage + .delete(&ctx.call.contract, &storage_key) + .is_some(); Ok(existed) } diff --git a/crates/synor-vm/src/storage.rs b/crates/synor-vm/src/storage.rs index db2db1c..6009740 100644 --- a/crates/synor-vm/src/storage.rs +++ b/crates/synor-vm/src/storage.rs @@ -13,7 +13,9 @@ use synor_types::Hash256; use crate::ContractId; /// Storage key (256-bit). -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, BorshSerialize, BorshDeserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, BorshSerialize, BorshDeserialize, +)] pub struct StorageKey(pub [u8; 32]); impl StorageKey { @@ -209,10 +211,7 @@ impl MemoryStorage { for (contract, pending_changes) in self.pending.drain() { for (key, new_value) in pending_changes { // Get old value from committed data - let old_value = self.data - .get(&contract) - .and_then(|m| m.get(&key)) - .cloned(); + let old_value = self.data.get(&contract).and_then(|m| m.get(&key)).cloned(); changes.push(crate::StorageChange { contract, @@ -233,10 +232,7 @@ impl MemoryStorage { for (contract, pending_changes) in &self.pending { for (key, new_value) in pending_changes { // Get old value from committed data - let old_value = self.data - .get(contract) - .and_then(|m| m.get(key)) - .cloned(); + let old_value = self.data.get(contract).and_then(|m| m.get(key)).cloned(); changes.push(crate::StorageChange { contract: *contract, @@ -261,10 +257,7 @@ impl ContractStorage for MemoryStorage { } // Then check committed - self.data - .get(contract) - .and_then(|m| m.get(key)) - .cloned() + self.data.get(contract).and_then(|m| m.get(key)).cloned() } fn set(&mut self, contract: &ContractId, key: StorageKey, value: StorageValue) { @@ -514,13 +507,21 @@ mod tests { let root1 = storage.root(&contract); - storage.set(&contract, StorageKey::from_str("a"), StorageValue::from_u64(1)); + storage.set( + &contract, + StorageKey::from_str("a"), + StorageValue::from_u64(1), + ); storage.commit(); let root2 = storage.root(&contract); assert_ne!(root1, root2); - storage.set(&contract, StorageKey::from_str("b"), StorageValue::from_u64(2)); + storage.set( + &contract, + StorageKey::from_str("b"), + StorageValue::from_u64(2), + ); storage.commit(); let root3 = storage.root(&contract);