From f3c1f4880da13f8aed7e910f330860ac1657116c Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Mon, 21 Aug 2017 15:57:41 +1000 Subject: [PATCH] Add nonce package for exchanges --- exchanges/alphapoint/alphapoint.go | 12 ++-- exchanges/anx/anx.go | 16 +++-- exchanges/bitfinex/bitfinex.go | 14 +++-- exchanges/bitstamp/bitstamp.go | 10 +++- exchanges/bittrex/bittrex.go | 8 ++- exchanges/btcc/btcc.go | 10 +++- exchanges/btce/btce.go | 8 ++- exchanges/btcmarkets/btcmarkets.go | 13 ++-- exchanges/coinut/coinut.go | 8 ++- exchanges/exchange.go | 2 + exchanges/gdax/gdax.go | 11 +++- exchanges/gemini/gemini.go | 14 +++-- exchanges/itbit/itbit.go | 19 +++--- exchanges/kraken/kraken.go | 8 ++- exchanges/lakebtc/lakebtc.go | 11 +++- exchanges/liqui/liqui.go | 8 ++- exchanges/localbitcoins/localbitcoins.go | 11 +++- exchanges/nonce/nonce.go | 49 ++++++++++++++++ exchanges/nonce/nonce_test.go | 75 ++++++++++++++++++++++++ exchanges/poloniex/poloniex.go | 10 ++-- 20 files changed, 256 insertions(+), 61 deletions(-) create mode 100644 exchanges/nonce/nonce.go create mode 100644 exchanges/nonce/nonce_test.go diff --git a/exchanges/alphapoint/alphapoint.go b/exchanges/alphapoint/alphapoint.go index e24f4a33..b09ab687 100644 --- a/exchanges/alphapoint/alphapoint.go +++ b/exchanges/alphapoint/alphapoint.go @@ -413,13 +413,17 @@ 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() + } + headers := make(map[string]string) headers["Content-Type"] = "application/json" data["apiKey"] = a.APIKey - nonce := time.Now().UnixNano() - nonceStr := strconv.FormatInt(nonce, 10) - data["apiNonce"] = nonce - hmac := common.GetHMAC(common.HashSHA256, []byte(nonceStr+a.ClientID+a.APIKey), []byte(a.APISecret)) + data["apiNonce"] = a.Nonce.Get() + hmac := common.GetHMAC(common.HashSHA256, []byte(a.Nonce.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, ALPHAPOINT_API_VERSION, path) PayloadJSON, err := common.JSONEncode(data) diff --git a/exchanges/anx/anx.go b/exchanges/anx/anx.go index cedd294d..92f25b89 100644 --- a/exchanges/anx/anx.go +++ b/exchanges/anx/anx.go @@ -290,8 +290,14 @@ 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() + } + request := make(map[string]interface{}) - request["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10)[0:13] + request["nonce"] = a.Nonce.String()[0:13] path = fmt.Sprintf("api/%s/%s", ANX_API_VERSION, path) if params != nil { @@ -300,23 +306,23 @@ func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interf } } - PayloadJson, err := common.JSONEncode(request) + PayloadJSON, err := common.JSONEncode(request) if err != nil { return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request") } if a.Verbose { - log.Printf("Request JSON: %s\n", PayloadJson) + log.Printf("Request JSON: %s\n", PayloadJSON) } - hmac := common.GetHMAC(common.HashSHA512, []byte(path+string("\x00")+string(PayloadJson)), []byte(a.APISecret)) + hmac := common.GetHMAC(common.HashSHA512, []byte(path+string("\x00")+string(PayloadJSON)), []byte(a.APISecret)) headers := make(map[string]string) headers["Rest-Key"] = a.APIKey headers["Rest-Sign"] = common.Base64Encode([]byte(hmac)) headers["Content-Type"] = "application/json" - resp, err := common.SendHTTPRequest("POST", ANX_API_URL+path, headers, bytes.NewBuffer(PayloadJson)) + resp, err := common.SendHTTPRequest("POST", ANX_API_URL+path, headers, bytes.NewBuffer(PayloadJSON)) if a.Verbose { log.Printf("Received raw: \n%s\n", resp) diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index 6f28fff6..6f8726eb 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -593,9 +593,15 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[ return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) } + if b.Nonce.Get() == 0 { + b.Nonce.Set(time.Now().UnixNano()) + } else { + b.Nonce.Inc() + } + request := make(map[string]interface{}) request["request"] = fmt.Sprintf("/v%s/%s", BITFINEX_API_VERSION, path) - request["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10) + request["nonce"] = b.Nonce.String() if params != nil { for key, value := range params { @@ -603,16 +609,16 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[ } } - PayloadJson, err := common.JSONEncode(request) + PayloadJSON, err := common.JSONEncode(request) if err != nil { return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request") } if b.Verbose { - log.Printf("Request JSON: %s\n", PayloadJson) + log.Printf("Request JSON: %s\n", PayloadJSON) } - PayloadBase64 := common.Base64Encode(PayloadJson) + PayloadBase64 := common.Base64Encode(PayloadJSON) hmac := common.GetHMAC(common.HashSHA512_384, []byte(PayloadBase64), []byte(b.APISecret)) headers := make(map[string]string) headers["X-BFX-APIKEY"] = b.APIKey diff --git a/exchanges/bitstamp/bitstamp.go b/exchanges/bitstamp/bitstamp.go index 7202be8b..8c4c3688 100644 --- a/exchanges/bitstamp/bitstamp.go +++ b/exchanges/bitstamp/bitstamp.go @@ -472,15 +472,19 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) } - nonce := strconv.FormatInt(time.Now().UnixNano(), 10) + if b.Nonce.Get() == 0 { + b.Nonce.Set(time.Now().UnixNano()) + } else { + b.Nonce.Inc() + } if values == nil { values = url.Values{} } values.Set("key", b.APIKey) - values.Set("nonce", nonce) - hmac := common.GetHMAC(common.HashSHA256, []byte(nonce+b.ClientID+b.APIKey), []byte(b.APISecret)) + values.Set("nonce", b.Nonce.String()) + hmac := common.GetHMAC(common.HashSHA256, []byte(b.Nonce.String()+b.ClientID+b.APIKey), []byte(b.APISecret)) values.Set("signature", common.StringToUpper(common.HexEncodeToString(hmac))) if v2 { diff --git a/exchanges/bittrex/bittrex.go b/exchanges/bittrex/bittrex.go index 2743ce5f..56f81aff 100644 --- a/exchanges/bittrex/bittrex.go +++ b/exchanges/bittrex/bittrex.go @@ -318,10 +318,14 @@ func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, r return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) } - nonce := strconv.FormatInt(time.Now().UnixNano(), 10) + if b.Nonce.Get() == 0 { + b.Nonce.Set(time.Now().UnixNano()) + } else { + b.Nonce.Inc() + } values.Set("apikey", b.APIKey) values.Set("apisecret", b.APISecret) - values.Set("nonce", nonce) + values.Set("nonce", b.Nonce.String()) rawQuery := path + "?" + values.Encode() hmac := common.GetHMAC( common.HashSHA512, []byte(rawQuery), []byte(b.APISecret), diff --git a/exchanges/btcc/btcc.go b/exchanges/btcc/btcc.go index 111791b9..802f114c 100644 --- a/exchanges/btcc/btcc.go +++ b/exchanges/btcc/btcc.go @@ -520,8 +520,12 @@ func (b *BTCC) SendAuthenticatedHTTPRequest(method string, params []interface{}) return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) } - nonce := strconv.FormatInt(time.Now().UnixNano(), 10)[0:16] - encoded := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=%d&method=%s¶ms=", nonce, b.APIKey, 1, method) + if b.Nonce.Get() == 0 { + b.Nonce.Set(time.Now().UnixNano()) + } else { + b.Nonce.Inc() + } + encoded := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=%d&method=%s¶ms=", b.Nonce.String()[0:16], b.APIKey, 1, method) if len(params) == 0 { params = make([]interface{}, 0) @@ -581,7 +585,7 @@ func (b *BTCC) SendAuthenticatedHTTPRequest(method string, params []interface{}) headers := make(map[string]string) headers["Content-type"] = "application/json-rpc" headers["Authorization"] = "Basic " + common.Base64Encode([]byte(b.APIKey+":"+common.HexEncodeToString(hmac))) - headers["Json-Rpc-Tonce"] = nonce + headers["Json-Rpc-Tonce"] = b.Nonce.String() resp, err := common.SendHTTPRequest("POST", apiURL, headers, strings.NewReader(string(data))) diff --git a/exchanges/btce/btce.go b/exchanges/btce/btce.go index 0dd0d822..f96819c3 100644 --- a/exchanges/btce/btce.go +++ b/exchanges/btce/btce.go @@ -297,8 +297,12 @@ func (b *BTCE) SendAuthenticatedHTTPRequest(method string, values url.Values, re return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) } - nonce := strconv.FormatInt(time.Now().Unix(), 10) - values.Set("nonce", nonce) + if b.Nonce.Get() == 0 { + b.Nonce.Set(time.Now().Unix()) + } else { + b.Nonce.Inc() + } + values.Set("nonce", b.Nonce.String()) values.Set("method", method) encoded := values.Encode() diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index ab193b9c..6f7fe3fb 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -6,7 +6,6 @@ import ( "fmt" "log" "net/url" - "strconv" "time" "github.com/thrasher-/gocryptotrader/common" @@ -285,7 +284,11 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interfa return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) } - nonce := strconv.FormatInt(time.Now().UnixNano(), 10)[0:13] + if b.Nonce.Get() == 0 { + b.Nonce.Set(time.Now().UnixNano()) + } else { + b.Nonce.Inc() + } request := "" payload := []byte("") @@ -294,9 +297,9 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interfa if err != nil { return err } - request = path + "\n" + nonce + "\n" + string(payload) + request = path + "\n" + b.Nonce.String()[0:13] + "\n" + string(payload) } else { - request = path + "\n" + nonce + "\n" + request = path + "\n" + b.Nonce.String()[0:13] + "\n" } hmac := common.GetHMAC(common.HashSHA512, []byte(request), []byte(b.APISecret)) @@ -310,7 +313,7 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interfa headers["Accept-Charset"] = "UTF-8" headers["Content-Type"] = "application/json" headers["apikey"] = b.APIKey - headers["timestamp"] = nonce + headers["timestamp"] = b.Nonce.String()[0:13] headers["signature"] = common.Base64Encode(hmac) resp, err := common.SendHTTPRequest(reqType, BTCMARKETS_API_URL+path, headers, bytes.NewBuffer(payload)) diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index c26dde45..5d262881 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -276,13 +276,17 @@ func (c *COINUT) SendAuthenticatedHTTPRequest(apiRequest string, params map[stri return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, c.Name) } - timestamp := time.Now().Unix() + if c.Nonce.Get() == 0 { + c.Nonce.Set(time.Now().Unix()) + } else { + c.Nonce.Inc() + } payload := []byte("") if params == nil { params = map[string]interface{}{} } - params["nonce"] = timestamp + params["nonce"] = c.Nonce.Get() params["request"] = apiRequest payload, err = common.JSONEncode(params) diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 5644cc15..c0c42854 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -7,6 +7,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" + "github.com/thrasher-/gocryptotrader/exchanges/nonce" "github.com/thrasher-/gocryptotrader/exchanges/orderbook" "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) @@ -43,6 +44,7 @@ type Base struct { RESTPollingDelay time.Duration AuthenticatedAPISupport bool APISecret, APIKey, ClientID string + Nonce nonce.Nonce TakerFee, MakerFee, Fee float64 BaseCurrencies []string AvailablePairs []string diff --git a/exchanges/gdax/gdax.go b/exchanges/gdax/gdax.go index e3a0a9fa..7c73f68d 100644 --- a/exchanges/gdax/gdax.go +++ b/exchanges/gdax/gdax.go @@ -374,7 +374,12 @@ func (g *GDAX) SendAuthenticatedHTTPRequest(method, path string, params map[stri return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name) } - timestamp := strconv.FormatInt(time.Now().Unix(), 10) + if g.Nonce.Get() == 0 { + g.Nonce.Set(time.Now().Unix()) + } else { + g.Nonce.Inc() + } + payload := []byte("") if params != nil { @@ -389,11 +394,11 @@ func (g *GDAX) SendAuthenticatedHTTPRequest(method, path string, params map[stri } } - message := timestamp + method + "/" + path + string(payload) + message := g.Nonce.String() + method + "/" + path + string(payload) hmac := common.GetHMAC(common.HashSHA256, []byte(message), []byte(g.APISecret)) headers := make(map[string]string) headers["CB-ACCESS-SIGN"] = common.Base64Encode([]byte(hmac)) - headers["CB-ACCESS-TIMESTAMP"] = timestamp + headers["CB-ACCESS-TIMESTAMP"] = g.Nonce.String() headers["CB-ACCESS-KEY"] = g.APIKey headers["CB-ACCESS-PASSPHRASE"] = g.ClientID headers["Content-Type"] = "application/json" diff --git a/exchanges/gemini/gemini.go b/exchanges/gemini/gemini.go index 3edf8ac4..654347da 100644 --- a/exchanges/gemini/gemini.go +++ b/exchanges/gemini/gemini.go @@ -249,9 +249,15 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name) } + if g.Nonce.Get() == 0 { + g.Nonce.Set(time.Now().UnixNano()) + } else { + g.Nonce.Inc() + } + request := make(map[string]interface{}) request["request"] = fmt.Sprintf("/v%s/%s", GEMINI_API_VERSION, path) - request["nonce"] = time.Now().UnixNano() + request["nonce"] = g.Nonce.Get() if params != nil { for key, value := range params { @@ -259,17 +265,17 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st } } - PayloadJson, err := common.JSONEncode(request) + PayloadJSON, err := common.JSONEncode(request) if err != nil { return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request") } if g.Verbose { - log.Printf("Request JSON: %s\n", PayloadJson) + log.Printf("Request JSON: %s\n", PayloadJSON) } - PayloadBase64 := common.Base64Encode(PayloadJson) + PayloadBase64 := common.Base64Encode(PayloadJSON) hmac := common.GetHMAC(common.HashSHA512_384, []byte(PayloadBase64), []byte(g.APISecret)) headers := make(map[string]string) headers["X-GEMINI-APIKEY"] = g.APIKey diff --git a/exchanges/itbit/itbit.go b/exchanges/itbit/itbit.go index 0a6c486f..47a97532 100644 --- a/exchanges/itbit/itbit.go +++ b/exchanges/itbit/itbit.go @@ -231,14 +231,12 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method string, path string, params return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, i.Name) } - timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)[0:13] - nonce, err := strconv.Atoi(timestamp) - - if err != nil { - return err + if i.Nonce.Get() == 0 { + i.Nonce.Set(time.Now().UnixNano()) + } else { + i.Nonce.Inc() } - nonce-- request := make(map[string]interface{}) url := ITBIT_API_URL + path @@ -262,21 +260,20 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method string, path string, params } } - nonceStr := strconv.Itoa(nonce) - message, err := common.JSONEncode([]string{method, url, string(PayloadJSON), nonceStr, timestamp}) + message, err := common.JSONEncode([]string{method, url, string(PayloadJSON), i.Nonce.String(), i.Nonce.String()[0:13]}) if err != nil { log.Println(err) return } - hash := common.GetSHA256([]byte(nonceStr + string(message))) + hash := common.GetSHA256([]byte(i.Nonce.String() + string(message))) hmac := common.GetHMAC(common.HashSHA512, []byte(url+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"] = nonceStr + headers["X-Auth-Timestamp"] = i.Nonce.String()[0:13] + headers["X-Auth-Nonce"] = i.Nonce.String() headers["Content-Type"] = "application/json" resp, err := common.SendHTTPRequest(method, url, headers, bytes.NewBuffer([]byte(PayloadJSON))) diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index 54712f4f..0be7fe3c 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -514,7 +514,13 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, values url.Values) } path := fmt.Sprintf("/%s/private/%s", KRAKEN_API_VERSION, method) - values.Set("nonce", strconv.FormatInt(time.Now().UnixNano(), 10)) + if k.Nonce.Get() == 0 { + k.Nonce.Set(time.Now().UnixNano()) + } else { + k.Nonce.Inc() + } + + values.Set("nonce", k.Nonce.String()) secret, err := common.Base64Decode(k.APISecret) if err != nil { diff --git a/exchanges/lakebtc/lakebtc.go b/exchanges/lakebtc/lakebtc.go index 794761a4..97121ecb 100644 --- a/exchanges/lakebtc/lakebtc.go +++ b/exchanges/lakebtc/lakebtc.go @@ -276,8 +276,13 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name) } - nonce := strconv.FormatInt(time.Now().UnixNano(), 10) - req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s¶ms=%s", nonce, l.APIKey, method, params) + if l.Nonce.Get() == 0 { + l.Nonce.Set(time.Now().UnixNano()) + } else { + l.Nonce.Inc() + } + + req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s¶ms=%s", l.Nonce.String(), l.APIKey, method, params) hmac := common.GetHMAC(common.HashSHA1, []byte(req), []byte(l.APISecret)) if l.Verbose { @@ -295,7 +300,7 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int } headers := make(map[string]string) - headers["Json-Rpc-Tonce"] = nonce + headers["Json-Rpc-Tonce"] = l.Nonce.String() headers["Authorization"] = "Basic " + common.Base64Encode([]byte(l.APIKey+":"+common.HexEncodeToString(hmac))) headers["Content-Type"] = "application/json-rpc" diff --git a/exchanges/liqui/liqui.go b/exchanges/liqui/liqui.go index 3ddac444..15ecdcd2 100644 --- a/exchanges/liqui/liqui.go +++ b/exchanges/liqui/liqui.go @@ -253,8 +253,12 @@ func (l *Liqui) SendAuthenticatedHTTPRequest(method string, values url.Values, r return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name) } - nonce := strconv.FormatInt(time.Now().Unix(), 10) - values.Set("nonce", nonce) + if l.Nonce.Get() == 0 { + l.Nonce.Set(time.Now().UnixNano()) + } else { + l.Nonce.Inc() + } + values.Set("nonce", l.Nonce.String()) values.Set("method", method) encoded := values.Encode() diff --git a/exchanges/localbitcoins/localbitcoins.go b/exchanges/localbitcoins/localbitcoins.go index adb2bd40..d657869e 100644 --- a/exchanges/localbitcoins/localbitcoins.go +++ b/exchanges/localbitcoins/localbitcoins.go @@ -271,7 +271,12 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, values return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name) } - nonce := strconv.FormatInt(time.Now().UnixNano(), 10) + if l.Nonce.Get() == 0 { + l.Nonce.Set(time.Now().UnixNano()) + } else { + l.Nonce.Inc() + } + payload := "" path = "/api/" + path @@ -279,11 +284,11 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, values payload = values.Encode() } - message := string(nonce) + l.APIKey + path + payload + message := l.Nonce.String() + l.APIKey + path + payload hmac := common.GetHMAC(common.HashSHA256, []byte(message), []byte(l.APISecret)) headers := make(map[string]string) headers["Apiauth-Key"] = l.APIKey - headers["Apiauth-Nonce"] = string(nonce) + headers["Apiauth-Nonce"] = l.Nonce.String() headers["Apiauth-Signature"] = common.StringToUpper(common.HexEncodeToString(hmac)) headers["Content-Type"] = "application/x-www-form-urlencoded" diff --git a/exchanges/nonce/nonce.go b/exchanges/nonce/nonce.go new file mode 100644 index 00000000..79ff8751 --- /dev/null +++ b/exchanges/nonce/nonce.go @@ -0,0 +1,49 @@ +package nonce + +import ( + "strconv" + "sync" +) + +// Nonce struct holds the nonce value +type Nonce struct { + n int64 + mtx sync.Mutex +} + +// Inc increments the nonce value +func (n *Nonce) Inc() { + n.mtx.Lock() + n.n++ + n.mtx.Unlock() +} + +// Get retrives the nonce value +func (n *Nonce) Get() int64 { + n.mtx.Lock() + defer n.mtx.Unlock() + return n.n +} + +// GetInc increments and returns the value of the nonce +func (n *Nonce) GetInc() int64 { + n.mtx.Lock() + defer n.mtx.Unlock() + n.n++ + return n.n +} + +// Set sets the nonce value +func (n *Nonce) Set(val int64) { + n.mtx.Lock() + n.n = val + n.mtx.Unlock() +} + +// Returns a string version of the nonce +func (n *Nonce) String() string { + n.mtx.Lock() + result := strconv.FormatInt(n.n, 10) + n.mtx.Unlock() + return result +} diff --git a/exchanges/nonce/nonce_test.go b/exchanges/nonce/nonce_test.go new file mode 100644 index 00000000..8bf6d6da --- /dev/null +++ b/exchanges/nonce/nonce_test.go @@ -0,0 +1,75 @@ +package nonce + +import ( + "testing" + "time" +) + +func TestInc(t *testing.T) { + var nonce Nonce + nonce.Set(1) + nonce.Inc() + expected := int64(2) + result := nonce.Get() + if result != expected { + t.Errorf("Test failed. Expected %d got %d", expected, result) + } +} + +func TestGet(t *testing.T) { + var nonce Nonce + nonce.Set(112321313) + expected := int64(112321313) + result := nonce.Get() + if expected != result { + t.Errorf("Test failed. Expected %d got %d", expected, result) + } +} + +func TestGetInc(t *testing.T) { + var nonce Nonce + nonce.Set(1) + expected := int64(2) + result := nonce.GetInc() + if expected != result { + t.Errorf("Test failed. Expected %d got %d", expected, result) + } +} + +func TestSet(t *testing.T) { + var nonce Nonce + nonce.Set(1) + expected := int64(1) + result := nonce.Get() + if expected != result { + t.Errorf("Test failed. Expected %d got %d", expected, result) + } +} + +func TestString(t *testing.T) { + var nonce Nonce + nonce.Set(12312313131) + expected := "12312313131" + result := nonce.String() + if expected != result { + t.Errorf("Test failed. Expected %s got %s", expected, result) + } +} + +func TestNonceConcurrency(t *testing.T) { + var nonce Nonce + nonce.Set(12312) + + for i := 0; i < 1000; i++ { + go nonce.Inc() + } + + // Allow sufficient time for all routines to finish + time.Sleep(time.Second) + + result := nonce.Get() + expected := int64(12312 + 1000) + if expected != result { + t.Errorf("Test failed. Expected %d got %d", expected, result) + } +} diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go index bf318fab..0ce8e3b0 100644 --- a/exchanges/poloniex/poloniex.go +++ b/exchanges/poloniex/poloniex.go @@ -752,10 +752,12 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values headers["Content-Type"] = "application/x-www-form-urlencoded" headers["Key"] = p.APIKey - nonce := time.Now().UnixNano() - nonceStr := strconv.FormatInt(nonce, 10) - - values.Set("nonce", nonceStr) + if p.Nonce.Get() == 0 { + p.Nonce.Set(time.Now().UnixNano()) + } else { + p.Nonce.Inc() + } + values.Set("nonce", p.Nonce.String()) values.Set("command", endpoint) hmac := common.GetHMAC(common.HashSHA512, []byte(values.Encode()), []byte(p.APISecret))