mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
* codebase: Rid base64/hex to string common funcs * codebase: Rid local scope variable usage and other improvements * codebase: Refactor currency pair usage across multiple exchanges - Updated HitBTC tests to use the new currency pair format. - Modified Kraken futures types to use currency.Pair instead of string for Symbol. - Adjusted Kraken wrapper methods to handle currency pairs correctly. - Refined OKX tests and types to utilize currency.Pair for instrument IDs. - Enhanced Poloniex tests to consistently use predefined currency pairs. - Streamlined order and orderbook tests to replace string pairs with currency.NewBTCUSD(). - Improved Yobit tests to utilize a standardized currency pair format. - Updated validator wrapper to use currency pairs directly instead of string conversions. * codebase: Use types.Number where possible * refactor: update PayoutFee type to types.Number for consistency * Refactor: Remove crypto functions to use standard library and other minor changes - Removed custom crypto functions for SHA256, SHA512, and MD5 from the common/crypto package. - Replaced usages of removed functions with standard library implementations in various files including: - cmd/websocket_client/main.go - engine/apiserver.go - exchanges/kraken/kraken.go - exchanges/lbank/lbank.go - exchanges/okx/okx_business_websocket.go - exchanges/kucoin/kucoin_websocket.go - gctscript/vm/vm.go - Updated tests to reflect changes in the crypto functions. - Renamed several functions for clarity, particularly in the context of order book updates across multiple exchanges. * refactor: replace assert with require for consistency in test assertions * refactor: Improve Binance futures candlestick test, standardise orderbook update function names and improve test parallelism * refactor: Replace require.Len with require.Equal for better output in TestGetFuturesKlineData
233 lines
7.7 KiB
Go
233 lines
7.7 KiB
Go
package exchange
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/config"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
|
"github.com/thrasher-corp/gocryptotrader/log"
|
|
)
|
|
|
|
var (
|
|
// ErrAuthenticationSupportNotEnabled defines an error when
|
|
// authenticatedSupport and authenticatedWebsocketApiSupport are set to
|
|
// false in config.json
|
|
ErrAuthenticationSupportNotEnabled = errors.New("REST or Websocket authentication support is not enabled")
|
|
// ErrCredentialsAreEmpty defines an error for when the credentials are
|
|
// completely empty but an attempt at retrieving credentials was made to
|
|
// undertake an authenticated HTTP request.
|
|
ErrCredentialsAreEmpty = errors.New("credentials are empty")
|
|
// Errors related to API requirements and failures
|
|
errRequiresAPIKey = errors.New("requires API key but default/empty one set")
|
|
errRequiresAPISecret = errors.New("requires API secret but default/empty one set")
|
|
errRequiresAPIPEMKey = errors.New("requires API PEM key but default/empty one set")
|
|
errRequiresAPIClientID = errors.New("requires API Client ID but default/empty one set")
|
|
errBase64DecodeFailure = errors.New("base64 decode has failed")
|
|
)
|
|
|
|
// SetKey sets new key for the default credentials
|
|
func (a *API) SetKey(key string) {
|
|
a.credMu.Lock()
|
|
defer a.credMu.Unlock()
|
|
a.credentials.Key = key
|
|
}
|
|
|
|
// SetSecret sets new secret for the default credentials
|
|
func (a *API) SetSecret(secret string) {
|
|
a.credMu.Lock()
|
|
defer a.credMu.Unlock()
|
|
a.credentials.Secret = secret
|
|
}
|
|
|
|
// SetClientID sets new clientID for the default credentials
|
|
func (a *API) SetClientID(clientID string) {
|
|
a.credMu.Lock()
|
|
defer a.credMu.Unlock()
|
|
a.credentials.ClientID = clientID
|
|
}
|
|
|
|
// SetPEMKey sets pem key for the default credentials
|
|
func (a *API) SetPEMKey(pem string) {
|
|
a.credMu.Lock()
|
|
defer a.credMu.Unlock()
|
|
a.credentials.PEMKey = pem
|
|
}
|
|
|
|
// SetSubAccount sets sub account for the default credentials
|
|
func (a *API) SetSubAccount(sub string) {
|
|
a.credMu.Lock()
|
|
defer a.credMu.Unlock()
|
|
a.credentials.SubAccount = sub
|
|
}
|
|
|
|
// CheckCredentials checks to see if the required fields have been set before
|
|
// sending an authenticated API request
|
|
func (b *Base) CheckCredentials(creds *account.Credentials, isContext bool) error {
|
|
if b.SkipAuthCheck {
|
|
return nil
|
|
}
|
|
|
|
// Individual package usage, allow request if API credentials are valid a
|
|
// and without needing to set AuthenticatedSupport to true
|
|
if !b.LoadedByConfig {
|
|
return b.VerifyAPICredentials(creds)
|
|
}
|
|
|
|
// Bot usage, AuthenticatedSupport can be disabled by user if desired, so
|
|
// don't allow authenticated requests. Context credentials set will override
|
|
// default credentials and supported checks.
|
|
if !b.API.AuthenticatedSupport && !b.API.AuthenticatedWebsocketSupport && !isContext {
|
|
return fmt.Errorf("%s %w", b.Name, ErrAuthenticationSupportNotEnabled)
|
|
}
|
|
|
|
// Check to see if the user has enabled AuthenticatedSupport, but has
|
|
// invalid API credentials set and loaded by config
|
|
return b.VerifyAPICredentials(creds)
|
|
}
|
|
|
|
// AreCredentialsValid returns if the supplied credentials are valid.
|
|
func (b *Base) AreCredentialsValid(ctx context.Context) bool {
|
|
creds, err := b.GetCredentials(ctx)
|
|
return err == nil && b.VerifyAPICredentials(creds) == nil
|
|
}
|
|
|
|
// GetDefaultCredentials returns the exchange.Base api credentials loaded by
|
|
// config.json
|
|
func (b *Base) GetDefaultCredentials() *account.Credentials {
|
|
b.API.credMu.RLock()
|
|
defer b.API.credMu.RUnlock()
|
|
if b.API.credentials == (account.Credentials{}) {
|
|
return nil
|
|
}
|
|
creds := b.API.credentials
|
|
return &creds
|
|
}
|
|
|
|
// GetCredentials checks and validates current credentials, context credentials
|
|
// override default credentials, if no credentials found, will return an error.
|
|
func (b *Base) GetCredentials(ctx context.Context) (*account.Credentials, error) {
|
|
value := ctx.Value(account.ContextCredentialsFlag)
|
|
if value != nil {
|
|
ctxCredStore, ok := value.(*account.ContextCredentialsStore)
|
|
if !ok {
|
|
return nil, common.GetTypeAssertError("*account.ContextCredentialsStore", value)
|
|
}
|
|
|
|
creds := ctxCredStore.Get()
|
|
if err := b.CheckCredentials(creds, true); err != nil {
|
|
return nil, fmt.Errorf("error checking credentials from context: %w", err)
|
|
}
|
|
return creds, nil
|
|
}
|
|
|
|
// Fallback to exchange loaded credentials
|
|
b.API.credMu.RLock()
|
|
creds := b.API.credentials
|
|
b.API.credMu.RUnlock()
|
|
if err := b.CheckCredentials(&creds, false); err != nil {
|
|
return nil, fmt.Errorf("error checking credentials: %w", err)
|
|
}
|
|
|
|
if subAccountOverride, ok := ctx.Value(account.ContextSubAccountFlag).(string); ok {
|
|
creds.SubAccount = subAccountOverride
|
|
}
|
|
|
|
return &creds, nil
|
|
}
|
|
|
|
// VerifyAPICredentials verifies the exchanges API credentials
|
|
func (b *Base) VerifyAPICredentials(creds *account.Credentials) error {
|
|
b.API.credMu.RLock()
|
|
defer b.API.credMu.RUnlock()
|
|
if creds.IsEmpty() {
|
|
return fmt.Errorf("%s %w", b.Name, ErrCredentialsAreEmpty)
|
|
}
|
|
if b.API.CredentialsValidator.RequiresKey &&
|
|
(creds.Key == "" || creds.Key == config.DefaultAPIKey) {
|
|
return fmt.Errorf("%s %w", b.Name, errRequiresAPIKey)
|
|
}
|
|
|
|
if b.API.CredentialsValidator.RequiresSecret &&
|
|
(creds.Secret == "" || creds.Secret == config.DefaultAPISecret) {
|
|
return fmt.Errorf("%s %w", b.Name, errRequiresAPISecret)
|
|
}
|
|
|
|
if b.API.CredentialsValidator.RequiresPEM &&
|
|
(creds.PEMKey == "" || strings.Contains(creds.PEMKey, "JUSTADUMMY")) {
|
|
return fmt.Errorf("%s %w", b.Name, errRequiresAPIPEMKey)
|
|
}
|
|
|
|
if b.API.CredentialsValidator.RequiresClientID &&
|
|
(creds.ClientID == "" || creds.ClientID == config.DefaultAPIClientID) {
|
|
return fmt.Errorf("%s %w", b.Name, errRequiresAPIClientID)
|
|
}
|
|
|
|
if b.API.CredentialsValidator.RequiresBase64DecodeSecret && !creds.SecretBase64Decoded {
|
|
decodedResult, err := base64.StdEncoding.DecodeString(creds.Secret)
|
|
if err != nil {
|
|
return fmt.Errorf("%s API secret %w: %s", b.Name, errBase64DecodeFailure, err)
|
|
}
|
|
creds.Secret = string(decodedResult)
|
|
creds.SecretBase64Decoded = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetCredentials is a method that sets the current API keys for the exchange
|
|
func (b *Base) SetCredentials(apiKey, apiSecret, clientID, subaccount, pemKey, oneTimePassword string) {
|
|
b.API.credMu.Lock()
|
|
defer b.API.credMu.Unlock()
|
|
b.API.credentials.Key = apiKey
|
|
b.API.credentials.ClientID = clientID
|
|
b.API.credentials.SubAccount = subaccount
|
|
b.API.credentials.PEMKey = pemKey
|
|
b.API.credentials.OneTimePassword = oneTimePassword
|
|
|
|
if b.API.CredentialsValidator.RequiresBase64DecodeSecret {
|
|
result, err := base64.StdEncoding.DecodeString(apiSecret)
|
|
if err != nil {
|
|
b.API.AuthenticatedSupport = false
|
|
b.API.AuthenticatedWebsocketSupport = false
|
|
log.Warnf(log.ExchangeSys,
|
|
warningBase64DecryptSecretKeyFailed,
|
|
b.Name)
|
|
return
|
|
}
|
|
b.API.credentials.Secret = string(result)
|
|
b.API.credentials.SecretBase64Decoded = true
|
|
} else {
|
|
b.API.credentials.Secret = apiSecret
|
|
}
|
|
}
|
|
|
|
// SetAPICredentialDefaults sets the API Credential validator defaults
|
|
func (b *Base) SetAPICredentialDefaults() {
|
|
b.API.credMu.Lock()
|
|
defer b.API.credMu.Unlock()
|
|
|
|
// Exchange hardcoded settings take precedence and overwrite the config settings
|
|
if b.Config.API.CredentialsValidator == nil {
|
|
b.Config.API.CredentialsValidator = new(config.APICredentialsValidatorConfig)
|
|
}
|
|
|
|
*b.Config.API.CredentialsValidator = b.API.CredentialsValidator
|
|
}
|
|
|
|
// IsWebsocketAuthenticationSupported returns whether the exchange supports
|
|
// websocket authenticated API requests
|
|
func (b *Base) IsWebsocketAuthenticationSupported() bool {
|
|
return b.API.AuthenticatedWebsocketSupport
|
|
}
|
|
|
|
// IsRESTAuthenticationSupported returns whether the exchange supports REST authenticated
|
|
// API requests
|
|
func (b *Base) IsRESTAuthenticationSupported() bool {
|
|
return b.API.AuthenticatedSupport
|
|
}
|