256 lines
6.3 KiB
Go
256 lines
6.3 KiB
Go
|
|
package helper
|
||
|
|
|
||
|
|
import (
|
||
|
|
"crypto"
|
||
|
|
"crypto/hmac"
|
||
|
|
"crypto/rand"
|
||
|
|
"crypto/rsa"
|
||
|
|
"crypto/sha256"
|
||
|
|
"crypto/tls"
|
||
|
|
"crypto/x509"
|
||
|
|
"encoding/base64"
|
||
|
|
"encoding/json"
|
||
|
|
"encoding/pem"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"io/ioutil"
|
||
|
|
"net/http"
|
||
|
|
"sort"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// BitgetClient Bitget HTTP客户端
|
||
|
|
type BitgetClient struct {
|
||
|
|
APIKey string
|
||
|
|
APISecret string
|
||
|
|
Passphrase string
|
||
|
|
HTTPClient *http.Client
|
||
|
|
BaseURL string
|
||
|
|
SignType string
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewBitgetClient 创建新的Bitget客户端
|
||
|
|
func NewBitgetClient(apiKey, apiSecret, passphrase string, proxyUrl string) (*BitgetClient, error) {
|
||
|
|
var proxyType, proxyAddr string
|
||
|
|
if proxyUrl != "" {
|
||
|
|
if strings.HasPrefix(proxyUrl, "http://") {
|
||
|
|
proxyType = "http"
|
||
|
|
proxyAddr = proxyUrl
|
||
|
|
} else if strings.HasPrefix(proxyUrl, "https://") {
|
||
|
|
proxyType = "https"
|
||
|
|
proxyAddr = proxyUrl
|
||
|
|
} else if strings.HasPrefix(proxyUrl, "socks5://") {
|
||
|
|
proxyType = "socks5"
|
||
|
|
proxyAddr = proxyUrl
|
||
|
|
} else {
|
||
|
|
// 默认为http代理
|
||
|
|
proxyType = "http"
|
||
|
|
proxyAddr = "http://" + proxyUrl
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// 创建HTTP客户端
|
||
|
|
client := &http.Client{
|
||
|
|
Transport: &http.Transport{
|
||
|
|
MaxIdleConns: 1000,
|
||
|
|
IdleConnTimeout: 10 * time.Second,
|
||
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
// 设置代理
|
||
|
|
if proxyAddr != "" {
|
||
|
|
err := CreateHtppProxy(proxyType, proxyAddr, client)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return &BitgetClient{
|
||
|
|
APIKey: apiKey,
|
||
|
|
APISecret: apiSecret,
|
||
|
|
Passphrase: passphrase,
|
||
|
|
HTTPClient: client,
|
||
|
|
BaseURL: "https://api.bitget.com",
|
||
|
|
SignType: "SHA256",
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// DoGet 执行GET请求
|
||
|
|
func (bc *BitgetClient) DoGet(uri string, params map[string]string) ([]byte, error) {
|
||
|
|
timestamp := bc.getTimestamp()
|
||
|
|
body := bc.buildGetParams(params)
|
||
|
|
|
||
|
|
sign := bc.sign("GET", uri, body, timestamp)
|
||
|
|
requestURL := bc.BaseURL + uri + body
|
||
|
|
|
||
|
|
request, err := http.NewRequest("GET", requestURL, nil)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
bc.setHeaders(request, timestamp, sign)
|
||
|
|
|
||
|
|
response, err := bc.HTTPClient.Do(request)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
defer response.Body.Close()
|
||
|
|
|
||
|
|
return ioutil.ReadAll(response.Body)
|
||
|
|
}
|
||
|
|
|
||
|
|
// DoPost 执行POST请求
|
||
|
|
func (bc *BitgetClient) DoPost(uri string, params interface{}) ([]byte, error) {
|
||
|
|
timestamp := bc.getTimestamp()
|
||
|
|
|
||
|
|
var body string
|
||
|
|
if params != nil {
|
||
|
|
bodyBytes, err := json.Marshal(params)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
body = string(bodyBytes)
|
||
|
|
}
|
||
|
|
|
||
|
|
sign := bc.sign("POST", uri, body, timestamp)
|
||
|
|
requestURL := bc.BaseURL + uri
|
||
|
|
|
||
|
|
request, err := http.NewRequest("POST", requestURL, strings.NewReader(body))
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
bc.setHeaders(request, timestamp, sign)
|
||
|
|
|
||
|
|
response, err := bc.HTTPClient.Do(request)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
defer response.Body.Close()
|
||
|
|
|
||
|
|
return ioutil.ReadAll(response.Body)
|
||
|
|
}
|
||
|
|
|
||
|
|
// getTimestamp 获取时间戳
|
||
|
|
func (bc *BitgetClient) getTimestamp() string {
|
||
|
|
return strconv.FormatInt(time.Now().Unix()*1000, 10)
|
||
|
|
}
|
||
|
|
|
||
|
|
// setHeaders 设置请求头
|
||
|
|
func (bc *BitgetClient) setHeaders(request *http.Request, timestamp, sign string) {
|
||
|
|
request.Header.Add("Content-Type", "application/json")
|
||
|
|
request.Header.Add("ACCESS-KEY", bc.APIKey)
|
||
|
|
request.Header.Add("ACCESS-SIGN", sign)
|
||
|
|
request.Header.Add("ACCESS-TIMESTAMP", timestamp)
|
||
|
|
request.Header.Add("ACCESS-PASSPHRASE", bc.Passphrase)
|
||
|
|
}
|
||
|
|
|
||
|
|
// sign 生成签名
|
||
|
|
func (bc *BitgetClient) sign(method, requestPath, body, timestamp string) string {
|
||
|
|
var payload strings.Builder
|
||
|
|
payload.WriteString(timestamp)
|
||
|
|
payload.WriteString(method)
|
||
|
|
payload.WriteString(requestPath)
|
||
|
|
if body != "" && body != "?" {
|
||
|
|
payload.WriteString(body)
|
||
|
|
}
|
||
|
|
|
||
|
|
if bc.SignType == "RSA" {
|
||
|
|
return bc.signByRSA(payload.String())
|
||
|
|
}
|
||
|
|
|
||
|
|
return bc.signByHMAC(payload.String())
|
||
|
|
}
|
||
|
|
|
||
|
|
// signByHMAC HMAC签名
|
||
|
|
func (bc *BitgetClient) signByHMAC(payload string) string {
|
||
|
|
hash := hmac.New(sha256.New, []byte(bc.APISecret))
|
||
|
|
hash.Write([]byte(payload))
|
||
|
|
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
|
||
|
|
}
|
||
|
|
|
||
|
|
// signByRSA RSA签名
|
||
|
|
func (bc *BitgetClient) signByRSA(payload string) string {
|
||
|
|
sign, _ := bc.rsaSign([]byte(payload), []byte(bc.APISecret), crypto.SHA256)
|
||
|
|
return base64.StdEncoding.EncodeToString(sign)
|
||
|
|
}
|
||
|
|
|
||
|
|
// rsaSign RSA签名实现
|
||
|
|
func (bc *BitgetClient) rsaSign(src []byte, priKey []byte, hash crypto.Hash) ([]byte, error) {
|
||
|
|
block, _ := pem.Decode(priKey)
|
||
|
|
if block == nil {
|
||
|
|
return nil, errors.New("key is invalid format")
|
||
|
|
}
|
||
|
|
|
||
|
|
var pkixPrivateKey interface{}
|
||
|
|
var err error
|
||
|
|
if block.Type == "RSA PRIVATE KEY" {
|
||
|
|
pkixPrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||
|
|
} else if block.Type == "PRIVATE KEY" {
|
||
|
|
pkixPrivateKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
h := hash.New()
|
||
|
|
_, err = h.Write(src)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
bytes := h.Sum(nil)
|
||
|
|
sign, err := rsa.SignPKCS1v15(rand.Reader, pkixPrivateKey.(*rsa.PrivateKey), hash, bytes)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return sign, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// buildGetParams 构建GET请求参数
|
||
|
|
func (bc *BitgetClient) buildGetParams(params map[string]string) string {
|
||
|
|
if len(params) == 0 {
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
return "?" + bc.sortParams(params)
|
||
|
|
}
|
||
|
|
|
||
|
|
// sortParams 参数排序
|
||
|
|
func (bc *BitgetClient) sortParams(params map[string]string) string {
|
||
|
|
keys := make([]string, 0, len(params))
|
||
|
|
for k := range params {
|
||
|
|
keys = append(keys, k)
|
||
|
|
}
|
||
|
|
sort.Strings(keys)
|
||
|
|
|
||
|
|
sorted := make([]string, 0, len(params))
|
||
|
|
for _, k := range keys {
|
||
|
|
sorted = append(sorted, k+"="+params[k])
|
||
|
|
}
|
||
|
|
return strings.Join(sorted, "&")
|
||
|
|
}
|
||
|
|
|
||
|
|
// SendSpotRequestAuth 发送现货认证请求
|
||
|
|
func (bc *BitgetClient) SendSpotRequestAuth(endpoint, method string, params map[string]interface{}) ([]byte, *http.Response, error) {
|
||
|
|
if method == "GET" {
|
||
|
|
paramStr := make(map[string]string)
|
||
|
|
for k, v := range params {
|
||
|
|
paramStr[k] = fmt.Sprintf("%v", v)
|
||
|
|
}
|
||
|
|
resp, err := bc.DoGet(endpoint, paramStr)
|
||
|
|
return resp, nil, err
|
||
|
|
} else {
|
||
|
|
resp, err := bc.DoPost(endpoint, params)
|
||
|
|
return resp, nil, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// SendFuturesRequestAuth 发送合约认证请求
|
||
|
|
func (bc *BitgetClient) SendFuturesRequestAuth(endpoint, method string, params map[string]interface{}) ([]byte, *http.Response, error) {
|
||
|
|
return bc.SendSpotRequestAuth(endpoint, method, params)
|
||
|
|
}
|