Skip to content

Commit 16c3c0e

Browse files
authored
Merge branch 'adshao:master' into master
2 parents 1a88f5f + c2c4643 commit 16c3c0e

File tree

6 files changed

+202
-49
lines changed

6 files changed

+202
-49
lines changed

v2/client.go

+22-12
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package binance
33
import (
44
"bytes"
55
"context"
6-
"crypto/hmac"
7-
"crypto/sha256"
86
"crypto/tls"
97
"fmt"
108
"io/ioutil"
@@ -323,6 +321,7 @@ func NewClient(apiKey, secretKey string) *Client {
323321
return &Client{
324322
APIKey: apiKey,
325323
SecretKey: secretKey,
324+
KeyType: common.KeyTypeHmac,
326325
BaseURL: getAPIEndpoint(),
327326
UserAgent: "Binance/golang",
328327
HTTPClient: http.DefaultClient,
@@ -343,6 +342,7 @@ func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client {
343342
return &Client{
344343
APIKey: apiKey,
345344
SecretKey: secretKey,
345+
KeyType: common.KeyTypeHmac,
346346
BaseURL: getAPIEndpoint(),
347347
UserAgent: "Binance/golang",
348348
HTTPClient: &http.Client{
@@ -373,6 +373,7 @@ type doFunc func(req *http.Request) (*http.Response, error)
373373
type Client struct {
374374
APIKey string
375375
SecretKey string
376+
KeyType string
376377
BaseURL string
377378
UserAgent string
378379
HTTPClient *http.Client
@@ -419,16 +420,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
419420
if r.secType == secTypeAPIKey || r.secType == secTypeSigned {
420421
header.Set("X-MBX-APIKEY", c.APIKey)
421422
}
422-
423+
kt := c.KeyType
424+
if kt == "" {
425+
kt = common.KeyTypeHmac
426+
}
427+
sf, err := common.SignFunc(kt)
428+
if err != nil {
429+
return err
430+
}
423431
if r.secType == secTypeSigned {
424432
raw := fmt.Sprintf("%s%s", queryString, bodyString)
425-
mac := hmac.New(sha256.New, []byte(c.SecretKey))
426-
_, err = mac.Write([]byte(raw))
433+
sign, err := sf(c.SecretKey, raw)
427434
if err != nil {
428435
return err
429436
}
430437
v := url.Values{}
431-
v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil))))
438+
v.Set(signatureKey, *sign)
432439
if queryString == "" {
433440
queryString = v.Encode()
434441
} else {
@@ -438,7 +445,7 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
438445
if queryString != "" {
439446
fullURL = fmt.Sprintf("%s?%s", fullURL, queryString)
440447
}
441-
c.debug("full url: %s, body: %s", fullURL, bodyString)
448+
c.debug("full url: %s, body: %s\n", fullURL, bodyString)
442449

443450
r.fullURL = fullURL
444451
r.header = header
@@ -457,7 +464,7 @@ func (c *Client) callAPI(ctx context.Context, r *request, opts ...RequestOption)
457464
}
458465
req = req.WithContext(ctx)
459466
req.Header = r.header
460-
c.debug("request: %#v", req)
467+
c.debug("request: %#v\n", req)
461468
f := c.do
462469
if f == nil {
463470
f = c.HTTPClient.Do
@@ -478,15 +485,18 @@ func (c *Client) callAPI(ctx context.Context, r *request, opts ...RequestOption)
478485
err = cerr
479486
}
480487
}()
481-
c.debug("response: %#v", res)
482-
c.debug("response body: %s", string(data))
483-
c.debug("response status code: %d", res.StatusCode)
488+
c.debug("response: %#v\n", res)
489+
c.debug("response body: %s\n", string(data))
490+
c.debug("response status code: %d\n", res.StatusCode)
484491

485492
if res.StatusCode >= http.StatusBadRequest {
486493
apiErr := new(common.APIError)
487494
e := json.Unmarshal(data, apiErr)
488495
if e != nil {
489-
c.debug("failed to unmarshal json: %s", e)
496+
c.debug("failed to unmarshal json: %s\n", e)
497+
}
498+
if !apiErr.IsValid() {
499+
apiErr.Response = data
490500
}
491501
return nil, apiErr
492502
}

v2/common/errors.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,21 @@ import (
66

77
// APIError define API error when response status is 4xx or 5xx
88
type APIError struct {
9-
Code int64 `json:"code"`
10-
Message string `json:"msg"`
9+
Code int64 `json:"code"`
10+
Message string `json:"msg"`
11+
Response []byte `json:"-"` // Assign the body value when the Code and Message fields are invalid.
1112
}
1213

1314
// Error return error code and message
1415
func (e APIError) Error() string {
15-
return fmt.Sprintf("<APIError> code=%d, msg=%s", e.Code, e.Message)
16+
if e.IsValid() {
17+
return fmt.Sprintf("<APIError> code=%d, msg=%s", e.Code, e.Message)
18+
}
19+
return fmt.Sprintf("<APIError> rsp=%s", string(e.Response))
20+
}
21+
22+
func (e APIError) IsValid() bool {
23+
return e.Code != 0 || e.Message != ""
1624
}
1725

1826
// IsAPIError check if e is an API error

v2/common/sign.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package common
2+
3+
import (
4+
"crypto"
5+
"crypto/ed25519"
6+
"crypto/hmac"
7+
"crypto/rand"
8+
"crypto/rsa"
9+
"crypto/sha256"
10+
"crypto/x509"
11+
"encoding/base64"
12+
"encoding/pem"
13+
"errors"
14+
"fmt"
15+
)
16+
17+
const (
18+
KeyTypeHmac = "HMAC"
19+
KeyTypeRsa = "RSA"
20+
KeyTypeEd25519 = "ED25519"
21+
)
22+
23+
func SignFunc(keyType string) (func(string, string) (*string, error), error) {
24+
switch {
25+
case keyType == KeyTypeHmac:
26+
return Hmac, nil
27+
case keyType == KeyTypeRsa:
28+
return Rsa, nil
29+
case keyType == KeyTypeEd25519:
30+
return Ed25519, nil
31+
default:
32+
return nil, fmt.Errorf("unsupported keyType=%s", keyType)
33+
}
34+
}
35+
36+
func Hmac(secretKey string, data string) (*string, error) {
37+
mac := hmac.New(sha256.New, []byte(secretKey))
38+
_, err := mac.Write([]byte(data))
39+
if err != nil {
40+
return nil, err
41+
}
42+
encodeData := fmt.Sprintf("%x", (mac.Sum(nil)))
43+
return &encodeData, nil
44+
}
45+
46+
func Rsa(secretKey string, data string) (*string, error) {
47+
block, _ := pem.Decode([]byte(secretKey))
48+
if block == nil {
49+
return nil, errors.New("Rsa pem.Decode failed, invalid pem format secretKey")
50+
}
51+
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
52+
if err != nil {
53+
return nil, fmt.Errorf("Rsa ParsePKCS8PrivateKey failed, error=%v", err.Error())
54+
}
55+
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
56+
if !ok {
57+
return nil, fmt.Errorf("Rsa convert PrivateKey failed")
58+
}
59+
hashed := sha256.Sum256([]byte(data))
60+
signature, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey, crypto.SHA256, hashed[:])
61+
if err != nil {
62+
return nil, err
63+
}
64+
encodedSignature := base64.StdEncoding.EncodeToString(signature)
65+
return &encodedSignature, nil
66+
}
67+
68+
func Ed25519(secretKey string, data string) (*string, error) {
69+
block, _ := pem.Decode([]byte(secretKey))
70+
if block == nil {
71+
return nil, fmt.Errorf("Ed25519 pem.Decode failed, invalid pem format secretKey")
72+
}
73+
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
74+
if err != nil {
75+
return nil, fmt.Errorf("Ed25519 call ParsePKCS8PrivateKey failed, error=%v", err.Error())
76+
}
77+
ed25519PrivateKey, ok := privateKey.(ed25519.PrivateKey)
78+
if !ok {
79+
return nil, fmt.Errorf("Ed25519 convert PrivateKey failed")
80+
}
81+
pk := ed25519.PrivateKey(ed25519PrivateKey)
82+
signature := ed25519.Sign(pk, []byte(data))
83+
encodedSignature := base64.StdEncoding.EncodeToString(signature)
84+
return &encodedSignature, nil
85+
}

v2/delivery/client.go

+44-12
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ package delivery
33
import (
44
"bytes"
55
"context"
6-
"crypto/hmac"
7-
"crypto/sha256"
6+
"crypto/tls"
87
"encoding/json"
98
"fmt"
109
"github.com/adshao/go-binance/v2/common"
@@ -190,19 +189,43 @@ func NewClient(apiKey, secretKey string) *Client {
190189
return &Client{
191190
APIKey: apiKey,
192191
SecretKey: secretKey,
192+
KeyType: common.KeyTypeHmac,
193193
BaseURL: getApiEndpoint(),
194194
UserAgent: "Binance/golang",
195195
HTTPClient: http.DefaultClient,
196196
Logger: log.New(os.Stderr, "Binance-golang ", log.LstdFlags),
197197
}
198198
}
199199

200+
func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client {
201+
proxy, err := url.Parse(proxyUrl)
202+
if err != nil {
203+
log.Fatal(err)
204+
}
205+
tr := &http.Transport{
206+
Proxy: http.ProxyURL(proxy),
207+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
208+
}
209+
return &Client{
210+
APIKey: apiKey,
211+
SecretKey: secretKey,
212+
KeyType: common.KeyTypeHmac,
213+
BaseURL: getApiEndpoint(),
214+
UserAgent: "Binance/golang",
215+
HTTPClient: &http.Client{
216+
Transport: tr,
217+
},
218+
Logger: log.New(os.Stderr, "Binance-golang ", log.LstdFlags),
219+
}
220+
}
221+
200222
type doFunc func(req *http.Request) (*http.Response, error)
201223

202224
// Client define API client
203225
type Client struct {
204226
APIKey string
205227
SecretKey string
228+
KeyType string
206229
BaseURL string
207230
UserAgent string
208231
HTTPClient *http.Client
@@ -249,16 +272,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
249272
if r.secType == secTypeAPIKey || r.secType == secTypeSigned {
250273
header.Set("X-MBX-APIKEY", c.APIKey)
251274
}
252-
275+
kt := c.KeyType
276+
if kt == "" {
277+
kt = common.KeyTypeHmac
278+
}
279+
sf, err := common.SignFunc(kt)
280+
if err != nil {
281+
return err
282+
}
253283
if r.secType == secTypeSigned {
254284
raw := fmt.Sprintf("%s%s", queryString, bodyString)
255-
mac := hmac.New(sha256.New, []byte(c.SecretKey))
256-
_, err = mac.Write([]byte(raw))
285+
sign, err := sf(c.SecretKey, raw)
257286
if err != nil {
258287
return err
259288
}
260289
v := url.Values{}
261-
v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil))))
290+
v.Set(signatureKey, *sign)
262291
if queryString == "" {
263292
queryString = v.Encode()
264293
} else {
@@ -268,7 +297,7 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
268297
if queryString != "" {
269298
fullURL = fmt.Sprintf("%s?%s", fullURL, queryString)
270299
}
271-
c.debug("full url: %s, body: %s", fullURL, bodyString)
300+
c.debug("full url: %s, body: %s\n", fullURL, bodyString)
272301

273302
r.fullURL = fullURL
274303
r.header = header
@@ -287,7 +316,7 @@ func (c *Client) callAPI(ctx context.Context, r *request, opts ...RequestOption)
287316
}
288317
req = req.WithContext(ctx)
289318
req.Header = r.header
290-
c.debug("request: %#v", req)
319+
c.debug("request: %#v\n", req)
291320
f := c.do
292321
if f == nil {
293322
f = c.HTTPClient.Do
@@ -308,15 +337,18 @@ func (c *Client) callAPI(ctx context.Context, r *request, opts ...RequestOption)
308337
err = cerr
309338
}
310339
}()
311-
c.debug("response: %#v", res)
312-
c.debug("response body: %s", string(data))
313-
c.debug("response status code: %d", res.StatusCode)
340+
c.debug("response: %#v\n", res)
341+
c.debug("response body: %s\n", string(data))
342+
c.debug("response status code: %d\n", res.StatusCode)
314343

315344
if res.StatusCode >= http.StatusBadRequest {
316345
apiErr := new(common.APIError)
317346
e := json.Unmarshal(data, apiErr)
318347
if e != nil {
319-
c.debug("failed to unmarshal json: %s", e)
348+
c.debug("failed to unmarshal json: %s\n", e)
349+
}
350+
if !apiErr.IsValid() {
351+
apiErr.Response = data
320352
}
321353
return nil, apiErr
322354
}

0 commit comments

Comments
 (0)