Update request.go to fix concurrency nonce issues (#285)

* Updates nonce generation to adhere to fifo channel buffer before request executes by routine

* removed unused variables, lns etc

* Fix requested changes and added in timer that disengages lock if out of scope error occurs

* Fixed woopsy daisy issue

* Add benchmark, reduce time in force to unlock before stack insertion, add nil check for edge case

* Remove unusued waitgroup field

* use return nonce.Value and method, rm redundant nonce code, fix tests.

* Fix linter issue: unnecessary conversion
This commit is contained in:
Ryan O'Hara-Reid
2019-05-06 13:46:34 +10:00
committed by Adrian Gallagher
parent 1967507d40
commit 35b94268e0
46 changed files with 312 additions and 258 deletions

View File

@@ -730,6 +730,7 @@ func (c *Coinmarketcap) SendHTTPRequest(method, endpoint string, v url.Values, r
strings.NewReader(""),
result,
false,
false,
c.Verbose)
}

View File

@@ -190,6 +190,7 @@ func (c *CurrencyConverter) SendHTTPRequest(endPoint string, values url.Values,
nil,
&result,
auth,
false,
c.Verbose)
if err != nil {
return fmt.Errorf("currency converter API SendHTTPRequest error %s with path %s",

View File

@@ -239,5 +239,6 @@ func (c *CurrencyLayer) SendHTTPRequest(endPoint string, values url.Values, resu
nil,
&result,
auth,
false,
c.Verbose)
}

View File

@@ -176,6 +176,7 @@ func (e *ExchangeRates) SendHTTPRequest(endPoint string, values url.Values, resu
nil,
&result,
false,
false,
e.Verbose)
if err != nil {
return fmt.Errorf("exchangeRatesAPI SendHTTPRequest error %s with path %s",

View File

@@ -263,5 +263,6 @@ func (f *Fixer) SendOpenHTTPRequest(endpoint string, v url.Values, result interf
nil,
result,
auth,
false,
f.Verbose)
}

View File

@@ -2,6 +2,8 @@ package fixer
import (
"testing"
"github.com/thrasher-/gocryptotrader/currency/forexprovider/base"
)
// Please set API key and apikey subscription level for correct due diligence
@@ -14,7 +16,20 @@ const (
var f Fixer
var isSetup bool
func setup(t *testing.T) {
if !isSetup {
err := f.Setup(base.Settings{})
if err != nil {
t.Fatal("Test Failed - Setup error", err)
}
isSetup = true
}
}
func TestGetRates(t *testing.T) {
setup(t)
_, err := f.GetRates("EUR", "AUD")
if err == nil {
t.Error("test failed - fixer GetRates() error", err)
@@ -22,6 +37,7 @@ func TestGetRates(t *testing.T) {
}
func TestGetLatestRates(t *testing.T) {
setup(t)
_, err := f.GetLatestRates("EUR", "AUD")
if err == nil {
t.Error("test failed - fixer GetLatestRates() error", err)
@@ -29,6 +45,7 @@ func TestGetLatestRates(t *testing.T) {
}
func TestGetHistoricalRates(t *testing.T) {
setup(t)
_, err := f.GetHistoricalRates("2013-12-24", "EUR", []string{"AUD,KRW"})
if err == nil {
t.Error("test failed - fixer GetHistoricalRates() error", err)
@@ -36,6 +53,7 @@ func TestGetHistoricalRates(t *testing.T) {
}
func TestConvertCurrency(t *testing.T) {
setup(t)
_, err := f.ConvertCurrency("AUD", "EUR", "", 1337)
if err == nil {
t.Error("test failed - fixer ConvertCurrency() error", err)
@@ -43,6 +61,7 @@ func TestConvertCurrency(t *testing.T) {
}
func TestGetTimeSeriesData(t *testing.T) {
setup(t)
_, err := f.GetTimeSeriesData("2013-12-24", "2013-12-25", "EUR", []string{"AUD,KRW"})
if err == nil {
t.Error("test failed - fixer GetTimeSeriesData() error", err)
@@ -50,6 +69,7 @@ func TestGetTimeSeriesData(t *testing.T) {
}
func TestGetFluctuationData(t *testing.T) {
setup(t)
_, err := f.GetFluctuationData("2013-12-24", "2013-12-25", "EUR", []string{"AUD,KRW"})
if err == nil {
t.Error("test failed - fixer GetFluctuationData() error", err)

View File

@@ -263,5 +263,6 @@ func (o *OXR) SendHTTPRequest(endpoint string, values url.Values, result interfa
nil,
result,
false,
false,
o.Verbose)
}

View File

@@ -534,7 +534,7 @@ func (a *Alphapoint) SendHTTPRequest(method, path string, data map[string]interf
return errors.New("unable to JSON request")
}
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, false, a.Verbose)
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, false, false, a.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated request
@@ -543,17 +543,14 @@ func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, a.Name)
}
if a.Nonce.Get() == 0 {
a.Nonce.Set(time.Now().UnixNano())
} else {
a.Nonce.Inc()
}
n := a.Requester.GetNonce(true)
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
data["apiKey"] = a.APIKey
data["apiNonce"] = a.Nonce.Get()
hmac := common.GetHMAC(common.HashSHA256, []byte(a.Nonce.String()+a.ClientID+a.APIKey), []byte(a.APISecret))
data["apiNonce"] = n
hmac := common.GetHMAC(common.HashSHA256, []byte(n.String()+a.ClientID+a.APIKey),
[]byte(a.APISecret))
data["apiSig"] = common.StringToUpper(common.HexEncodeToString(hmac))
path = fmt.Sprintf("%s/ajax/v%s/%s", a.APIUrl, alphapointAPIVersion, path)
@@ -562,5 +559,5 @@ func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[
return errors.New("unable to JSON request")
}
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, true, a.Verbose)
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, true, true, a.Verbose)
}

View File

@@ -402,7 +402,7 @@ func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (
// SendHTTPRequest sends an unauthenticated HTTP request
func (a *ANX) SendHTTPRequest(path string, result interface{}) error {
return a.SendPayload(http.MethodGet, path, nil, nil, result, false, a.Verbose)
return a.SendPayload(http.MethodGet, path, nil, nil, result, false, false, a.Verbose)
}
// SendAuthenticatedHTTPRequest sends a authenticated HTTP request
@@ -411,14 +411,9 @@ func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interf
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, a.Name)
}
if a.Nonce.Get() == 0 {
a.Nonce.Set(time.Now().UnixNano())
} else {
a.Nonce.Inc()
}
n := a.Requester.GetNonce(true)
req := make(map[string]interface{})
req["nonce"] = a.Nonce.String()[0:13]
req["nonce"] = n.String()[0:13]
path = fmt.Sprintf("api/%s/%s", anxAPIVersion, path)
for key, value := range params {
@@ -440,7 +435,7 @@ func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interf
headers["Rest-Sign"] = common.Base64Encode(hmac)
headers["Content-Type"] = "application/json"
return a.SendPayload(http.MethodPost, a.APIUrl+path, headers, bytes.NewBuffer(PayloadJSON), result, true, a.Verbose)
return a.SendPayload(http.MethodPost, a.APIUrl+path, headers, bytes.NewBuffer(PayloadJSON), result, true, true, a.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -594,7 +594,7 @@ func (b *Binance) GetAccount() (*Account, error) {
// SendHTTPRequest sends an unauthenticated request
func (b *Binance) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, b.Verbose)
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose)
}
// SendAuthHTTPRequest sends an authenticated HTTP request
@@ -630,7 +630,7 @@ func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, re
Message string `json:"msg"`
}{}
err := b.SendPayload(method, path, headers, bytes.NewBuffer(nil), &interim, true, b.Verbose)
err := b.SendPayload(method, path, headers, bytes.NewBuffer(nil), &interim, true, false, b.Verbose)
if err != nil {
return err
}

View File

@@ -616,6 +616,7 @@ func (b *Bitfinex) WithdrawCryptocurrency(withdrawType, wallet, address, payment
&response)
}
// WithdrawFIAT requests a withdrawal from a designated fiat wallet
func (b *Bitfinex) WithdrawFIAT(withdrawalType, walletType string, withdrawRequest *exchange.WithdrawRequest) ([]Withdrawal, error) {
response := []Withdrawal{}
req := make(map[string]interface{})
@@ -1000,7 +1001,7 @@ func (b *Bitfinex) CloseMarginFunding(swapID int64) (Offer, error) {
// SendHTTPRequest sends an unauthenticated request
func (b *Bitfinex) SendHTTPRequest(path string, result interface{}, verbose bool) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, verbose)
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, verbose)
}
// SendAuthenticatedHTTPRequest sends an autheticated http request and json
@@ -1011,15 +1012,11 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[
b.Name)
}
if b.Nonce.Get() == 0 {
b.Nonce.Set(time.Now().UnixNano())
} else {
b.Nonce.Inc()
}
n := b.Requester.GetNonce(true)
req := make(map[string]interface{})
req["request"] = fmt.Sprintf("%s%s", bitfinexAPIVersion, path)
req["nonce"] = b.Nonce.String()
req["nonce"] = n.String()
for key, value := range params {
req[key] = value
@@ -1048,6 +1045,7 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[
nil,
result,
true,
true,
b.Verbose)
}

View File

@@ -378,7 +378,7 @@ func (b *Bitflyer) GetTradingCommission() {
// SendHTTPRequest sends an unauthenticated request
func (b *Bitflyer) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, b.Verbose)
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose)
}
// SendAuthHTTPRequest sends an authenticated HTTP request

View File

@@ -543,7 +543,7 @@ func (b *Bithumb) MarketSellOrder(currency string, units float64) (MarketSell, e
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bithumb) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, b.Verbose)
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to bithumb
@@ -556,15 +556,11 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, r
params = url.Values{}
}
if b.Nonce.Get() == 0 {
b.Nonce.Set(time.Now().UnixNano() / int64(time.Millisecond))
} else {
b.Nonce.Inc()
}
n := b.Requester.GetNonceMilli().String()
params.Set("endpoint", path)
payload := params.Encode()
hmacPayload := path + string(0) + payload + string(0) + b.Nonce.String()
hmacPayload := path + string(0) + payload + string(0) + n
hmac := common.GetHMAC(common.HashSHA512,
[]byte(hmacPayload),
[]byte(b.APISecret))
@@ -573,7 +569,7 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, r
headers := make(map[string]string)
headers["Api-Key"] = b.APIKey
headers["Api-Sign"] = common.Base64Encode([]byte(hmacStr))
headers["Api-Nonce"] = b.Nonce.String()
headers["Api-Nonce"] = n
headers["Content-Type"] = "application/x-www-form-urlencoded"
var intermediary json.RawMessage
@@ -589,6 +585,7 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, r
bytes.NewBufferString(payload),
&intermediary,
true,
true,
b.Verbose)
if err != nil {
return err

View File

@@ -850,14 +850,14 @@ func (b *Bitmex) SendHTTPRequest(path string, params Parameter, result interface
if err != nil {
return err
}
err = b.SendPayload(http.MethodGet, encodedPath, nil, nil, &respCheck, false, b.Verbose)
err = b.SendPayload(http.MethodGet, encodedPath, nil, nil, &respCheck, false, false, b.Verbose)
if err != nil {
return err
}
return b.CaptureError(respCheck, result)
}
}
err := b.SendPayload(http.MethodGet, path, nil, nil, &respCheck, false, b.Verbose)
err := b.SendPayload(http.MethodGet, path, nil, nil, &respCheck, false, false, b.Verbose)
if err != nil {
return err
}
@@ -907,6 +907,7 @@ func (b *Bitmex) SendAuthenticatedHTTPRequest(verb, path string, params Paramete
bytes.NewBuffer([]byte(payload)),
&respCheck,
true,
false,
b.Verbose)
if err != nil {
return err

View File

@@ -645,7 +645,7 @@ func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount s
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bitstamp) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, b.Verbose)
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated request
@@ -654,19 +654,15 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
if b.Nonce.Get() == 0 {
b.Nonce.Set(time.Now().UnixNano())
} else {
b.Nonce.Inc()
}
n := b.Requester.GetNonce(true).String()
if values == nil {
values = url.Values{}
}
values.Set("key", b.APIKey)
values.Set("nonce", b.Nonce.String())
hmac := common.GetHMAC(common.HashSHA256, []byte(b.Nonce.String()+b.ClientID+b.APIKey), []byte(b.APISecret))
values.Set("nonce", n)
hmac := common.GetHMAC(common.HashSHA256, []byte(n+b.ClientID+b.APIKey), []byte(b.APISecret))
values.Set("signature", common.StringToUpper(common.HexEncodeToString(hmac)))
if v2 {
@@ -691,7 +687,7 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
Error string `json:"error"`
}{}
err := b.SendPayload(http.MethodPost, path, headers, readerValues, &interim, true, b.Verbose)
err := b.SendPayload(http.MethodPost, path, headers, readerValues, &interim, true, true, b.Verbose)
if err != nil {
return err
}

View File

@@ -493,7 +493,7 @@ func (b *Bittrex) GetDepositHistory(currency string) (WithdrawalHistory, error)
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bittrex) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, b.Verbose)
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated http request to a desired
@@ -503,13 +503,10 @@ func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, r
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
if b.Nonce.Get() == 0 {
b.Nonce.Set(time.Now().UnixNano())
} else {
b.Nonce.Inc()
}
n := b.Requester.GetNonce(true).String()
values.Set("apikey", b.APIKey)
values.Set("nonce", b.Nonce.String())
values.Set("nonce", n)
rawQuery := path + "?" + values.Encode()
hmac := common.GetHMAC(
common.HashSHA512, []byte(rawQuery), []byte(b.APISecret),
@@ -517,7 +514,7 @@ func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, r
headers := make(map[string]string)
headers["apisign"] = common.HexEncodeToString(hmac)
return b.SendPayload(http.MethodGet, rawQuery, headers, nil, result, true, b.Verbose)
return b.SendPayload(http.MethodGet, rawQuery, headers, nil, result, true, true, b.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -431,7 +431,7 @@ func (b *BTCMarkets) WithdrawAUD(accountName, accountNumber, bankName, bsbNumber
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *BTCMarkets) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, b.Verbose)
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose)
}
// SendAuthenticatedRequest sends an authenticated HTTP request
@@ -441,11 +441,8 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data, result
b.Name)
}
if b.Nonce.Get() == 0 {
b.Nonce.Set(time.Now().UnixNano())
} else {
b.Nonce.Inc()
}
n := b.Requester.GetNonce(true).String()[0:13]
var req string
payload := []byte("")
@@ -454,9 +451,9 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data, result
if err != nil {
return err
}
req = path + "\n" + b.Nonce.String()[0:13] + "\n" + string(payload)
req = path + "\n" + n + "\n" + string(payload)
} else {
req = path + "\n" + b.Nonce.String()[0:13] + "\n"
req = path + "\n" + n + "\n"
}
hmac := common.GetHMAC(common.HashSHA512,
@@ -474,7 +471,7 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data, result
headers["Accept-Charset"] = "UTF-8"
headers["Content-Type"] = "application/json"
headers["apikey"] = b.APIKey
headers["timestamp"] = b.Nonce.String()[0:13]
headers["timestamp"] = n
headers["signature"] = common.Base64Encode(hmac)
return b.SendPayload(reqType,
@@ -483,6 +480,7 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data, result
bytes.NewBuffer(payload),
result,
true,
true,
b.Verbose)
}

View File

@@ -252,7 +252,7 @@ func (b *BTSE) GetFills(orderID, productID, before, after, limit string) (*Fille
// SendHTTPRequest sends an HTTP request to the desired endpoint
func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}) error {
p := fmt.Sprintf("%s/%s", btseAPIURL, endpoint)
return b.SendPayload(method, p, nil, nil, &result, false, b.Verbose)
return b.SendPayload(method, p, nil, nil, &result, false, false, b.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the desired endpoint
@@ -278,7 +278,7 @@ func (b *BTSE) SendAuthenticatedHTTPRequest(method, endpoint string, req map[str
log.Debugf("Sending %s request to URL %s with params %s\n", method, p, string(payload))
}
return b.SendPayload(method, p, headers, strings.NewReader(string(payload)),
&result, true, b.Verbose)
&result, true, false, b.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -792,7 +792,7 @@ func (c *CoinbasePro) GetTrailingVolume() ([]Volume, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (c *CoinbasePro) SendHTTPRequest(path string, result interface{}) error {
return c.SendPayload(http.MethodGet, path, nil, nil, result, false, c.Verbose)
return c.SendPayload(http.MethodGet, path, nil, nil, result, false, false, c.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP reque
@@ -815,12 +815,12 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(method, path string, params m
}
}
nonce := c.Nonce.GetValue(c.Name, false).String()
message := nonce + method + "/" + path + string(payload)
n := c.Requester.GetNonce(true).String()
message := n + method + "/" + path + string(payload)
hmac := common.GetHMAC(common.HashSHA256, []byte(message), []byte(c.APISecret))
headers := make(map[string]string)
headers["CB-ACCESS-SIGN"] = common.Base64Encode(hmac)
headers["CB-ACCESS-TIMESTAMP"] = nonce
headers["CB-ACCESS-TIMESTAMP"] = n
headers["CB-ACCESS-KEY"] = c.APIKey
headers["CB-ACCESS-PASSPHRASE"] = c.ClientID
headers["Content-Type"] = "application/json"
@@ -831,6 +831,7 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(method, path string, params m
bytes.NewBuffer(payload),
result,
true,
true,
c.Verbose)
}

View File

@@ -338,16 +338,12 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, c.Name)
}
if c.Nonce.Get() == 0 {
c.Nonce.Set(time.Now().Unix())
} else {
c.Nonce.Inc()
}
n := c.Requester.GetNonce(false)
if params == nil {
params = map[string]interface{}{}
}
params["nonce"] = c.Nonce.Get()
params["nonce"] = n
params["request"] = apiRequest
payload, err := common.JSONEncode(params)
@@ -374,6 +370,7 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{
bytes.NewBuffer(payload),
&rawMsg,
authenticated,
true,
c.Verbose)
if err != nil {
return err

View File

@@ -225,7 +225,7 @@ func (c *COINUT) GetNonce() int64 {
c.Nonce.Inc()
}
return c.Nonce.Get()
return int64(c.Nonce.Get())
}
// WsSetInstrumentList fetches instrument list and propagates a local cache

View File

@@ -13,7 +13,6 @@ import (
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/currency"
"github.com/thrasher-/gocryptotrader/exchanges/nonce"
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-/gocryptotrader/exchanges/request"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
@@ -262,7 +261,6 @@ type Base struct {
APIWithdrawPermissions uint32
APIAuthPEMKeySupport bool
APISecret, APIKey, APIAuthPEMKey, ClientID string
Nonce nonce.Nonce
TakerFee, MakerFee, Fee float64
BaseCurrencies currency.Currencies
AvailablePairs currency.Pairs

View File

@@ -371,7 +371,7 @@ func (e *EXMO) GetWalletHistory(date int64) (WalletHistory, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (e *EXMO) SendHTTPRequest(path string, result interface{}) error {
return e.SendPayload(http.MethodGet, path, nil, nil, result, false, e.Verbose)
return e.SendPayload(http.MethodGet, path, nil, nil, result, false, false, e.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -381,12 +381,8 @@ func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Va
e.Name)
}
if e.Nonce.Get() == 0 {
e.Nonce.Set(time.Now().UnixNano())
} else {
e.Nonce.Inc()
}
vals.Set("nonce", e.Nonce.String())
n := e.Requester.GetNonce(true).String()
vals.Set("nonce", n)
payload := vals.Encode()
hash := common.GetHMAC(common.HashSHA512,
@@ -413,6 +409,7 @@ func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Va
strings.NewReader(payload),
result,
true,
true,
e.Verbose)
}

View File

@@ -391,7 +391,7 @@ func (g *Gateio) CancelExistingOrder(orderID int64, symbol string) (bool, error)
// SendHTTPRequest sends an unauthenticated HTTP request
func (g *Gateio) SendHTTPRequest(path string, result interface{}) error {
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, g.Verbose)
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, false, g.Verbose)
}
// CancelAllExistingOrders all orders for a given symbol and side
@@ -484,6 +484,7 @@ func (g *Gateio) SendAuthenticatedHTTPRequest(method, endpoint, param string, re
strings.NewReader(param),
&intermidiary,
true,
false,
g.Verbose)
if err != nil {
return err

View File

@@ -490,7 +490,7 @@ func (g *Gemini) PostHeartbeat() (string, error) {
// SendHTTPRequest sends an unauthenticated request
func (g *Gemini) SendHTTPRequest(path string, result interface{}) error {
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, g.Verbose)
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, false, g.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the
@@ -503,7 +503,7 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
headers := make(map[string]string)
req := make(map[string]interface{})
req["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
req["nonce"] = g.Nonce.GetValue(g.Name, false)
req["nonce"] = g.Requester.GetNonce(true).String()
for key, value := range params {
req[key] = value
@@ -528,7 +528,7 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
headers["X-GEMINI-SIGNATURE"] = common.HexEncodeToString(hmac)
headers["Cache-Control"] = "no-cache"
return g.SendPayload(method, g.APIUrl+"/v1/"+path, headers, strings.NewReader(""), result, true, g.Verbose)
return g.SendPayload(method, g.APIUrl+"/v1/"+path, headers, strings.NewReader(""), result, true, false, g.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -591,7 +591,7 @@ func (h *HitBTC) TransferBalance(currency, from, to string, amount float64) (boo
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HitBTC) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, h.Verbose)
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated http request
@@ -611,6 +611,7 @@ func (h *HitBTC) SendAuthenticatedHTTPRequest(method, endpoint string, values ur
bytes.NewBufferString(values.Encode()),
result,
true,
false,
h.Verbose)
}

View File

@@ -831,7 +831,7 @@ func (h *HUOBI) CancelWithdraw(withdrawID int64) (int64, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBI) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, h.Verbose)
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
@@ -908,7 +908,7 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
body = encoded
}
return h.SendPayload(method, urlPath, headers, bytes.NewReader(body), result, true, h.Verbose)
return h.SendPayload(method, urlPath, headers, bytes.NewReader(body), result, true, false, h.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -826,7 +826,7 @@ func (h *HUOBIHADAX) CancelWithdraw(withdrawID int64) (int64, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBIHADAX) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, h.Verbose)
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose)
}
// SendAuthenticatedHTTPPostRequest sends authenticated requests to the HUOBI API
@@ -853,7 +853,7 @@ func (h *HUOBIHADAX) SendAuthenticatedHTTPPostRequest(method, endpoint, postBody
signatureParams.Set("Signature", common.Base64Encode(hmac))
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.APIUrl, endpoint),
signatureParams)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(postBodyValues), result, true, h.Verbose)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(postBodyValues), result, true, false, h.Verbose)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
@@ -879,7 +879,7 @@ func (h *HUOBIHADAX) SendAuthenticatedHTTPRequest(method, endpoint string, value
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.APIUrl, endpoint),
values)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(""), result, true, h.Verbose)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(""), result, true, false, h.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -343,7 +343,7 @@ func (i *ItBit) WalletTransfer(walletID, sourceWallet, destWallet string, amount
// SendHTTPRequest sends an unauthenticated HTTP request
func (i *ItBit) SendHTTPRequest(path string, result interface{}) error {
return i.SendPayload(http.MethodGet, path, nil, nil, result, false, i.Verbose)
return i.SendPayload(http.MethodGet, path, nil, nil, result, false, false, i.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated request to itBit
@@ -377,22 +377,22 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method, path string, params map[str
}
}
nonce := i.Nonce.GetValue(i.Name, false).String()
n := i.Requester.GetNonce(true).String()
timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
message, err := common.JSONEncode([]string{method, urlPath, string(PayloadJSON), nonce, timestamp})
message, err := common.JSONEncode([]string{method, urlPath, string(PayloadJSON), n, timestamp})
if err != nil {
return err
}
hash := common.GetSHA256([]byte(nonce + string(message)))
hash := common.GetSHA256([]byte(n + string(message)))
hmac := common.GetHMAC(common.HashSHA512, []byte(urlPath+string(hash)), []byte(i.APISecret))
signature := common.Base64Encode(hmac)
headers := make(map[string]string)
headers["Authorization"] = i.ClientID + ":" + signature
headers["X-Auth-Timestamp"] = timestamp
headers["X-Auth-Nonce"] = nonce
headers["X-Auth-Nonce"] = n
headers["Content-Type"] = "application/json"
var intermediary json.RawMessage
@@ -403,7 +403,7 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method, path string, params map[str
RequestID string `json:"requestId"`
}{}
err = i.SendPayload(method, urlPath, headers, bytes.NewBuffer(PayloadJSON), &intermediary, true, i.Verbose)
err = i.SendPayload(method, urlPath, headers, bytes.NewBuffer(PayloadJSON), &intermediary, true, true, i.Verbose)
if err != nil {
return err
}

View File

@@ -926,7 +926,7 @@ func GetError(apiErrors []string) error {
// SendHTTPRequest sends an unauthenticated HTTP requests
func (k *Kraken) SendHTTPRequest(path string, result interface{}) error {
return k.SendPayload(http.MethodGet, path, nil, nil, result, false, k.Verbose)
return k.SendPayload(http.MethodGet, path, nil, nil, result, false, false, k.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -937,13 +937,9 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
}
path := fmt.Sprintf("/%s/private/%s", krakenAPIVersion, method)
if k.Nonce.Get() == 0 {
k.Nonce.Set(time.Now().UnixNano())
} else {
k.Nonce.Inc()
}
params.Set("nonce", k.Nonce.String())
n := k.Requester.GetNonce(true).String()
params.Set("nonce", n)
secret, err := common.Base64Decode(k.APISecret)
if err != nil {
@@ -972,6 +968,7 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
strings.NewReader(encoded),
result,
true,
true,
k.Verbose)
}

View File

@@ -410,7 +410,7 @@ type WebsocketSubscriptionData struct {
Depth int64 `json:"depth,omitempty"` // Optional - depth associated with book subscription in number of levels each side, default 10. Valid Options are: 10, 25, 100, 500, 1000
}
// WebsocketDataResponse holds all data response types
// WebsocketEventResponse holds all data response types
type WebsocketEventResponse struct {
Event string `json:"event"`
Status string `json:"status"`
@@ -422,26 +422,32 @@ type WebsocketEventResponse struct {
WebsocketErrorResponse
}
// WebsocketSubscriptionEventResponse defines a websocket socket event response
type WebsocketSubscriptionEventResponse struct {
ChannelID int64 `json:"channelID"`
}
// WebsocketSubscriptionResponseData defines a websocket subscription response
type WebsocketSubscriptionResponseData struct {
Name string `json:"name"`
}
// WebsocketStatusResponse defines a websocket status response
type WebsocketStatusResponse struct {
ConnectionID float64 `json:"connectionID"`
Version string `json:"version"`
}
// WebsocketDataResponse defines a websocket data type
type WebsocketDataResponse []interface{}
// WebsocketErrorResponse defines a websocket error response
type WebsocketErrorResponse struct {
ErrorMessage string `json:"errorMessage"`
}
// Holds relevant data for channels to identify what we're doing
// WebsocketChannelData Holds relevant data for channels to identify what we're
// doing
type WebsocketChannelData struct {
Subscription string
Pair currency.Pair

View File

@@ -267,7 +267,7 @@ func (k *Kraken) WsHandleDataResponse(response WebsocketDataResponse) {
}
}
// WsHandleDataResponse classifies the WS response and sends to appropriate handler
// WsHandleEventResponse classifies the WS response and sends to appropriate handler
func (k *Kraken) WsHandleEventResponse(response *WebsocketEventResponse) {
switch response.Event {
case krakenWsHeartbeat:
@@ -391,7 +391,7 @@ func getSubscriptionChannelData(id int64) WebsocketChannelData {
return WebsocketChannelData{}
}
// resubscribeToChannel will attempt to unsubscribe and resubscribe to a channel
// ResubscribeToChannel will attempt to unsubscribe and resubscribe to a channel
func (k *Kraken) ResubscribeToChannel(channel string, pair currency.Pair) {
// Kraken WS formats pairs with / but config and REST use -
formattedPair := strings.ToUpper(strings.Replace(pair.String(), "-", "/", 1))

View File

@@ -334,7 +334,7 @@ func (l *LakeBTC) CreateWithdraw(amount float64, accountID string) (Withdraw, er
// SendHTTPRequest sends an unauthenticated http request
func (l *LakeBTC) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, l.Verbose)
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, false, l.Verbose)
}
// SendAuthenticatedHTTPRequest sends an autheticated HTTP request to a LakeBTC
@@ -343,13 +343,9 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
}
if l.Nonce.Get() == 0 {
l.Nonce.Set(time.Now().UnixNano())
} else {
l.Nonce.Inc()
}
n := l.Requester.GetNonce(true).String()
req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s&params=%s", l.Nonce.String(), l.APIKey, method, params)
req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s&params=%s", n, l.APIKey, method, params)
hmac := common.GetHMAC(common.HashSHA1, []byte(req), []byte(l.APISecret))
if l.Verbose {
@@ -371,7 +367,7 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
headers["Authorization"] = "Basic " + common.Base64Encode([]byte(l.APIKey+":"+common.HexEncodeToString(hmac)))
headers["Content-Type"] = "application/json-rpc"
return l.SendPayload(http.MethodPost, l.APIUrl, headers, strings.NewReader(string(data)), result, true, l.Verbose)
return l.SendPayload(http.MethodPost, l.APIUrl, headers, strings.NewReader(string(data)), result, true, true, l.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -726,7 +726,7 @@ func (l *LocalBitcoins) GetOrderbook(currency string) (Orderbook, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (l *LocalBitcoins) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, l.Verbose)
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, false, l.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to
@@ -736,19 +736,15 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, params
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
}
if l.Nonce.Get() == 0 {
l.Nonce.Set(time.Now().UnixNano())
} else {
l.Nonce.Inc()
}
n := l.Requester.GetNonce(true).String()
path = "/api/" + path
encoded := params.Encode()
message := l.Nonce.String() + l.APIKey + path + encoded
message := n + l.APIKey + path + encoded
hmac := common.GetHMAC(common.HashSHA256, []byte(message), []byte(l.APISecret))
headers := make(map[string]string)
headers["Apiauth-Key"] = l.APIKey
headers["Apiauth-Nonce"] = l.Nonce.String()
headers["Apiauth-Nonce"] = n
headers["Apiauth-Signature"] = common.StringToUpper(common.HexEncodeToString(hmac))
headers["Content-Type"] = "application/x-www-form-urlencoded"
@@ -760,7 +756,7 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, params
path += "?" + encoded
}
return l.SendPayload(method, l.APIUrl+path, headers, strings.NewReader(encoded), result, true, l.Verbose)
return l.SendPayload(method, l.APIUrl+path, headers, strings.NewReader(encoded), result, true, true, l.Verbose)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -2,18 +2,12 @@ package nonce
import (
"strconv"
"sync"
"sync/atomic"
"time"
)
// Nonce struct holds the nonce value
type Nonce struct {
// Standard nonce
n int64
// Hash table exclusive exchange specific nonce values
boundedCall map[string]int64
boundedMtx sync.Mutex
}
// Inc increments the nonce value
@@ -22,12 +16,12 @@ func (n *Nonce) Inc() {
}
// Get retrives the nonce value
func (n *Nonce) Get() int64 {
return atomic.LoadInt64(&n.n)
func (n *Nonce) Get() Value {
return Value(atomic.LoadInt64(&n.n))
}
// GetInc increments and returns the value of the nonce
func (n *Nonce) GetInc() int64 {
func (n *Nonce) GetInc() Value {
n.Inc()
return n.Get()
}
@@ -39,34 +33,12 @@ func (n *Nonce) Set(val int64) {
// String returns a string version of the nonce
func (n *Nonce) String() string {
return strconv.FormatInt(n.Get(), 10)
return n.Get().String()
}
// Value is a return type for GetValue
type Value int64
// GetValue returns a nonce value and can be set as a higher precision. Values
// stored in an exchange specific hash table using a single locked call.
func (n *Nonce) GetValue(exchName string, nanoPrecision bool) Value {
n.boundedMtx.Lock()
defer n.boundedMtx.Unlock()
if n.boundedCall == nil {
n.boundedCall = make(map[string]int64)
}
if n.boundedCall[exchName] == 0 {
if nanoPrecision {
n.boundedCall[exchName] = time.Now().UnixNano()
return Value(n.boundedCall[exchName])
}
n.boundedCall[exchName] = time.Now().Unix()
return Value(n.boundedCall[exchName])
}
n.boundedCall[exchName]++
return Value(n.boundedCall[exchName])
}
// String is a Value method that changes format to a string
func (v Value) String() string {
return strconv.FormatInt(int64(v), 10)

View File

@@ -1,7 +1,6 @@
package nonce
import (
"strconv"
"testing"
"time"
)
@@ -10,7 +9,7 @@ func TestInc(t *testing.T) {
var nonce Nonce
nonce.Set(1)
nonce.Inc()
expected := int64(2)
expected := Value(2)
result := nonce.Get()
if result != expected {
t.Errorf("Test failed. Expected %d got %d", expected, result)
@@ -20,7 +19,7 @@ func TestInc(t *testing.T) {
func TestGet(t *testing.T) {
var nonce Nonce
nonce.Set(112321313)
expected := int64(112321313)
expected := Value(112321313)
result := nonce.Get()
if expected != result {
t.Errorf("Test failed. Expected %d got %d", expected, result)
@@ -30,7 +29,7 @@ func TestGet(t *testing.T) {
func TestGetInc(t *testing.T) {
var nonce Nonce
nonce.Set(1)
expected := int64(2)
expected := Value(2)
result := nonce.GetInc()
if expected != result {
t.Errorf("Test failed. Expected %d got %d", expected, result)
@@ -40,7 +39,7 @@ func TestGetInc(t *testing.T) {
func TestSet(t *testing.T) {
var nonce Nonce
nonce.Set(1)
expected := int64(1)
expected := Value(1)
result := nonce.Get()
if expected != result {
t.Errorf("Test failed. Expected %d got %d", expected, result)
@@ -55,30 +54,10 @@ func TestString(t *testing.T) {
if expected != result {
t.Errorf("Test failed. Expected %s got %s", expected, result)
}
}
func TestGetValue(t *testing.T) {
var nonce Nonce
timeNowNano := strconv.FormatInt(time.Now().UnixNano(), 10)
time.Sleep(time.Millisecond * 100)
nValue := nonce.GetValue("dingdong", true).String()
if timeNowNano == nValue {
t.Error("Test failed - GetValue() error, incorrect values")
}
if len(nValue) != 19 {
t.Error("Test failed - GetValue() error, incorrect values")
}
timeNowUnix := nonce.GetValue("dongding", false)
if len(timeNowUnix.String()) != 10 {
t.Error("Test failed - GetValue() error, incorrect values")
}
n := nonce.GetValue("dongding", false)
if n != timeNowUnix+1 {
t.Error("Test failed - GetValue() error, incorrect values")
v := nonce.Get()
if expected != v.String() {
t.Errorf("Test failed. Expected %s got %s", expected, result)
}
}
@@ -94,7 +73,7 @@ func TestNonceConcurrency(t *testing.T) {
time.Sleep(time.Second)
result := nonce.Get()
expected := int64(12312 + 1000)
expected := Value(12312 + 1000)
if expected != result {
t.Errorf("Test failed. Expected %d got %d", expected, result)
}

View File

@@ -632,7 +632,7 @@ func (o *OKGroup) SendHTTPRequest(httpMethod, requestType, requestPath string, d
errCap := errCapFormat{}
errCap.Result = true
err = o.SendPayload(strings.ToUpper(httpMethod), path, headers, bytes.NewBuffer(payload), &intermediary, authenticated, o.Verbose)
err = o.SendPayload(strings.ToUpper(httpMethod), path, headers, bytes.NewBuffer(payload), &intermediary, authenticated, false, o.Verbose)
if err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package poloniex
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -479,14 +480,21 @@ func (p *Poloniex) GetAuthenticatedTradeHistory(start, end, limit int64) (Authen
}
values.Set("currencyPair", "all")
var result AuthenticatedTradeHistoryAll
var result json.RawMessage
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexTradeHistory, values, &result.Data)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexTradeHistory, values, &result)
if err != nil {
return AuthenticatedTradeHistoryAll{}, err
}
return result, nil
var nodata []interface{}
err = json.Unmarshal(result, &nodata)
if err == nil {
return AuthenticatedTradeHistoryAll{}, nil
}
var mainResult AuthenticatedTradeHistoryAll
return mainResult, json.Unmarshal(result, &mainResult.Data)
}
// PlaceOrder places a new order on the exchange
@@ -856,6 +864,7 @@ func (p *Poloniex) SendHTTPRequest(path string, result interface{}) error {
nil,
result,
false,
false,
p.Verbose)
}
@@ -869,13 +878,9 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values
headers["Content-Type"] = "application/x-www-form-urlencoded"
headers["Key"] = p.APIKey
if p.Nonce.Get() == 0 {
p.Nonce.Set(time.Now().UnixNano())
} else {
p.Nonce.Inc()
}
n := p.Requester.GetNonce(true).String()
values.Set("nonce", p.Nonce.String())
values.Set("nonce", n)
values.Set("command", endpoint)
hmac := common.GetHMAC(common.HashSHA512,
@@ -892,6 +897,7 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values
bytes.NewBufferString(values.Encode()),
result,
true,
true,
p.Verbose)
}

View File

@@ -19,25 +19,27 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
p.SetDefaults()
}
var isSetup bool
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
t.Error("Test Failed - Poloniex Setup() init error")
if !isSetup {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
t.Error("Test Failed - Poloniex Setup() init error")
}
poloniexConfig.AuthenticatedAPISupport = true
poloniexConfig.APIKey = apiKey
poloniexConfig.APISecret = apiSecret
p.SetDefaults()
p.Setup(&poloniexConfig)
isSetup = true
}
poloniexConfig.AuthenticatedAPISupport = true
poloniexConfig.APIKey = apiKey
poloniexConfig.APISecret = apiSecret
p.Setup(&poloniexConfig)
}
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := p.GetTicker()
if err != nil {
t.Error("Test faild - Poloniex GetTicker() error")
@@ -45,6 +47,7 @@ func TestGetTicker(t *testing.T) {
}
func TestGetVolume(t *testing.T) {
t.Parallel()
_, err := p.GetVolume()
if err != nil {
t.Error("Test faild - Poloniex GetVolume() error")
@@ -52,6 +55,7 @@ func TestGetVolume(t *testing.T) {
}
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := p.GetOrderbook("BTC_XMR", 50)
if err != nil {
t.Error("Test faild - Poloniex GetOrderbook() error", err)
@@ -59,6 +63,7 @@ func TestGetOrderbook(t *testing.T) {
}
func TestGetTradeHistory(t *testing.T) {
t.Parallel()
_, err := p.GetTradeHistory("BTC_XMR", "", "")
if err != nil {
t.Error("Test faild - Poloniex GetTradeHistory() error", err)
@@ -66,6 +71,7 @@ func TestGetTradeHistory(t *testing.T) {
}
func TestGetChartData(t *testing.T) {
t.Parallel()
_, err := p.GetChartData("BTC_XMR", "1405699200", "1405699400", "300")
if err != nil {
t.Error("Test faild - Poloniex GetChartData() error", err)
@@ -73,6 +79,7 @@ func TestGetChartData(t *testing.T) {
}
func TestGetCurrencies(t *testing.T) {
t.Parallel()
_, err := p.GetCurrencies()
if err != nil {
t.Error("Test faild - Poloniex GetCurrencies() error", err)
@@ -80,6 +87,7 @@ func TestGetCurrencies(t *testing.T) {
}
func TestGetLoanOrders(t *testing.T) {
t.Parallel()
_, err := p.GetLoanOrders("BTC")
if err != nil {
t.Error("Test faild - Poloniex GetLoanOrders() error", err)
@@ -101,6 +109,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
p.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
@@ -115,7 +124,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
var feeBuilder = setFeeBuilder()
@@ -135,14 +144,6 @@ func TestGetFee(t *testing.T) {
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if resp, err := p.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.001), resp)
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
@@ -195,7 +196,8 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := p.FormatWithdrawPermissions()
@@ -206,7 +208,7 @@ func TestFormatWithdrawPermissions(t *testing.T) {
}
func TestGetActiveOrders(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
@@ -222,7 +224,7 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
@@ -248,7 +250,7 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -276,7 +278,7 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -303,7 +305,7 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -334,6 +336,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := p.ModifyOrder(&exchange.ModifyOrder{OrderID: "1337", Price: 1337})
if err == nil {
t.Error("Test Failed - ModifyOrder() error")
@@ -341,7 +344,7 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
@@ -364,7 +367,7 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -380,7 +383,7 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
p.SetDefaults()
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -396,6 +399,9 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() {
_, err := p.GetDepositAddress(currency.DASH, "")
if err != nil {

View File

@@ -163,8 +163,8 @@ type OpenOrdersResponse struct {
Data []Order
}
// AuthentictedTradeHistory holds client trade history information
type AuthentictedTradeHistory struct {
// AuthenticatedTradeHistory holds client trade history information
type AuthenticatedTradeHistory struct {
GlobalTradeID int64 `json:"globalTradeID"`
TradeID int64 `json:"tradeID,string"`
Date string `json:"date"`
@@ -179,12 +179,12 @@ type AuthentictedTradeHistory struct {
// AuthenticatedTradeHistoryAll holds the full client trade history
type AuthenticatedTradeHistoryAll struct {
Data map[string][]AuthentictedTradeHistory
Data map[string][]AuthenticatedTradeHistory
}
// AuthenticatedTradeHistoryResponse is a response type for trade history
type AuthenticatedTradeHistoryResponse struct {
Data []AuthentictedTradeHistory
Data []AuthenticatedTradeHistory
}
// ResultingTrades holds resultant trade information

View File

@@ -13,6 +13,7 @@ import (
"time"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/exchanges/nonce"
log "github.com/thrasher-/gocryptotrader/logger"
)
@@ -36,7 +37,10 @@ type Requester struct {
timeoutRetryAttempts int
m sync.Mutex
Jobs chan Job
disengage chan struct{}
WorkerStarted bool
Nonce nonce.Nonce
fifoLock sync.Mutex
}
// RateLimit struct
@@ -214,6 +218,7 @@ func New(name string, authLimit, unauthLimit *RateLimit, httpRequester *http.Cli
AuthLimit: authLimit,
Name: name,
Jobs: make(chan Job, maxRequestJobs),
disengage: make(chan struct{}, 1),
timeoutRetryAttempts: defaultTimeoutRetryAttempts,
}
}
@@ -391,29 +396,39 @@ func (r *Requester) worker() {
}
// SendPayload handles sending HTTP/HTTPS requests
func (r *Requester) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, verbose bool) error {
func (r *Requester) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, nonceEnabled, verbose bool) error {
if !nonceEnabled {
r.lock()
}
if r == nil || r.Name == "" {
r.unlock()
return errors.New("not initiliased, SetDefaults() called before making request?")
}
if !IsValidMethod(method) {
r.unlock()
return fmt.Errorf("incorrect method supplied %s: supported %s", method, supportedMethods)
}
if path == "" {
r.unlock()
return errors.New("invalid path")
}
req, err := r.checkRequest(method, path, body, headers)
if err != nil {
r.unlock()
return err
}
if !r.RequiresRateLimiter() {
r.unlock()
return r.DoRequest(req, path, body, result, authRequest, verbose)
}
if len(r.Jobs) == maxRequestJobs {
r.unlock()
return errors.New("max request jobs reached")
}
@@ -443,6 +458,7 @@ func (r *Requester) SendPayload(method, path string, headers map[string]string,
log.Debugf("%s request. Attaching new job.", r.Name)
}
r.Jobs <- newJob
r.unlock()
if verbose {
log.Debugf("%s request. Waiting for job to complete.", r.Name)
@@ -455,6 +471,34 @@ func (r *Requester) SendPayload(method, path string, headers map[string]string,
return resp.Error
}
// GetNonce returns a nonce for requests. This locks and enforces concurrent
// nonce FIFO on the buffered job channel
func (r *Requester) GetNonce(isNano bool) nonce.Value {
r.lock()
if r.Nonce.Get() == 0 {
if isNano {
r.Nonce.Set(time.Now().UnixNano())
} else {
r.Nonce.Set(time.Now().Unix())
}
return r.Nonce.Get()
}
r.Nonce.Inc()
return r.Nonce.Get()
}
// GetNonceMilli returns a nonce for requests. This locks and enforces concurrent
// nonce FIFO on the buffered job channel this is for millisecond
func (r *Requester) GetNonceMilli() nonce.Value {
r.lock()
if r.Nonce.Get() == 0 {
r.Nonce.Set(time.Now().UnixNano() / int64(time.Millisecond))
return r.Nonce.Get()
}
r.Nonce.Inc()
return r.Nonce.Get()
}
// SetProxy sets a proxy address to the client transport
func (r *Requester) SetProxy(p *url.URL) error {
if p.String() == "" {
@@ -467,3 +511,33 @@ func (r *Requester) SetProxy(p *url.URL) error {
}
return nil
}
// lock locks and sets up an issue timer, if something errors out of scope it
// automatically unlocks
func (r *Requester) lock() {
if r.disengage == nil {
r.disengage = make(chan struct{}, 1)
}
var wg sync.WaitGroup
r.fifoLock.Lock()
wg.Add(1)
go func() {
timer := time.NewTimer(50 * time.Millisecond)
wg.Done()
select {
case <-timer.C:
log.Errorf("Unlocking due to possible error for %s", r.Name)
r.fifoLock.Unlock()
case <-r.disengage:
return
}
}()
wg.Wait()
}
// unlock unlocks mtx and shuts down a timer
func (r *Requester) unlock() {
r.disengage <- struct{}{}
r.fifoLock.Unlock()
}

View File

@@ -199,8 +199,8 @@ func TestCheckRequest(t *testing.T) {
}
func TestDoRequest(t *testing.T) {
var test *Requester
err := test.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, true)
var test = new(Requester)
err := test.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true)
if err == nil {
t.Fatal("not iniitalised")
}
@@ -211,17 +211,17 @@ func TestDoRequest(t *testing.T) {
}
r.Name = "bitfinex"
err = r.SendPayload("BLAH", "https://www.google.com", nil, nil, nil, false, true)
err = r.SendPayload("BLAH", "https://www.google.com", nil, nil, nil, false, false, true)
if err == nil {
t.Fatal("unexpected values")
}
err = r.SendPayload(http.MethodGet, "", nil, nil, nil, false, true)
err = r.SendPayload(http.MethodGet, "", nil, nil, nil, false, false, true)
if err == nil {
t.Fatal("unexpected values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, true)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true)
if err != nil {
t.Fatal("unexpected values")
}
@@ -233,7 +233,7 @@ func TestDoRequest(t *testing.T) {
r.SetRateLimit(false, time.Second, 0)
r.SetRateLimit(true, time.Second, 0)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, true)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true)
if err != nil {
t.Fatal("unexpected values")
}
@@ -250,7 +250,7 @@ func TestDoRequest(t *testing.T) {
t.Fatal("unexepcted values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, true)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true)
if err != nil {
t.Fatal("unexpected values")
}
@@ -261,27 +261,27 @@ func TestDoRequest(t *testing.T) {
t.Fatal("unexepcted values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, true, true)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, true, false, true)
if err != nil {
t.Fatal("unexpected values")
}
var result interface{}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, true)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, true)
if err != nil {
t.Fatal(err)
}
headers := make(map[string]string)
headers["content-type"] = "content/text"
err = r.SendPayload(http.MethodPost, "https://bitfinex.com", headers, nil, result, false, true)
err = r.SendPayload(http.MethodPost, "https://bitfinex.com", headers, nil, result, false, false, true)
if err != nil {
t.Fatal(err)
}
r.StartCycle()
r.UnauthLimit.SetRequests(100)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -297,7 +297,7 @@ func TestDoRequest(t *testing.T) {
}
r.HTTPClient.Timeout = 1 * time.Second
err = r.SendPayload(http.MethodPost, "https://httpstat.us/200?sleep=20000", nil, nil, nil, false, true)
err = r.SendPayload(http.MethodPost, "https://httpstat.us/200?sleep=20000", nil, nil, nil, false, false, true)
if err == nil {
t.Fatal(err)
}
@@ -322,3 +322,11 @@ func TestDoRequest(t *testing.T) {
t.Error("failed to set proxy")
}
}
func BenchmarkRequestLockMech(b *testing.B) {
var r = new(Requester)
var meep interface{}
for n := 0; n < b.N; n++ {
r.SendPayload(http.MethodGet, "127.0.0.1", nil, nil, &meep, false, false, false)
}
}

View File

@@ -336,6 +336,7 @@ func (y *Yobit) SendHTTPRequest(path string, result interface{}) error {
nil,
result,
false,
false,
y.Verbose)
}
@@ -350,12 +351,9 @@ func (y *Yobit) SendAuthenticatedHTTPRequest(path string, params url.Values, res
params = url.Values{}
}
if y.Nonce.Get() == 0 {
y.Nonce.Set(time.Now().Unix())
} else {
y.Nonce.Inc()
}
params.Set("nonce", y.Nonce.String())
n := y.Requester.GetNonce(false).String()
params.Set("nonce", n)
params.Set("method", path)
encoded := params.Encode()
@@ -381,6 +379,7 @@ func (y *Yobit) SendAuthenticatedHTTPRequest(path string, params url.Values, res
strings.NewReader(encoded),
result,
true,
true,
y.Verbose)
}

View File

@@ -361,7 +361,7 @@ func (z *ZB) GetCryptoAddress(currency currency.Code) (UserAddress, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (z *ZB) SendHTTPRequest(path string, result interface{}) error {
return z.SendPayload(http.MethodGet, path, nil, nil, result, false, z.Verbose)
return z.SendPayload(http.MethodGet, path, nil, nil, result, false, false, z.Verbose)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the zb API
@@ -397,6 +397,7 @@ func (z *ZB) SendAuthenticatedHTTPRequest(httpMethod string, params url.Values,
strings.NewReader(""),
&intermediary,
true,
false,
z.Verbose)
if err != nil {
return err

View File

@@ -346,7 +346,7 @@ func (z *ZB) GetActiveOrders(getOrdersRequest *exchange.GetOrdersRequest) ([]exc
Amount: order.TotalAmount,
Exchange: z.Name,
OrderDate: orderDate,
Price: float64(order.Price),
Price: order.Price,
OrderSide: orderSide,
CurrencyPair: symbol,
})
@@ -403,7 +403,7 @@ func (z *ZB) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) ([]exc
Amount: order.TotalAmount,
Exchange: z.Name,
OrderDate: orderDate,
Price: float64(order.Price),
Price: order.Price,
OrderSide: orderSide,
CurrencyPair: symbol,
})

View File

@@ -1319,5 +1319,19 @@
"supportedExchanges": "ANX,Kraken"
}
],
"connectionMonitor": {
"preferredDNSList": [
"8.8.8.8",
"8.8.4.4",
"1.1.1.1",
"1.0.0.1"
],
"preferredDomainList": [
"www.google.com",
"www.cloudflare.com",
"www.facebook.com"
],
"checkInterval": 1000000000
},
"fiatDispayCurrency": ""
}