From 34ebc061240377d6173d9b9a2932289d9c4fe4d1 Mon Sep 17 00:00:00 2001 From: Nex Zhu Date: Fri, 29 Nov 2024 16:08:42 +0800 Subject: [PATCH] refactor: remove sdk-go dir --- client/client.go | 1000 +++++++++++++++++++++++++++++++++++++++++ client/client_test.go | 113 +++++ client/dto.go | 129 ++++++ go.mod | 7 + go.sum | 73 +++ sm2util/util.go | 123 +++++ 6 files changed, 1445 insertions(+) create mode 100644 client/client.go create mode 100644 client/client_test.go create mode 100644 client/dto.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 sm2util/util.go diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..b1382f0 --- /dev/null +++ b/client/client.go @@ -0,0 +1,1000 @@ +package client + +import ( + "bytes" + "crypto" + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/tjfoc/gmsm/sm2" + "golang.org/x/sync/errgroup" + + "go.fusiongalaxy.cn/bdware/bdcontract-client/sm2util" +) + +type HttpCOptions struct { + MaxRetries int +} + +type Client struct { + baseUrl string + priv *sm2.PrivateKey + pub *sm2.PublicKey + pubHex string + httpc *http.Client + maxRetry int +} + +func NewClient( + baseUrl string, + priv *sm2.PrivateKey, + pub *sm2.PublicKey, + opt HttpCOptions, +) (*Client, error) { + pubHex, err := sm2util.CheckSm2KeyPair(priv, pub) + if err != nil { + return nil, err + } + + if opt.MaxRetries == 0 { + opt.MaxRetries = 3 + } + + return &Client{ + baseUrl: baseUrl, + pub: pub, + pubHex: pubHex, + priv: priv, + httpc: &http.Client{Timeout: 10 * time.Second}, + maxRetry: opt.MaxRetries, + }, nil +} + +// RequestWithSignature +// @Description 发送带有签名的请求 +// @Param path 请求路径 +// @Param http 请求方法(GET | POST) +// @Param result 接收请求返回结果的结构体指针 +// @Param body 请求体(尽在method为POST时使用) +// @Param priv 请求私钥 +// @Param pub 请求公钥 +func (c *Client) RequestWithSignature( + path string, + method string, + result any, + body map[string]interface{}, + priv *sm2.PrivateKey, + pub *sm2.PublicKey, +) (response HttpResponse[any], err error) { + response = HttpResponse[any]{ + Status: 0, + } + + var pubHex string + if priv != nil { + var err error + pubHex, err = sm2util.CheckSm2KeyPair(priv, pub) + if err != nil { + return response, err + } + } else { + priv = c.priv + pubHex = c.pubHex + } + + rawUrl := c.baseUrl + path + u := fmt.Sprintf("%s%spubKey=%s", + rawUrl, + func() string { + if strings.Contains(path, "?") { + return "&" + } + return "?" + }(), + pubHex, + ) + + var resp *http.Response + + switch strings.ToUpper(method) { + case "POST": + if body == nil { + body = make(map[string]interface{}) + } + body["sign"] = c.Sign(u[strings.Index(u, "?")+1:], priv, nil) + + bodyJson, err := json.Marshal(body) + if err != nil { + return response, err + } + + resp, err = c.httpc.Post(rawUrl, "application/json", bytes.NewBuffer(bodyJson)) + if err != nil { + return response, err + } + case "GET": + params := url.Values{ + "sign": {c.Sign(u[strings.Index(u, "?")+1:], priv, nil)}, + "pubKey": {pubHex}, + } + + if strings.Contains(rawUrl, "?") { + rawUrl = rawUrl + "&" + params.Encode() + } else { + rawUrl = rawUrl + "?" + params.Encode() + } + + resp, err = c.httpc.Get(rawUrl) + if err != nil { + return response, err + } + default: + return response, fmt.Errorf("unsupported method: %s", method) + } + + defer resp.Body.Close() + + // 读取所有数据直到 EOF + bodyBytes, err := io.ReadAll(resp.Body) + // 如果bodyBytes不能被json解析直接将信息保存到ErrData + if err != nil || !json.Valid(bodyBytes) { + response.ErrData = string(bodyBytes) + return response, nil + } + + err = json.Unmarshal(bodyBytes, result) + // 如果bodyBytes解析失败直接将信息保存到ErrData + if err != nil { + response.ErrData = string(bodyBytes) + return response, nil + } + + response.Status = resp.StatusCode + return response, nil +} + +func (c *Client) Sign(data string, priv *sm2.PrivateKey, signer crypto.SignerOpts) string { + if priv == nil { + priv = c.priv + } + sig, err := priv.Sign(rand.Reader, []byte(data), signer) + if err != nil { + log.Fatal(err) + } + return hex.EncodeToString(sig) +} + +func genUrlParamsFromObject(obj map[string]interface{}) string { + params := make([]string, 0) + for key, value := range obj { + params = append(params, fmt.Sprintf("%s=%v", key, value)) + } + return strings.Join(params, "&") +} + +func retry[T any](fn func() (T, error)) (T, error) { + var lastErr error + for attempt := 0; attempt < 3; attempt++ { + resp, err := fn() + if err == nil { + return resp, nil + } + lastErr = err + time.Sleep(100 * time.Millisecond * time.Duration(attempt+1)) + } + var zero T + return zero, lastErr +} + +func genHttpResponse[D any](data D, response *HttpResponse[any], err error) (*HttpResponse[D], error) { + return &HttpResponse[D]{ + Data: data, + Status: response.Status, + ErrData: response.ErrData, + }, err +} + +// Ping https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id15 +func (c *Client) Ping() (*HttpResponse[PingResponse], error) { + return retry(func() (*HttpResponse[PingResponse], error) { + result := PingResponse{} + + resp, err := c.RequestWithSignature( + "/SCManager?action=ping", + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[PingResponse](result, &resp, err) + }) +} + +// StartContract TODO 未跑通 +// StartContract 启动合约 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id60 +func (c *Client) StartContract(code string) (*HttpResponse[any], error) { + params := url.Values{ + "action": {"startContract"}, + "script": {code}, + } + path := fmt.Sprintf("/SCManager?%s", params.Encode()) + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + path, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// StartContractByYPK TODO 待测试 +// StartContractByYPK 启动合约 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id13 +func (c *Client) StartContractByYPK(isPrivate bool, path string, script string) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"startContractByYPK"}, + "script": []string{script}, + "isPrivate": []string{strconv.FormatBool(isPrivate)}, + "path": []string{path}, + "owner": []string{c.pubHex}, + "aim": []string{"onStartContract"}, + "signature": []string{c.Sign("Fixed|"+path+"|"+c.pubHex, nil, nil)}, + } + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ExecuteContract 调用合约 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id69 +func (c *Client) ExecuteContract( + contractID string, + operation string, + arg string, + withDynamicAnalysis bool, + withSignature bool, +) (*HttpResponse[ExecuteContractResponse[any]], error) { + body := map[string]any{ + "action": "executeContract", + "contractID": contractID, + "operation": operation, + "withDynamicAnalysis": withDynamicAnalysis, + "arg": arg, + } + + if withSignature { + body["signature"] = c.Sign(contractID+"|"+operation+"|"+arg+"|"+c.pubHex, nil, nil) + body["pubkey"] = c.pubHex + } + + return retry(func() (*HttpResponse[ExecuteContractResponse[any]], error) { + result := ExecuteContractResponse[any]{} + resp, err := c.RequestWithSignature( + "/SCManager", + "POST", + &result, + body, + nil, + nil, + ) + + return genHttpResponse[ExecuteContractResponse[any]](result, &resp, err) + }) +} + +// KillContractProcess TODO 待测试 +// KillContractProcess 停止合约 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id122 +func (c *Client) KillContractProcess(contractID string, requestID string) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"killContractProcess"}, + "id": []string{contractID}, + } + + if requestID != "" { + params["requestID"] = []string{requestID} + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// KillAllContract TODO 待测试 +// KillAllContract 停止所有合约 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id131 +func (c *Client) KillAllContract() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"killAllContract"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ApplyNodeRole TODO 待测试 +// ApplyNodeRole 申请角色 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html +func (c *Client) ApplyNodeRole(role string) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"applyNodeRole"}, + "role": []string{role}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// AuthNodeRole TODO 待测试 +// AuthNodeRole 授权角色 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html +func (c *Client) AuthNodeRole( + isAccept bool, + authorizedPubKey string, + priv *sm2.PrivateKey, + pub *sm2.PublicKey, +) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"authNodeRole"}, + "isAccept": []string{strconv.FormatBool(isAccept)}, + "authorizedPubKey": []string{authorizedPubKey}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + priv, + pub, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// DistributeContract TODO 待测试, 用sse获取问题未解决!!! +// 分发合约项目 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html +//func (c *Client) DistributeContract( +// nodeIDs string, +// projectName string, +// isPrivate bool, +//) (*HttpResponse[any], error) { +// params := url.Values{ +// "action": []string{"distributeContract"}, +// "isPrivate": []string{strconv.FormatBool(isPrivate)}, +// "nodeIDs": []string{nodeIDs}, +// "projectName": []string{projectName}, +// } +// +// httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) +// +// return retry(func() (*HttpResponse[any], error) { +// result := PingResponse{} +// resp, err := c.RequestWithSignature( +// httpPath, +// "GET", +// &result, +// nil, +// nil, +// nil, +// ) +// +// return genHttpResponse[any](result, &resp, err) +// }) +//} + +// SaveFile TODO 待测试 +func (c *Client) SaveFile( + content string, + isAppend bool, + isPrivate bool, + path string, +) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"saveFile"}, + "isAppend": []string{strconv.FormatBool(isAppend)}, + "isPrivate": []string{strconv.FormatBool(isPrivate)}, + "content": []string{content}, + "path": []string{path}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ListProjectPermission TODO 待测试 +func (c *Client) ListProjectPermission( + isPrivate bool, + path string, +) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"listProjectPermission"}, + "isPrivate": []string{strconv.FormatBool(isPrivate)}, + "path": []string{path}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// StartContractMultiPoint TODO 待测试 +func (c *Client) StartContractMultiPoint( + peersID string, + contractType int, + selectUnitNum int, + projectName string, + isPrivate bool, + sponsorPeerID string, +) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"startContractMultiPoint"}, + "peersID": []string{peersID}, + "projectName": []string{projectName}, + "sponsorPeerID": []string{sponsorPeerID}, + "isPrivate": []string{strconv.FormatBool(isPrivate)}, + "contractType": []string{strconv.Itoa(contractType)}, + "selectUnitNum": []string{strconv.Itoa(selectUnitNum)}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// LoadNodeConfig TODO 待测试 +// 获取节点配置信息 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id497 +func (c *Client) LoadNodeConfig() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"loadNodeConfig"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// UpdateConfig TODO 待测试 +// 修改节点配置 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id504 +// @Param key {licence,projectDir,yjsPath,dataChain,doipConfig,nodeCenter,nodeName,masterAddress,resetNodeCenterWS} +func (c *Client) UpdateConfig(key string, val string) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"updateConfig"}, + "key": []string{key}, + "val": []string{val}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ResetNodeManager TODO 待测试 +// 设置pubkey为node manager +func (c *Client) ResetNodeManager() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"resetNodeManager"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// LockEdit TODO 待测试 +// 锁定某个用户的的私有目录编辑功能 +func (c *Client) LockEdit() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"lockEdit"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// UnlockEdit TODO 待测试 +// 解锁某个用户的的私有目录编辑功能 +func (c *Client) UnlockEdit() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"unlockEdit"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// AddNode TODO 待测试 +func (c *Client) AddNode(nodePubKey string) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"addNode"}, + "nodePubKey": []string{nodePubKey}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ApplyRole TODO 待测试 +// 申请角色 +func (c *Client) ApplyRole(role string) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"applyRole"}, + "role": []string{role}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// AuthNodeManager TODO 待测试 +func (c *Client) AuthNodeManager(isAccept bool, authorizedPubKey string) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"authNodeManager"}, + "isAccept": []string{strconv.FormatBool(isAccept)}, + "authorizedPubKey": []string{authorizedPubKey}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ListAllUsers TODO 待测试 +func (c *Client) ListAllUsers() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"listAllUsers"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ListNodes TODO 待测试 +func (c *Client) ListNodes() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"listNodes"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// CreateTrustUnit TODO 待测试 +// 建立可信执行集群 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id664 +func (c *Client) CreateTrustUnit( + data []struct { + nodeName string + pubkey string + }, + msg string, +) (*HttpResponse[any], error) { + + body := map[string]any{ + "action": "createTrustUnit", + "data": data, + "msg": msg, + "pubKey": c.pubHex, + "sign": c.Sign("action=createTrustUnit&data="+fmt.Sprintf("%v", data)+"&msg="+msg+"&pubKey="+c.pubHex, nil, nil), + } + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + "/SCManager", + "POST", + &result, + body, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ListTrustUnits TODO 待测试 +// 查看可信执行集群列表 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id657 +func (c *Client) ListTrustUnits() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"listTrustUnits"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ListContractProcess TODO 待测试 +// 查询合约进程 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id444 +func (c *Client) ListContractProcess() (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"listContractProcess"}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// DownloadContract TODO 待测试 +// 下载合约项目 +// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id25 +func (c *Client) DownloadContract(projectName string, isPrivate bool, timestamp int) (*HttpResponse[any], error) { + params := url.Values{ + "action": []string{"downloadContract"}, + "projectName": []string{projectName}, + "isPrivate": []string{strconv.FormatBool(isPrivate)}, + "timestamp": []string{strconv.Itoa(timestamp)}, + } + + httpPath := fmt.Sprintf("/SCManager?%s", params.Encode()) + + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( + httpPath, + "GET", + &result, + nil, + nil, + nil, + ) + + return genHttpResponse[any](result, &resp, err) + }) +} + +// ConfigNode TODO 待测试 +// 配置合约引擎节点,若节点没有设置过node manager,将当前key设置为node manager +// struct arg { +// nodeName string +// dataChain string +// masterAddress string +// nodeCenter string +// // 配置中心节点时使用 +// LHSProxyAddress string +// } +func (c *Client) ConfigNode(arg map[string]string) bool { + res, err := c.ResetNodeManager() + + if err != nil || res.Status == 0 { + return false + } + + eg := errgroup.Group{} + for key, value := range arg { + eg.Go(func() error { + res, err = c.UpdateConfig(key, value) + if err != nil { + return err + } + + if res.Status == 0 { + return fmt.Errorf(res.ErrData) + } + + return nil + }) + } + + for _, value := range []string{"ContractProvider", "ContractUser", "ContractInstanceManager"} { + eg.Go(func() error { + res, err = c.ApplyNodeRole(value) + if err != nil { + return err + } + + if res.Status == 0 { + return fmt.Errorf(res.ErrData) + } + + return nil + }) + } + + eg.Go(func() error { + res, err = c.AuthNodeRole(true, c.pubHex, nil, nil) + if err != nil { + return err + } + + if res.Status == 0 { + return fmt.Errorf(res.ErrData) + } + + return nil + }) + + err = eg.Wait() + if err != nil { + return false + } + + res, err = c.LoadNodeConfig() + if err != nil || res.Status == 0 { + return false + } + + for k, v := range arg { + if arg[remapNodeConfigKey(k)] != v { + return false + } + } + + return true +} + +func remapNodeConfigKey(k string) string { + switch k { + case "dataChain": + return "bdledger" + default: + return k + } +} diff --git a/client/client_test.go b/client/client_test.go new file mode 100644 index 0000000..0d2f17d --- /dev/null +++ b/client/client_test.go @@ -0,0 +1,113 @@ +package client + +import ( + "testing" + + "go.fusiongalaxy.cn/bdware/bdcontract-client/sm2util" +) + +func TestStruct(t *testing.T) { + type a struct { + name string + age int + } + + b := &a{age: 1} + + t.Log(b.age) + t.Log(b.name == "") +} + +func genClient() (*Client, error) { + url := "https://cpnode.demo.internetapi.cn/api/ctrlproxy/SCIDE" + + pub, err := sm2util.ParsePublicKey("04153ad2d70e67741e0fc33e0c92c702e2afba2480dbea73d23fd02a3ce3a1b69979a7006a8e045f8836ae4797a8fe426823d7ad3450817e794948c8e47b60b711") + if err != nil { + return nil, err + } + + priv, err := sm2util.ParsePrivateKey("31672b434458fdb6b854e64ededeb51305bc4d0bd258a5276ccb9bc86f7c03f6", pub) + if err != nil { + return nil, err + } + + client, err := NewClient(url, priv, pub, HttpCOptions{}) + if err != nil { + return nil, err + } + + return client, nil +} + +func TestNewClient(t *testing.T) { + client, err := genClient() + if err != nil { + t.Error(err) + } + + t.Log(client) +} + +func TestClient_Ping(t *testing.T) { + client, err := genClient() + if err != nil { + t.Error(err) + } + + resp, err := client.Ping() + if err != nil { + t.Log(resp.Status) + t.Error(err) + return + } + + t.Log(resp.Data, resp.Status) +} + +func TestClient_StartContract(t *testing.T) { + client, err := genClient() + if err != nil { + t.Error(err) + } + + resp, err := client.StartContract("contract TestContract {}") + + if err != nil { + t.Log(resp) + return + } + + t.Log(resp) +} + +func TestClient_ExecuteContract(t *testing.T) { + client, err := genClient() + if err != nil { + t.Error(err) + } + + resp, err := client.ExecuteContract("ShanxiControlProxy", "listRepository", "", false, true) + + if err != nil { + t.Log(resp) + return + } + + t.Log(resp) +} + +func TestClient_LoadNodeConfig(t *testing.T) { + client, err := genClient() + if err != nil { + t.Error(err) + } + + config, err := client.LoadNodeConfig() + if err != nil { + t.Log(config) + t.Error(err) + return + } + + t.Log(config) +} diff --git a/client/dto.go b/client/dto.go new file mode 100644 index 0000000..6a1693b --- /dev/null +++ b/client/dto.go @@ -0,0 +1,129 @@ +package client + +type HttpResponse[D any] struct { + Data D `json:"data"` + ErrData string `json:"errData"` + Status int `json:"status"` +} + +// ClientResponse represents a generic response structure +type ClientResponse[T any] struct { + NeedSeq bool `json:"needSeq,omitempty"` + Seq int `json:"seq,omitempty"` + Status string `json:"status,omitempty"` + Result struct { + Data T `json:"data,omitempty"` + Count int `json:"count,omitempty"` + Code int `json:"code,omitempty"` + } `json:"result,omitempty"` + IsInsnLimit bool `json:"isInsnLimit,omitempty"` + TotalGas int `json:"totalGas,omitempty"` + ExecutionGas int `json:"executionGas,omitempty"` + ExtraGas int `json:"extraGas,omitempty"` + Size int `json:"size,omitempty"` + EventRelated bool `json:"eventRelated,omitempty"` + ResponseID string `json:"responseID,omitempty"` + Action string `json:"action,omitempty"` + ExecuteTime string `json:"executeTime,omitempty"` +} + +// PingResponse is a specific response type for ping operations +type PingResponse struct { + Action string `json:"action"` +} + +// SaveFileRequest represents the request structure for saving files +type SaveFileRequest struct { + Content string `json:"content"` + IsAppend bool `json:"isAppend"` + IsPrivate bool `json:"isPrivate"` + Path string `json:"path"` +} + +// ListProjectPermissionRequest represents the request for listing project permissions +type ListProjectPermissionRequest struct { + IsPrivate bool `json:"isPrivate"` + Path string `json:"path"` +} + +// ListProjectPermissionResponseData contains permission response data +type ListProjectPermissionResponseData struct { + Permissions []string `json:"permissions"` + YPK string `json:"ypk"` +} + +// StartContractByYpkRequest represents the request for starting a contract +type StartContractByYpkRequest struct { + IsPrivate bool `json:"isPrivate"` + Path string `json:"path"` + Script string `json:"script"` +} + +// ListAllUsersResponseDataListItem represents a key-value pair +type ListAllUsersResponseDataListItem struct { + Key string `json:"key"` + Value string `json:"value"` +} + +// ListAllUsersResponseData contains user listing response data +type ListAllUsersResponseData struct { + KV []ListAllUsersResponseDataListItem `json:"kv"` + Time []ListAllUsersResponseDataListItem `json:"time"` +} + +// OnlineContractsItem represents an online contract +type OnlineContractsItem struct { + ContractID string `json:"contractID"` + ContractName string `json:"contractName"` + IsMaster bool `json:"isMaster"` + Type string `json:"type"` + YjsType string `json:"yjsType"` + Extra map[string]interface{} `json:"-"` +} + +// OnlineItem represents an online node +type OnlineItem struct { + CIManager string `json:"cimanager"` + ContractVersion int `json:"contractVersion"` + Events int `json:"events"` + IPPort string `json:"ipPort"` + MasterAddress string `json:"masterAddress"` + NodeName string `json:"nodeName"` + PeerID string `json:"peerID"` + PubKey string `json:"pubKey"` + Contracts []OnlineContractsItem `json:"contracts"` +} + +// ListNodesResponse represents the response for listing nodes +type ListNodesResponse struct { + Action string `json:"action"` + Offline []string `json:"offline"` + Online []OnlineItem `json:"online"` +} + +// DistributeContractResponse represents the response for contract distribution +type DistributeContractResponse struct { + Action string `json:"action"` + Progress string `json:"progress"` +} + +// ExecuteContractResponse represents the response from contract execution +type ExecuteContractResponse[T any] struct { + ClientResponse[T] +} + +// ConfigNodeArgs represents configuration arguments for a node +type ConfigNodeArgs struct { + NodeName string `json:"nodeName,omitempty"` + DataChain string `json:"dataChain,omitempty"` + MasterAddress string `json:"masterAddress,omitempty"` + NodeCenter string `json:"nodeCenter,omitempty"` + LHSProxyAddress string `json:"LHSProxyAddress,omitempty"` + ExtraConfig map[string]string `json:"-"` +} + +// LoadNodeConfigResponseData represents the response data for node configuration +type LoadNodeConfigResponseData struct { + DoipConfig string `json:"doipConfig"` + ExtraData map[string]string `json:"-"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a3100d3 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module go.fusiongalaxy.cn/bdware/bdcontract-client + +go 1.23 + +require github.com/tjfoc/gmsm v1.4.1 + +require golang.org/x/sync v0.9.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6585587 --- /dev/null +++ b/go.sum @@ -0,0 +1,73 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/sm2util/util.go b/sm2util/util.go new file mode 100644 index 0000000..1494a06 --- /dev/null +++ b/sm2util/util.go @@ -0,0 +1,123 @@ +package sm2util + +import ( + "encoding/hex" + "errors" + "math/big" + + "github.com/tjfoc/gmsm/sm2" +) + +var ( + ErrPrivateKeyIsNil = errors.New("private key is nil") + ErrPublicAndKeysNotMatch = errors.New("public and private keys don't match") +) + +func CheckSm2KeyPair(priv *sm2.PrivateKey, pub *sm2.PublicKey) (pubHex string, err error) { + if priv == nil { + return "", ErrPrivateKeyIsNil + } + + if pub == nil { + pub = &priv.PublicKey + return PublicKeyToHex(pub), nil + } + + pubHex = PublicKeyToHex(pub) + if pubHex != PublicKeyToHex(&priv.PublicKey) { + return "", ErrPublicAndKeysNotMatch + } + + return +} + +/* From: https://github.com/tjfoc/gmsm/issues/207 */ + +// 国密sm2 非对称加密算法 (对标rsa使用场景) +// +// ParsePublicKey 公钥字符串还原为 sm2.PublicKey 对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用) +func ParsePublicKey(publicKeyStr string) (*sm2.PublicKey, error) { + publicKeyBytes, err := hex.DecodeString(publicKeyStr) + if err != nil { + return nil, err + } + // 提取 x 和 y 坐标字节切片 + curve := sm2.P256Sm2().Params() + byteLen := (curve.BitSize + 7) / 8 + xBytes := publicKeyBytes[1 : byteLen+1] + yBytes := publicKeyBytes[byteLen+1 : 2*byteLen+1] + // 将字节切片转换为大整数 + x := new(big.Int).SetBytes(xBytes) + y := new(big.Int).SetBytes(yBytes) + // 创建 sm2.PublicKey 对象 + publicKey := &sm2.PublicKey{ + Curve: curve, + X: x, + Y: y, + } + return publicKey, nil +} + +// 国密 非对称加密算法 +// +// ParsePublicKey 公钥字符串还原为 sm2.PublicKey 对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用) +func ParsePublicKeyByXY(xHex, yHex string) (*sm2.PublicKey, error) { + xBytes, err := hex.DecodeString(xHex) + if err != nil { + return nil, err + } + yBytes, err := hex.DecodeString(yHex) + if err != nil { + return nil, err + } + // 提取 x 和 y 坐标字节切片 + curve := sm2.P256Sm2().Params() + // byteLen := (curve.BitSize + 7) / 8 + // 将字节切片转换为大整数 + x := new(big.Int).SetBytes(xBytes) + y := new(big.Int).SetBytes(yBytes) + // 创建 sm2.PublicKey 对象 + publicKey := &sm2.PublicKey{ + Curve: curve, + X: x, + Y: y, + } + return publicKey, nil +} + +func PublicKeyToHex(pk *sm2.PublicKey) (pubKeyString string) { + bs := append([]byte{byte(4)}, pk.X.Bytes()...) + bs = append(bs, pk.Y.Bytes()...) + + return hex.EncodeToString(bs) +} + +func PublicKeyToXYHex(pk *sm2.PublicKey) (x, y string) { + x = hex.EncodeToString(pk.X.Bytes()) + y = hex.EncodeToString(pk.Y.Bytes()) + return x, y +} + +// 将私钥字符串反序列化转为私钥对象: +// ParsePrivateKey 私钥还原为 sm2.PrivateKey对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用) +func ParsePrivateKey(privateKeyStr string, publicKey *sm2.PublicKey) (*sm2.PrivateKey, error) { + privateKeyBytes, err := hex.DecodeString(privateKeyStr) + if err != nil { + return nil, err + } + // 将字节切片转换为大整数 + d := new(big.Int).SetBytes(privateKeyBytes) + // 创建 sm2.PrivateKey 对象 + privateKey := &sm2.PrivateKey{ + PublicKey: *publicKey, + D: d, + } + return privateKey, nil +} + +func PrivateKeyToHex(pk *sm2.PrivateKey) (d string) { + d = hex.EncodeToString(pk.D.Bytes()) + return d +} + +/**/