// Package main 是 TCS-IPTV 可信数据空间的 ChainMaker 智能合约(Go,三期 A.2)。 // // 本合约为独立合约模块(独立 go.mod),按 ChainMaker docker-go/wasm 合约规范部署。 // 与 internal/chain.Client 接口语义一一对应;MVP/二期用 MemoryChain 等价实现, // 具备链环境后部署本合约,由 chain-svc 通过 ChainMaker Go SDK 调用替换 MemoryChain。 // // 状态键设计(KV): // // content:{maCode} -> Content JSON // binding:{maCode}:{idx} -> HashBinding JSON // hashidx:{fileHash} -> maCode(防换壳重发) // mapping:{maCode}:{idx} -> Mapping JSON // ctid2ma:{ctid} -> maCode // // 权限:通过 sender 组织/角色证书判断(仅监管组织可 IssueMA/Revoke)。 package main import ( "encoding/json" "errors" "chainmaker.org/chainmaker/contract-sdk-go/v2/pb/protogo" "chainmaker.org/chainmaker/contract-sdk-go/v2/sandbox" "chainmaker.org/chainmaker/contract-sdk-go/v2/sdk" ) // TCSRegistry 合约。 type TCSRegistry struct{} const ( orgRegulator = "regulator" // 监管组织(仅其可签发/下架) ) // InitContract 合约初始化。 func (t *TCSRegistry) InitContract() protogo.Response { return sdk.Success([]byte("tcs_registry initialized")) } // UpgradeContract 合约升级。 func (t *TCSRegistry) UpgradeContract() protogo.Response { return sdk.Success([]byte("tcs_registry upgraded")) } // senderOrg 取调用方组织标识(基于证书 OU/OrgId)。 func senderOrg() string { org, _ := sdk.Instance.GetSenderOrgId() return org } // IssueMA 签发 MA 码并 1:1 强绑定哈希(仅监管组织)。 func (t *TCSRegistry) IssueMA() protogo.Response { if senderOrg() != orgRegulator { return sdk.Error("permission denied: only regulator can issue MA") } args := sdk.Instance.GetArgs() maCode := string(args["ma_code"]) ctid := string(args["ctid"]) fileHash := string(args["file_hash"]) contentJSON := args["content"] // MA 不可重复签发 if existing, _ := sdk.Instance.GetStateByte("content", maCode); len(existing) > 0 { return sdk.Error("MA already issued (1:1 binding immutable)") } // 防换壳重发:同哈希不可绑定到不同 MA if bound, _ := sdk.Instance.GetStateByte("hashidx", fileHash); len(bound) > 0 { return sdk.Error("content hash already exists") } _ = sdk.Instance.PutStateByte("content", maCode, contentJSON) binding := map[string]string{"hash_type": "file_sha256", "hash_value": fileHash, "version": "v1.0"} bj, _ := json.Marshal(binding) _ = sdk.Instance.PutStateByte("binding", maCode+":0", bj) _ = sdk.Instance.PutStateByte("hashidx", fileHash, []byte(maCode)) _ = sdk.Instance.PutStateByte("ctid2ma", ctid, []byte(maCode)) sdk.Instance.EmitEvent("RegisterSuccess", []string{maCode, fileHash}) return sdk.Success([]byte(maCode)) } // RegisterMapping 注册三方编码映射(MA 必须已签发)。 func (t *TCSRegistry) RegisterMapping() protogo.Response { args := sdk.Instance.GetArgs() maCode := string(args["ma_code"]) if v, _ := sdk.Instance.GetStateByte("content", maCode); len(v) == 0 { return sdk.Error("MA not issued") } idx := string(args["idx"]) _ = sdk.Instance.PutStateByte("mapping", maCode+":"+idx, args["mapping"]) return sdk.Success([]byte("ok")) } // VerifyHash 校验提交哈希是否与绑定哈希一致。 func (t *TCSRegistry) VerifyHash() protogo.Response { args := sdk.Instance.GetArgs() maCode := string(args["ma_code"]) fileHash := string(args["file_hash"]) bound, _ := sdk.Instance.GetStateByte("hashidx", fileHash) if string(bound) == maCode { return sdk.Success([]byte("true")) } return sdk.Success([]byte("false")) } // Revoke 下架(仅监管组织)。 func (t *TCSRegistry) Revoke() protogo.Response { if senderOrg() != orgRegulator { return sdk.Error("permission denied: only regulator can revoke") } args := sdk.Instance.GetArgs() maCode := string(args["ma_code"]) cj, _ := sdk.Instance.GetStateByte("content", maCode) if len(cj) == 0 { return sdk.Error("not found") } var content map[string]interface{} _ = json.Unmarshal(cj, &content) content["status"] = "revoked" nj, _ := json.Marshal(content) _ = sdk.Instance.PutStateByte("content", maCode, nj) sdk.Instance.EmitEvent("Revoked", []string{maCode}) return sdk.Success([]byte("ok")) } // QueryContent 查询内容主记录。 func (t *TCSRegistry) QueryContent() protogo.Response { maCode := string(sdk.Instance.GetArgs()["ma_code"]) v, _ := sdk.Instance.GetStateByte("content", maCode) if len(v) == 0 { return sdk.Error("not found") } return sdk.Success(v) } func main() { err := sandbox.Start(new(TCSRegistry)) if err != nil { _ = errors.New(err.Error()) } }