From 72499ec23c6bce89b1bb424321321a8fe65bd61b Mon Sep 17 00:00:00 2001 From: zhaoweijie Date: Thu, 28 Nov 2024 14:16:37 +0800 Subject: [PATCH] feat(sdk-go): add contract sdk --- sdk-go/client/client.go | 890 +++++++++++++++++++++++++++++++++-- sdk-go/client/client_test.go | 113 +++++ sdk-go/client/dto.go | 40 +- sdk-go/go.mod | 2 + sdk-go/go.sum | 2 + 5 files changed, 991 insertions(+), 56 deletions(-) create mode 100644 sdk-go/client/client_test.go diff --git a/sdk-go/client/client.go b/sdk-go/client/client.go index cbe9acb..b1382f0 100644 --- a/sdk-go/client/client.go +++ b/sdk-go/client/client.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "crypto" "crypto/rand" "encoding/hex" "encoding/json" @@ -10,10 +11,12 @@ import ( "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" ) @@ -56,19 +59,32 @@ func NewClient( }, 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, -) (statusCode int, respBody io.ReadCloser, err error) { +) (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 0, nil, err + return response, err } } else { priv = c.priv @@ -87,30 +103,70 @@ func (c *Client) RequestWithSignature( pubHex, ) + var resp *http.Response + switch strings.ToUpper(method) { case "POST": - body["sign"] = c.Sign(u[strings.Index(u, "?")+1:], priv) + 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 0, nil, err + return response, err } - resp, err := c.httpc.Post(rawUrl, "application/json", bytes.NewBuffer(bodyJson)) + resp, err = c.httpc.Post(rawUrl, "application/json", bytes.NewBuffer(bodyJson)) if err != nil { - return 0, nil, err + return response, err } - respBody = resp.Body + 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) } - return statusCode, respBody, nil + 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) string { +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), nil) + sig, err := priv.Sign(rand.Reader, []byte(data), signer) if err != nil { log.Fatal(err) } @@ -125,68 +181,820 @@ func genUrlParamsFromObject(obj map[string]interface{}) string { return strings.Join(params, "&") } -func retry[T any](fn func() (int, T, error)) (int, T, error) { +func retry[T any](fn func() (T, error)) (T, error) { var lastErr error for attempt := 0; attempt < 3; attempt++ { - resp, statusCode, err := fn() + resp, err := fn() if err == nil { - return resp, statusCode, nil + return resp, nil } lastErr = err time.Sleep(100 * time.Millisecond * time.Duration(attempt+1)) } var zero T - return 0, zero, lastErr + return zero, lastErr } -func (c *Client) Ping() (statusCode int, resp *PingResponse, err error) { - return retry(func() (statusCode int, resp *PingResponse, err error) { - statusCode, respBody, err := c.RequestWithSignature( +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, ) - if err != nil { - return 0, nil, err - } - defer respBody.Close() - decoder := json.NewDecoder(respBody) - if err := decoder.Decode(&resp); err != nil { - return 0, nil, err - } - - return statusCode, resp, nil + return genHttpResponse[PingResponse](result, &resp, err) }) } -func (c *Client) StartContract(code string) (statusCode int, resp string, err error) { +// 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() (int, string, error) { - statusCode, respBody, err := c.RequestWithSignature( + return retry(func() (*HttpResponse[any], error) { + result := PingResponse{} + resp, err := c.RequestWithSignature( path, "GET", + &result, nil, nil, nil, ) - if err != nil { - return 0, "", err - } - var resp string - defer respBody.Close() - buf := new(strings.Builder) - if _, err := io.Copy(buf, respBody); err != nil { - return 0, "", err - } - - return statusCode, resp, 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/sdk-go/client/client_test.go b/sdk-go/client/client_test.go new file mode 100644 index 0000000..0d2f17d --- /dev/null +++ b/sdk-go/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/sdk-go/client/dto.go b/sdk-go/client/dto.go index ac2cbb8..6a1693b 100644 --- a/sdk-go/client/dto.go +++ b/sdk-go/client/dto.go @@ -1,13 +1,35 @@ 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 { - Data *T `json:"data,omitempty"` + 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 { - ClientResponse[string] // will contain "pong" + Action string `json:"action"` } // SaveFileRequest represents the request structure for saving files @@ -85,21 +107,9 @@ type DistributeContractResponse struct { Progress string `json:"progress"` } -// ExecuteContractArgs represents arguments for contract execution -type ExecuteContractArgs struct { - Method string `json:"method,omitempty"` - WithSignature bool `json:"withSignature,omitempty"` - WithDynamicAnalysis bool `json:"withDynamicAnalysis,omitempty"` -} - // ExecuteContractResponse represents the response from contract execution type ExecuteContractResponse[T any] struct { - Status *bool `json:"status,omitempty"` - Data *T `json:"data,omitempty"` - ExecuteTime *float64 `json:"executeTime,omitempty"` - CID *string `json:"cid,omitempty"` - IsPrivate *bool `json:"isPrivate,omitempty"` - AdditionalData map[string]interface{} `json:"-"` + ClientResponse[T] } // ConfigNodeArgs represents configuration arguments for a node diff --git a/sdk-go/go.mod b/sdk-go/go.mod index 71dd786..a3100d3 100644 --- a/sdk-go/go.mod +++ b/sdk-go/go.mod @@ -3,3 +3,5 @@ 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/sdk-go/go.sum b/sdk-go/go.sum index b379cd2..6585587 100644 --- a/sdk-go/go.sum +++ b/sdk-go/go.sum @@ -41,6 +41,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG 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=