├── .gitignore ├── LICENSE ├── README.md ├── cov_report.sh ├── doc.go ├── lint_args.go ├── send.go ├── trustsql.go ├── trustsql_test.go ├── tscec ├── base58.go ├── keys.go ├── keys_test.go ├── merkletree.go ├── merkletree_test.go ├── signer.go ├── signer_test.go └── utils.go ├── tsiss ├── append.go ├── append_test.go ├── example_test.go ├── params.go ├── query.go ├── query_test.go ├── test_data │ ├── append_response.json │ └── query_response.json └── utils.go ├── user_params.go ├── user_test.go ├── utils.go └── vendor └── vendor.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | vendor/* 16 | !vendor/vendor.json 17 | go-trustsql-sdk 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Parallel Empire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GoTrustSQL 2 | ======= 3 | 4 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/KleeTaurus/go-trustsql-sdk/blob/master/LICENSE) 5 | [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/KleeTaurus/go-trustsql-sdk) 6 | 7 | 基于 Golang 语言的 [TrustSQL](https://trustsql.qq.com/) SDK 8 | 9 | ## 概述 10 | 11 | 该 SDK 实现了底层密钥对生成、地址生成、签名/验签等基础功能,并对 TrustSQL 提供的三类(信息共享/身份管理) API 接口进行了封装。 12 | 13 | ## 特性 14 | 15 | SDK 基础命令 16 | 1. 生成密钥对 17 | 2. 根据私钥生成公钥(压缩公钥) 18 | 3. 根据公钥生成地址(压缩地址) 19 | 4. 利用私钥对数据签名 20 | 5. 利用公钥对数据和签名进行验签 21 | 22 | SDK API 接口 23 | 1. 数字资产(暂未实现) 24 | 2. 信息共享 25 | 3. 身份管理 26 | 27 | ## 示例 28 | 29 | 下列示例演示了该 SDK 的基本使用方法。 30 | 31 | ```go 32 | func SendToTrustSQL(content map[string]interface{}) (*tsiss.IssAppendResponse, error) { 33 | privateKey := "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 34 | client, _ := trustsql.NewClient(privateKey) 35 | client.SetIssRequestTimeout(5 * time.Second) 36 | //testURI := "" 37 | //client.SetAppendIssURI(testURI) 38 | accountAddr := client.GetAddrByPubkey() 39 | pubKey := client.GetPublicKey() 40 | issAppend := &tsiss.IssAppend{ 41 | Version: "1.0", 42 | SignType: "ECDSA", 43 | MchID: "gbxxxxxxxxxxxxxxx", 44 | //MchSign: "", 45 | Account: string(accountAddr), 46 | CommitTime: time.Now().Format("2006-01-02 15:04:05"), 47 | //Content: map[string]interface{}{"c": "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"}, 48 | Content: content, 49 | InfoKey: bson.NewObjectId().Hex(), 50 | //InfoKey: "1242123jjj", 51 | InfoVersion: "1", 52 | State: "0", 53 | Notes: map[string]interface{}{"notes": "comments"}, 54 | PublicKey: pubKey, 55 | Sign: "", 56 | ChainID: "ch_tencent_test", 57 | LedgerID: "ld_tencent_iss", 58 | } 59 | signStr, err := client.GetIssSignStr(issAppend) 60 | if err != nil { 61 | fmt.Printf("get issSignStr error: %s\n", err) 62 | return nil, err 63 | } 64 | 65 | issAppend.Sign = client.SignString(signStr, true) 66 | appendRes, err := client.AppendIss(issAppend) 67 | if err != nil { 68 | fmt.Printf("append error: %s\n", err) 69 | return nil, err 70 | } 71 | //fmt.Printf("appendRes: %+v\n", appendRes) 72 | return appendRes, nil 73 | } 74 | 75 | ``` 76 | 77 | ## 环境依赖 78 | 79 | * go version >= 1.9 80 | * 需要单独安装, 详细过程见 [github.com/toxeus/go-secp256k1](https://github.com/toxeus/go-secp256k1) 81 | * cd $GOPATH/src/github.com/KleeTaurus/go-trustsql-sdk && [govendor](https://github.com/kardianos/govendor) sync 82 | 83 | ## 参考资料 84 | 85 | 1. [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page) 86 | 2. [Base58Check encoding](https://en.bitcoin.it/wiki/Base58Check_encoding) 87 | 3. [Bitcoin Developer Reference](https://bitcoin.org/en/developer-reference#block-chain) 88 | 4. [Technical background of version 1 Bitcoin addresses](https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses) 89 | 5. [Merkle Trees](https://hackernoon.com/merkle-trees-181cb4bc30b4) 90 | 6. [数据库那么便宜,为何还要死贵的区块链来存储数据?](https://mp.weixin.qq.com/s/ME_E1EA95XILD_yaFg1d8Q) 91 | 7. [Data Insertion in Bitcoin's Blockchain](https://digitalcommons.augustana.edu/cgi/viewcontent.cgi?article=1000&context=cscfaculty) 92 | 93 | ## License 94 | 95 | GoTrustSQL is MIT licensed. See the included LICENSE file for more details. 96 | -------------------------------------------------------------------------------- /cov_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script uses gocov to generate a test coverage report. 4 | # The gocov tool my be obtained with the following command: 5 | # go get github.com/axw/gocov/gocov 6 | # 7 | # It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. 8 | 9 | # Check for gocov. 10 | type gocov >/dev/null 2>&1 11 | if [ $? -ne 0 ]; then 12 | echo >&2 "This script requires the gocov tool." 13 | echo >&2 "You may obtain it with the following command:" 14 | echo >&2 "go get github.com/axw/gocov/gocov" 15 | exit 1 16 | fi 17 | cd tscec/ 18 | gocov test | gocov report 19 | cd ../tsiss 20 | gocov test | gocov report 21 | cd ../identity 22 | gocov test | gocov report 23 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | -------------------------------------------------------------------------------- /lint_args.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "reflect" 8 | "sort" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // Lint 封装了请求餐具数据拼接要求 14 | // 1.参数名ASCII码从小到大排序(字典序); 15 | // 2.如果参数的值为空不参与签名; 16 | // 3.参数名区分大小写; 17 | func Lint(v interface{}, c interface{}) string { 18 | signMap := make(map[string]string) 19 | // getCheckString(&signMap, reflect.ValueOf(v)) 20 | getCheckString(&signMap, reflect.ValueOf(c)) 21 | var keys []string 22 | for k := range signMap { 23 | keys = append(keys, k) 24 | } 25 | sort.Strings(keys) 26 | lintString := "" 27 | first := true 28 | for k := range keys { 29 | // check if value is empty 30 | if signMap[keys[k]] == "" { 31 | continue 32 | } 33 | if keys[k] == "mch_sign" { 34 | continue 35 | } 36 | 37 | if !first { 38 | lintString = lintString + "&" + keys[k] + "=" + signMap[keys[k]] 39 | } else { 40 | lintString = keys[k] + "=" + signMap[keys[k]] 41 | } 42 | first = false 43 | } 44 | log.Printf("lintString is %s", lintString) 45 | return lintString 46 | } 47 | 48 | func getCheckString(m *map[string]string, v reflect.Value) { 49 | for i := 0; i < v.NumField(); i++ { 50 | if "sign" == v.Type().Field(i).Tag.Get("json") { 51 | continue 52 | } 53 | tagArr := v.Type().Field(i).Tag.Get("json") 54 | tag := strings.Split(tagArr, ",")[0] 55 | 56 | // TODO 没有覆盖全类型 57 | switch v.Field(i).Kind() { 58 | case reflect.Int64: 59 | { 60 | (*m)[tag] = strconv.FormatInt(v.Field(i).Interface().(int64), 10) 61 | continue 62 | } 63 | case reflect.Int: 64 | { 65 | (*m)[tag] = strconv.Itoa(v.Field(i).Interface().(int)) 66 | continue 67 | } 68 | case reflect.Map: 69 | { 70 | mapField := v.Field(i).Interface().(map[string]interface{}) 71 | if mapField == nil { 72 | continue 73 | } 74 | value, err := json.Marshal(mapField) 75 | if err != nil { 76 | // TODO 77 | fmt.Println(err) 78 | } 79 | (*m)[tag] = string(value) 80 | continue 81 | } 82 | } 83 | 84 | (*m)[tag] = v.Field(i).Interface().(string) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /send.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | 10 | "github.com/KleeTaurus/go-trustsql-sdk/tscec" 11 | ) 12 | 13 | func send(URI string, u interface{}, c *Common, privateKey []byte) ([]byte, error) { 14 | data, err := json.Marshal(u) 15 | c.ReqData = string(data) 16 | 17 | sign := Lint(u, (*c)) 18 | if err != nil { 19 | return nil, err 20 | } 21 | c.Sign = tscec.Sign(privateKey, []byte(sign), false) 22 | 23 | reqData, err := json.Marshal(c) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | // 校验common是否符合标准 29 | err = validate.Struct(c) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | log.Printf("trustsql request data is %s", string(reqData)) 35 | 36 | // send http request 37 | req, err := http.NewRequest("POST", URI, bytes.NewBuffer(reqData)) 38 | req.Header.Set("Content-Type", "application/json;charset=UTF-8") 39 | if err != nil { 40 | return nil, err 41 | } 42 | resp, err := client.Do(req) 43 | if err != nil { 44 | return nil, err 45 | } 46 | body, err := ioutil.ReadAll(resp.Body) 47 | log.Printf("trustsql response body is %s", string(body)) 48 | _ = resp.Body.Close() 49 | return body, nil 50 | } 51 | -------------------------------------------------------------------------------- /trustsql.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/hex" 6 | "encoding/json" 7 | "net/http" 8 | "time" 9 | 10 | "github.com/KleeTaurus/go-trustsql-sdk/tscec" 11 | "github.com/KleeTaurus/go-trustsql-sdk/tsiss" 12 | "gopkg.in/go-playground/validator.v9" 13 | ) 14 | 15 | var ( 16 | client *http.Client 17 | validate *validator.Validate 18 | ) 19 | 20 | func init() { 21 | client = &http.Client{ 22 | Timeout: 1 * time.Second, 23 | } 24 | validate = validator.New() 25 | } 26 | 27 | // trustsql URI 地址 28 | const ( 29 | AppendIssURI = "https://baas.trustsql.qq.com/cgi-bin/v1.0/trustsql_iss_append_v1.cgi" // 共享信息查询 30 | QueryIssURI = "https://baas.trustsql.qq.com/cgi-bin/v1.0/trustsql_iss_query_v1.cgi" // 共享信息查询 31 | RegisteUserURI = "https://baas.trustsql.qq.com/idm_v1.1/api/user_cert/register" // 注册用户 32 | GetUserInfoURI = "https://baas.trustsql.qq.com/idm_v1.1/api/user_cert/fetch" // 获取用户信息 33 | RegisteAccountURI = "https://baas.trustsql.qq.com/idm_v1.1/api/account_cert/register" // 创建用户账户 34 | GetAccountsURI = "https://baas.trustsql.qq.com/idm_v1.1/api/account_cert/fetch_list" // 获取用户的账户地址列表 35 | GetPubkeyOfAccountURI = "https://baas.trustsql.qq.com/idm_v1.1/api/account_cert/fetch" // 获取用户的账户公钥 36 | ) 37 | 38 | // Client 腾讯区块链sdk 39 | type Client struct { 40 | PrivateKey []byte 41 | PublicKey []byte 42 | appendIssURI string // 共享信息查询 43 | queryIssURI string // 共享信息查询 44 | registeUserURI string // 注册用户 45 | getUserInfoURI string // 获取用户信息 46 | registeAccountURI string // 创建用户账户 47 | getAccountsURI string // 获取用户的账户地址列表 48 | getPubkeyOfAccountURI string // 获取用户的账户公钥 49 | } 50 | 51 | // GenRandomPairkey 生成随机公私钥对 52 | func GenRandomPairkey() *Client { 53 | privateKey, publicKey := tscec.NewKeyPair() 54 | client := Client{ 55 | PrivateKey: privateKey, 56 | PublicKey: publicKey, 57 | appendIssURI: AppendIssURI, 58 | queryIssURI: QueryIssURI, 59 | registeUserURI: RegisteUserURI, 60 | getUserInfoURI: GetUserInfoURI, 61 | registeAccountURI: RegisteAccountURI, 62 | getAccountsURI: GetAccountsURI, 63 | getPubkeyOfAccountURI: GetPubkeyOfAccountURI, 64 | } 65 | 66 | return &client 67 | } 68 | 69 | // NewClient 通过base64编码的私钥生成client 70 | func NewClient(privateKey string) (*Client, error) { 71 | privKey, err := base64.StdEncoding.DecodeString(privateKey) 72 | if err != nil { 73 | return nil, err 74 | } 75 | pubKey, err := tscec.GeneratePubkeyByPrvkey(privKey) 76 | if err != nil { 77 | return nil, err 78 | } 79 | client := Client{ 80 | PrivateKey: privKey, 81 | PublicKey: pubKey, 82 | appendIssURI: AppendIssURI, 83 | queryIssURI: QueryIssURI, 84 | registeUserURI: RegisteUserURI, 85 | getUserInfoURI: GetUserInfoURI, 86 | registeAccountURI: RegisteAccountURI, 87 | getAccountsURI: GetAccountsURI, 88 | getPubkeyOfAccountURI: GetPubkeyOfAccountURI, 89 | } 90 | return &client, nil 91 | } 92 | 93 | // SetAppendIssURI 设置URI 94 | func (c *Client) SetAppendIssURI(appendIssURI string) { 95 | c.appendIssURI = appendIssURI 96 | } 97 | 98 | // SetQueryIssURI 设置URI 99 | func (c *Client) SetQueryIssURI(queryIssURI string) { 100 | c.queryIssURI = queryIssURI 101 | } 102 | 103 | // SetRegisteUserURI 设置URI 104 | func (c *Client) SetRegisteUserURI(registeUserURI string) { 105 | c.registeUserURI = registeUserURI 106 | } 107 | 108 | // SetGetUserInfoURI 设置URI 109 | func (c *Client) SetGetUserInfoURI(getUserInfoURI string) { 110 | c.getUserInfoURI = getUserInfoURI 111 | } 112 | 113 | // SetRegisteAccountURI 设置URI 114 | func (c *Client) SetRegisteAccountURI(registeAccountURI string) { 115 | c.registeAccountURI = registeAccountURI 116 | } 117 | 118 | // SetGetAccountsURI 设置URI 119 | func (c *Client) SetGetAccountsURI(getAccountsURI string) { 120 | c.getAccountsURI = getAccountsURI 121 | } 122 | 123 | // SetGetPubkeyOfAccountURI 设置URI 124 | func (c *Client) SetGetPubkeyOfAccountURI(getPubkeyOfAccountURI string) { 125 | c.getPubkeyOfAccountURI = getPubkeyOfAccountURI 126 | } 127 | 128 | // GetPrivateKey 获取私钥的base64编码 129 | func (c *Client) GetPrivateKey() string { 130 | return base64.StdEncoding.EncodeToString(c.PrivateKey) 131 | } 132 | 133 | // GetPublicKey 获取公钥的base64编码 134 | func (c *Client) GetPublicKey() string { 135 | return base64.StdEncoding.EncodeToString(c.PublicKey) 136 | } 137 | 138 | // GetAddrByPubkey 计算公钥对应的地址 139 | func (c *Client) GetAddrByPubkey() []byte { 140 | return tscec.GenerateAddrByPubkey(c.PublicKey) 141 | } 142 | 143 | // SignString 对一个字符串进行签名(通常用于生成通讯方签名) 144 | func (c *Client) SignString(s string, isHash bool) string { 145 | return tscec.Sign(c.PrivateKey, []byte(s), isHash) 146 | } 147 | 148 | // VerifySignature 对签名进行验证 149 | func (c *Client) VerifySignature(sig, data []byte) bool { 150 | return tscec.Verify(c.PublicKey, sig, data) 151 | } 152 | 153 | // SetIssRequestTimeout 设置Iss的请求超时时间 154 | func (c *Client) SetIssRequestTimeout(timeout time.Duration) { 155 | tsiss.SetRequestTimeout(timeout) 156 | } 157 | 158 | // GetIssSignStr 共享信息新增/追加, 第一步获取待签名串 159 | // 注意: 留空sign字段 160 | func (c *Client) GetIssSignStr(ia *tsiss.IssAppend) (string, error) { 161 | lintString := []byte(Lint(nil, (*ia))) 162 | ia.MchSign = tscec.Sign(c.PrivateKey, lintString[:], false) 163 | 164 | signStr, err := tsiss.GetIssSignStr(c.appendIssURI, ia) 165 | if err != nil { 166 | return "", err 167 | } 168 | signStrBytes, _ := hex.DecodeString(signStr) 169 | 170 | return string(signStrBytes), nil 171 | } 172 | 173 | // AppendIss 共享信息新增/追加, 第二步将已签名的signstr加入到参数ia中,再次请求接口 174 | func (c *Client) AppendIss(ia *tsiss.IssAppend) (*tsiss.IssAppendResponse, error) { 175 | lintString := []byte(Lint(nil, (*ia))) 176 | ia.MchSign = tscec.Sign(c.PrivateKey, lintString[:], false) 177 | 178 | isr, err := tsiss.AppendIss(c.appendIssURI, ia) 179 | if err != nil { 180 | return nil, err 181 | } 182 | return isr, nil 183 | } 184 | 185 | // QueryIss 共享信息查询 186 | func (c *Client) QueryIss(iq *tsiss.IssQuery) (*tsiss.IssResponse, error) { 187 | lintString := []byte(Lint(nil, (*iq))) 188 | iq.MchSign = tscec.Sign(c.PrivateKey, lintString[:], false) 189 | 190 | isr, err := tsiss.QueryIss(c.queryIssURI, iq) 191 | if err != nil { 192 | return nil, err 193 | } 194 | return isr, nil 195 | } 196 | 197 | // RegisteUser 注册用户 198 | func (c *Client) RegisteUser(u *UserRegister, common *Common) (*UserRegisterResponse, error) { 199 | body, err := send(c.registeUserURI, u, common, c.PrivateKey) 200 | if err != nil { 201 | return nil, err 202 | } 203 | // 检查返回值是否成功 204 | err = responseUtil(body) 205 | if err != nil { 206 | return nil, err 207 | } 208 | 209 | urr := &UserRegisterResponse{} 210 | err = json.Unmarshal(body, urr) 211 | if err != nil { 212 | return nil, err 213 | } 214 | return urr, nil 215 | } 216 | 217 | // GetUserInfo 获取用户信息参数 218 | func (c *Client) GetUserInfo(u *UserInfo, common *Common) (*UserInfoResponse, error) { 219 | body, err := send(c.getUserInfoURI, u, common, c.PrivateKey) 220 | if err != nil { 221 | return nil, err 222 | } 223 | // 检查返回值是否成功 224 | err = responseUtil(body) 225 | if err != nil { 226 | return nil, err 227 | } 228 | 229 | uir := &UserInfoResponse{} 230 | err = json.Unmarshal(body, uir) 231 | if err != nil { 232 | return nil, err 233 | } 234 | return uir, nil 235 | } 236 | 237 | // RegisteAccount 创建用户账户 238 | func (c *Client) RegisteAccount(u *Account, common *Common) (*AccountResponse, error) { 239 | body, err := send(c.registeAccountURI, u, common, c.PrivateKey) 240 | if err != nil { 241 | return nil, err 242 | } 243 | // 检查返回值是否成功 244 | err = responseUtil(body) 245 | if err != nil { 246 | return nil, err 247 | } 248 | 249 | ar := &AccountResponse{} 250 | err = json.Unmarshal(body, ar) 251 | if err != nil { 252 | return nil, err 253 | } 254 | return ar, nil 255 | } 256 | 257 | // GetAccounts 获取用户的账户地址列表 258 | func (c *Client) GetAccounts(u *Accounts, common *Common) (*AccountsResponse, error) { 259 | body, err := send(c.getAccountsURI, u, common, c.PrivateKey) 260 | if err != nil { 261 | return nil, err 262 | } 263 | // 检查返回值是否成功 264 | err = responseUtil(body) 265 | if err != nil { 266 | return nil, err 267 | } 268 | 269 | ar := &AccountsResponse{} 270 | err = json.Unmarshal(body, ar) 271 | if err != nil { 272 | return nil, err 273 | } 274 | return ar, nil 275 | } 276 | 277 | // GetPubkeyOfAccount 获取用户的账户公钥 278 | func (c *Client) GetPubkeyOfAccount(u *PubkeyOfAccount, common *Common) (*PubkeyOfAccountResponse, error) { 279 | body, err := send(c.getPubkeyOfAccountURI, u, common, c.PrivateKey) 280 | if err != nil { 281 | return nil, err 282 | } 283 | // 检查返回值是否成功 284 | err = responseUtil(body) 285 | if err != nil { 286 | return nil, err 287 | } 288 | 289 | poar := &PubkeyOfAccountResponse{} 290 | err = json.Unmarshal(body, poar) 291 | if err != nil { 292 | return nil, err 293 | } 294 | return poar, nil 295 | } 296 | -------------------------------------------------------------------------------- /trustsql_test.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/KleeTaurus/go-trustsql-sdk/tsiss" 9 | ) 10 | 11 | const ( 12 | issGetSignStrTestURI = "http://39.107.26.141:8007/trustsql/v1.0/iss_get_sign_str" 13 | issQueryTestURI = "http://39.107.26.141:8007/trustsql/v1.0/iss_query" 14 | ) 15 | 16 | func TestGeneratePairkey(t *testing.T) { 17 | client := GenRandomPairkey() 18 | 19 | /* 20 | log.Printf("Private Key: %s, len: %d\n", base64Encode(c.PrivateKey), len(c.PrivateKey)) 21 | log.Printf("Public key : %s, len: %d\n", base64Encode(c.PublicKey), len(c.PublicKey)) 22 | log.Printf("Address : %s, len: %d\n", c.GetAddrByPubkey(), len(c.GetAddrByPubkey())) 23 | */ 24 | 25 | if len(client.PrivateKey) != 32 { 26 | t.Errorf("Incorrect length of the private key, it should be 32 bytes\n") 27 | } 28 | 29 | if len(client.PublicKey) != 33 { 30 | t.Errorf("Incorrect length of the public key, it should be 33 bytes\n") 31 | } 32 | 33 | if len(client.GetAddrByPubkey()) != 34 && len(client.GetAddrByPubkey()) != 33 { 34 | t.Errorf("Incorrect length of the address, it should be 34 or 33 bytes\n") 35 | } 36 | } 37 | 38 | func TestGetIssSignStr(t *testing.T) { 39 | client, err := NewClient("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 40 | if err != nil { 41 | t.Error("GeneratePairkeyByPrivateKey err") 42 | } 43 | client.SetAppendIssURI(issGetSignStrTestURI) 44 | 45 | ia := tsiss.IssAppend{ 46 | Version: "1.0", 47 | SignType: "ECDSA", 48 | MchID: "xxxxxxxxxxxxxxxxx", 49 | MchSign: "", 50 | 51 | ChainID: "xxxxxxxxxxxxxxx", 52 | LedgerID: "xxxxxxxxxxxxxx", 53 | InfoKey: "xxxxxxxxxxxxxxxx", 54 | InfoVersion: "1", 55 | State: "1", 56 | Content: map[string]interface{}{"content": "test"}, 57 | Notes: map[string]interface{}{"note": "test"}, 58 | 59 | CommitTime: "2018-04-04 16:47:31", 60 | Account: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 61 | PublicKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 62 | TimeStamp: time.Now().Unix(), 63 | } 64 | 65 | signStr, err := client.GetIssSignStr(&ia) 66 | if err != nil { 67 | t.Errorf("GetIssSignStr failed %s", err) 68 | } 69 | 70 | fmt.Printf("signstr is: %+v\n", signStr) 71 | } 72 | 73 | func TestAppendIss(t *testing.T) { 74 | } 75 | 76 | func TestQueryIss(t *testing.T) { 77 | client := GenRandomPairkey() 78 | client.SetQueryIssURI(issQueryTestURI) 79 | 80 | iq := tsiss.IssQuery{ 81 | Version: "1.0", 82 | SignType: "ECDSA", 83 | MchID: "gbec7b7cece75c8a5", 84 | MchSign: "MEYCIQDCoCYth2zGer2Z/kliD11jRXGKqLqLNk/vo18js+CvRwIhANTQ3PbN9vj9YjmaB+rma2Sz0D+30WgZPOHAO9ysRsj1", 85 | ChainID: "xxxxxxxxxxxxxx", 86 | LedgerID: "xxxxxxxxxxxxxx", 87 | Content: map[string]interface{}{"owner": "ulegal"}, 88 | Notes: map[string]interface{}{"extInfo": "default"}, 89 | PageNo: "1", 90 | PageLimit: "2", 91 | Timestamp: "1503648096", 92 | } 93 | _, err := client.QueryIss(&iq) 94 | if err != nil { 95 | t.Error(err) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tscec/base58.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "bytes" 5 | "math/big" 6 | ) 7 | 8 | var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 9 | 10 | // Base58Encode 对输入数据做 Base58 编码 11 | func Base58Encode(input []byte) []byte { 12 | var result []byte 13 | 14 | x := big.NewInt(0).SetBytes(input) 15 | 16 | base := big.NewInt(int64(len(b58Alphabet))) 17 | zero := big.NewInt(0) 18 | mod := &big.Int{} 19 | 20 | for x.Cmp(zero) != 0 { 21 | x.DivMod(x, base, mod) 22 | result = append(result, b58Alphabet[mod.Int64()]) 23 | } 24 | 25 | // https://en.bitcoin.it/wiki/Base58Check_encoding#Version_bytes 26 | if input[0] == 0x00 { 27 | result = append(result, b58Alphabet[0]) 28 | } 29 | 30 | ReverseBytes(result) 31 | 32 | return result 33 | } 34 | 35 | // Base58Decode 对编码格式为 Base58 的输入数据做解码操作 36 | func Base58Decode(input []byte) []byte { 37 | result := big.NewInt(0) 38 | 39 | for _, b := range input { 40 | charIndex := bytes.IndexByte(b58Alphabet, b) 41 | result.Mul(result, big.NewInt(58)) 42 | result.Add(result, big.NewInt(int64(charIndex))) 43 | } 44 | 45 | decoded := result.Bytes() 46 | 47 | if input[0] == b58Alphabet[0] { 48 | decoded = append([]byte{0x00}, decoded...) 49 | } 50 | 51 | return decoded 52 | } 53 | -------------------------------------------------------------------------------- /tscec/keys.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "crypto/sha256" 8 | "log" 9 | 10 | "github.com/btcsuite/btcd/btcec" 11 | 12 | "golang.org/x/crypto/ripemd160" 13 | ) 14 | 15 | const ( 16 | version = byte(0x00) 17 | addressChecksumLen = 4 18 | privateKeyLen = 32 19 | ) 20 | 21 | // NewKeyPair 生成公私钥对 22 | func NewKeyPair() ([]byte, []byte) { 23 | curve := elliptic.P256() 24 | privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) 25 | if err != nil { 26 | log.Panic(err) 27 | } 28 | 29 | publicKey, err := GeneratePubkeyByPrvkey(privateKey.D.Bytes()) 30 | if err != nil { 31 | log.Panic(err) 32 | } 33 | 34 | return privateKey.D.Bytes(), publicKey 35 | } 36 | 37 | // GeneratePubkeyByPrvkey 根据私钥计算公钥 38 | func GeneratePubkeyByPrvkey(p []byte) ([]byte, error) { 39 | curve := btcec.S256() 40 | _, publicKey := btcec.PrivKeyFromBytes(curve, p) 41 | // publicKey := privateKey.PubKey() 42 | // fmt.Println("-----------------------") 43 | // fmt.Println(privateKey.PubKey()) 44 | // fmt.Println("-----------------------") 45 | // return publicKey.SerializeUncompressed(), nil 46 | return publicKey.SerializeCompressed(), nil 47 | } 48 | 49 | // GenerateAddrByPubkey 计算公钥对应的地址 50 | func GenerateAddrByPubkey(publicKey []byte) []byte { 51 | publicKeyHash := HashPublicKey(publicKey) 52 | 53 | // https: //en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses 54 | 55 | versionPayload := append([]byte{version}, publicKeyHash...) 56 | checksum := checksum(versionPayload) 57 | 58 | fullPayload := append(versionPayload, checksum...) 59 | address := Base58Encode(fullPayload) 60 | 61 | return address 62 | } 63 | 64 | // HashPublicKey 哈希公钥 65 | func HashPublicKey(publicKey []byte) []byte { 66 | publicKeySHA256 := sha256.Sum256(publicKey) 67 | 68 | RIPEMD160Hasher := ripemd160.New() 69 | _, err := RIPEMD160Hasher.Write(publicKeySHA256[:]) 70 | if err != nil { 71 | log.Panic(err) 72 | } 73 | publicKeyRIPEMD160 := RIPEMD160Hasher.Sum(nil) 74 | 75 | return publicKeyRIPEMD160 76 | } 77 | -------------------------------------------------------------------------------- /tscec/keys_test.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "encoding/base64" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | func TestPublicKeyAndAddress(t *testing.T) { 10 | 11 | keyPairs := [][]string{ 12 | []string{"Pc2DI133bYC6xcsKD1+Wwb1XkTZSqALiAhcGrzTXStM=", "Aim0N7NBzpOlic/LWbAaqc54IY+mK25Acf9tylpUi3CX", "19pvQGqsp6fig1tc6bjqWBGZmaCJbCkK83"}, 13 | []string{"XaaMJXKPjXKQKAjEGgiXaBhqIifI2b8fsjI+1qDIN2Y=", "A1v+Zfu1kbUEfDfi3kGoOxxxdB1JdFmcI5xScpubpids", "17dTMucUBwPkUEHwiVs7NY1tYn327yPpYx"}, 14 | []string{"98opKP6MzyTlNPcSN2ELywFSlASuervz/5okTxkvC/E=", "A4HNHgvDKMMluv1akCFDAtF5rNISWIsJXYPdk3yeXxgh", "1LEi1KXDU9BWS2ZFYBVWLGMunqi8Hxvwx"}} 15 | 16 | for i := range keyPairs { 17 | privateKey := keyPairs[i][0] 18 | publicKey := keyPairs[i][1] 19 | address := keyPairs[i][2] 20 | 21 | derivedPublicKey, err := GeneratePubkeyByPrvkey(base64Decode(privateKey)) 22 | if err != nil { 23 | log.Panic(err) 24 | } 25 | if publicKey != base64Encode(derivedPublicKey) { 26 | t.Errorf("Incorrect derived public key, target: %s, derived: %s\n", publicKey, base64Encode(derivedPublicKey)) 27 | } 28 | 29 | derivedAddress := string(GenerateAddrByPubkey(base64Decode(publicKey))) 30 | if address != derivedAddress { 31 | t.Errorf("Incorrect derived address, target: %s, derived: %s\n", address, derivedAddress) 32 | } 33 | } 34 | } 35 | 36 | func base64Encode(input []byte) string { 37 | return base64.StdEncoding.EncodeToString(input) 38 | } 39 | 40 | func base64Decode(input string) []byte { 41 | src, err := base64.StdEncoding.DecodeString(input) 42 | if err != nil { 43 | log.Panic(err) 44 | } 45 | 46 | return src 47 | } 48 | -------------------------------------------------------------------------------- /tscec/merkletree.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | ) 7 | 8 | func ComputeMerkleRootFromPath(leafHash string, merklePath []string, leafIndex uint32) string { 9 | var hash string = leafHash 10 | for _, p := range merklePath { 11 | if (leafIndex & uint32(1)) != 0 { 12 | hash = combineHash(p, hash) 13 | } else { 14 | hash = combineHash(hash, p) 15 | } 16 | leafIndex >>= 1 17 | } 18 | return hash 19 | } 20 | 21 | func ComputeMerkleRoot(leaves []string) string { 22 | var merkleRoot string 23 | merkleComputation(leaves, &merkleRoot, 0, nil) 24 | return merkleRoot 25 | } 26 | 27 | func ComputeMerklePath(leaves []string, leafIndex uint32) []string { 28 | merklePath := []string{} 29 | merkleComputation(leaves, nil, leafIndex, &merklePath) 30 | return merklePath 31 | } 32 | 33 | // Ported from consensus/merkle::MerkleComputation in Bitcoin Core 34 | func merkleComputation(leaves []string, pRoot *string, leafIndex uint32, pPath *[]string) { 35 | if len(leaves) == 0 { 36 | return 37 | } 38 | 39 | // the number of leaves processed so far. 40 | var count uint32 = 0 41 | // inner is an array of eagerly computed subtree hashes, indexed by tree 42 | // level (0 being the leaves). 43 | // For example, when count is 25 (11001 in binary), inner[4] is the hash of 44 | // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to 45 | // the last leaf. The other inner entries are undefined. 46 | var inner [32]string 47 | var level uint32 48 | var h string 49 | var matchLevel int = -1 50 | 51 | // First process all leaves into 'inner' values. 52 | for count < uint32(len(leaves)) { 53 | h = leaves[count] 54 | var matchh bool = (count == leafIndex) 55 | count++ 56 | // For each of the lower bits in count that are 0, do 1 step. Each 57 | // corresponds to an inner value that existed before processing the 58 | // current leaf, and each needs a hash to combine it. 59 | for level = 0; (count & ((uint32(1)) << level)) == 0; level++ { 60 | condition := (count & ((uint32(1)) << level)) == 0 61 | fmt.Printf("count: %d, level: %d, condition: %t\n", count, level, condition) 62 | if pPath != nil { 63 | if matchh { 64 | *pPath = append(*pPath, inner[level]) 65 | } else if matchLevel == int(level) { 66 | *pPath = append(*pPath, h) 67 | matchh = true 68 | } 69 | } 70 | h = combineHash(inner[level], h) 71 | } 72 | inner[level] = h 73 | if matchh { 74 | matchLevel = int(level) 75 | } 76 | } 77 | 78 | // Do a final 'sweep' over the rightmost branch of the tree to process 79 | // odd levels, and reduce everything to a single top value. 80 | // Level is the level (counted from the bottom) up to which we've sweeped. 81 | level = 0 82 | // As long as bit number level in count is zero, skip it. It means there 83 | // is nothing left at this level. 84 | for (count & (uint32(1) << level)) == 0 { 85 | level++ 86 | } 87 | h = inner[level] 88 | var matchh bool = (matchLevel == int(level)) 89 | for count != (uint32(1) << level) { 90 | // If we reach this point, h is an inner value that is not the top. 91 | // We combine it with itself (Bitcoin's special rule for odd levels in 92 | // the tree) to produce a higher level one. 93 | if pPath != nil && matchh { 94 | *pPath = append(*pPath, h) 95 | } 96 | h = combineHash(h, h) 97 | // Increment count to the value it would have if two entries at this 98 | // level had existed 99 | count += (uint32(1) << level) 100 | level++ 101 | // And propagate the result upwards accordingly. 102 | for (count & (uint32(1) << level)) == 0 { 103 | if pPath != nil { 104 | if matchh { 105 | *pPath = append(*pPath, inner[level]) 106 | } else if matchLevel == int(level) { 107 | *pPath = append(*pPath, h) 108 | matchh = true 109 | } 110 | } 111 | h = combineHash(inner[level], h) 112 | level++ 113 | } 114 | } 115 | 116 | // Return result. 117 | if pRoot != nil { 118 | *pRoot = h 119 | } 120 | } 121 | 122 | func dsha256(s string) string { 123 | firstHash := sha256.Sum256([]byte(s)) 124 | // Convert array to slice: firstHash[:] 125 | secondHash := sha256.Sum256(firstHash[:]) 126 | // convert byte array to str 127 | ret := fmt.Sprintf("%s", secondHash) 128 | return ret 129 | } 130 | 131 | func combineHash(x, y string) string { 132 | cat := x + y 133 | return dsha256(cat) 134 | } 135 | -------------------------------------------------------------------------------- /tscec/merkletree_test.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCalcMerkleRoot(t *testing.T) { 8 | leafHashes := []string{} 9 | leaves := []string{ 10 | "1", 11 | "2", 12 | "3", 13 | } 14 | // test for calc merkle root 15 | for _, l := range leaves { 16 | leafHashes = append(leafHashes, dsha256(l)) 17 | } 18 | var root string 19 | root = ComputeMerkleRoot(leafHashes) 20 | 21 | h1 := combineHash(leafHashes[0], leafHashes[1]) 22 | h2 := combineHash(leafHashes[2], leafHashes[2]) 23 | h := combineHash(h1, h2) 24 | if root != h { 25 | t.Errorf("calc merkle root failed, got: %x, want: %x", root, h) 26 | } 27 | 28 | // test for calc merkle path && validate through merkle path 29 | for pos := uint32(0); pos <= 2; pos++ { 30 | var merklePath []string 31 | merklePath = ComputeMerklePath(leafHashes, pos) 32 | t.Logf("merkle path: \n") 33 | for i, p := range merklePath { 34 | t.Logf("%d\t%x\n", i, p) 35 | } 36 | 37 | merkleRoot := ComputeMerkleRootFromPath(leafHashes[pos], merklePath, pos) 38 | 39 | if merkleRoot != root { 40 | t.Errorf("validate failed, got: %x, want: %x", merkleRoot, root) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tscec/signer.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/base64" 6 | "fmt" 7 | 8 | "github.com/btcsuite/btcd/btcec" 9 | ) 10 | 11 | // Sign 签名 12 | func Sign(pkBytes []byte, data []byte, isHash bool) string { 13 | privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) 14 | 15 | if !isHash { 16 | dataHash := sha256.Sum256(data) 17 | copy(data[:], dataHash[:]) 18 | } 19 | 20 | signature, _ := privKey.Sign(data) 21 | return string(base64.StdEncoding.EncodeToString(signature.Serialize())) 22 | } 23 | 24 | // Verify 验证签名 25 | func Verify(pubkey, sig, data []byte) bool { 26 | // signature, _ := btcec.ParseSignature(data, btcec.S256()) 27 | 28 | // // Verify the signature for the message using the public key. 29 | // messageHash := chainhash.DoubleHashB(data) 30 | // verified := signature.Verify(messageHash, pubkey) 31 | // fmt.Printf("Signature Verified? %v\n", verified) 32 | // return verified 33 | fmt.Print(pubkey) 34 | fmt.Print(sig) 35 | fmt.Print(data) 36 | return true 37 | } 38 | -------------------------------------------------------------------------------- /tscec/signer_test.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestSign(t *testing.T) { 10 | fmt.Println("\n======test sign begin==========") 11 | privateKey, publicKey := NewKeyPair() 12 | fmt.Printf("privkey: %s\n", base64.StdEncoding.EncodeToString(privateKey)) 13 | fmt.Printf("pubkey: %s\n", base64.StdEncoding.EncodeToString(publicKey)) 14 | 15 | signMsg := "hello world, you are welcome" 16 | ret := Sign(privateKey, []byte(signMsg), false) 17 | 18 | fmt.Printf("data: %s\n", signMsg) 19 | fmt.Printf("signature: %s\n", ret) 20 | fmt.Println("======test sign end==========") 21 | } 22 | 23 | func TestVerify(t *testing.T) { 24 | message := "hello world, you are welcome" 25 | sig := "MEUCIQCmKnxXF32Ni5/jWHYcBn57fvjXIF4+fvL/Tix+LDItvAIgN00bzhgpd/eMjOteN+SfsqpdnXJFepWToZ37VrY5qzE=" 26 | privateKey := "OvxHYMWUE31PtBohtbXdF9fOXafS2fe3GoZtE9SVG2I=" 27 | pubKey := "BBJjY/x2Patnk3aFCh/4u3q5p8c30RUiKiVHBl4Mg4w1SvMprrS26Z47WaXEj1Fe68deacd63mWTImOxupoNvWI=" 28 | 29 | fmt.Println("\n======test verify begin==========") 30 | fmt.Printf("privkey: %s\n", privateKey) 31 | fmt.Printf("pubkey: %s\n", pubKey) 32 | fmt.Printf("data: %s\n", message) 33 | fmt.Printf("signature: %s\n", sig) 34 | 35 | pkey, _ := base64.StdEncoding.DecodeString(pubKey) 36 | signature, _ := base64.StdEncoding.DecodeString(sig) 37 | 38 | ret := Verify(pkey, signature, []byte(message)) 39 | 40 | fmt.Printf("return: %t\n", ret) 41 | fmt.Println("======test verify end==========") 42 | } 43 | -------------------------------------------------------------------------------- /tscec/utils.go: -------------------------------------------------------------------------------- 1 | package tscec 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | "encoding/binary" 7 | "log" 8 | ) 9 | 10 | // 计算公钥校验码 11 | func checksum(payload []byte) []byte { 12 | firstSHA := sha256.Sum256(payload) 13 | secondSHA := sha256.Sum256(firstSHA[:]) 14 | 15 | return secondSHA[:addressChecksumLen] 16 | } 17 | 18 | // IntToHex converts an int64 to a byte array 19 | func IntToHex(num int64) []byte { 20 | buff := new(bytes.Buffer) 21 | err := binary.Write(buff, binary.BigEndian, num) 22 | if err != nil { 23 | log.Panic(err) 24 | } 25 | 26 | return buff.Bytes() 27 | } 28 | 29 | // ReverseBytes reverses a byte array 30 | func ReverseBytes(data []byte) { 31 | for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 { 32 | data[i], data[j] = data[j], data[i] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tsiss/append.go: -------------------------------------------------------------------------------- 1 | package tsiss 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | // send 共享信息新增/追加 12 | func send(appendIssURI string, iss *IssAppend) ([]byte, error) { 13 | // 校验common是否符合标准 14 | err := validate.Struct(iss) 15 | if err != nil { 16 | return nil, err 17 | } 18 | data, err := json.Marshal(iss) 19 | 20 | log.Printf("trustsql append iss request data is %s", string(data)) 21 | 22 | req, err := http.NewRequest("POST", appendIssURI, bytes.NewBuffer(data)) 23 | req.Header.Set("Content-Type", "application/json;charset=UTF-8") 24 | if err != nil { 25 | return nil, err 26 | } 27 | resp, err := client.Do(req) 28 | if err != nil { 29 | return nil, err 30 | } 31 | body, err := ioutil.ReadAll(resp.Body) 32 | _ = resp.Body.Close() 33 | log.Printf("trustsql response body is %s", string(body)) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | // 检查返回值是否成功 39 | err = responseUtil(body) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return body, nil 45 | } 46 | 47 | type signStr struct { 48 | SignStr string `json:"sign_str"` 49 | } 50 | 51 | // GetIssSignStr 共享信息新增/追加 52 | func GetIssSignStr(appendIssURI string, iss *IssAppend) (string, error) { 53 | body, err := send(appendIssURI, iss) 54 | if err != nil { 55 | return "", err 56 | } 57 | s := signStr{} 58 | err = json.Unmarshal(body, &s) 59 | if err != nil { 60 | return "", err 61 | } 62 | return s.SignStr, nil 63 | } 64 | 65 | // AppendIss 共享信息新增/追加 66 | func AppendIss(appendIssURI string, iss *IssAppend) (*IssAppendResponse, error) { 67 | body, err := send(appendIssURI, iss) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | issAppendResponse := IssAppendResponse{} 73 | err = json.Unmarshal(body, &issAppendResponse) 74 | if err != nil { 75 | return nil, err 76 | } 77 | err = validate.Struct(issAppendResponse) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return &issAppendResponse, nil 82 | } 83 | -------------------------------------------------------------------------------- /tsiss/append_test.go: -------------------------------------------------------------------------------- 1 | package tsiss 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "testing" 7 | ) 8 | 9 | const AppendIssTestURI = "http://39.107.26.141:8007/trustsql/v1.0/iss_append" 10 | 11 | func TestAppendIss(t *testing.T) { 12 | issAppend := &IssAppend{ 13 | Version: "1.0", 14 | SignType: "ECDSA", 15 | MchID: "gbec7b7cece75c8a5", 16 | MchSign: "MEYCIQDCoCYth2zGer2Z/kliD11jRXGKqLqLNk/vo18js+CvRwIhANTQ3PbN9vj9YjmaB+rma2Sz0D+30WgZPOHAO9ysRsj1", 17 | Account: "1PPdKF7brmyiQF7zKtGe1KnEURGRb1jBE2", 18 | CommitTime: "2017-08-20 15:00:00", 19 | Content: map[string]interface{}{"content": "test"}, 20 | InfoKey: "teast123", 21 | InfoVersion: "1", 22 | State: "0", 23 | Notes: map[string]interface{}{"notes": "test"}, 24 | PublicKey: "BNTUTn8geMERremCxomOVXSGLitaGe8FnjpRNXCLiNtd8yl9G6vtqCyGlmpEz901t6WVHItMqZ9ozt5o/Xf6uL4=", 25 | Sign: "MEQCIE4YbWYw4FyUMd12HHJqsAGfor9xKdb7e0feM+G0yklAAiBEy1wk1MFL5ZcrySzvO9g7pYVVDkJwKrwZ56Gdlzlybg==", 26 | 27 | ChainID: "xxx test chain id", 28 | LedgerID: "xxx test ledger_id", 29 | } 30 | iss, err := AppendIss(AppendIssTestURI, issAppend) 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | t.Log(iss.MchID) 35 | } 36 | 37 | func TestAppendIssValidate(t *testing.T) { 38 | dat, err := ioutil.ReadFile("test_data/append_response.json") 39 | if err != nil { 40 | t.Error(err) 41 | } 42 | // fmt.Print(string(dat)) 43 | issResponse := IssAppendResponse{} 44 | json.Unmarshal(dat, &issResponse) 45 | t.Log(issResponse.MchID) 46 | } 47 | -------------------------------------------------------------------------------- /tsiss/example_test.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package tsiss 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func ExamplAppendIss() { 10 | ia := &IssAppend{ 11 | Version: "", 12 | SignType: "", 13 | MchID: "", 14 | MchSign: "", 15 | 16 | NodeID: "", 17 | ChainID: "", 18 | LedgerID: "", 19 | InfoKey: "", 20 | InfoVersion: "", 21 | State: "", 22 | 23 | CommitTime: "", 24 | Account: "", 25 | PublicKey: "", 26 | Sign: "", 27 | } 28 | 29 | isr, err := AppendIss(ia) 30 | if err != nil { 31 | fmt.Println(err) 32 | } 33 | fmt.Println(isr) 34 | } 35 | -------------------------------------------------------------------------------- /tsiss/params.go: -------------------------------------------------------------------------------- 1 | package tsiss 2 | 3 | // IssQuery 共享信息查询参数 4 | type IssQuery struct { 5 | /// 协议参数 6 | Version string `json:"version" validate:"required"` // 接口版本, 版本号,默认为1.0 7 | SignType string `json:"sign_type" validate:"required"` // 签名方式,签名类型,取值:ECDSA 8 | MchID string `json:"mch_id" validate:"required"` // 通讯方ID 9 | MchSign string `json:"mch_sign" validate:"required"` // 通讯方签名 10 | 11 | /// 业务参数 12 | NodeID string `json:"node_id,omitempty"` // 节点ID,可以通过baas平台->相应链->基本信息页面中获取 13 | ChainID string `json:"chain_id" validate:"required"` // 链ID,可以通过baas平台->相应链->基本信息页面中获取 14 | LedgerID string `json:"ledger_id" validate:"required"` // 账本ID,可以通过baas平台->相应链->相应账本页面中获取 15 | InfoKey string `json:"info_key,omitempty"` // 信息标识,查询符合此信息标识号的记录 16 | InfoVersion string `json:"info_version,omitempty"` // XXX 信息版本号, 查询符合此信息版本号的记录 17 | State string `json:"state,omitempty"` // XXX 记录状态, 由业务自行定义。 18 | Content map[string]interface{} `json:"content,omitempty"` // 记录内容,可以选填部分json字段,查询符合这些字段的记录 19 | Notes map[string]interface{} `json:"notes,omitempty"` // 记录注释 20 | Range map[string]interface{} `json:"range,omitempty"` // 范围查询条件,作为查询条件,范围查询条件 21 | Account string `json:"account,omitempty"` // 记录方地址,查询符合此记录方地址的记录 22 | THash string `json:"t_hash,omitempty"` // 记录哈希,查询符合此记录哈希的记录 23 | PageNo string `json:"page_no,omitempty"` // XXX 页码, 第几页,默认1 24 | PageLimit string `json:"page_limit,omitempty"` // XXX 每页数量,分页显示时每页显示多少条,默认10 25 | Timestamp string `json:"timestamp" validate:"required"` // XXX 请求时间戳,当前unix时间戳(秒) 26 | 27 | /// Range范围查询条件(string) 28 | BHeight map[string]interface{} `json:"b_height,omitempty"` // 区块高度,条件范围,区块高度范围 29 | CommitTime map[string]interface{} `json:"commit_time,omitempty"` // 记录时间,条件范围,记录时间范围 30 | 31 | /// 条件范围(JsonObject) 32 | From string `json:"from,omitempty"` // 开始,作为查询条件,查询这个条件之后的信息记录(>=) 33 | To string `json:"to,omitempty"` // 结束,作为查询条件,查询这个条件之前的信息记录(<=) 34 | } 35 | 36 | // Info 记录列表 37 | type Info struct { 38 | InfoKey string `json:"info_key"` // 信息标识,同信息录入接口 39 | InfoVersion string `json:"info_version"` // 信息版本号 40 | State string `json:"state"` // 记录状态 41 | Content map[string]interface{} `json:"content"` // 记录内容 42 | Notes map[string]interface{} `json:"notes"` // 记录注释 43 | CommitTime string `json:"commit_time"` // 记录时间 44 | Account string `json:"account"` // 记录方地址 45 | PublicKey string `json:"public_key"` // 记录方公钥 46 | Sign string `json:"sign"` // 记录方签名 47 | THash string `json:"t_hash"` // 记录哈希 48 | BHeight string `json:"b_height"` // 区块高度 49 | BPrevHash string `json:"b_prev_hash"` // 前块哈希 50 | BHash string `json:"b_hash"` // 本块哈希 51 | BTime string `json:"b_time"` // 区块时间 52 | } 53 | 54 | // IssResponse 返回参数 55 | type IssResponse struct { 56 | // 协议参数 57 | Version string `json:"version" validate:"required"` // 接口版本, 版本号,默认为1.0 58 | SignType string `json:"sign_type" validate:"required"` // 签名方式,签名类型,取值:ECDSA 59 | MchID string `json:"mch_id" validate:"required"` // 通讯方ID 60 | MchSign string `json:"mch_sign" validate:"required"` // 通讯方签名 61 | // 业务参数 62 | Retcode string `json:"retcode"` // 返回状态码,0表示成功,其它为失败 63 | Retmsg string `json:"retmsg"` // 返回信息,如非空,为错误原因。 64 | TotalInfos string `json:"total_infos"` // 记录总数,符合条件的记录总条数 65 | Infos []Info `json:"infos"` // 记录列表,本次查询出的记录列表json数组 66 | } 67 | 68 | // IssAppend 共享信息新增/追加,请求参数 69 | type IssAppend struct { 70 | Version string `json:"version" validate:"required"` // 接口版本, 版本号,默认为1.0 71 | SignType string `json:"sign_type" validate:"required"` // 签名方式,签名类型,取值:ECDSA 72 | MchID string `json:"mch_id" validate:"required"` // 通讯方ID 73 | MchSign string `json:"mch_sign" validate:"required"` // 通讯方签名 74 | 75 | /// 业务参数 76 | NodeID string `json:"node_id,omitempty"` // 节点ID,可以通过baas平台->相应链->基本信息页面中获取 77 | ChainID string `json:"chain_id" validate:"required"` // 链ID,可以通过baas平台->相应链->基本信息页面中获取 78 | LedgerID string `json:"ledger_id" validate:"required"` // 账本ID,可以通过baas平台->相应链->相应账本页面中获取 79 | InfoKey string `json:"info_key,omitempty" validate:"required"` // 信息标识,查询符合此信息标识号的记录 80 | InfoVersion string `json:"info_version,omitempty" validate:"required"` // XXX 信息版本号, 查询符合此信息版本号的记录 81 | State string `json:"state,omitempty" validate:"required"` // XXX 记录状态, 由业务自行定义。 82 | Content map[string]interface{} `json:"content,omitempty" validate:"required"` // 记录内容,可以选填部分json字段,查询符合这些字段的记录 83 | Notes map[string]interface{} `json:"notes,omitempty" validate:"required"` // 记录注释 84 | 85 | CommitTime string `json:"commit_time,omitempty" validate:"required"` // 由业务自行定义。 格式:YYYY-MM-DD HH:mm:SS 86 | Account string `json:"account,omitempty" validate:"required"` // 记录方地址,查询符合此记录方地址的记录 87 | PublicKey string `json:"public_key" validate:"required"` // 记录方公钥 88 | Sign string `json:"sign,omitempty"` // 记录方签名,使用SDK函数IssSign进行签名 89 | TimeStamp int64 `json:"timestamp" validate:"required"` // 当前unix时间戳(秒),与服务器时间相差过远会返回失败 90 | } 91 | 92 | // IssAppendResponse 共享信息新增/追加, 返回参数 93 | type IssAppendResponse struct { 94 | Version string `json:"version" validate:"required"` // 接口版本, 版本号,默认为1.0 95 | SignType string `json:"sign_type" validate:"required"` // 签名方式,签名类型,取值:ECDSA 96 | MchID string `json:"mch_id" validate:"required"` // 通讯方ID 97 | MchSign string `json:"mch_sign" validate:"required"` // 通讯方签名 98 | 99 | /// 业务参数 100 | Retcode string `json:"retcode"` // XXX 返回状态码,0表示成功,其它为失败 101 | Retmsg string `json:"retmsg"` // 返回信息,如非空,为错误原因。 102 | InfoKey string `json:"info_key,omitempty"` // 信息标识,查询符合此信息标识号的记录 103 | InfoVersion string `json:"info_version,omitempty"` // XXX 信息版本号, 查询符合此信息版本号的记录 104 | State string `json:"state,omitempty"` // XXX 记录状态, 由业务自行定义。 105 | Content map[string]interface{} `json:"content,omitempty"` // 记录内容,可以选填部分json字段,查询符合这些字段的记录 106 | Notes map[string]interface{} `json:"notes,omitempty"` // 记录注释 107 | CommitTime string `json:"commit_time,omitempty"` // 由业务自行定义。 格式:YYYY-MM-DD HH:mm:SS 108 | Account string `json:"account,omitempty"` // 记录方地址,查询符合此记录方地址的记录 109 | PublicKey string `json:"public_key"` // 记录方公钥 110 | Sign string `json:"sign"` // 记录方签名,使用SDK函数IssSign进行签名 111 | THash string `json:"t_hash,omitempty"` // 记录哈希 112 | BHeight string `json:"b_height,omitempty"` // XXX 区块高度 113 | BPrevHash string `json:"b_prev_hash"` // 前块哈希 114 | BHash string `json:"b_hash"` // 本块哈希 115 | BTime string `json:"b_time"` // 区块时间 116 | } 117 | -------------------------------------------------------------------------------- /tsiss/query.go: -------------------------------------------------------------------------------- 1 | package tsiss 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "time" 10 | 11 | "gopkg.in/go-playground/validator.v9" 12 | ) 13 | 14 | var ( 15 | client *http.Client 16 | validate *validator.Validate 17 | ) 18 | 19 | func init() { 20 | client = &http.Client{ 21 | Timeout: 1 * time.Second, 22 | } 23 | validate = validator.New() 24 | } 25 | 26 | // 设置http请求过期时间 27 | func SetRequestTimeout(timeout time.Duration) { 28 | client.Timeout = timeout 29 | } 30 | 31 | // QueryIss 共享信息查询 32 | func QueryIss(queryIssURI string, iss *IssQuery) (*IssResponse, error) { 33 | // 校验common是否符合标准 34 | err := validate.Struct(iss) 35 | if err != nil { 36 | return nil, err 37 | } 38 | data, err := json.Marshal(iss) 39 | 40 | log.Printf("trustsql request data is %s", string(data)) 41 | 42 | // send http request 43 | req, err := http.NewRequest("POST", queryIssURI, bytes.NewBuffer(data)) 44 | req.Header.Set("Content-Type", "application/json;charset=UTF-8") 45 | if err != nil { 46 | return nil, err 47 | } 48 | resp, err := client.Do(req) 49 | if err != nil { 50 | return nil, err 51 | } 52 | body, err := ioutil.ReadAll(resp.Body) 53 | log.Printf("trustsql response body is %s", string(body)) 54 | _ = resp.Body.Close() 55 | 56 | // 检查返回值是否成功 57 | err = responseUtil(body) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | issResponse := IssResponse{} 63 | err = json.Unmarshal(body, &issResponse) 64 | if err != nil { 65 | return nil, err 66 | } 67 | err = validate.Struct(issResponse) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return &issResponse, nil 72 | } 73 | -------------------------------------------------------------------------------- /tsiss/query_test.go: -------------------------------------------------------------------------------- 1 | package tsiss 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "testing" 7 | ) 8 | 9 | const QueryIssTestURI = "http://39.107.26.141:8007/trustsql/v1.0/iss_query" 10 | 11 | func TestQueryIss(t *testing.T) { 12 | issQuery := IssQuery{ 13 | Version: "1.0", 14 | SignType: "ECDSA", 15 | MchID: "gbec7b7cece75c8a5", 16 | MchSign: "MEYCIQDCoCYth2zGer2Z/kliD11jRXGKqLqLNk/vo18js+CvRwIhANTQ3PbN9vj9YjmaB+rma2Sz0D+30WgZPOHAO9ysRsj1", 17 | ChainID: "aa", 18 | LedgerID: "bb", 19 | Timestamp: "1503648096", 20 | NodeID: "cc", 21 | } 22 | iss, err := QueryIss(QueryIssTestURI, &issQuery) 23 | if err != nil { 24 | t.Error(err) 25 | } 26 | t.Log(iss.Infos[0].Content) 27 | } 28 | 29 | func TestIssResponse(t *testing.T) { 30 | dat, err := ioutil.ReadFile("test_data/query_response.json") 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | // fmt.Print(string(dat)) 35 | issResponse := IssResponse{} 36 | json.Unmarshal(dat, &issResponse) 37 | t.Log(issResponse.MchID) 38 | t.Log(issResponse.Infos[0].InfoKey) 39 | } 40 | -------------------------------------------------------------------------------- /tsiss/test_data/append_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "retcode":"0", 3 | "retmsg":"OK", 4 | "account":"1PPdKF7brmyiQF7zKtGe1KnEURGRb1jBE2", 5 | "b_hash":"5f7ccabc3d7cd95a607660524b15a5b329aea6653cc8ef7c74c3b75de3f9d5da", 6 | "b_height":"36557", 7 | "b_prev_hash":"ebeecd6f5ca27e57e65400578bad1e6e6f9f832b2b32390e5335c2314f21bdb9", 8 | "b_time":"2017-08-25 15:58:24", 9 | "commit_time":"2017-08-20 15:00:00", 10 | "content":{ 11 | "content":"test" 12 | }, 13 | "info_key":"teast123", 14 | "info_version":"1", 15 | "mch_id":"trust_infoshare", 16 | "mch_sign":"MEUCIQCqZCH21r1m5rBCCMNh3WR+js0bHngBu9+SmjCqamjgQgIgN3gmOt/x5wENsR5g9mk38lgBMWXkRwv2RDI8PaLrspI=", 17 | "notes":{ 18 | "notes":"test" 19 | }, 20 | "public_key":"BNTUTn8geMERremCxomOVXSGLitaGe8FnjpRNXCLiNtd8yl9G6vtqCyGlmpEz901t6WVHItMqZ9ozt5o/Xf6uL4=", 21 | "sign":"MEQCIE4YbWYw4FyUMd12HHJqsAGfor9xKdb7e0feM+G0yklAAiBEy1wk1MFL5ZcrySzvO9g7pYVVDkJwKrwZ56Gdlzlybg==", 22 | "sign_type":"ECDSA", 23 | "state":"0", 24 | "t_hash":"af265f9ebf462dbb2ee09f213363a1737711862cea036bef0a638f3af8055869", 25 | "version":"1.0", 26 | "mch_sign_verify":true, 27 | "sign_verify":true 28 | } 29 | -------------------------------------------------------------------------------- /tsiss/test_data/query_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "retcode": "0", 3 | "retmsg": "OK", 4 | "infos": [ 5 | { 6 | "info_key": "UlegalFri Aug 11 16:08:15 CST 2017", 7 | "info_version": "1", 8 | "state": "2", 9 | "content": { 10 | "owner": "ulegal", 11 | "source": "ulegal", 12 | "hashValue": "c630aaa58fef9a453c9fba6c1f71c5f984ac0199144bba0f8f8c3b7b5ef82386" 13 | }, 14 | "notes": { 15 | "extInfo": "default" 16 | }, 17 | "commit_time": "2017-08-11 16:08:15", 18 | "account": "157o8FzGFAeo3xnkvYskSwfgTarZZjwBNa", 19 | "public_key": "BOj8B7xi4997gGx6hjTx+ID7KFSCtjTy594D7XmY32Or80Z5Z5DnM6sEJqyKP4QKJOPtxqNW+HdVHU48R9G5Ndw=", 20 | "sign": "MEQCIFnQp35qMb6B2kSbYALA9sjMNyQJeUSkPPALJpzk8T7kAiBXmiNJXzm1Ot4tz7h7bN55sUHE+vqmO3KvHgJZ+zPDaA==", 21 | "t_hash": "8197583ab3a59216b2fcecc663b2dddddaa6d14042f1a3c3722fa1cbf59e5b27", 22 | "b_height": "15367", 23 | "b_prev_hash": "66f9ecfe6f58ab6b56e1225e7337e1f25f03aa4b12ec4d6de8051526938cc893", 24 | "b_hash": "3d80a2c5c10d3530fa0a19e30ccb8a480457558d567338d784630b52909a3e94", 25 | "b_time": "2017-08-11 16:08:15", 26 | "sign_verify": true 27 | }, 28 | { 29 | "info_key": "UlegalFri Aug 11 16:08:14 CST 2017", 30 | "info_version": "1", 31 | "state": "2", 32 | "content": { 33 | "owner": "ulegal", 34 | "source": "ulegal", 35 | "hashValue": "c630aaa58fef9a453c9fba6c1f71c5f984ac0199144bba0f8f8c3b7b5ef82386" 36 | }, 37 | "notes": { 38 | "extInfo": "default" 39 | }, 40 | "commit_time": "2017-08-11 16:08:14", 41 | "account": "157o8FzGFAeo3xnkvYskSwfgTarZZjwBNa", 42 | "public_key": "BOj8B7xi4997gGx6hjTx+ID7KFSCtjTy594D7XmY32Or80Z5Z5DnM6sEJqyKP4QKJOPtxqNW+HdVHU48R9G5Ndw=", 43 | "sign": "MEUCIQCKiN/7zEK7/fE4ULRlAgvwrMod+tIjr/CoXJwLXjGxFgIgUyrIWauQ9R9kVXewxHj5skhjy/cVDmhC8p1TEx4zJl8=", 44 | "t_hash": "6fdc8a97f1c36123c270d4e0ac2a43a5a72a2c76bf0c402da827c855a86e3b3d", 45 | "b_height": "15366", 46 | "b_prev_hash": "895d84b2ce38c192054fb71ae559e44f1d1608cbf321d24f8698bc2f6d72c9d3", 47 | "b_hash": "66f9ecfe6f58ab6b56e1225e7337e1f25f03aa4b12ec4d6de8051526938cc893", 48 | "b_time": "2017-08-11 16:08:14", 49 | "sign_verify": true 50 | } 51 | ], 52 | "mch_id": "trust_infoshare", 53 | "mch_sign": "MEUCIQCoiRmDkdatPZp5n2UJ9CJVkKhifF9YXT+eIK0WK9I38QIgEdTx7Zb7HNCKVW/owCMmhgwPdET32sDVkO8FMyCU1r8=", 54 | "sign_type": "ECDSA", 55 | "total_infos": "827", 56 | "version": "1.0", 57 | "mch_sign_verify": true 58 | } 59 | -------------------------------------------------------------------------------- /tsiss/utils.go: -------------------------------------------------------------------------------- 1 | package tsiss 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type ret struct { 9 | Retcode string `json:"retcode"` 10 | Retmsg string `json:"retmsg"` 11 | InfoNo *json.RawMessage `json:"info_no"` 12 | } 13 | 14 | func responseUtil(body []byte) error { 15 | var response ret 16 | err := json.Unmarshal(body, &response) 17 | if err != nil { 18 | return fmt.Errorf("json unmarshal response body error: %v", err) 19 | } 20 | switch response.Retcode { 21 | case "0": 22 | return nil 23 | default: 24 | return fmt.Errorf("request error, error code is %s, msg is %s", response.Retcode, response.Retmsg) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /user_params.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | 3 | // Common 公共信息 4 | type Common struct { 5 | MchID string `json:"mch_id" validate:"required"` 6 | ProductCode string `json:"product_code" validate:"required"` 7 | SeqNo string `json:"seq_no" validate:"required"` 8 | Sign string `json:"sign" validate:"required"` 9 | Type string `json:"type" validate:"required"` 10 | TimeStamp int64 `json:"time_stamp" validate:"required"` 11 | ReqData string `json:"req_data" validate:"required"` 12 | } 13 | 14 | // UserRegister 注册用户需要的信息 15 | type UserRegister struct { 16 | PublicKey string `json:"public_key" validate:"required"` 17 | UserID string `json:"user_id" validate:"required"` 18 | UserFullName string `json:"user_fullName" validate:"required"` 19 | } 20 | 21 | // UserRegisterResponse 注册用户返回信息 22 | type UserRegisterResponse struct { 23 | UserID string `json:"user_id"` // 用户ID 24 | PublicKey string `json:"public_key"` // 用户公钥 25 | UserAddress string `json:"user_address"` // 用户密钥地址 26 | UserFullName string `json:"user_fullName"` // 用户名 27 | Created string `json:"created"` // 创建时间 28 | State string `json:"state"` // 状态 29 | } 30 | 31 | // UserInfo 获取用户信息参数 32 | type UserInfo struct { 33 | UserID string `json:"user_id" validate:"required"` 34 | } 35 | 36 | // UserInfoResponse 获取用户信息返回值 37 | type UserInfoResponse struct { 38 | UserID string `json:"user_id"` // 用户ID 39 | PublicKey string `json:"public_key"` // 用户公钥 40 | UserAddress string `json:"user_address"` // 用户密钥地址 41 | Created string `json:"created"` // 创建时间 42 | State string `json:"state"` // 状态 43 | 44 | } 45 | 46 | // Account 创建用户账户参数 47 | type Account struct { 48 | UserID string `json:"user_id" validate:"required"` 49 | PublicKey string `json:"public_key" validate:"required"` 50 | } 51 | 52 | // AccountResponse 创建用户账户返回值 53 | type AccountResponse struct { 54 | UserID string `json:"user_id"` // 用户ID 55 | AccountAddress string `json:"account_address"` // 创建的账户地址 56 | PublicKey string `json:"public_key"` // 用户公钥 57 | Created string `json:"created"` // 创建时间 58 | State string `json:"state"` // 状态 59 | } 60 | 61 | // Accounts 获取用户的账户地址列表参数 62 | type Accounts struct { 63 | UserID string `json:"user_id" validate:"required"` 64 | State string `json:"state" validate:"omitempty"` 65 | BeginTime string `json:"begin_time" validate:"omitempty"` 66 | EndTime string `json:"end_time" validate:"omitempty"` 67 | Page int `json:"page" validate:"omitempty"` 68 | Limit int `json:"limit" validate:"omitempty"` 69 | } 70 | 71 | // AccountsResponse 获取用户的账户地址列表返回值 72 | type AccountsResponse struct { 73 | UserID string `json:"user_id"` // 用户ID 74 | AccountAddress string `json:"account_address"` // 创建的账户地址 75 | PublicKey string `json:"public_key"` // 用户公钥 76 | Created string `json:"created"` // 创建时间 77 | State string `json:"state"` // 状态 78 | } 79 | 80 | // PubkeyOfAccount 获取用户的账户公钥参数 81 | type PubkeyOfAccount struct { 82 | UserID string `json:"user_id" validate:"required"` 83 | AccountAddress string `json:"account_address" validate:"required"` 84 | } 85 | 86 | // PubkeyOfAccountResponse 获取用户的账户公钥返回值 87 | type PubkeyOfAccountResponse struct { 88 | UserID string `json:"user_id"` // 用户ID 89 | AccountAddress string `json:"account_address"` // 创建的账户地址 90 | PublicKey string `json:"public_key"` // 用户公钥 91 | Created string `json:"created"` // 创建时间 92 | State string `json:"state"` // 状态 93 | } 94 | -------------------------------------------------------------------------------- /user_test.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | "gopkg.in/go-playground/validator.v9" 10 | ) 11 | 12 | func common() Common { 13 | return Common{ 14 | MchID: "asdfsafsadaa", 15 | ProductCode: "123456789012", 16 | SeqNo: "12345678901234567890123456789012", 17 | Sign: "1111111111111111111111111111111111111111111111111111111111111111", 18 | Type: "123456789012", 19 | TimeStamp: time.Now().Unix(), 20 | ReqData: "asdf", 21 | } 22 | 23 | } 24 | 25 | func TestCommonValidate(t *testing.T) { 26 | c := common() 27 | validate = validator.New() 28 | 29 | errs := validate.Struct(c) 30 | if errs != nil { 31 | fmt.Println(errs) 32 | } 33 | _, _ = json.MarshalIndent(c, "", " ") 34 | // fmt.Println(string(jc)) 35 | } 36 | 37 | func TestRegisteUser(t *testing.T) { 38 | client := GenRandomPairkey() 39 | 40 | c := common() 41 | u := UserRegister{ 42 | UserID: "1111111", 43 | PublicKey: "2222222", 44 | UserFullName: "3333333333", 45 | } 46 | _, err := client.RegisteUser(&u, &c) 47 | if err != nil { 48 | fmt.Println(err) 49 | } 50 | } 51 | 52 | // func TestGetUserInfo(t *testing.T) { 53 | // privateKey, _ := tscec.NewKeyPair() 54 | // c := common() 55 | // u := identity.UserInfo{ 56 | // UserID: "1111111", 57 | // } 58 | // _, err := identity.GetUserInfo(&u, &c, privateKey) 59 | // if err != nil { 60 | // fmt.Println(err) 61 | // } 62 | // } 63 | 64 | // func TestRegisteAccount(t *testing.T) { 65 | // privateKey, _ := tscec.NewKeyPair() 66 | // c := common() 67 | // u := identity.Account{ 68 | // UserID: "1111111", 69 | // PublicKey: "publicKey test data", 70 | // } 71 | // _, err := identity.RegisteAccount(&u, &c, privateKey) 72 | // if err != nil { 73 | // fmt.Println(err) 74 | // } 75 | // } 76 | 77 | // func TestGetAccounts(t *testing.T) { 78 | // privateKey, _ := tscec.NewKeyPair() 79 | // c := common() 80 | // u := identity.Accounts{ 81 | // UserID: "1111111", 82 | // State: "state test data", 83 | // BeginTime: "1111-11-11 22:22:22", 84 | // EndTime: "1111-11-11 22:22:22", 85 | // Page: 213, 86 | // Limit: 234, 87 | // } 88 | // _, err := identity.GetAccounts(&u, &c, privateKey) 89 | // if err != nil { 90 | // fmt.Println(err) 91 | // } 92 | // } 93 | 94 | // func TestGetPubkeyOfAccount(t *testing.T) { 95 | // privateKey, _ := tscec.NewKeyPair() 96 | // c := common() 97 | // u := identity.PubkeyOfAccount{ 98 | // UserID: "1111111", 99 | // AccountAddress: "accout_address test data", 100 | // } 101 | // _, err := identity.GetPubkeyOfAccount(&u, &c, privateKey) 102 | // if err != nil { 103 | // fmt.Println(err) 104 | // } 105 | // } 106 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package trustsql 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type ret struct { 9 | Retcode string `json:"retcode"` 10 | Retmsg string `json:"retmsg"` 11 | InfoNo *json.RawMessage `json:"info_no"` 12 | } 13 | 14 | func responseUtil(body []byte) error { 15 | var response ret 16 | err := json.Unmarshal(body, &response) 17 | if err != nil { 18 | return fmt.Errorf("json unmarshal response body error: %v", err) 19 | } 20 | switch response.Retcode { 21 | case "0": 22 | return nil 23 | default: 24 | return fmt.Errorf("request error, error code is %s, msg is %s", response.Retcode, response.Retmsg) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "tmTwnJZMpwYat5xtkhAJCGxEf3w=", 7 | "path": "github.com/btcsuite/btcd/btcec", 8 | "revision": "2be2f12b358dc57d70b8f501b00be450192efbc3", 9 | "revisionTime": "2018-02-20T00:36:29Z" 10 | }, 11 | { 12 | "checksumSHA1": "uC0/YlX5RUmxgBWUtR8J2QBWEIo=", 13 | "path": "github.com/go-playground/locales", 14 | "revision": "e4cbcb5d0652150d40ad0646651076b6bd2be4f6", 15 | "revisionTime": "2017-10-14T05:53:35Z" 16 | }, 17 | { 18 | "checksumSHA1": "YWIa2z/i81ttb1Db9fm95XnIgwI=", 19 | "path": "github.com/go-playground/locales/currency", 20 | "revision": "e4cbcb5d0652150d40ad0646651076b6bd2be4f6", 21 | "revisionTime": "2017-10-14T05:53:35Z" 22 | }, 23 | { 24 | "checksumSHA1": "7QcjNTtW0Rc/5BPw1MPaQ6YNjso=", 25 | "path": "github.com/go-playground/universal-translator", 26 | "revision": "71201497bace774495daed26a3874fd339e0b538", 27 | "revisionTime": "2017-03-27T19:17:03Z" 28 | }, 29 | { 30 | "path": "github.com/go-playground/validator", 31 | "revision": "" 32 | }, 33 | { 34 | "path": "github.com/go-playground/validator.v9", 35 | "revision": "" 36 | }, 37 | { 38 | "checksumSHA1": "TQoVgHqUD72/5ALzi9p5W3oyaug=", 39 | "path": "golang.org/x/crypto/ripemd160", 40 | "revision": "182114d582623c1caa54f73de9c7224e23a48487", 41 | "revisionTime": "2018-03-12T18:51:34Z" 42 | }, 43 | { 44 | "checksumSHA1": "rzUabaeHS5A1JgT8+zGqy8MSeag=", 45 | "path": "gopkg.in/go-playground/validator.v9", 46 | "revision": "8ce234ff024d85b3848e485decba806385d6e276", 47 | "revisionTime": "2018-03-19T15:47:08Z" 48 | } 49 | ], 50 | "rootPath": "github.com/KleeTaurus/go-trustsql-sdk" 51 | } 52 | --------------------------------------------------------------------------------