websocket: Remove GenerateMessageID (#2008)

* Exchanges: Remove example BespokeGenerateMessageID

* Okx: Replace conn.RequestIDGenerator with MesssageID

Continued overall direction to remove the closed-loop of e => conn => e
roundtrip for message ids

* Exchanges: Add MessageSequence

This method removes the either/or nature of message id generation.
We don't tie the message ids to connections, or to anything.
Consumers just call whichever they want, or even combine them as they
want.
Anything more complicated will need a separate installation anyway

* GateIO: Split usage of MessageID and MessageSequence

* Binance: Switch to UUID message IDs

* Kraken: Switch to e.MessageSequence

* Kucoin: Switch to MessageID

* HitBTC: Switch to UUIDv7 for ws message ID

* Bybit: Switch to UUIDv7 for ws message ID

* Bitfinex: Switch to UUIDv7 and MessageSequence

Tested CID - It accepts 53 bits only for an int, so MessageSequence
makes sense. Can't use MessageID

* Websocket: Remove now unused MessageID function

Moved all MessageID usage into funcs and onto base methods, to remove
the closed loop of message IDs

* Docs: Update guidance for message signatures
This commit is contained in:
Gareth Kirwan
2025-10-24 07:14:24 +07:00
committed by GitHub
parent 0f70cfd8b6
commit bda9bbec66
33 changed files with 137 additions and 224 deletions

View File

@@ -33,7 +33,6 @@ import (
type Exchange struct {
exchange.Base
messageIDSeq common.Counter
instrumentsInfoMapLock sync.Mutex
instrumentsInfoMap map[string][]Instrument
}

View File

@@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/gofrs/uuid"
"github.com/shopspring/decimal"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/key"
@@ -224,7 +225,6 @@ func (e *Exchange) Setup(exch *config.Exchange) error {
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: websocketResponseMaxLimit,
RateLimit: request.NewRateLimitWithWeight(time.Second, 2, 1),
RequestIDGenerator: e.messageIDSeq.IncrementAndGet,
}); err != nil {
return err
}
@@ -235,7 +235,6 @@ func (e *Exchange) Setup(exch *config.Exchange) error {
ResponseMaxLimit: websocketResponseMaxLimit,
Authenticated: true,
RateLimit: request.NewRateLimitWithWeight(time.Second, 2, 1),
RequestIDGenerator: e.messageIDSeq.IncrementAndGet,
})
}
@@ -3038,3 +3037,8 @@ func (e *Exchange) GetCurrencyTradeURL(ctx context.Context, a asset.Item, cp cur
return "", fmt.Errorf("%w %q", asset.ErrNotSupported, a)
}
}
// MessageID returns a universally unique ID using UUID V7, with hyphens removed to fit the maximum 32-character field for okx
func (e *Exchange) MessageID() string {
return strings.Replace(uuid.Must(uuid.NewV7()).String(), "-", "", 4)
}

View File

@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"reflect"
"strconv"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/encoding/json"
@@ -28,10 +27,8 @@ func (e *Exchange) WSPlaceOrder(ctx context.Context, arg *PlaceOrderRequestParam
return nil, err
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*OrderData
if err := e.SendAuthenticatedWebsocketRequest(ctx, placeOrderEPL, id, "order", []PlaceOrderRequestParam{*arg}, &resp); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, placeOrderEPL, e.MessageID(), "order", []PlaceOrderRequestParam{*arg}, &resp); err != nil {
return nil, err
}
return singleItem(resp)
@@ -49,10 +46,8 @@ func (e *Exchange) WSPlaceMultipleOrders(ctx context.Context, args []PlaceOrderR
}
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*OrderData
return resp, e.SendAuthenticatedWebsocketRequest(ctx, placeMultipleOrdersEPL, id, "batch-orders", args, &resp)
return resp, e.SendAuthenticatedWebsocketRequest(ctx, placeMultipleOrdersEPL, e.MessageID(), "batch-orders", args, &resp)
}
// WSCancelOrder cancels an order
@@ -67,10 +62,8 @@ func (e *Exchange) WSCancelOrder(ctx context.Context, arg *CancelOrderRequestPar
return nil, order.ErrOrderIDNotSet
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*OrderData
if err := e.SendAuthenticatedWebsocketRequest(ctx, cancelOrderEPL, id, "cancel-order", []CancelOrderRequestParam{*arg}, &resp); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, cancelOrderEPL, e.MessageID(), "cancel-order", []CancelOrderRequestParam{*arg}, &resp); err != nil {
return nil, err
}
@@ -92,10 +85,8 @@ func (e *Exchange) WSCancelMultipleOrders(ctx context.Context, args []CancelOrde
}
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*OrderData
return resp, e.SendAuthenticatedWebsocketRequest(ctx, cancelMultipleOrdersEPL, id, "batch-cancel-orders", args, &resp)
return resp, e.SendAuthenticatedWebsocketRequest(ctx, cancelMultipleOrdersEPL, e.MessageID(), "batch-cancel-orders", args, &resp)
}
// WSAmendOrder amends an order
@@ -113,10 +104,8 @@ func (e *Exchange) WSAmendOrder(ctx context.Context, arg *AmendOrderRequestParam
return nil, errInvalidNewSizeOrPriceInformation
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*OrderData
if err := e.SendAuthenticatedWebsocketRequest(ctx, amendOrderEPL, id, "amend-order", []AmendOrderRequestParams{*arg}, &resp); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, amendOrderEPL, e.MessageID(), "amend-order", []AmendOrderRequestParams{*arg}, &resp); err != nil {
return nil, err
}
return singleItem(resp)
@@ -140,10 +129,8 @@ func (e *Exchange) WSAmendMultipleOrders(ctx context.Context, args []AmendOrderR
}
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*OrderData
return resp, e.SendAuthenticatedWebsocketRequest(ctx, amendMultipleOrdersEPL, id, "batch-amend-orders", args, &resp)
return resp, e.SendAuthenticatedWebsocketRequest(ctx, amendMultipleOrdersEPL, e.MessageID(), "batch-amend-orders", args, &resp)
}
// WSMassCancelOrders cancels all MMP pending orders of an instrument family. Only applicable to Option in Portfolio Margin mode, and MMP privilege is required.
@@ -161,12 +148,10 @@ func (e *Exchange) WSMassCancelOrders(ctx context.Context, args []CancelMassReqP
}
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resps []*struct {
Result bool `json:"result"`
}
if err := e.SendAuthenticatedWebsocketRequest(ctx, amendOrderEPL, id, "mass-cancel", args, &resps); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, amendOrderEPL, e.MessageID(), "mass-cancel", args, &resps); err != nil {
return err
}
@@ -188,10 +173,8 @@ func (e *Exchange) WSPlaceSpreadOrder(ctx context.Context, arg *SpreadOrderParam
return nil, err
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*SpreadOrderResponse
if err := e.SendAuthenticatedWebsocketRequest(ctx, placeSpreadOrderEPL, id, "sprd-order", []SpreadOrderParam{*arg}, &resp); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, placeSpreadOrderEPL, e.MessageID(), "sprd-order", []SpreadOrderParam{*arg}, &resp); err != nil {
return nil, err
}
@@ -210,10 +193,8 @@ func (e *Exchange) WSAmendSpreadOrder(ctx context.Context, arg *AmendSpreadOrder
return nil, errSizeOrPriceIsRequired
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*SpreadOrderResponse
if err := e.SendAuthenticatedWebsocketRequest(ctx, amendSpreadOrderEPL, id, "sprd-amend-order", []AmendSpreadOrderParam{*arg}, &resp); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, amendSpreadOrderEPL, e.MessageID(), "sprd-amend-order", []AmendSpreadOrderParam{*arg}, &resp); err != nil {
return nil, err
}
@@ -234,10 +215,8 @@ func (e *Exchange) WSCancelSpreadOrder(ctx context.Context, orderID, clientOrder
arg["clOrdId"] = clientOrderID
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resp []*SpreadOrderResponse
if err := e.SendAuthenticatedWebsocketRequest(ctx, cancelSpreadOrderEPL, id, "sprd-cancel-order", []map[string]string{arg}, &resp); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, cancelSpreadOrderEPL, e.MessageID(), "sprd-cancel-order", []map[string]string{arg}, &resp); err != nil {
return nil, err
}
@@ -251,10 +230,8 @@ func (e *Exchange) WSCancelAllSpreadOrders(ctx context.Context, spreadID string)
arg["sprdId"] = spreadID
}
id := strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10)
var resps []*ResponseResult
if err := e.SendAuthenticatedWebsocketRequest(ctx, cancelAllSpreadOrderEPL, id, "sprd-mass-cancel", []map[string]string{arg}, &resps); err != nil {
if err := e.SendAuthenticatedWebsocketRequest(ctx, cancelAllSpreadOrderEPL, e.MessageID(), "sprd-mass-cancel", []map[string]string{arg}, &resps); err != nil {
return err
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"testing"
"github.com/gofrs/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -281,3 +282,20 @@ func TestSingleItem(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, got)
}
func TestMessageID(t *testing.T) {
t.Parallel()
id := new(Exchange).MessageID()
require.Len(t, id, 32, "Must return the correct length of message id")
u, err := uuid.FromString(id)
require.NoError(t, err, "MessageID must return a valid UUID")
assert.Equal(t, byte(0x7), u.Version(), "MessageID should return a V7 uuid")
}
// BenchmarkMessageID-8 4736883 259.9 ns/op 96 B/op 4 allocs/op
func BenchmarkMessageID(b *testing.B) {
e := new(Exchange)
for b.Loop() {
_ = e.MessageID()
}
}