至信SDK
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
8.0 KiB

package main
import (
"context"
"fmt"
"log"
"os"
"strings"
"time"
"git.yuanzoo.cn/zhangzhengqiang/popi-zxl/pkg/zxchainsdk"
)
const (
defaultDemoWorkPreviewURL = "https://popiart-public-1313913486.cos.ap-guangzhou.myqcloud.com/2026-05-09-6.jpg"
defaultTestEvID = "302d355f4b7911f1bf330a77039e1980"
)
func getenvDefault(key, fallback string) string {
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
return value
}
return fallback
}
// loadDotEnv 加载当前目录下的 .env,方便本地测试。
func loadDotEnv(path string) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()
content, err := os.ReadFile(path)
if err != nil {
return
}
// 按行分割
lines := strings.Split(string(content), "\n")
for i := 0; i < len(lines); i++ {
line := strings.TrimSpace(lines[i])
if line == "" || strings.HasPrefix(line, "#") || !strings.Contains(line, "=") {
continue
}
// 检查是否是多行值(以引号开始但不以引号结束)
if strings.HasPrefix(line, `ZXCHAIN_PRIVATE_KEY="`) && !strings.HasSuffix(line, `"`) {
// 收集多行私钥内容
var privateKeyLines []string
privateKeyLines = append(privateKeyLines, strings.TrimPrefix(line, `ZXCHAIN_PRIVATE_KEY="`))
// 继续读取后续行直到找到结束引号
for i+1 < len(lines) && !strings.HasSuffix(strings.TrimSpace(lines[i+1]), `"`) {
i++
privateKeyLines = append(privateKeyLines, lines[i])
}
// 添加最后一行(去掉结尾引号)
if i+1 < len(lines) {
i++
lastLine := strings.TrimSpace(lines[i])
if strings.HasSuffix(lastLine, `"`) {
privateKeyLines = append(privateKeyLines, strings.TrimSuffix(lastLine, `"`))
}
}
privateKey := strings.Join(privateKeyLines, "\n")
_ = os.Setenv("ZXCHAIN_PRIVATE_KEY", privateKey)
continue
}
// 处理普通单行配置
key, value, ok := strings.Cut(line, "=")
if !ok {
continue
}
key = strings.TrimSpace(key)
value = strings.TrimSpace(value)
if key == "" || os.Getenv(key) != "" {
continue
}
value = strings.Trim(value, `"'`)
value = strings.ReplaceAll(value, `\n`, "\n")
_ = os.Setenv(key, strings.TrimSpace(value))
}
}
// newClient 初始化至信链 SDK 客户端。
func newClient() *zxchainsdk.SDKClient {
privateKey := os.Getenv("ZXCHAIN_PRIVATE_KEY")
client, err := zxchainsdk.NewSDKClient(
os.Getenv("ZXCHAIN_SECRET_ID"),
os.Getenv("ZXCHAIN_SECRET_KEY"),
privateKey,
)
if err != nil {
log.Fatalf("初始化至信链客户端失败: %v", err)
}
return client
}
// h1 演示 Hash 上链。
func h1() {
client := newClient()
filePath := "/Users/ywlmac/Desktop/81520.jpeg"
fileBytes, err := os.ReadFile(filePath)
if err != nil {
log.Fatalf("读取图片文件失败: %v", err)
}
result, err := client.CreateHashAttestation(context.Background(), zxchainsdk.HashAttestationRequest{
BizID: fmt.Sprintf("demo-image-%d", time.Now().Unix()),
Scene: "sdk-image-hash-demo",
ContentBytes: fileBytes,
FileName: "81520.jpeg",
FileSize: int64(len(fileBytes)),
ContentType: "image/jpeg",
StorageURI: filePath,
Metadata: map[string]string{
"source": "sdk-test",
},
})
if err != nil {
log.Fatalf("Hash 上链失败: %v", err)
}
fmt.Println("Hash 上链成功")
fmt.Println("evId:", result.ReceiptID)
fmt.Println("txId:", result.RequestID)
fmt.Println("sm3Hash:", result.SM3Hash)
fmt.Println("blockHeight:", result.BlockHeight)
fmt.Println("txTime:", result.TxTime)
}
// h2 演示 Hash 证书查询。
func h2(evID string) {
client := newClient()
// 把这里替换成 h1 输出的 evId。
// evID := "替换成你的evId"
result, err := client.GetHashCert(context.Background(), evID)
if err != nil {
log.Fatalf("查询 Hash 证书失败: %v", err)
}
fmt.Println("Hash 证书查询成功")
fmt.Println("evId:", result.EvID)
fmt.Println("certURL:", result.CertURL)
}
// h3 演示 Hash 取证查询。
func h3(evID string) {
client := newClient()
// 至少填写一个查询条件:evId、sm3Hash 或 txId。
req := zxchainsdk.HashAttestationQueryRequest{
EvID: evID,
// Hash: "也可以替换成 h1 输出的 sm3Hash",
// TxID: "也可以替换成 h1 输出的 txId",
}
var result *zxchainsdk.HashAttestationQueryResult
var err error
for attempt := 1; attempt <= 5; attempt++ {
result, err = client.QueryHashAttestation(context.Background(), req)
if err == nil {
break
}
if attempt < 5 {
log.Printf("Hash 取证查询暂未成功,第 %d 次失败:%v,等待后重试", attempt, err)
time.Sleep(time.Duration(attempt*2) * time.Second)
}
}
if err != nil {
log.Fatalf("Hash 取证查询失败: %v", err)
}
fmt.Println("Hash 取证查询成功")
fmt.Println("evId:", result.EvID)
fmt.Println("txId:", result.TxID)
fmt.Println("blockHeight:", result.BlockHeight)
fmt.Println("txTime:", result.TxTime)
fmt.Println("extendInfo:", result.ExtendInfo)
}
// h4 演示简化的一站式证书生成(从 WorkPreviewURL 自动下载并计算哈希)。
func h4() (string, error) {
client := newClient()
result, err := client.CreateSimpleHashAttestationCertificate(context.Background(), zxchainsdk.SimpleHashAttestationCertificateRequest{
UserName: getenvDefault("ZXCHAIN_TEST_USER_NAME", "zzzzzq"), // 申请用户名称
UserID: getenvDefault("ZXCHAIN_TEST_USER_ID", "56142321"), // 申请用户 ID
WorkName: getenvDefault("ZXCHAIN_TEST_WORK_NAME", "测试作品"), // 作品名称
TaskID: getenvDefault("ZXCHAIN_TEST_TASK_ID", "1231223"), // 生成任务 ID
CompletedAt: getenvDefault("ZXCHAIN_TEST_COMPLETED_AT", "2026.5.11 15:20:01"), // 作品完成时间
WorkPreviewURL: getenvDefault("ZXCHAIN_WORK_PREVIEW_URL", defaultDemoWorkPreviewURL), // 作品预览图 URL
EvidenceHash: os.Getenv("ZXCHAIN_EVIDENCE_HASH"), // 可选:传入后不下载预览图计算哈希
// EvidenceHash 不传,SDK 会自动从 WorkPreviewURL 下载并计算 SM3 哈希
CertificateStorage: &zxchainsdk.COSCertificateStorage{
BucketURL: os.Getenv("COS_BUCKET_URL"),
PublicBaseURL: os.Getenv("COS_PUBLIC_BASE_URL"),
SecretID: os.Getenv("COS_SECRET_ID"),
SecretKey: os.Getenv("COS_SECRET_KEY"),
},
})
if err != nil {
return "", fmt.Errorf("简化证书生成失败: %w", err)
}
fmt.Println("简化证书生成成功")
fmt.Println("UserName:", result.UserName)
fmt.Println("UserID:", result.UserID)
fmt.Println("WorkName:", result.WorkName)
fmt.Println("TaskID:", result.TaskID)
fmt.Println("CompletedAt:", result.CompletedAt)
fmt.Println("WorkPreviewURL:", result.WorkPreviewURL)
fmt.Println("EvidenceHash:", result.EvidenceHash)
fmt.Println("ReceiptID (evId):", result.ReceiptID)
fmt.Println("RequestID (txId):", result.RequestID)
fmt.Println("BlockHeight:", result.BlockHeight)
fmt.Println("TxTime:", result.TxTime)
fmt.Println("SM3Hash:", result.SM3Hash)
fmt.Println("PopiCertificateCertNo:", result.PopiCertificateCertNo)
fmt.Println("PopiCertificateVerifyURL:", result.PopiCertificateVerifyURL)
fmt.Println("PopiCertificateCertificateImageURL:", result.PopiCertificateCertificateImageURL)
fmt.Println("ZXChainCertURL:", result.ZXChainCertURL)
return result.ReceiptID, nil
}
func main() {
loadDotEnv(".env")
// 只测试 h4 和 h3:先生成证书,再用 h4 返回的 evId 查询 Hash 取证。
evID, err := h4()
if err != nil {
log.Printf("h4 测试失败: %v", err)
evID = os.Getenv("ZXCHAIN_TEST_EV_ID")
if evID == "" {
evID = defaultTestEvID
}
log.Printf("继续使用已有 evId 测试 h3: %s", evID)
}
h3(evID)
// 第一次测试:执行 Hash 上链,复制输出里的 evId。
//h1()
// 第二次测试:把 h1() 注释掉,填好 h2 里的 evId,再打开下面这一行。
//h2("302d355f4b7911f1bf330a77039e1980")
// 第三次测试:把 h1() 注释掉,填好 h3 里的 evId/sm3Hash/txId,再打开下面这一行。
//h3("302d355f4b7911f1bf330a77039e1980")
}