/
client.go
209 lines (164 loc) · 5.21 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package antchain
import (
"context"
"crypto"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
"github.com/google/uuid"
"github.com/tidwall/gjson"
"github.com/shenghui0779/sdk-go/lib"
lib_crypto "github.com/shenghui0779/sdk-go/lib/crypto"
"github.com/shenghui0779/sdk-go/lib/curl"
)
// Config 客户端配置
type Config struct {
BizID string `json:"biz_id"` // 链ID (a00e36c5)
TenantID string `json:"tenant_id"` // 租户ID
AccessID string `json:"access_id"` // AccessID
AccessKey *lib_crypto.PrivateKey `json:"access_key"` // AccessKey
Account string `json:"account"` // 链账户
MyKmsKeyID string `json:"mykmskey_id"` // 托管标识
}
// Client 发送请求使用的客户端
type Client interface {
// CreateAccount 创建账户
CreateAccount(ctx context.Context, account, kmsID string, gas int) (string, error)
// Deposit 存证
Deposit(ctx context.Context, content string, gas int) (string, error)
// DeploySolidity 部署Solidity合约
DeploySolidity(ctx context.Context, name, code string, gas int) (string, error)
// AsyncCallSolidity 异步调用Solidity合约
AsyncCallSolidity(ctx context.Context, contractName, methodSign, inputParams, outTypes string, gas int) (string, error)
// QueryTransaction 查询交易
QueryTransaction(ctx context.Context, hash string) (string, error)
// QueryReceipt 查询交易回执
QueryReceipt(ctx context.Context, hash string) (string, error)
// QueryBlockHeader 查询块头
QueryBlockHeader(ctx context.Context, blockNumber int64) (string, error)
// QueryBlockBody 查询块体
QueryBlockBody(ctx context.Context, blockNumber int64) (string, error)
// QueryLastBlock 查询最新块高
QueryLastBlock(ctx context.Context) (string, error)
// QueryAccount 查询账户
QueryAccount(ctx context.Context, account string) (string, error)
}
// ChainCallOption 链调用选项
type ChainCallOption func(params lib.X)
func WithParam(key string, value any) ChainCallOption {
return func(params lib.X) {
params[key] = value
}
}
type client struct {
endpoint string
config *Config
httpCli curl.Client
logger func(ctx context.Context, data map[string]string)
}
func (c *client) shakehand(ctx context.Context) (string, error) {
timeStr := strconv.FormatInt(time.Now().UnixMilli(), 10)
sign, err := c.config.AccessKey.Sign(crypto.SHA256, []byte(c.config.AccessID+timeStr))
if err != nil {
return "", err
}
params := lib.X{
"accessId": c.config.AccessID,
"time": timeStr,
"secret": hex.EncodeToString(sign),
}
return c.do(ctx, c.endpoint+SHAKE_HAND, params)
}
func (c *client) chainCall(ctx context.Context, method string, options ...ChainCallOption) (string, error) {
token, err := c.shakehand(ctx)
if err != nil {
return "", err
}
params := lib.X{}
for _, fn := range options {
fn(params)
}
params["bizid"] = c.config.BizID
params["accessId"] = c.config.AccessID
params["method"] = method
params["token"] = token
return c.do(ctx, c.endpoint+CHAIN_CALL, params)
}
func (c *client) chainCallForBiz(ctx context.Context, method string, options ...ChainCallOption) (string, error) {
token, err := c.shakehand(ctx)
if err != nil {
return "", err
}
params := lib.X{}
for _, fn := range options {
fn(params)
}
params["orderId"] = uuid.New().String()
params["bizid"] = c.config.BizID
params["account"] = c.config.Account
params["mykmsKeyId"] = c.config.MyKmsKeyID
params["method"] = method
params["accessId"] = c.config.AccessID
params["tenantid"] = c.config.TenantID
params["token"] = token
return c.do(ctx, c.endpoint+CHAIN_CALL_FOR_BIZ, params)
}
func (c *client) do(ctx context.Context, reqURL string, params lib.X) (string, error) {
log := lib.NewReqLog(http.MethodPost, reqURL)
defer log.Do(ctx, c.logger)
body, err := json.Marshal(params)
if err != nil {
return "", err
}
log.SetReqBody(string(body))
resp, err := c.httpCli.Do(ctx, http.MethodPost, reqURL, body, curl.WithHeader(curl.HeaderContentType, curl.ContentJSON))
if err != nil {
return "", err
}
defer resp.Body.Close()
log.SetRespHeader(resp.Header)
log.SetStatusCode(resp.StatusCode)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("HTTP Request Error, StatusCode = %d", resp.StatusCode)
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
log.SetRespBody(string(b))
ret := gjson.ParseBytes(b)
if !ret.Get("success").Bool() {
return "", fmt.Errorf("%s | %s", ret.Get("code").String(), ret.Get("data").String())
}
return ret.Get("data").String(), nil
}
// Option 自定义设置项
type Option func(c *client)
// WithHttpCli 设置自定义 HTTP Client
func WithHttpCli(httpCli *http.Client) Option {
return func(c *client) {
c.httpCli = curl.NewHTTPClient(httpCli)
}
}
// WithLogger 设置日志记录
func WithLogger(fn func(ctx context.Context, data map[string]string)) Option {
return func(c *client) {
c.logger = fn
}
}
// NewClient 生成蚂蚁联盟链客户端
func NewClient(cfg *Config, options ...Option) Client {
c := &client{
endpoint: "https://rest.baas.alipay.com",
config: cfg,
httpCli: curl.NewDefaultClient(),
}
for _, fn := range options {
fn(c)
}
return c
}