diff --git a/build.bat b/build.bat deleted file mode 100644 index c01d45d..0000000 --- a/build.bat +++ /dev/null @@ -1,100 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM GBT项目构建脚本 (Windows版本) - -echo 🚀 开始构建GBT项目... - -REM 检查Rust环境 -where cargo >nul 2>&1 -if %errorlevel% neq 0 ( - echo ❌ 错误: 未找到cargo,请先安装Rust - echo 访问: https://rustup.rs/ - pause - exit /b 1 -) - -REM 检查Rust版本 -for /f "tokens=2" %%i in ('rustc --version') do set RUST_VERSION=%%i -echo 📦 Rust版本: %RUST_VERSION% - -REM 检查是否在正确的目录 -if not exist "Cargo.toml" ( - echo ❌ 错误: 未找到Cargo.toml,请在gbt目录下运行此脚本 - pause - exit /b 1 -) - -REM 清理之前的构建 -echo 🧹 清理之前的构建... -cargo clean -if %errorlevel% neq 0 ( - echo ❌ 清理失败 - pause - exit /b 1 -) - -REM 更新依赖 -echo 📥 更新依赖... -cargo update -if %errorlevel% neq 0 ( - echo ❌ 更新依赖失败 - pause - exit /b 1 -) - -REM 检查代码 -echo 🔍 检查代码... -cargo check -if %errorlevel% neq 0 ( - echo ❌ 代码检查失败 - pause - exit /b 1 -) - -REM 运行测试 -echo 🧪 运行测试... -cargo test -if %errorlevel% neq 0 ( - echo ❌ 测试失败 - pause - exit /b 1 -) - -REM 构建发布版本 -echo 🔨 构建发布版本... -cargo build --release -if %errorlevel% neq 0 ( - echo ❌ 构建失败 - pause - exit /b 1 -) - -REM 检查构建结果 -if exist "target\release\gbt.exe" ( - echo ✅ 构建成功! - echo 📁 可执行文件位置: target\release\gbt.exe - - REM 显示文件信息 - echo 📊 文件信息: - dir target\release\gbt.exe - - REM 显示版本信息 - echo ℹ️ 版本信息: - target\release\gbt.exe --version 2>nul || echo 无法获取版本信息 - -) else ( - echo ❌ 构建失败! - pause - exit /b 1 -) - -echo 🎉 GBT项目构建完成! -echo. -echo 📖 使用方法: -echo target\release\gbt.exe --wallet-address ^ -echo. -echo 📖 更多选项: -echo target\release\gbt.exe --help -echo. -pause \ No newline at end of file diff --git a/log4rs_gbt.yml b/log4rs_gbt.yml new file mode 100644 index 0000000..b70686b --- /dev/null +++ b/log4rs_gbt.yml @@ -0,0 +1,108 @@ +# GBT Client 日志配置文件 +# 基于 log4rs 配置,用于GBT客户端的日志管理 +refresh_rate: 30 seconds + +appenders: + # 控制台输出 + stdout: + kind: console + encoder: + pattern: "{d(%H:%M)} {h({l}):5} {m}{n}" + filters: + - kind: threshold + level: info + + # GBT客户端日志文件 + gbt: + kind: rolling_file + path: "{{log_dir}}/log/gbt/gbt.log" + policy: + kind: compound + trigger: + kind: size + limit: 10mb + roller: + kind: fixed_window + base: 1 + count: 5 + pattern: "{{log_dir}}/log/gbt/gbt.{}.log" + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" + + # ZMQ通信日志 + zmq: + kind: rolling_file + path: "{{log_dir}}/log/gbt/zmq.log" + policy: + kind: compound + trigger: + kind: size + limit: 10mb + roller: + kind: fixed_window + base: 1 + count: 5 + pattern: "{{log_dir}}/log/gbt/zmq.{}.log" + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" + + # gRPC通信日志 + grpc: + kind: rolling_file + path: "{{log_dir}}/log/gbt/grpc.log" + policy: + kind: compound + trigger: + kind: size + limit: 10mb + roller: + kind: fixed_window + base: 1 + count: 5 + pattern: "{{log_dir}}/log/gbt/grpc.{}.log" + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" + +# 根日志配置 +root: + level: warn + appenders: + - stdout + +loggers: + # GBT客户端日志 + gbt: + level: debug + appenders: + - gbt + - stdout + additive: false + + # ZMQ相关日志 + zmq: + level: debug + appenders: + - zmq + - stdout + additive: false + + # gRPC相关日志 + tonic: + level: debug + appenders: + - grpc + - stdout + additive: false + + # 其他第三方库日志 + tokio_util: + level: warn + appenders: + - gbt + additive: false + + h2: + level: warn + appenders: + - grpc + additive: false \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3042c75..d2044a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,15 +56,203 @@ use tari_core::{ }, }; use tari_utilities::hex::Hex; +use hex::FromHex; const LOG_TARGET: &str = "gbt::main"; +// 自定义的Block结构体,用于16进制序列化 +#[derive(Debug, Serialize, Deserialize)] +struct HexBlock { + header: Option, + body: Option, // 保持原有的body结构 +} + +#[derive(Debug, Serialize, Deserialize)] +struct HexBlockHeader { + #[serde(serialize_with = "serialize_bytes_as_hex")] + hash: Vec, + version: u32, + height: u64, + #[serde(serialize_with = "serialize_bytes_as_hex")] + prev_hash: Vec, + timestamp: u64, + #[serde(serialize_with = "serialize_bytes_as_hex")] + output_mr: Vec, + #[serde(serialize_with = "serialize_bytes_as_hex")] + block_output_mr: Vec, + #[serde(serialize_with = "serialize_bytes_as_hex")] + kernel_mr: Vec, + #[serde(serialize_with = "serialize_bytes_as_hex")] + input_mr: Vec, + #[serde(serialize_with = "serialize_bytes_as_hex")] + total_kernel_offset: Vec, + nonce: u64, + pow: HexProofOfWork, + kernel_mmr_size: u64, + output_mmr_size: u64, + #[serde(serialize_with = "serialize_bytes_as_hex")] + total_script_offset: Vec, + #[serde(serialize_with = "serialize_bytes_as_hex")] + validator_node_mr: Vec, + validator_node_size: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +struct HexProofOfWork { + pow_algo: u64, + #[serde(serialize_with = "serialize_bytes_as_hex")] + pow_data: Vec, +} + +// 自定义序列化函数:将字节数组序列化为16进制字符串 +fn serialize_bytes_as_hex(bytes: &[u8], serializer: S) -> Result +where + S: serde::Serializer, +{ + let hex_string = hex::encode(bytes); + serializer.serialize_str(&hex_string) +} + +// 将Block转换为HexBlock +fn convert_block_to_hex(block: &Block) -> Result { + let header = if let Some(ref h) = block.header { + Some(HexBlockHeader { + hash: h.hash.clone(), + version: h.version, + height: h.height, + prev_hash: h.prev_hash.clone(), + timestamp: h.timestamp, + output_mr: h.output_mr.clone(), + block_output_mr: h.block_output_mr.clone(), + kernel_mr: h.kernel_mr.clone(), + input_mr: h.input_mr.clone(), + total_kernel_offset: h.total_kernel_offset.clone(), + nonce: h.nonce, + pow: HexProofOfWork { + pow_algo: h.pow.as_ref().map(|p| p.pow_algo).unwrap_or(0), + pow_data: h.pow.as_ref().map(|p| p.pow_data.clone()).unwrap_or_default(), + }, + kernel_mmr_size: h.kernel_mmr_size, + output_mmr_size: h.output_mmr_size, + total_script_offset: h.total_script_offset.clone(), + validator_node_mr: h.validator_node_mr.clone(), + validator_node_size: h.validator_node_size, + }) + } else { + None + }; + + // 将body转换为JSON值,保持原有结构 + let body_json = serde_json::to_value(&block.body).map_err(|e| anyhow!("Body serialization error: {}", e))?; + + Ok(HexBlock { + header, + body: Some(body_json), + }) +} + +// 自定义反序列化函数:将16进制字符串反序列化为字节数组 +fn deserialize_hex_to_bytes<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let hex_string = String::deserialize(deserializer)?; + hex::FromHex::from_hex(&hex_string).map_err(|e: hex::FromHexError| serde::de::Error::custom(e.to_string())) +} + +// 用于反序列化的结构体 +#[derive(Debug, Deserialize)] +struct HexBlockDeserialize { + header: Option, + body: Option, +} + +#[derive(Debug, Deserialize)] +struct HexBlockHeaderDeserialize { + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + hash: Vec, + version: u32, + height: u64, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + prev_hash: Vec, + timestamp: u64, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + output_mr: Vec, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + block_output_mr: Vec, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + kernel_mr: Vec, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + input_mr: Vec, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + total_kernel_offset: Vec, + nonce: u64, + pow: HexProofOfWorkDeserialize, + kernel_mmr_size: u64, + output_mmr_size: u64, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + total_script_offset: Vec, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + validator_node_mr: Vec, + validator_node_size: u64, +} + +#[derive(Debug, Deserialize)] +struct HexProofOfWorkDeserialize { + pow_algo: u64, + #[serde(deserialize_with = "deserialize_hex_to_bytes")] + pow_data: Vec, +} + +// 将16进制JSON转换回Block +fn convert_hex_to_block(hex_block: &HexBlockDeserialize) -> Result { + let header = if let Some(ref h) = hex_block.header { + Some(minotari_app_grpc::tari_rpc::BlockHeader { + hash: h.hash.clone(), + version: h.version, + height: h.height, + prev_hash: h.prev_hash.clone(), + timestamp: h.timestamp, + output_mr: h.output_mr.clone(), + block_output_mr: h.block_output_mr.clone(), + kernel_mr: h.kernel_mr.clone(), + input_mr: h.input_mr.clone(), + total_kernel_offset: h.total_kernel_offset.clone(), + nonce: h.nonce, + pow: Some(minotari_app_grpc::tari_rpc::ProofOfWork { + pow_algo: h.pow.pow_algo, + pow_data: h.pow.pow_data.clone(), + }), + kernel_mmr_size: h.kernel_mmr_size, + output_mmr_size: h.output_mmr_size, + total_script_offset: h.total_script_offset.clone(), + validator_node_mr: h.validator_node_mr.clone(), + validator_node_size: h.validator_node_size, + }) + } else { + None + }; + + // 将body从JSON值转换回原始结构 + let body = if let Some(ref body_json) = hex_block.body { + serde_json::from_value(body_json.clone()).map_err(|e| anyhow!("Body deserialization error: {}", e))? + } else { + None + }; + + Ok(Block { + header, + body, + }) +} + // ZMQ消息结构 #[derive(Debug, Serialize, Deserialize, Clone)] pub struct MiningTask { pub coinbase_hash: String, pub height: u64, pub target: u64, + pub output_smt_size: u64, // 新增:output_smt_size pub block_template: String, // 序列化的区块模板 } @@ -231,6 +419,13 @@ impl GbtClient { .ok_or_else(|| anyhow!("No header in block template"))? .height; + // 获取output_smt_size + let output_smt_size = block_template + .header + .as_ref() + .ok_or_else(|| anyhow!("No header in block template"))? + .output_smt_size; + // 获取挖矿数据 let miner_data = template_response .miner_data @@ -280,13 +475,15 @@ impl GbtClient { // 计算coinbase哈希 let coinbase_hash = coinbase_output.hash().to_hex(); - // 序列化区块模板 - let block_template_json = serde_json::to_string(&block).map_err(|e| anyhow!("Serialization error: {}", e))?; + // 使用自定义的16进制序列化 + let hex_block = convert_block_to_hex(&block)?; + let block_template_json = serde_json::to_string(&hex_block).map_err(|e| anyhow!("Serialization error: {}", e))?; let mining_task = MiningTask { coinbase_hash, height, target: target_difficulty, + output_smt_size, block_template: block_template_json, }; @@ -307,7 +504,8 @@ impl GbtClient { .send_multipart(&["mining_task".as_bytes(), task_json.as_bytes()], 0) .map_err(|e| anyhow!("ZMQ send error: {}", e))?; - info!(target: LOG_TARGET, "Sent mining task for height {} with target {}", task.height, task.target); + info!(target: LOG_TARGET, "Sent mining task for height {} with target {} and output_smt_size {}", + task.height, task.target, task.output_smt_size); Ok(()) } @@ -343,9 +541,17 @@ impl GbtClient { // 提交区块到BaseNode pub async fn submit_block_to_base_node(&mut self, submit_request: &SubmitRequest) -> Result { - // 反序列化区块数据 - let block: Block = serde_json::from_str(&submit_request.block_data) - .map_err(|e| anyhow!("Block deserialization error: {}", e))?; + // 反序列化区块数据(支持16进制格式) + let block: Block = if submit_request.block_data.contains("\"hash\":") { + // 尝试解析为16进制格式 + let hex_block: HexBlockDeserialize = serde_json::from_str(&submit_request.block_data) + .map_err(|e| anyhow!("Hex block deserialization error: {}", e))?; + convert_hex_to_block(&hex_block)? + } else { + // 尝试解析为原始Base64格式(向后兼容) + serde_json::from_str(&submit_request.block_data) + .map_err(|e| anyhow!("Block deserialization error: {}", e))? + }; info!(target: LOG_TARGET, "Submitting block to base node for height {}", submit_request.height); @@ -461,8 +667,17 @@ struct Args { #[tokio::main] async fn main() -> Result<()> { - // 初始化日志 - env_logger::init(); + // 初始化日志系统 + let log_config_path = PathBuf::from("log4rs_gbt.yml"); + if log_config_path.exists() { + // 使用log4rs配置文件 + let base_path = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + tari_common::initialize_logging(&log_config_path, &base_path, "") + .map_err(|e| anyhow!("Failed to initialize logging: {}", e))?; + } else { + // 回退到env_logger + env_logger::init(); + } let args = Args::parse();