From 31fe74a380d4cafdc00650eb4ef8fbf5dbdf948c Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Fri, 28 Feb 2020 16:31:52 +1100 Subject: [PATCH] Fix Coinut nonce issue based on new API nonce rule (#456) --- exchanges/coinut/coinut.go | 12 ++++++---- exchanges/coinut/coinut_test.go | 9 ++++++++ exchanges/coinut/coinut_websocket.go | 33 ++++++++++------------------ exchanges/coinut/coinut_wrapper.go | 2 ++ 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index f9eb20bb..c8e34c22 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "math/rand" "net/http" "strconv" "strings" @@ -40,6 +41,7 @@ const ( coinutPositionOpen = "user_open_positions" coinutStatusOK = "OK" + coinutMaxNonce = 16777215 // See https://github.com/coinut/api/wiki/Websocket-API#nonce ) var ( @@ -269,12 +271,11 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{ return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, c.Name) } - n := c.Requester.GetNonce(false) - if params == nil { params = map[string]interface{}{} } - params["nonce"] = n + + params["nonce"] = getNonce() params["request"] = apiRequest payload, err := json.Marshal(params) @@ -302,7 +303,6 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{ Body: bytes.NewBuffer(payload), Result: &rawMsg, AuthRequest: authenticated, - NonceEnabled: true, Verbose: c.Verbose, HTTPDebugging: c.HTTPDebugging, HTTPRecording: c.HTTPRecording, @@ -495,3 +495,7 @@ func (i *instrumentMap) GetInstrumentIDs() []int64 { } return instruments } + +func getNonce() int64 { + return rand.Int63n(coinutMaxNonce-1) + 1 +} diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index f27e7aa5..8d63ed54 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -640,3 +640,12 @@ func TestCurrencyMapInstrumentIDs(t *testing.T) { t.Error("unexpected result") } } + +func TestGetNonce(t *testing.T) { + result := getNonce() + for x := 0; x < 100000; x++ { + if result <= 0 || result > coinutMaxNonce { + t.Fatal("invalid nonce value") + } + } +} diff --git a/exchanges/coinut/coinut_websocket.go b/exchanges/coinut/coinut_websocket.go index 7ae11b30..4b3e9757 100644 --- a/exchanges/coinut/coinut_websocket.go +++ b/exchanges/coinut/coinut_websocket.go @@ -233,24 +233,13 @@ func (c *COINUT) wsProcessResponse(resp []byte) { } } -// GetNonce returns a nonce for a required request -func (c *COINUT) GetNonce() int64 { - if c.Nonce.Get() == 0 { - c.Nonce.Set(time.Now().Unix()) - } else { - c.Nonce.Inc() - } - - return int64(c.Nonce.Get()) -} - // WsGetInstruments fetches instrument list and propagates a local cache func (c *COINUT) WsGetInstruments() (Instruments, error) { var list Instruments request := wsRequest{ Request: "inst_list", SecType: strings.ToUpper(asset.Spot.String()), - Nonce: c.WebsocketConn.GenerateMessageID(false), + Nonce: getNonce(), } resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request) if err != nil { @@ -344,7 +333,7 @@ func (c *COINUT) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscrip InstID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, asset.Spot).String()), Subscribe: true, - Nonce: c.WebsocketConn.GenerateMessageID(false), + Nonce: getNonce(), } return c.WebsocketConn.SendJSONMessage(subscribe) } @@ -356,7 +345,7 @@ func (c *COINUT) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscr InstID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, asset.Spot).String()), Subscribe: false, - Nonce: c.WebsocketConn.GenerateMessageID(false), + Nonce: getNonce(), } resp, err := c.WebsocketConn.SendMessageReturnResponse(subscribe.Nonce, subscribe) if err != nil { @@ -378,7 +367,7 @@ func (c *COINUT) wsAuthenticate() error { return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", c.Name) } timestamp := time.Now().Unix() - nonce := c.WebsocketConn.GenerateMessageID(false) + nonce := getNonce() payload := c.API.Credentials.ClientID + "|" + strconv.FormatInt(timestamp, 10) + "|" + strconv.FormatInt(nonce, 10) @@ -420,7 +409,7 @@ func (c *COINUT) wsGetAccountBalance() (*UserBalance, error) { } accBalance := wsRequest{ Request: "user_balance", - Nonce: c.WebsocketConn.GenerateMessageID(false), + Nonce: getNonce(), } resp, err := c.WebsocketConn.SendMessageReturnResponse(accBalance.Nonce, accBalance) if err != nil { @@ -444,7 +433,7 @@ func (c *COINUT) wsSubmitOrder(o *WsSubmitOrderParameters) (*WsStandardOrderResp curr := c.FormatExchangeCurrency(o.Currency, asset.Spot).String() var orderSubmissionRequest WsSubmitOrderRequest orderSubmissionRequest.Request = "new_order" - orderSubmissionRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) + orderSubmissionRequest.Nonce = getNonce() orderSubmissionRequest.InstID = c.instrumentMap.LookupID(curr) orderSubmissionRequest.Qty = o.Amount orderSubmissionRequest.Price = o.Price @@ -561,7 +550,7 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO }) } - orderRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) + orderRequest.Nonce = getNonce() orderRequest.Request = "new_orders" resp, err := c.WebsocketConn.SendMessageReturnResponse(orderRequest.Nonce, orderRequest) if err != nil { @@ -612,7 +601,7 @@ func (c *COINUT) wsGetOpenOrders(curr string) (*WsUserOpenOrdersResponse, error) } var openOrdersRequest WsGetOpenOrdersRequest openOrdersRequest.Request = "user_open_orders" - openOrdersRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) + openOrdersRequest.Nonce = getNonce() openOrdersRequest.InstID = c.instrumentMap.LookupID(curr) resp, err := c.WebsocketConn.SendMessageReturnResponse(openOrdersRequest.Nonce, openOrdersRequest) @@ -641,7 +630,7 @@ func (c *COINUT) wsCancelOrder(cancellation *WsCancelOrderParameters) (*CancelOr cancellationRequest.Request = "cancel_order" cancellationRequest.InstID = c.instrumentMap.LookupID(curr) cancellationRequest.OrderID = cancellation.OrderID - cancellationRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) + cancellationRequest.Nonce = getNonce() resp, err := c.WebsocketConn.SendMessageReturnResponse(cancellationRequest.Nonce, cancellationRequest) if err != nil { @@ -677,7 +666,7 @@ func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*Cance } cancelOrderRequest.Request = "cancel_orders" - cancelOrderRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) + cancelOrderRequest.Nonce = getNonce() resp, err := c.WebsocketConn.SendMessageReturnResponse(cancelOrderRequest.Nonce, cancelOrderRequest) if err != nil { return response, err @@ -698,7 +687,7 @@ func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) (*WsTrad var request WsTradeHistoryRequest request.Request = "trade_history" request.InstID = c.instrumentMap.LookupID(curr) - request.Nonce = c.WebsocketConn.GenerateMessageID(false) + request.Nonce = getNonce() request.Start = start request.Limit = limit diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index f96fc25a..5fc94839 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -3,6 +3,7 @@ package coinut import ( "errors" "fmt" + "math/rand" "strconv" "strings" "sync" @@ -125,6 +126,7 @@ func (c *COINUT) SetDefaults() { c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout c.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit + rand.Seed(time.Now().UnixNano()) } // Setup sets the current exchange configuration