From e0f77b4b913a43b5bf6a5da0450485452b21ec5e Mon Sep 17 00:00:00 2001 From: lzx <393768033@qq.com> Date: Wed, 2 Jul 2025 17:01:00 +0800 Subject: [PATCH] update --- Cargo.toml | 4 + IMPLEMENTATION_SUMMARY.md | 290 ----------------------- OUTPUT_SMT_SIZE_FIX_SUMMARY.md | 219 ----------------- README.md | 141 ++++++----- build.bat | 69 ------ build.sh | 85 ------- gbt_process | 14 -- src/main.rs | 413 ++++++++------------------------- test_gbt.py | 152 ------------ test_output_smt_debug.rs | 145 ------------ 10 files changed, 184 insertions(+), 1348 deletions(-) delete mode 100644 IMPLEMENTATION_SUMMARY.md delete mode 100644 OUTPUT_SMT_SIZE_FIX_SUMMARY.md delete mode 100644 build.bat delete mode 100644 build.sh delete mode 100644 gbt_process delete mode 100644 test_gbt.py delete mode 100644 test_output_smt_debug.rs diff --git a/Cargo.toml b/Cargo.toml index 09a92fd..060c4fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,10 @@ minotari_app_utilities = { path = "../applications/minotari_app_utilities", feat minotari_app_grpc = { path = "../applications/minotari_app_grpc" } tari_utilities = { version = "0.8" } jmt = { version = "0.11.0", features = ["mocks"] } +tari_hashing = { path = "../hashing" } +tari_crypto = { version = "0.18" } +blake2 = "0.10" +digest = "0.10" base64 = "0.13.0" borsh = "1.5.7" diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md deleted file mode 100644 index 902bc4d..0000000 --- a/IMPLEMENTATION_SUMMARY.md +++ /dev/null @@ -1,290 +0,0 @@ -# GBT项目修改实现总结 - -## 概述 - -根据您的要求,对GBT项目进行了全面的重构和功能增强,实现了以下核心功能: - -1. **HTTP API集成**:通过HTTP请求获取上一个区块的`output_smt_size` -2. **任务ID生成**:生成16位16进制任务ID -3. **简化的output_smt_size计算**:使用`outputs长度 - inputs长度`的差值计算 -4. **新的数据结构**:重新设计了MiningTask、MiningMsg等结构 -5. **ZMQ通信优化**:改进了消息格式和通信流程 - -## 主要修改 - -### 1. 依赖项更新 (`Cargo.toml`) - -新增依赖: -- `reqwest = { version = "0.11", features = ["json"] }` - HTTP客户端 -- `uuid = { version = "1.0", features = ["v4"] }` - 任务ID生成 - -### 2. 数据结构重新设计 - -#### 原结构 → 新结构 - -**MiningTask**: -```rust -// 旧结构 -pub struct MiningTask { - pub coinbase_hash: String, - pub height: u64, - pub target: u64, - pub output_smt_size: u64, - pub block_template: String, -} - -// 新结构 -pub struct MiningTask { - pub job_id: String, // 新增:16位16进制任务ID - pub block_header: BlockHeader, // 新增:完整的区块头 - pub block_body: BlockBody, // 新增:完整的区块体 - pub output_smt_size: u64, // 保留:当前output_smt_size - pub coinbase_hash: String, // 保留:coinbase哈希 - pub target: u64, // 保留:目标难度(从gRPC获取) -} -``` - -**新增结构**: -```rust -// 区块头结构 -pub struct BlockHeader { - pub hash: String, - pub version: u32, - pub height: u64, - pub prev_hash: String, - pub timestamp: u64, - pub output_mr: String, - pub block_output_mr: String, - pub kernel_mr: String, - pub input_mr: String, - pub total_kernel_offset: String, - pub nonce: u64, // 初始为0,等待后续修改 - pub pow: ProofOfWork, - pub kernel_mmr_size: u64, - pub output_mmr_size: u64, - pub total_script_offset: String, - pub validator_node_mr: String, - pub validator_node_size: u64, -} - -// 区块体结构 -pub struct BlockBody { - pub inputs: Vec, - pub outputs: Vec, - pub kernels: Vec, -} - -// 挖矿消息结构 -pub struct MiningMsg { - pub job_id: String, - pub block_header: BlockHeader, - pub output_smt_size: u64, - pub coinbase_hash: String, - pub target: u64, // 新增:目标难度 -} - -// 提交请求结构 -pub struct SubmitRequest { - pub job_id: String, // 改为任务ID - pub nonce: u64, - pub solution: String, -} -``` - -### 3. 核心功能实现 - -#### 3.1 HTTP API集成 - -```rust -// 通过HTTP获取上一个区块的output_smt_size -async fn get_prev_output_smt_size(&self, height: u64) -> Result { - let prev_height = height - 1; - - // 检查缓存 - { - let cache = self.prev_output_smt_size_cache.lock().await; - if let Some(&size) = cache.get(&prev_height) { - return Ok(size); - } - } - - // 通过HTTP请求获取 - let url = format!("http://{}/get_header_by_height?height={}", - self.config.base_node_http_address, prev_height); - - let response = self.http_client.get(&url).send().await?; - let header_data: serde_json::Value = response.json().await?; - - let output_smt_size = header_data["output_smt_size"] - .as_u64() - .ok_or_else(|| anyhow!("Missing output_smt_size in response"))?; - - // 更新缓存 - { - let mut cache = self.prev_output_smt_size_cache.lock().await; - cache.insert(prev_height, output_smt_size); - } - - Ok(output_smt_size) -} -``` - -#### 3.2 任务ID生成 - -```rust -// 生成16位16进制任务ID -fn generate_job_id() -> String { - let uuid = Uuid::new_v4(); - uuid.to_string().replace("-", "")[..16].to_string() -} -``` - -#### 3.3 简化的output_smt_size计算 - -```rust -// 计算当前output_smt_size -let current_output_smt_size = prev_output_smt_size + - block_body.outputs.len() as u64 - - block_body.inputs.len() as u64; -``` - -#### 3.4 ZMQ消息格式 - -**发送挖矿消息**: -```rust -let mining_msg = MiningMsg { - job_id: mining_task.job_id.clone(), - block_header: mining_task.block_header.clone(), - output_smt_size: mining_task.output_smt_size, - coinbase_hash: mining_task.coinbase_hash.clone(), - target: mining_task.target, -}; - -let msg_json = serde_json::to_string(&mining_msg)?; -self.publisher_socket.send_multipart(&["mining_msg".as_bytes(), msg_json.as_bytes()], 0)?; -``` - -**接收提交请求**: -```rust -// 消息格式: "submit {json_data}" -let submit_json = &message_str[7..]; // 去掉"submit "前缀 -let submit_request: SubmitRequest = serde_json::from_str(submit_json)?; -``` - -### 4. 工作流程 - -#### 4.1 获取区块模板流程 - -1. **获取区块模板**:通过gRPC从BaseNode获取 -2. **生成coinbase**:自动生成coinbase交易 -3. **获取target**:从`miner_data.target_difficulty`获取目标难度 -4. **HTTP请求**:获取上一个区块的`output_smt_size` -5. **计算output_smt_size**:`当前 = 上一个 + outputs长度 - inputs长度` -6. **生成任务ID**:16位16进制字符串 -7. **构造MiningTask**:包含完整信息(包括target) -8. **发送ZMQ消息**:发布挖矿任务 - -#### 4.2 提交处理流程 - -1. **接收ZMQ消息**:监听submit主题 -2. **验证任务ID**:查找对应的MiningTask -3. **构造区块**:使用提交的nonce和solution -4. **提交到BaseNode**:通过gRPC提交 -5. **返回结果**:发送submit_result消息 - -### 5. 配置参数 - -新增配置: -- `--base-node-http`:BaseNode HTTP地址(默认:127.0.0.1:9000) - -### 6. 缓存机制 - -- **任务缓存**:按height保存,最多3个区块 -- **output_smt_size缓存**:避免重复HTTP请求 - -## 测试工具 - -### Python测试脚本 (`test_gbt.py`) - -功能: -- 模拟ZMQ客户端 -- 接收挖矿任务 -- 自动提交模拟结果 -- 统计信息显示 - -使用方法: -```bash -python3 test_gbt.py -``` - -## 构建脚本 - -### Linux/macOS (`build.sh`) -- 检查Rust环境 -- 检查系统依赖 -- 自动构建release版本 - -### Windows (`build.bat`) -- 检查Rust环境 -- 检查Windows依赖 -- 自动构建release版本 - -## 使用示例 - -### 基本运行 -```bash -./target/release/gbt -``` - -### 自定义配置 -```bash -./target/release/gbt \ - --base-node 127.0.0.1:18102 \ - --base-node-http 127.0.0.1:9000 \ - --network mainnet \ - --wallet-address YOUR_WALLET_ADDRESS \ - --coinbase-extra "your_pool_name" -``` - -## 消息格式示例 - -### 挖矿任务消息 -```json -{ - "job_id": "a1b2c3d4e5f67890", - "block_header": { - "hash": "hash_here", - "height": 12345, - "nonce": 0, - // ... 其他字段 - }, - "output_smt_size": 1000, - "coinbase_hash": "coinbase_hash_here", - "target": 1000000 -} -``` - -### 提交请求消息 -```json -{ - "job_id": "a1b2c3d4e5f67890", - "nonce": 123456789, - "solution": "solution_hash_here" -} -``` - -## 总结 - -本次修改完全实现了您要求的功能: - -1. ✅ **HTTP API集成**:通过HTTP获取上一个区块的output_smt_size -2. ✅ **任务ID生成**:16位16进制随机ID -3. ✅ **简化的计算**:output_smt_size = 上一个 + outputs - inputs -4. ✅ **新的数据结构**:完整的MiningTask、MiningMsg等 -5. ✅ **ZMQ通信**:优化的消息格式和流程 -6. ✅ **任务管理**:基于job_id的任务跟踪 -7. ✅ **缓存机制**:避免重复HTTP请求 -8. ✅ **测试工具**:Python测试脚本 -9. ✅ **构建脚本**:跨平台构建支持 - -所有功能都已实现并通过测试,可以直接使用。 \ No newline at end of file diff --git a/OUTPUT_SMT_SIZE_FIX_SUMMARY.md b/OUTPUT_SMT_SIZE_FIX_SUMMARY.md deleted file mode 100644 index b768dc5..0000000 --- a/OUTPUT_SMT_SIZE_FIX_SUMMARY.md +++ /dev/null @@ -1,219 +0,0 @@ -# GBT项目Output SMT Size计算修正总结 - -## 问题描述 - -GBT项目计算的`output_smt_size`与区块链上的实际值不一致: - -- **区块链数据**: `output_smt_size: 809326` -- **GBT计算值**: `output_smt_size: 809271` -- **差异**: `809326 - 809271 = 55` - -## 根本原因分析 - -### 1. 原有计算方法的缺陷 - -GBT项目原本使用简化的计算方法: -```rust -// 错误的计算方法 -let current_output_smt_size = prev_output_smt_size + outputs.len() - inputs.len(); -``` - -这种方法没有考虑: -- **销毁输出**: 销毁输出不应该增加SMT叶子数量 -- **销毁输入**: 销毁输入不应该减少SMT叶子数量 -- **SMT变化统计**: 应该使用`new_leaves`和`stale_leaves`概念 - -### 2. 与Tari核心代码的差异 - -Tari核心代码的正确计算逻辑: -```rust -// Tari核心代码(正确) -let mut size = tip_header.output_smt_size; -size += changes.node_stats.first().map(|s| s.new_leaves).unwrap_or(0) as u64; -size = size.saturating_sub(changes.node_stats.first().map(|s| s.stale_leaves).unwrap_or(0) as u64); -``` - -## 修正方案 - -### 1. 实现正确的计算方法 - -在GBT项目中实现了与Tari核心逻辑一致的`calculate_output_smt_size`方法: - -```rust -fn calculate_output_smt_size( - prev_output_smt_size: u64, - outputs: &[String], // JSON字符串格式 - inputs: &[String], // JSON字符串格式 -) -> u64 { - let mut size = prev_output_smt_size; - let mut new_leaves = 0u64; - let mut stale_leaves = 0u64; - - // 计算新叶子数量(排除销毁输出) - for (i, output_json) in outputs.iter().enumerate() { - match serde_json::from_str::(output_json) { - Ok(output) => { - // 检查是否为销毁输出 - let is_burned = if let Some(features) = output.get("features") { - if let Some(output_type) = features.get("output_type") { - output_type.as_u64() == Some(2) // Burn = 2 - } else { - false - } - } else if let Some(output_type) = output.get("output_type") { - output_type.as_u64() == Some(2) // Burn = 2 - } else { - false - }; - - if !is_burned { - new_leaves += 1; - } - }, - Err(e) => { - warn!("Failed to parse output {} JSON: {}, assuming not burned", i, e); - new_leaves += 1; // 保守策略 - } - } - } - - // 计算移除叶子数量(排除销毁输入) - for (i, input_json) in inputs.iter().enumerate() { - match serde_json::from_str::(input_json) { - Ok(input) => { - // 检查是否为销毁输入 - let is_burned = if let Some(features) = input.get("features") { - if let Some(output_type) = features.get("output_type") { - output_type.as_u64() == Some(2) // Burn = 2 - } else { - false - } - } else if let Some(output_type) = input.get("output_type") { - output_type.as_u64() == Some(2) // Burn = 2 - } else { - false - }; - - if !is_burned { - stale_leaves += 1; - } - }, - Err(e) => { - warn!("Failed to parse input {} JSON: {}, assuming not burned", i, e); - stale_leaves += 1; // 保守策略 - } - } - } - - size += new_leaves; - size = size.saturating_sub(stale_leaves); - - info!("output_smt_size calculation: {} (prev) + {} (new_leaves) - {} (stale_leaves) = {} (current)", - prev_output_smt_size, new_leaves, stale_leaves, size); - - size -} -``` - -### 2. 关键改进点 - -1. **销毁输出识别**: 正确识别`output_type = 2`的销毁输出 -2. **销毁输入识别**: 正确识别销毁输入,不减少SMT叶子数量 -3. **JSON解析容错**: 支持多种JSON结构,提供错误处理 -4. **详细日志**: 添加调试信息,便于问题排查 -5. **饱和减法**: 使用`saturating_sub`防止下溢 - -### 3. 销毁输出类型确认 - -通过查看Tari核心代码确认: -```rust -pub enum OutputType { - Standard = 0, - Coinbase = 1, - Burn = 2, // 销毁输出类型 - ValidatorNodeRegistration = 3, - CodeTemplateRegistration = 4, -} -``` - -## 测试验证 - -### 1. 创建调试脚本 - -创建了`test_output_smt_debug.rs`脚本来验证计算逻辑: -- 模拟GBT项目的计算过程 -- 提供详细的调试输出 -- 支持实际区块数据的测试 - -### 2. 测试用例 - -```rust -// 测试用例示例 -let outputs = vec![ - r#"{"features": {"output_type": 0}}"#.to_string(), // Standard - r#"{"features": {"output_type": 1}}"#.to_string(), // Coinbase - r#"{"features": {"output_type": 2}}"#.to_string(), // Burned -]; - -let inputs = vec![ - r#"{"features": {"output_type": 0}}"#.to_string(), // Standard - r#"{"features": {"output_type": 2}}"#.to_string(), // Burned -]; -``` - -## 使用方法 - -### 1. 在GBT项目中使用 - -GBT项目已经在`get_block_template_and_coinbase`方法中使用了新的计算方法: - -```rust -// 计算当前output_smt_size -let current_output_smt_size = calculate_output_smt_size(prev_output_smt_size, &block_body.outputs, &block_body.inputs); -``` - -### 2. 调试和验证 - -1. **启用调试日志**: 设置日志级别为DEBUG以查看详细计算过程 -2. **运行调试脚本**: 使用`test_output_smt_debug.rs`验证计算逻辑 -3. **对比实际数据**: 将计算结果与区块链上的实际值对比 - -## 预期效果 - -修正后的GBT项目应该能够: - -1. ✅ **正确计算output_smt_size**: 与区块链上的实际值一致 -2. ✅ **处理销毁输出**: 正确识别和排除销毁输出 -3. ✅ **处理销毁输入**: 正确识别和排除销毁输入 -4. ✅ **提供调试信息**: 详细的日志输出便于问题排查 -5. ✅ **容错处理**: 对JSON解析错误进行合理处理 - -## 后续改进建议 - -### 1. 数据验证 - -- 获取实际的outputs和inputs JSON数据 -- 验证JSON结构与预期是否一致 -- 确认销毁输出的识别逻辑 - -### 2. 性能优化 - -- 考虑批量JSON解析优化 -- 添加缓存机制减少重复计算 -- 优化日志输出性能 - -### 3. 监控和告警 - -- 添加计算结果监控 -- 设置差异阈值告警 -- 记录计算性能指标 - -## 总结 - -通过本次修正,GBT项目的`output_smt_size`计算方法现在与Tari核心代码保持一致,能够正确处理销毁输出和销毁输入,确保计算结果的准确性。修正后的代码提供了详细的调试信息,便于问题排查和验证。 - ---- - -**修正状态**: ✅ 已完成 -**测试状态**: 🔄 需要实际数据验证 -**文档状态**: ✅ 完整 \ No newline at end of file diff --git a/README.md b/README.md index 9d69318..3f98e0e 100644 --- a/README.md +++ b/README.md @@ -4,81 +4,101 @@ ## 功能特性 -### 1. 区块模板获取 - 通过gRPC从BaseNode获取区块模板 - 自动生成coinbase交易 -- 支持多种挖矿算法(SHA3X、RandomXM、RandomXT) - -### 2. HTTP API集成 -- 通过HTTP请求获取上一个区块的`output_smt_size` -- 缓存机制避免重复请求 -- 自动计算当前区块的`output_smt_size` - -### 3. 任务管理 -- 生成16位16进制任务ID -- 任务缓存管理(最多保存3个区块) -- 支持任务状态跟踪 - -### 4. ZMQ通信 -- 发布挖矿任务消息 -- 接收矿工提交结果 -- 支持多种消息类型 +- 支持SHA3X挖矿算法 +- ZMQ推送标准JSON格式的挖矿任务 +- 每秒获取一次模板,高度变立即推送,否则每5秒推送一次最新模板 ## 数据结构 +### BlockHeader(区块头) +```rust +pub struct BlockHeader { + pub hash: String, + pub version: u32, + pub height: u64, + pub prev_hash: String, + pub timestamp: u64, + pub output_mr: String, + pub block_output_mr: String, + pub kernel_mr: String, + pub input_mr: String, + pub total_kernel_offset: String, + pub nonce: u64, + pub pow: ProofOfWork, + pub kernel_mmr_size: u64, + pub output_mmr_size: u64, + pub total_script_offset: String, + pub validator_node_mr: String, + pub validator_node_size: u64, + pub output_smt_size: u64, +} +``` + +### ProofOfWork +```rust +pub struct ProofOfWork { + pub pow_algo: u64, + pub pow_data: String, +} +``` + +### BlockBody(区块体) +```rust +pub struct BlockBody { + pub inputs: Vec, + pub outputs: Vec, + pub kernels: Vec, +} +``` + ### MiningTask(挖矿任务) ```rust pub struct MiningTask { - pub job_id: String, // 16位16进制任务ID - pub block_header: BlockHeader, // 区块头信息 - pub block_body: BlockBody, // 区块体信息 - pub output_smt_size: u64, // 当前output_smt_size - pub coinbase_hash: String, // coinbase哈希 - pub target: u64, // 目标难度 + pub block_header: BlockHeader, + pub block_body: BlockBody, + pub output_smt_size: u64, + pub coinbase_hash: String, + pub target: u64, + pub created_at: u64, } ``` ### MiningMsg(挖矿消息) ```rust pub struct MiningMsg { - pub job_id: String, // 任务ID - pub block_header: BlockHeader, // 区块头 - pub output_smt_size: u64, // output_smt_size - pub coinbase_hash: String, // coinbase哈希 - pub target: u64, // 目标难度 + pub height: u64, + pub mining_hash: String, + pub target: u64, + pub block_header: BlockHeader, + pub block_body: BlockBody, } ``` ### SubmitRequest(提交请求) ```rust pub struct SubmitRequest { - pub job_id: String, // 任务ID - pub nonce: u64, // 挖矿nonce - pub solution: String, // 挖矿解 + pub job_id: String, + pub nonce: u64, + pub solution: String, +} +``` + +### SubmitResult(提交结果) +```rust +pub struct SubmitResult { + pub job_id: String, + pub result: u8, // 1表示成功,0表示失败 } ``` ## 工作流程 -### 1. 获取区块模板 -1. 通过gRPC获取区块模板 -2. 生成coinbase交易 -3. 通过HTTP获取上一个区块的`output_smt_size` -4. 计算当前`output_smt_size = 上一个output_smt_size + outputs长度 - inputs长度` -5. 生成任务ID -6. 构造MiningTask实例 - -### 2. 发送挖矿任务 -1. 构造MiningMsg实例 -2. 通过ZMQ发布消息 -3. 缓存任务信息 - -### 3. 接收提交结果 -1. 监听ZMQ提交消息 -2. 验证任务ID -3. 构造完整区块 -4. 提交到BaseNode -5. 返回提交结果 +1. 每秒从BaseNode获取一次区块模板。 +2. 若区块高度变化,立即推送新任务;否则每5秒推送一次最新模板。 +3. 通过ZMQ发布标准JSON格式的MiningMsg消息,block_body为JSON对象。 +4. 任务缓存、job_id、submit相关逻辑已移除,代码结构更简洁。 ## 配置参数 @@ -89,8 +109,12 @@ pub struct SubmitRequest { | `--network` | `mainnet` | 网络类型 | | `--wallet-address` | 默认地址 | 钱包地址 | | `--coinbase-extra` | `m2pool.com` | Coinbase额外数据 | -| `--zmq-pub-port` | `5555` | ZMQ发布端口 | -| `--zmq-sub-port` | `5556` | ZMQ订阅端口 | +| `--zmq-pub-port` | `31000` | ZMQ发布端口 | +| `--zmq-sub-port` | `31001` | ZMQ订阅端口 | +| `--tls` | 关闭 | 启用TLS | +| `--tls-domain` | 无 | TLS域名 | +| `--tls-ca-cert` | 无 | TLS CA证书文件 | +| `--config-dir` | `.` | 配置目录 | ## 构建和运行 @@ -111,8 +135,8 @@ cargo build --release --network mainnet \ --wallet-address YOUR_WALLET_ADDRESS \ --coinbase-extra "your_pool_name" \ - --zmq-pub-port 5555 \ - --zmq-sub-port 5556 + --zmq-pub-port 31000 \ + --zmq-sub-port 31001 ``` ## ZMQ消息格式 @@ -120,7 +144,7 @@ cargo build --release ### 挖矿任务消息 ``` Topic: "mining_msg" -Data: JSON格式的MiningMsg +Data: JSON格式的MiningMsg,block_body为标准JSON对象 ``` ### 提交请求消息 @@ -138,11 +162,11 @@ Data: JSON格式的SubmitResult ## 依赖项 - `reqwest`: HTTP客户端 -- `uuid`: 任务ID生成 - `zmq`: ZMQ通信 - `serde`: 序列化/反序列化 - `tokio`: 异步运行时 - `tonic`: gRPC客户端 +- 其它Tari相关依赖 ## 注意事项 @@ -154,8 +178,6 @@ Data: JSON格式的SubmitResult ## 故障排除 -### 常见问题 - 1. **连接BaseNode失败** - 检查BaseNode是否运行 - 验证gRPC地址和端口 @@ -178,5 +200,4 @@ Data: JSON格式的SubmitResult ## 开发 -### 添加新功能 -1. 修改`src/main.rs` \ No newline at end of file +如需扩展功能,请直接修改`src/main.rs`,结构清晰,易于维护。 \ No newline at end of file diff --git a/build.bat b/build.bat deleted file mode 100644 index 4751282..0000000 --- a/build.bat +++ /dev/null @@ -1,69 +0,0 @@ -@echo off -REM GBT构建脚本 (Windows版本) -REM 用于编译GBT客户端 - -echo === Tari GBT客户端构建脚本 === -echo. - -REM 检查Rust环境 -where cargo >nul 2>nul -if %errorlevel% neq 0 ( - echo 错误: 未找到cargo,请先安装Rust - echo 访问 https://rustup.rs/ 安装Rust - pause - exit /b 1 -) - -echo ✓ Rust环境检查通过 -for /f "tokens=2" %%i in ('cargo --version') do echo Rust版本: %%i - -echo. -echo 检查依赖项... - -REM 检查ZMQ (Windows下通常通过vcpkg安装) -where zmq >nul 2>nul -if %errorlevel% neq 0 ( - echo 警告: 未找到ZMQ,可能需要安装 - echo 建议使用vcpkg安装: vcpkg install zeromq - echo. - echo 继续构建,但可能遇到ZMQ相关错误... -) - -echo ✓ 依赖检查完成 - -REM 清理旧的构建 -echo. -echo 清理旧的构建文件... -cargo clean - -REM 构建 -echo. -echo 开始构建GBT客户端... -echo 构建模式: release -echo. - -cargo build --release - -if %errorlevel% equ 0 ( - echo. - echo ✓ 构建成功! - echo. - echo 可执行文件位置: target\release\gbt.exe - echo. - echo 使用方法: - echo target\release\gbt.exe --help - echo. - echo 示例: - echo target\release\gbt.exe --base-node 127.0.0.1:18102 --base-node-http 127.0.0.1:9000 - echo. - echo 测试ZMQ通信: - echo python test_gbt.py -) else ( - echo. - echo ✗ 构建失败! - echo 请检查错误信息并修复问题 - pause - exit /b 1 -) - -pause \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100644 index 28a543d..0000000 --- a/build.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash - -# GBT项目构建脚本 -# 支持Linux和macOS - -set -e - -echo "🚀 开始构建GBT项目..." - -# 检查Rust环境 -if ! command -v cargo &> /dev/null; then - echo "❌ 错误: 未找到cargo,请先安装Rust" - echo "访问 https://rustup.rs/ 安装Rust" - exit 1 -fi - -# 检查Rust版本 -RUST_VERSION=$(rustc --version | cut -d' ' -f2) -echo "📦 Rust版本: $RUST_VERSION" - -# 检查依赖 -echo "" -echo "检查依赖项..." - -# 检查ZMQ -if ! pkg-config --exists libzmq3; then - echo "警告: 未找到ZMQ开发库" - echo "Ubuntu/Debian: sudo apt-get install libzmq3-dev" - echo "CentOS/RHEL: sudo yum install zeromq-devel" - echo "macOS: brew install zeromq" - echo "" - echo "继续构建,但可能遇到ZMQ相关错误..." -fi - -# 检查OpenSSL -if ! pkg-config --exists openssl; then - echo "警告: 未找到OpenSSL开发库" - echo "Ubuntu/Debian: sudo apt-get install libssl-dev" - echo "CentOS/RHEL: sudo yum install openssl-devel" - echo "macOS: brew install openssl" - echo "" - echo "继续构建,但可能遇到TLS相关错误..." -fi - -echo "✓ 依赖检查完成" - -# 清理之前的构建 -echo "🧹 清理之前的构建..." -cargo clean - -# 更新依赖 -echo "📥 更新依赖..." -cargo update - -# 检查代码 -echo "🔍 检查代码..." -cargo check - -# 运行测试 -echo "🧪 运行测试..." -cargo test - -# 构建发布版本 -echo "🔨 构建发布版本..." -cargo build --release - -# 检查构建结果 -if [ -f "target/release/gbt" ]; then - echo "✅ 构建成功!" - echo "📁 可执行文件位置: target/release/gbt" - - # 显示文件信息 - echo "📊 文件信息:" - ls -lh target/release/gbt - - # 显示版本信息 - echo "ℹ️ 版本信息:" - ./target/release/gbt --version 2>/dev/null || echo "无法获取版本信息" - -else - echo "❌ 构建失败!" - exit 1 -fi - -echo "🎉 GBT项目构建完成!" \ No newline at end of file diff --git a/gbt_process b/gbt_process deleted file mode 100644 index 5957cdb..0000000 --- a/gbt_process +++ /dev/null @@ -1,14 +0,0 @@ -// 自建矿池直接与BaseNode交互 -let base_node_client = connect_base_node(&config).await?; - -// 获取区块模板 -let template_response = base_node_client.get_new_block_template(request).await?; - -// 生成币基交易 -let (coinbase_output, coinbase_kernel) = generate_coinbase(...).await?; - -// 构建完整区块 -let block_result = base_node_client.get_new_block(block_template).await?; - -// 提交区块 -base_node_client.submit_block(block).await?; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 09a323a..496d6fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,23 +20,23 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; +use std::{path::PathBuf, time::{SystemTime, UNIX_EPOCH}}; +use tokio::time::sleep; use anyhow::{anyhow, Result}; use clap::Parser; use log::*; use reqwest::Client; use serde::{Deserialize, Serialize}; -use tokio::{sync::Mutex, time::sleep}; use tonic::transport::{Certificate, ClientTlsConfig, Endpoint}; -use uuid::Uuid; -use zmq::{Context, Message, Socket}; +use zmq::{Context, Socket}; use minotari_app_grpc::{ authentication::ClientAuthenticationInterceptor, conversions::transaction_output::grpc_output_with_payref, tari_rpc::{ base_node_client::BaseNodeClient, pow_algo::PowAlgos, Block, NewBlockTemplateRequest, PowAlgo, + TransactionInput, TransactionOutput, TransactionKernel, }, }; use minotari_app_utilities::parse_miner_input::BaseNodeGrpcClient; @@ -57,6 +57,10 @@ use tari_core::{ }, }; use tari_utilities::hex::Hex; +use tari_hashing::DomainSeparatedBorshHasher; +use blake2::Blake2b; +use digest::consts::U32; +use tari_core::blocks::BlocksHashDomain; const LOG_TARGET: &str = "gbt::main"; @@ -80,6 +84,7 @@ pub struct BlockHeader { pub total_script_offset: String, pub validator_node_mr: String, pub validator_node_size: u64, + pub output_smt_size: u64, // 新增:output_smt_size字段 } // 工作量证明结构 @@ -92,30 +97,30 @@ pub struct ProofOfWork { // 区块体结构 #[derive(Debug, Serialize, Deserialize, Clone)] pub struct BlockBody { - pub inputs: Vec, - pub outputs: Vec, - pub kernels: Vec, + pub inputs: Vec, + pub outputs: Vec, + pub kernels: Vec, } // 挖矿任务结构 #[derive(Debug, Serialize, Deserialize, Clone)] pub struct MiningTask { - pub job_id: String, pub block_header: BlockHeader, pub block_body: BlockBody, pub output_smt_size: u64, pub coinbase_hash: String, pub target: u64, // 新增:目标难度 + pub created_at: u64, // 新增:创建时间戳(Unix时间戳) } // 挖矿消息结构 #[derive(Debug, Serialize, Deserialize, Clone)] pub struct MiningMsg { - pub job_id: String, + pub height: u64, + pub mining_hash: String, + pub target: u64, pub block_header: BlockHeader, - pub output_smt_size: u64, - pub coinbase_hash: String, - pub target: u64, // 新增:目标难度 + pub block_body: BlockBody, } // 提交请求结构 @@ -214,6 +219,29 @@ fn calculate_output_smt_size( size } +// === 新增:mining_hash 计算方法 === +fn calculate_mining_hash(header: &BlockHeader) -> Result { + let mut hasher = DomainSeparatedBorshHasher::>::new_with_label("block_header"); + + hasher.update_consensus_encode(&header.version); + hasher.update_consensus_encode(&header.height); + hasher.update_consensus_encode(&header.prev_hash); + hasher.update_consensus_encode(&header.timestamp); + hasher.update_consensus_encode(&header.input_mr); + hasher.update_consensus_encode(&header.output_mr); + hasher.update_consensus_encode(&header.output_smt_size); + hasher.update_consensus_encode(&header.block_output_mr); + hasher.update_consensus_encode(&header.kernel_mr); + hasher.update_consensus_encode(&header.kernel_mmr_size); + hasher.update_consensus_encode(&header.total_kernel_offset); + hasher.update_consensus_encode(&header.total_script_offset); + hasher.update_consensus_encode(&header.validator_node_mr); + hasher.update_consensus_encode(&header.validator_node_size); + + let mining_hash = hasher.finalize(); + Ok(hex::encode(mining_hash.as_slice())) +} + // 配置结构 #[derive(Debug, Clone)] pub struct GbtConfig { @@ -244,13 +272,6 @@ pub struct GbtClient { #[allow(dead_code)] zmq_context: Context, publisher_socket: Socket, - subscriber_socket: Socket, - - // 挖矿任务缓存,按height保存,最多保存3个区块 - mining_tasks: Arc>>, - - // 缓存上一个区块的output_smt_size,避免重复请求 - prev_output_smt_size_cache: Arc>>, } impl GbtClient { @@ -278,23 +299,13 @@ impl GbtClient { let publisher_socket = zmq_context .socket(zmq::PUB) .map_err(|e| anyhow!("ZMQ publisher error: {}", e))?; - let subscriber_socket = zmq_context - .socket(zmq::SUB) - .map_err(|e| anyhow!("ZMQ subscriber error: {}", e))?; // 绑定ZMQ套接字 let publisher_addr = format!("tcp://*:{}", config.zmq_publisher_port); - let subscriber_addr = format!("tcp://localhost:{}", config.zmq_subscriber_port); publisher_socket .bind(&publisher_addr) .map_err(|e| anyhow!("ZMQ bind error: {}", e))?; - subscriber_socket - .connect(&subscriber_addr) - .map_err(|e| anyhow!("ZMQ connect error: {}", e))?; - subscriber_socket - .set_subscribe(b"submit") - .map_err(|e| anyhow!("ZMQ subscribe error: {}", e))?; Ok(Self { base_node_client, @@ -305,9 +316,6 @@ impl GbtClient { config, zmq_context, publisher_socket, - subscriber_socket, - mining_tasks: Arc::new(Mutex::new(HashMap::new())), - prev_output_smt_size_cache: Arc::new(Mutex::new(HashMap::new())), }) } @@ -349,68 +357,6 @@ impl GbtClient { Ok(node_conn) } - // 生成16位16进制任务ID - fn generate_job_id() -> String { - let uuid = Uuid::new_v4(); - uuid.to_string().replace("-", "")[..16].to_string() - } - - // 通过HTTP获取上一个区块的output_smt_size - async fn get_prev_output_smt_size(&self, height: u64) -> Result { - let prev_height = height - 1; - - // 检查缓存 - { - let cache = self.prev_output_smt_size_cache.lock().await; - if let Some(&size) = cache.get(&prev_height) { - return Ok(size); - } - } - - // 通过HTTP请求获取 - let url = format!("http://{}/get_header_by_height?height={}", - self.config.base_node_http_address, prev_height); - - info!(target: LOG_TARGET, "Fetching previous block output_smt_size from: {}", url); - - let response = self.http_client - .get(&url) - .send() - .await - .map_err(|e| anyhow!("HTTP request error: {}", e))?; - - if !response.status().is_success() { - return Err(anyhow!("HTTP request failed with status: {}", response.status())); - } - - let header_data: serde_json::Value = response - .json() - .await - .map_err(|e| anyhow!("JSON parsing error: {}", e))?; - - let output_smt_size = header_data["output_smt_size"] - .as_u64() - .ok_or_else(|| anyhow!("Missing output_smt_size in response"))?; - - // 更新缓存 - { - let mut cache = self.prev_output_smt_size_cache.lock().await; - cache.insert(prev_height, output_smt_size); - - // 清理缓存,只保留最近3个区块 - if cache.len() > 3 { - let mut heights: Vec = cache.keys().cloned().collect(); - heights.sort(); - for height in heights.iter().take(heights.len() - 3) { - cache.remove(height); - } - } - } - - info!(target: LOG_TARGET, "Previous block output_smt_size: {}", output_smt_size); - Ok(output_smt_size) - } - // 将gRPC Block转换为自定义结构 fn convert_block_to_structures(&self, block: &Block) -> Result<(BlockHeader, BlockBody)> { let header = block.header.as_ref() @@ -441,23 +387,14 @@ impl GbtClient { total_script_offset: header.total_script_offset.to_hex(), validator_node_mr: header.validator_node_mr.to_hex(), validator_node_size: header.validator_node_size, + output_smt_size: header.output_mmr_size, // 使用从gRPC获取的output_mmr_size作为初始值 }; - // 构造BlockBody - 使用serde_json序列化然后反序列化为字符串 - let inputs: Vec = body.inputs.iter() - .map(|i| serde_json::to_string(i).unwrap_or_default()) - .collect(); - let outputs: Vec = body.outputs.iter() - .map(|o| serde_json::to_string(o).unwrap_or_default()) - .collect(); - let kernels: Vec = body.kernels.iter() - .map(|k| serde_json::to_string(k).unwrap_or_default()) - .collect(); - + // 构造BlockBody - 直接克隆原始结构体 let block_body = BlockBody { - inputs, - outputs, - kernels, + inputs: body.inputs.clone(), + outputs: body.outputs.clone(), + kernels: body.kernels.clone(), }; Ok((block_header, block_body)) @@ -543,242 +480,90 @@ impl GbtClient { let coinbase_hash = coinbase_output.hash().to_hex(); // 转换为自定义结构 - let (block_header, block_body) = self.convert_block_to_structures(&block)?; + let (mut block_header, block_body) = self.convert_block_to_structures(&block)?; - // 获取上一个区块的output_smt_size - let prev_output_smt_size = self.get_prev_output_smt_size(height).await?; + // 先将 outputs 和 inputs 序列化为 JSON 字符串 + let output_jsons: Vec = block_body.outputs.iter() + .map(|o| serde_json::to_string(o).unwrap_or_default()) + .collect(); + let input_jsons: Vec = block_body.inputs.iter() + .map(|i| serde_json::to_string(i).unwrap_or_default()) + .collect(); + let current_output_smt_size = calculate_output_smt_size(0, &output_jsons, &input_jsons); - // 计算当前output_smt_size - let current_output_smt_size = calculate_output_smt_size(prev_output_smt_size, &block_body.outputs, &block_body.inputs); - - // 生成任务ID - let job_id = Self::generate_job_id(); - - info!(target: LOG_TARGET, "Generated job_id: {}, output_smt_size: {} (prev: {}), target: {}", - job_id, current_output_smt_size, prev_output_smt_size, target_difficulty); + // 更新block_header中的output_smt_size + block_header.output_smt_size = current_output_smt_size; let mining_task = MiningTask { - job_id: job_id.clone(), - block_header, + block_header: block_header.clone(), block_body, output_smt_size: current_output_smt_size, coinbase_hash, target: target_difficulty, + created_at: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(), }; - // 缓存挖矿任务 - { - let mut tasks = self.mining_tasks.lock().await; - tasks.insert(height, mining_task.clone()); - - // 清理缓存,只保留最近3个区块 - if tasks.len() > 3 { - let mut heights: Vec = tasks.keys().cloned().collect(); - heights.sort(); - for height in heights.iter().take(heights.len() - 3) { - tasks.remove(height); - } - } - } - Ok(mining_task) } // 发送挖矿消息 - pub fn send_mining_msg(&self, mining_task: &MiningTask) -> Result<()> { + pub fn send_mining_msg(&self, block_header: &BlockHeader, block_body: &BlockBody, target: u64) -> Result<()> { let mining_msg = MiningMsg { - job_id: mining_task.job_id.clone(), - block_header: mining_task.block_header.clone(), - output_smt_size: mining_task.output_smt_size, - coinbase_hash: mining_task.coinbase_hash.clone(), - target: mining_task.target, + height: block_header.height, + mining_hash: calculate_mining_hash(block_header)?, + target, + block_header: block_header.clone(), + block_body: block_body.clone(), }; - let msg_json = serde_json::to_string(&mining_msg).map_err(|e| anyhow!("Serialization error: {}", e))?; - self.publisher_socket .send_multipart(&["mining_msg".as_bytes(), msg_json.as_bytes()], 0) .map_err(|e| anyhow!("ZMQ send error: {}", e))?; - - info!(target: LOG_TARGET, "Sent mining message for job_id {} with height {}, output_smt_size {}, and target {}", - mining_task.job_id, mining_task.block_header.height, mining_task.output_smt_size, mining_task.target); + info!(target: LOG_TARGET, "Sent mining message for height {}, target {}", block_header.height, target); Ok(()) } - // 接收提交请求 - pub async fn receive_submit(&mut self) -> Result> { - let mut message = Message::new(); - - // 非阻塞接收 - match self.subscriber_socket.recv(&mut message, zmq::DONTWAIT) { - Ok(_) => { - let message_str = message.as_str().ok_or_else(|| anyhow!("Message decode error"))?; - - if message_str.starts_with("submit ") { - let submit_json = &message_str[7..]; // 去掉"submit "前缀 - let submit_request: SubmitRequest = - serde_json::from_str(submit_json).map_err(|e| anyhow!("Deserialization error: {}", e))?; - - info!(target: LOG_TARGET, "Received submit for job_id {} with nonce {}", - submit_request.job_id, submit_request.nonce); - - Ok(Some(submit_request)) - } else { - Ok(None) - } - }, - Err(zmq::Error::EAGAIN) => { - // 没有消息可读 - Ok(None) - }, - Err(e) => Err(anyhow!("ZMQ receive error: {}", e)), - } - } - - // 提交区块到BaseNode - pub async fn submit_block_to_base_node(&mut self, submit_request: &SubmitRequest) -> Result { - // 查找对应的挖矿任务 - let mining_task = { - let tasks = self.mining_tasks.lock().await; - let mut found_task = None; - for task in tasks.values() { - if task.job_id == submit_request.job_id { - found_task = Some(task.clone()); - break; - } - } - found_task.ok_or_else(|| anyhow!("Mining task not found for job_id: {}", submit_request.job_id))? - }; - - // 构造提交区块 - let mut submit_block = Block { - header: None, - body: None, - }; - - // 构造header - let mut header = minotari_app_grpc::tari_rpc::BlockHeader::default(); - header.hash = hex::decode(&mining_task.block_header.hash)?; - header.version = mining_task.block_header.version; - header.height = mining_task.block_header.height; - header.prev_hash = hex::decode(&mining_task.block_header.prev_hash)?; - header.timestamp = mining_task.block_header.timestamp; - header.output_mr = hex::decode(&mining_task.block_header.output_mr)?; - header.block_output_mr = hex::decode(&mining_task.block_header.block_output_mr)?; - header.kernel_mr = hex::decode(&mining_task.block_header.kernel_mr)?; - header.input_mr = hex::decode(&mining_task.block_header.input_mr)?; - header.total_kernel_offset = hex::decode(&mining_task.block_header.total_kernel_offset)?; - header.nonce = submit_request.nonce; // 使用提交的nonce - header.pow = Some(minotari_app_grpc::tari_rpc::ProofOfWork { - pow_algo: mining_task.block_header.pow.pow_algo, - pow_data: hex::decode(&mining_task.block_header.pow.pow_data)?, - }); - header.kernel_mmr_size = mining_task.block_header.kernel_mmr_size; - header.output_mmr_size = mining_task.output_smt_size; // 使用计算出的output_smt_size - header.total_script_offset = hex::decode(&mining_task.block_header.total_script_offset)?; - header.validator_node_mr = hex::decode(&mining_task.block_header.validator_node_mr)?; - header.validator_node_size = mining_task.block_header.validator_node_size; - - submit_block.header = Some(header); - - // 构造body - 从JSON字符串反序列化 - let mut body = minotari_app_grpc::tari_rpc::AggregateBody::default(); - - // 反序列化inputs - for input_str in &mining_task.block_body.inputs { - if let Ok(input) = serde_json::from_str(input_str) { - body.inputs.push(input); - } - } - - // 反序列化outputs - for output_str in &mining_task.block_body.outputs { - if let Ok(output) = serde_json::from_str(output_str) { - body.outputs.push(output); - } - } - - // 反序列化kernels - for kernel_str in &mining_task.block_body.kernels { - if let Ok(kernel) = serde_json::from_str(kernel_str) { - body.kernels.push(kernel); - } - } - - submit_block.body = Some(body); - - info!(target: LOG_TARGET, "Submitting block to base node for job_id {}", submit_request.job_id); - - // 提交区块 - let response = self.base_node_client.submit_block(submit_block).await?; - let response_inner = response.into_inner(); - - // 比较结果 - 使用Debug格式进行比较 - let result = if format!("{:?}", response_inner) == submit_request.solution { - 1 - } else { - 0 - }; - - let submit_result = SubmitResult { - job_id: submit_request.job_id.clone(), - result, - }; - - // 发送提交结果 - let result_json = serde_json::to_string(&submit_result).map_err(|e| anyhow!("Serialization error: {}", e))?; - self.publisher_socket - .send_multipart(&["submit_result".as_bytes(), result_json.as_bytes()], 0) - .map_err(|e| anyhow!("ZMQ send error: {}", e))?; - - info!(target: LOG_TARGET, "Submitted block result for job_id {}: {}", submit_request.job_id, result); - - Ok(submit_result) - } - - // 主循环 pub async fn run(&mut self) -> Result<()> { info!(target: LOG_TARGET, "Starting GBT client"); - + let mut last_height: Option = None; + let mut last_send_time: u64 = 0; loop { - // 1. 获取区块模板和构造coinbase - match self.get_block_template_and_coinbase().await { - Ok(mining_task) => { - // 2. 通过ZMQ发送挖矿消息 - if let Err(e) = self.send_mining_msg(&mining_task) { - error!(target: LOG_TARGET, "Failed to send mining message: {}", e); - } - }, + // 1. 每秒请求一次模板 + let mining_task = match self.get_block_template_and_coinbase().await { + Ok(task) => task, Err(e) => { error!(target: LOG_TARGET, "Failed to get block template: {}", e); - sleep(Duration::from_secs(5)).await; + sleep(std::time::Duration::from_secs(1)).await; continue; + } + }; + let current_height = mining_task.block_header.height; + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); + let mut should_send = false; + match last_height { + None => { + should_send = true; + info!(target: LOG_TARGET, "First template, sending immediately"); }, - } - - // 3. 接收外部提交 - match self.receive_submit().await { - Ok(Some(submit_request)) => { - // 4. 提交区块到BaseNode - match self.submit_block_to_base_node(&submit_request).await { - Ok(_) => { - info!(target: LOG_TARGET, "Successfully processed submit for job_id {}", submit_request.job_id); - }, - Err(e) => { - error!(target: LOG_TARGET, "Failed to submit block: {}", e); - }, + Some(prev_height) => { + if current_height != prev_height { + should_send = true; + info!(target: LOG_TARGET, "Height changed: {} -> {}, sending immediately", prev_height, current_height); + } else if now >= last_send_time + 5 { + should_send = true; + info!(target: LOG_TARGET, "Same height {}, sending after 5 seconds", current_height); } - }, - Ok(None) => { - // 没有提交,继续循环 - }, - Err(e) => { - error!(target: LOG_TARGET, "Failed to receive submit: {}", e); - }, + } } - - // 等待一段时间再获取下一个区块模板 - sleep(Duration::from_secs(1)).await; + if should_send { + if let Err(e) = self.send_mining_msg(&mining_task.block_header, &mining_task.block_body, mining_task.target) { + error!(target: LOG_TARGET, "Failed to send mining message: {}", e); + } else { + last_height = Some(current_height); + last_send_time = now; + } + } + sleep(std::time::Duration::from_secs(1)).await; } } } @@ -818,11 +603,11 @@ struct Args { coinbase_extra: String, /// ZMQ publisher port - #[arg(long, default_value = "5555")] + #[arg(long, default_value = "31000")] zmq_pub_port: u16, /// ZMQ subscriber port - #[arg(long, default_value = "5556")] + #[arg(long, default_value = "31001")] zmq_sub_port: u16, /// Enable TLS diff --git a/test_gbt.py b/test_gbt.py deleted file mode 100644 index bd0b352..0000000 --- a/test_gbt.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python3 -""" -GBT测试脚本 -用于测试GBT客户端的ZMQ通信功能 -""" - -import zmq -import json -import time -import threading -from typing import Dict, Any - -class GbtTester: - def __init__(self, pub_port: int = 5555, sub_port: int = 5556): - self.pub_port = pub_port - self.sub_port = sub_port - self.context = zmq.Context() - - # 订阅者套接字(接收挖矿任务) - self.subscriber = self.context.socket(zmq.SUB) - self.subscriber.connect(f"tcp://localhost:{self.sub_port}") - self.subscriber.setsockopt_string(zmq.SUBSCRIBE, "mining_msg") - - # 发布者套接字(发送提交结果) - self.publisher = self.context.socket(zmq.PUB) - self.publisher.bind(f"tcp://*:{self.pub_port}") - - self.running = False - self.received_tasks: Dict[str, Dict[str, Any]] = {} - - def start(self): - """启动测试器""" - self.running = True - - # 启动接收线程 - self.receive_thread = threading.Thread(target=self._receive_loop) - self.receive_thread.daemon = True - self.receive_thread.start() - - print(f"GBT测试器已启动") - print(f"订阅端口: {self.sub_port}") - print(f"发布端口: {self.pub_port}") - - def stop(self): - """停止测试器""" - self.running = False - self.subscriber.close() - self.publisher.close() - self.context.term() - - def _receive_loop(self): - """接收消息循环""" - while self.running: - try: - # 非阻塞接收 - message = self.subscriber.recv_multipart(flags=zmq.NOBLOCK) - if message: - topic = message[0].decode('utf-8') - data = message[1].decode('utf-8') - - if topic == "mining_msg": - self._handle_mining_msg(data) - - except zmq.Again: - # 没有消息,继续循环 - time.sleep(0.1) - continue - except Exception as e: - print(f"接收消息错误: {e}") - time.sleep(1) - - def _handle_mining_msg(self, data: str): - """处理挖矿消息""" - try: - mining_msg = json.loads(data) - job_id = mining_msg.get('job_id') - - print(f"\n收到挖矿任务: {job_id}") - print(f"区块高度: {mining_msg.get('block_header', {}).get('height')}") - print(f"output_smt_size: {mining_msg.get('output_smt_size')}") - print(f"target: {mining_msg.get('target')}") - print(f"coinbase_hash: {mining_msg.get('coinbase_hash')[:16]}...") - - # 保存任务 - self.received_tasks[job_id] = mining_msg - - # 模拟挖矿(延迟1秒后提交) - threading.Timer(1.0, self._submit_result, args=[job_id]).start() - - except json.JSONDecodeError as e: - print(f"JSON解析错误: {e}") - except Exception as e: - print(f"处理挖矿消息错误: {e}") - - def _submit_result(self, job_id: str): - """提交挖矿结果""" - if job_id not in self.received_tasks: - print(f"任务 {job_id} 不存在") - return - - # 构造提交请求 - submit_request = { - "job_id": job_id, - "nonce": 12345, # 模拟nonce - "solution": "test_solution_hash_12345" # 模拟solution - } - - try: - # 发送提交请求 - message = f"submit {json.dumps(submit_request)}" - self.publisher.send_string(message) - - print(f"已提交挖矿结果: {job_id}") - print(f"nonce: {submit_request['nonce']}") - print(f"solution: {submit_request['solution']}") - - except Exception as e: - print(f"提交结果错误: {e}") - - def get_stats(self) -> Dict[str, Any]: - """获取统计信息""" - return { - "received_tasks": len(self.received_tasks), - "task_ids": list(self.received_tasks.keys()) - } - -def main(): - """主函数""" - print("GBT ZMQ通信测试器") - print("=" * 50) - - # 创建测试器 - tester = GbtTester() - - try: - # 启动测试器 - tester.start() - - # 主循环 - while True: - time.sleep(5) - stats = tester.get_stats() - print(f"\n统计信息: 已接收 {stats['received_tasks']} 个任务") - - except KeyboardInterrupt: - print("\n正在停止测试器...") - finally: - tester.stop() - print("测试器已停止") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/test_output_smt_debug.rs b/test_output_smt_debug.rs deleted file mode 100644 index 3ca0fcd..0000000 --- a/test_output_smt_debug.rs +++ /dev/null @@ -1,145 +0,0 @@ -// 调试脚本:测试GBT项目的output_smt_size计算 -// 使用实际的区块数据来验证计算是否正确 - -use serde_json::Value; - -// 模拟GBT项目中的calculate_output_smt_size函数 -fn calculate_output_smt_size( - prev_output_smt_size: u64, - outputs: &[String], // JSON字符串格式 - inputs: &[String], // JSON字符串格式 -) -> u64 { - let mut size = prev_output_smt_size; - let mut new_leaves = 0u64; - let mut stale_leaves = 0u64; - - println!("=== 计算新叶子数量(排除销毁输出) ==="); - // 计算新叶子数量(排除销毁输出) - for (i, output_json) in outputs.iter().enumerate() { - match serde_json::from_str::(output_json) { - Ok(output) => { - // 尝试多种可能的JSON结构 - let is_burned = if let Some(features) = output.get("features") { - if let Some(output_type) = features.get("output_type") { - let burned = output_type.as_u64() == Some(2); // Burn = 2 - println!("Output {}: features.output_type = {:?}, burned = {}", i, output_type, burned); - burned - } else { - println!("Output {}: features.output_type not found, assuming not burned", i); - false - } - } else if let Some(output_type) = output.get("output_type") { - let burned = output_type.as_u64() == Some(2); // Burn = 2 - println!("Output {}: output_type = {:?}, burned = {}", i, output_type, burned); - burned - } else { - println!("Output {}: no output_type found, assuming not burned", i); - false - }; - - if !is_burned { - new_leaves += 1; - println!("Output {}: not burned, adding leaf (new_leaves = {})", i, new_leaves); - } else { - println!("Output {}: burned, skipping", i); - } - }, - Err(e) => { - println!("Failed to parse output {} JSON: {}, assuming not burned", i, e); - new_leaves += 1; // 保守策略:假设不是销毁输出 - } - } - } - - println!("\n=== 计算移除叶子数量(排除销毁输入) ==="); - // 计算移除叶子数量(排除销毁输入) - for (i, input_json) in inputs.iter().enumerate() { - match serde_json::from_str::(input_json) { - Ok(input) => { - // 尝试多种可能的JSON结构 - let is_burned = if let Some(features) = input.get("features") { - if let Some(output_type) = features.get("output_type") { - let burned = output_type.as_u64() == Some(2); // Burn = 2 - println!("Input {}: features.output_type = {:?}, burned = {}", i, output_type, burned); - burned - } else { - println!("Input {}: features.output_type not found, assuming not burned", i); - false - } - } else if let Some(output_type) = input.get("output_type") { - let burned = output_type.as_u64() == Some(2); // Burn = 2 - println!("Input {}: output_type = {:?}, burned = {}", i, output_type, burned); - burned - } else { - println!("Input {}: no output_type found, assuming not burned", i); - false - }; - - if !is_burned { - stale_leaves += 1; - println!("Input {}: not burned, removing leaf (stale_leaves = {})", i, stale_leaves); - } else { - println!("Input {}: burned, skipping", i); - } - }, - Err(e) => { - println!("Failed to parse input {} JSON: {}, assuming not burned", i, e); - stale_leaves += 1; // 保守策略:假设不是销毁输入 - } - } - } - - size += new_leaves; - size = size.saturating_sub(stale_leaves); - - println!("\n=== 计算结果 ==="); - println!("output_smt_size calculation: {} (prev) + {} (new_leaves) - {} (stale_leaves) = {} (current)", - prev_output_smt_size, new_leaves, stale_leaves, size); - - size -} - -fn main() { - println!("GBT项目 output_smt_size 计算调试"); - println!("================================\n"); - - // 使用你提供的实际数据 - let prev_output_smt_size = 809326; // 区块链上的实际值 - - // 这里需要实际的outputs和inputs JSON数据 - // 由于你没有提供具体的outputs和inputs数据,我们创建一个示例 - - println!("测试用例1: 基本计算"); - println!("prev_output_smt_size = {}", prev_output_smt_size); - - // 示例数据(你需要用实际的数据替换) - let outputs = vec![ - r#"{"features": {"output_type": 0}}"#.to_string(), // Standard output - r#"{"features": {"output_type": 1}}"#.to_string(), // Coinbase output - r#"{"features": {"output_type": 2}}"#.to_string(), // Burned output - ]; - - let inputs = vec![ - r#"{"features": {"output_type": 0}}"#.to_string(), // Standard input - r#"{"features": {"output_type": 2}}"#.to_string(), // Burned input - ]; - - let calculated_size = calculate_output_smt_size(prev_output_smt_size, &outputs, &inputs); - - println!("\n=== 最终结果 ==="); - println!("期望值: 809326"); - println!("计算值: {}", calculated_size); - println!("差异: {}", 809326i64 - calculated_size as i64); - - if calculated_size == 809326 { - println!("✅ 计算正确!"); - } else { - println!("❌ 计算错误!需要调试JSON数据结构"); - } - - println!("\n=== 调试建议 ==="); - println!("1. 检查实际的outputs和inputs JSON结构"); - println!("2. 确认output_type字段的位置和值"); - println!("3. 验证销毁输出的识别逻辑"); - println!("4. 检查是否有其他影响SMT大小的因素"); -} \ No newline at end of file