This commit is contained in:
lzx 2025-07-02 17:01:00 +08:00
parent 58d4635c8e
commit e0f77b4b91
10 changed files with 184 additions and 1348 deletions

View File

@ -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"

View File

@ -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<String>,
pub outputs: Vec<String>,
pub kernels: Vec<String>,
}
// 挖矿消息结构
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<u64> {
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. ✅ **构建脚本**:跨平台构建支持
所有功能都已实现并通过测试,可以直接使用。

View File

@ -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::<serde_json::Value>(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::<serde_json::Value>(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核心代码保持一致能够正确处理销毁输出和销毁输入确保计算结果的准确性。修正后的代码提供了详细的调试信息便于问题排查和验证。
---
**修正状态**: ✅ 已完成
**测试状态**: 🔄 需要实际数据验证
**文档状态**: ✅ 完整

141
README.md
View File

@ -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<TransactionInput>,
pub outputs: Vec<TransactionOutput>,
pub kernels: Vec<TransactionKernel>,
}
```
### 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格式的MiningMsgblock_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`
如需扩展功能,请直接修改`src/main.rs`,结构清晰,易于维护。

View File

@ -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

View File

@ -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项目构建完成!"

View File

@ -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?;

View File

@ -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<String>,
pub outputs: Vec<String>,
pub kernels: Vec<String>,
pub inputs: Vec<TransactionInput>,
pub outputs: Vec<TransactionOutput>,
pub kernels: Vec<TransactionKernel>,
}
// 挖矿任务结构
#[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<String> {
let mut hasher = DomainSeparatedBorshHasher::<BlocksHashDomain, Blake2b<U32>>::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<Mutex<HashMap<u64, MiningTask>>>,
// 缓存上一个区块的output_smt_size避免重复请求
prev_output_smt_size_cache: Arc<Mutex<HashMap<u64, u64>>>,
}
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<u64> {
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<u64> = 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<String> = body.inputs.iter()
.map(|i| serde_json::to_string(i).unwrap_or_default())
.collect();
let outputs: Vec<String> = body.outputs.iter()
.map(|o| serde_json::to_string(o).unwrap_or_default())
.collect();
let kernels: Vec<String> = 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<String> = block_body.outputs.iter()
.map(|o| serde_json::to_string(o).unwrap_or_default())
.collect();
let input_jsons: Vec<String> = 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<u64> = 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<Option<SubmitRequest>> {
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<SubmitResult> {
// 查找对应的挖矿任务
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<u64> = 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

View File

@ -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()

View File

@ -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::<Value>(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::<Value>(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大小的因素");
}