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.
250 lines
7.7 KiB
250 lines
7.7 KiB
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.yuanzoo.cn/zhangzhengqiang/popi-zxl/pkg/zxchainsdk"
|
|
)
|
|
|
|
// 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: "zzzzzq", // 申请用户名称
|
|
UserID: "56142321", // 申请用户 ID
|
|
WorkName: "测试作品", // 作品名称
|
|
TaskID: "1231223", // 生成任务 ID
|
|
CompletedAt: "2026.5.11 15:20:01", // 作品完成时间
|
|
WorkPreviewURL: "https://popiart-public-1313913486.cos.ap-guangzhou.myqcloud.com/media/2026/0511/83218.jpg", // 作品预览图 URL
|
|
// 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 = "302d355f4b7911f1bf330a77039e1980"
|
|
}
|
|
log.Printf("继续使用已有 evId 测试 h3: %s", evID)
|
|
}
|
|
h3(evID)
|
|
|
|
// 第一次测试:执行 Hash 上链,复制输出里的 evId。
|
|
//h1()
|
|
|
|
// 第二次测试:把 h1() 注释掉,填好 h2 里的 evId,再打开下面这一行。
|
|
//h2("302d355f4b7911f1bf330a77039e1980")
|
|
|
|
// 第三次测试:把 h1() 注释掉,填好 h3 里的 evId/sm3Hash/txId,再打开下面这一行。
|
|
//h3("302d355f4b7911f1bf330a77039e1980")
|
|
}
|
|
|