feat(sdk-go): initial version
This commit is contained in:
192
sdk-go/client/client.go
Normal file
192
sdk-go/client/client.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tjfoc/gmsm/sm2"
|
||||
|
||||
"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
|
||||
}
|
||||
|
||||
func (c *Client) RequestWithSignature(
|
||||
path string,
|
||||
method string,
|
||||
body map[string]interface{},
|
||||
priv *sm2.PrivateKey,
|
||||
pub *sm2.PublicKey,
|
||||
) (statusCode int, respBody io.ReadCloser, err error) {
|
||||
var pubHex string
|
||||
if priv != nil {
|
||||
var err error
|
||||
pubHex, err = sm2util.CheckSm2KeyPair(priv, pub)
|
||||
if err != nil {
|
||||
return 0, nil, 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,
|
||||
)
|
||||
|
||||
switch strings.ToUpper(method) {
|
||||
case "POST":
|
||||
body["sign"] = c.Sign(u[strings.Index(u, "?")+1:], priv)
|
||||
|
||||
bodyJson, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
resp, err := c.httpc.Post(rawUrl, "application/json", bytes.NewBuffer(bodyJson))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
respBody = resp.Body
|
||||
}
|
||||
|
||||
return statusCode, respBody, nil
|
||||
}
|
||||
|
||||
func (c *Client) Sign(data string, priv *sm2.PrivateKey) string {
|
||||
if priv == nil {
|
||||
priv = c.priv
|
||||
}
|
||||
sig, err := priv.Sign(rand.Reader, []byte(data), nil)
|
||||
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() (int, T, error)) (int, T, error) {
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < 3; attempt++ {
|
||||
resp, statusCode, err := fn()
|
||||
if err == nil {
|
||||
return resp, statusCode, nil
|
||||
}
|
||||
lastErr = err
|
||||
time.Sleep(100 * time.Millisecond * time.Duration(attempt+1))
|
||||
}
|
||||
var zero T
|
||||
return 0, 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(
|
||||
"/SCManager?action=ping",
|
||||
"GET",
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) StartContract(code string) (statusCode int, resp string, err 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(
|
||||
path,
|
||||
"GET",
|
||||
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
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user