Files
go-trustlog/README.md
ryan 065a3ce47a docs: 微调README - 更新Go版本要求,精简底部信息
- Go版本要求: 1.21+ → 1.25+
- 移除重复的测试统计信息
- 移除License和贡献说明
- 保持简洁实用的文档风格
2025-12-26 13:58:24 +08:00

471 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Go-Trustlog SDK
[![Go Version](https://img.shields.io/badge/Go-1.25+-blue.svg)](https://golang.org)
[![Test Coverage](https://img.shields.io/badge/coverage-53.7%25-yellow.svg)](.)
[![Test Status](https://img.shields.io/badge/tests-100%25%20passing-brightgreen.svg)](.)
基于 Watermill 和 gRPC 的可信日志SDK提供操作记录发布、查询验证和数据库持久化功能。
---
## ✨ 核心特性
### 📦 双数据模型
- **Operation**(操作记录):完整业务操作,支持取证验证
- **Record**(简单记录):轻量级事件日志
### 💾 数据库持久化
- **三种策略**:仅落库、既落库又存证、仅存证
- **异步机制**Cursor + Retry 双层架构保证最终一致性
- **多数据库**PostgreSQL、MySQL、SQLite
- **可靠重试**:指数退避 + 死信队列
### 🔄 消息发布
- **直接发布**Pulsar Publisher 实时发送
- **事务发布**Watermill Forwarder 保证事务性
### 🔍 查询验证
- **统一客户端**:单一连接池支持 Operation 和 Record 查询
- **流式验证**:实时获取取证进度
- **负载均衡**:多服务器轮询
---
## 🚀 快速开始
### 安装
```bash
# 配置私有仓库如果使用私有Git
git config --global url."git@gitea.internetapi.cn:".insteadOf "https://gitea.internetapi.cn"
go env -w GOPRIVATE="go.yandata.net"
# 安装SDK
go get go.yandata.net/iod/iod/go-trustlog
```
### 1. 基本使用 - 发布操作
```go
package main
import (
"context"
"time"
"go.yandata.net/iod/iod/go-trustlog/api/highclient"
"go.yandata.net/iod/iod/go-trustlog/api/model"
)
func main() {
// 创建客户端
client, err := highclient.NewHighClient(highclient.HighClientConfig{
PulsarURL: "pulsar://localhost:6650",
})
if err != nil {
panic(err)
}
defer client.Close()
// 创建操作记录
op, err := model.NewFullOperation(
model.OpSourceDOIP, // 操作来源
model.OpCodeCreateID, // 操作代码: 100 (创建标识符)
"10.1000", // DO前缀
"my-repo", // 仓库名
"10.1000/my-repo/object-001", // 完整DOID
"producer-001", // 生产者ID
"user@example.com", // 操作者
map[string]string{"action": "create"}, // 请求体
map[string]string{"status": "ok"}, // 响应体
time.Now(),
)
if err != nil {
panic(err)
}
// 发布操作
ctx := context.Background()
if err := client.OperationPublish(ctx, op); err != nil {
panic(err)
}
}
```
### 2. 数据库持久化 - 三种策略
```go
import (
"database/sql"
"go.yandata.net/iod/iod/go-trustlog/api/persistence"
_ "github.com/lib/pq"
)
func main() {
// 连接数据库
db, err := sql.Open("postgres",
"host=localhost port=5432 user=postgres password=postgres dbname=trustlog sslmode=disable")
if err != nil {
panic(err)
}
defer db.Close()
// 初始化表结构
if err := persistence.InitDB(db, "postgres"); err != nil {
panic(err)
}
// 策略1: 仅落库(不存证)
clientDBOnly, err := persistence.NewPersistenceClient(persistence.PersistenceConfig{
Strategy: persistence.StrategyDBOnly,
DB: db,
}, log)
// 策略2: 既落库又存证(推荐)
clientBoth, err := persistence.NewPersistenceClient(persistence.PersistenceConfig{
Strategy: persistence.StrategyDBAndTrustlog,
DB: db,
PulsarURL: "pulsar://localhost:6650",
}, log)
// 策略3: 仅存证(不落库)
clientTrustlogOnly, err := persistence.NewPersistenceClient(persistence.PersistenceConfig{
Strategy: persistence.StrategyTrustlogOnly,
PulsarURL: "pulsar://localhost:6650",
}, log)
// 保存操作(自动根据策略处理)
if err := clientBoth.SaveOperation(context.Background(), op); err != nil {
panic(err)
}
}
```
### 3. 查询操作
```go
import (
"go.yandata.net/iod/iod/go-trustlog/api/queryclient"
)
func main() {
// 创建查询客户端
queryClient, err := queryclient.NewQueryClient(queryclient.ClientConfig{
Servers: []string{"localhost:9090"},
})
if err != nil {
panic(err)
}
defer queryClient.Close()
// 列表查询
resp, err := queryClient.ListOperations(context.Background(), queryclient.ListOperationsRequest{
OpSource: model.OpSourceDOIP,
OpCode: model.OpCodeCreateID,
PageSize: 10,
PageNumber: 1,
})
if err != nil {
panic(err)
}
for _, op := range resp.Data {
fmt.Printf("Operation: %s, DOID: %s\n", op.OpId, op.Doid)
}
// 验证操作
validResp, err := queryClient.ValidateOperation(context.Background(), queryclient.ValidationRequest{
OpID: "op-001",
OpSource: model.OpSourceDOIP,
OpCode: model.OpCodeCreateID,
DoRepository: "my-repo",
})
if err != nil {
panic(err)
}
fmt.Printf("Valid: %v\n", validResp.Valid)
}
```
### 4. 数据库查询(持久化后)
```go
import "go.yandata.net/iod/iod/go-trustlog/api/persistence"
func main() {
repo := persistence.NewOperationRepository(db, log)
// 按条件查询
result, err := repo.Query(context.Background(), &persistence.OperationQueryRequest{
OpSource: &opSource,
OpCode: &opCode,
TrustlogStatus: &status,
StartTime: &startTime,
EndTime: &endTime,
PageSize: 10,
PageNumber: 1,
SortBy: "timestamp",
SortOrder: "DESC",
})
fmt.Printf("Total: %d operations\n", result.Total)
for i, op := range result.Operations {
fmt.Printf("[%d] %s - Status: %s\n", i+1, op.OpID, result.Statuses[i])
}
}
```
---
## 📊 操作代码 (OpCode)
OpCode 使用 int32 类型,基于 DOIP/IRP 协议标准:
| OpCode | 名称 | 说明 |
|--------|------|------|
| 0 | OpCodeReserved | 保留 |
| 1 | OpCodeResolution | 标识符查询 |
| 2 | OpCodeGetSiteInfo | 获取HS_SITE元素 |
| 100 | OpCodeCreateID | 创建新标识符 |
| 101 | OpCodeDeleteID | 删除标识符 |
| 102 | OpCodeAddElement | 添加元素 |
| 103 | OpCodeRemoveElement | 删除元素 |
| 104 | OpCodeModifyElement | 修改元素 |
| 105 | OpCodeListIDs | 列出标识符 |
| 106 | OpCodeListDerivedPrefixes | 列出派生前缀 |
| 200 | OpCodeChallengeResponse | 挑战响应 |
| 201 | OpCodeVerifyResponse | 验证挑战响应 |
| 300 | OpCodeHomePrefix | Home prefix |
| 301 | OpCodeUnhomePrefix | Unhome prefix |
| 302 | OpCodeListHomedPrefixes | 列出homed前缀 |
| 400 | OpCodeSessionSetup | 会话建立 |
| 401 | OpCodeSessionTerminate | 会话终止 |
| 500 | OpCodeQueryIDs | 查询DOIDs |
| 501 | OpCodeRenameID | 重命名DOID |
| 502 | OpCodeResolveAltID | 解析替代标识符 |
| 503 | OpCodeRegisterAltID | 注册替代标识符 |
**使用示例**:
```go
op, err := model.NewFullOperation(
model.OpSourceDOIP,
model.OpCodeCreateID, // 使用常量,类型安全
// ... 其他参数
)
```
---
## 🏗️ 架构设计
### 持久化策略架构
```
┌─────────────────────────────────────────────────────────────┐
│ Application │
└───────────────────┬─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ PersistenceClient │
│ (根据策略选择DB Only / DB+Trustlog / Trustlog Only) │
└───────────┬──────────────────────────┬──────────────────────┘
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────────┐
│ Database (SQL) │ │ Pulsar Publisher │
│ ┌─────────────────┐ │ │ (operation topic) │
│ │ operation │ │ └───────────────────────────┘
│ │ (主表) │ │
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ trustlog_cursor │ │ ← Cursor Worker 扫描
│ │ (游标表) │ │ (异步存证未完成记录)
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ trustlog_retry │ │ ← Retry Worker 重试
│ │ (重试表) │ │ (失败记录指数退避)
│ └─────────────────┘ │
└───────────────────────┘
```
### 数据库表结构
**operation 表** (主表)
```sql
CREATE TABLE operation (
op_id VARCHAR(32) PRIMARY KEY,
op_actor VARCHAR(64),
doid VARCHAR(512),
producer_id VARCHAR(32),
request_body_hash VARCHAR(128),
response_body_hash VARCHAR(128),
sign VARCHAR(512),
op_source VARCHAR(10),
op_code INTEGER, -- 操作代码
do_prefix VARCHAR(128),
do_repository VARCHAR(64),
client_ip VARCHAR(32), -- 客户端IP仅落库
server_ip VARCHAR(32), -- 服务端IP仅落库
trustlog_status VARCHAR(32), -- NOT_TRUSTLOGGED / TRUSTLOGGED
timestamp TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
**trustlog_cursor 表** (游标表)
```sql
CREATE TABLE trustlog_cursor (
cursor_key VARCHAR(64) PRIMARY KEY,
cursor_value VARCHAR(128),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
**trustlog_retry 表** (重试表)
```sql
CREATE TABLE trustlog_retry (
op_id VARCHAR(32) PRIMARY KEY,
retry_count INTEGER DEFAULT 0,
retry_status VARCHAR(32), -- PENDING / RETRYING / DEAD_LETTER
next_retry_at TIMESTAMP,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
---
## 🔧 配置说明
### PersistenceConfig
```go
type PersistenceConfig struct {
Strategy PersistenceStrategy // 持久化策略
DB *sql.DB // 数据库连接
PulsarURL string // Pulsar地址
// Cursor Worker配置可选
CursorWorkerInterval time.Duration // 扫描间隔默认10s
CursorBatchSize int // 批处理大小默认100
// Retry Worker配置可选
RetryWorkerInterval time.Duration // 重试间隔默认30s
RetryMaxAttempts int // 最大重试次数默认5
RetryBaseDelay time.Duration // 基础延迟默认1分钟
}
```
### 策略选择指南
| 策略 | 适用场景 | 优点 | 缺点 |
|------|----------|------|------|
| **StrategyDBOnly** | 内部审计、快速查询 | 高性能、本地存储 | 无区块链保障 |
| **StrategyDBAndTrustlog** | 生产环境(推荐) | 可查询 + 可验证 | 需要数据库维护 |
| **StrategyTrustlogOnly** | 纯粹存证、简单场景 | 无状态、易部署 | 查询需通过gRPC |
---
## ⚙️ 高级特性
### 1. 集群安全(并发控制)
使用 PostgreSQL 的 `SELECT FOR UPDATE SKIP LOCKED` 确保多实例场景下的并发安全:
```go
// Cursor Worker 自动处理集群并发
// 多个实例同时扫表时,自动跳过已锁定记录
```
### 2. IP字段记录
```go
// IP字段仅存储在数据库中不参与存证哈希计算
op.ClientIP = &clientIP // 可选
op.ServerIP = &serverIP // 可选
```
### 3. 自定义签名
```go
import "go.yandata.net/iod/iod/go-trustlog/api/sm2signer"
// 使用SM2算法签名
signer := sm2signer.NewSM2Signer(privateKeyBytes)
if err := client.OperationPublishWithSigner(ctx, op, signer); err != nil {
panic(err)
}
```
### 4. 异步发布
```go
// 异步发布(不阻塞)
client.OperationPublishAsync(ctx, op)
```
---
## 📝 注意事项
### 1. OpCode 类型变更
从 v2.0 开始,`OpCode``string` 改为 `int32`
```go
// ❌ 旧版本
op.OpType = "Create"
// ✅ 新版本
op.OpCode = model.OpCodeCreateID // int32: 100
```
### 2. 数据库初始化
首次使用前必须初始化表结构:
```go
if err := persistence.InitDB(db, "postgres"); err != nil {
panic(err)
}
```
### 3. Cursor Worker 启动
使用 `StrategyDBAndTrustlog` 策略时Cursor Worker 自动启动:
```go
client, _ := persistence.NewPersistenceClient(config, log)
defer client.Close() // 自动停止 Worker
```
### 4. 事务支持
SaveTx 支持在已有事务中保存:
```go
tx, _ := db.Begin()
repo.SaveTx(ctx, tx, op, persistence.StatusNotTrustlogged)
tx.Commit()
```
---
## 🧪 测试
运行测试套件:
```bash
# 单元测试
go test ./api/... -short
# 完整测试需要PostgreSQL和Pulsar
go test ./api/... -timeout 5m
# 覆盖率报告
go test ./api/... -coverprofile=coverage.out -coverpkg=./api/...
go tool cover -html=coverage.out
```
---
## 📚 相关资源
- [DOIP协议规范](https://www.dona.net/doipv2)
- [Watermill文档](https://watermill.io/)
- [Apache Pulsar](https://pulsar.apache.org/)
---