feat(phase3): 备案对接/全国统计/号段管理/BFF安全化/链合约源码
- A.1 备案对接: BindFiling/QueryFiling 关联网标号+备案号 - A.2 监管上报: DailyRegulatoryReport 日报 - B.1 号段管理: ListSegments + /admin/segments - C.1/C.2 全国统计按省聚合 + 跨省协同(单一可信源天然联动) - F.2 全国监管大屏: NationalStats(按省/类目/状态) - B(遗留) 监管大屏BFF: internal/bff + cmd/console-bff, 密钥仅存后端浏览器只用会话令牌 - G 真实链合约源码: contracts/tcs_registry/registry.go (ChainMaker Go) - 新增9个API+BFF服务; 5项新测试; 端到端BFF验证 - D/E(压测/等保/HSM)/F.1(标准)/真实链部署 标注需外部环境
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user