forked from stellar-deprecated/kelp
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support dynamic CCXT headers for exchanges such as Coinbase (stellar-…
…deprecated#314) * 1 - add infrastructure for HeaderFn to networking lib and wiring into ccxt.go * 2 - make header function method with STATIC type and ability to pass in custom mappings to framework with wiring for CCXT * 3 - support backward compatible case of not having any pre-specified function int he exchange header value, added LIST_OF_HACKS.md * 4 - add dynamic header functions to support coinbase pro * 5 - base64 encode signature for coinbase pro, use COINBASEPRO prefix instead of COINBASE * 6 - show all function names when there is an error in MakeHeaderFn * 7 - update sample config files with sample config entries for coinbase, mark it as a tested exchange * 8 - updated Exchanges section of README.md
- Loading branch information
1 parent
f83c435
commit 335d191
Showing
10 changed files
with
255 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# LIST OF HACKS | ||
|
||
## Awating v2.0 | ||
|
||
Incomplete list of hacks in the codebase that should be fixed before upgrading to v2.0 which will change the API to Kelp in some way | ||
|
||
- LOH-1 - support backward-compatible case of not having any pre-specified function | ||
|
||
## Workarounds | ||
|
||
Incomplete list of workaround hacks in the codebase that should be fixed at some point |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package networking | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/stellar/kelp/support/utils" | ||
) | ||
|
||
// HeaderFn represents a function that transforms headers | ||
type HeaderFn func(string, string, string) string // (string httpMethod, string requestPath, string body) | ||
|
||
// makeStaticHeaderFn is a convenience method | ||
func makeStaticHeaderFn(value string) (HeaderFn, error) { | ||
// need to convert to HeaderFn to work as a api.ExchangeHeader.Value | ||
return HeaderFn(func(method string, requestPath string, body string) string { | ||
return value | ||
}), nil | ||
} | ||
|
||
// HeaderFnFactory is a factory method for the HeaderFn | ||
type HeaderFnFactory func(string) (HeaderFn, error) | ||
|
||
var defaultMappings = map[string]HeaderFnFactory{ | ||
"STATIC": HeaderFnFactory(makeStaticHeaderFn), | ||
} | ||
|
||
func headerFnNames(maps ...map[string]HeaderFnFactory) []string { | ||
names := []string{} | ||
for _, m := range maps { | ||
if m != nil { | ||
for k, _ := range m { | ||
names = append(names, k) | ||
} | ||
} | ||
} | ||
return utils.Dedupe(names) | ||
} | ||
|
||
// MakeHeaderFn is a factory method that makes a HeaderFn | ||
func MakeHeaderFn(value string, primaryMappings map[string]HeaderFnFactory) (HeaderFn, error) { | ||
numSeparators := strings.Count(value, ":") | ||
|
||
if numSeparators == 0 { | ||
// LOH-1 - support backward-compatible case of not having any pre-specified function | ||
return makeStaticHeaderFn(value) | ||
} else if numSeparators != 1 { | ||
names := headerFnNames(primaryMappings, defaultMappings) | ||
return nil, fmt.Errorf("invalid format of header value (%s), needs exactly one colon (:) to separate the header function from the input value to that function. list of available header functions: [%s]", value, strings.Join(names, ", ")) | ||
} | ||
|
||
valueParts := strings.Split(value, ":") | ||
fnType := valueParts[0] | ||
fnInputValue := valueParts[1] | ||
|
||
if primaryMappings != nil { | ||
if makeHeaderFn, ok := primaryMappings[fnType]; ok { | ||
return makeHeaderFn(fnInputValue) | ||
} | ||
} | ||
|
||
if makeHeaderFn, ok := defaultMappings[fnType]; ok { | ||
return makeHeaderFn(fnInputValue) | ||
} | ||
|
||
names := headerFnNames(primaryMappings, defaultMappings) | ||
return nil, fmt.Errorf("invalid function prefix (%s) as part of header value (%s). list of available header functions: [%s]", fnType, value, strings.Join(names, ", ")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package sdk | ||
|
||
import ( | ||
"crypto/hmac" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/stellar/kelp/support/networking" | ||
) | ||
|
||
type ccxtMapper struct { | ||
timestamp int64 | ||
} | ||
|
||
// makeHeaderMappingsFromNewTimestamp creates a new ccxtMapper so the timestamp can be consistent across HeaderFns and returns the required map | ||
func makeHeaderMappingsFromNewTimestamp() map[string]networking.HeaderFnFactory { | ||
c := &ccxtMapper{ | ||
timestamp: time.Now().Unix(), | ||
} | ||
|
||
return map[string]networking.HeaderFnFactory{ | ||
"COINBASEPRO__CB-ACCESS-SIGN": networking.HeaderFnFactory(c.coinbaseSignFn), | ||
"TIMESTAMP": networking.HeaderFnFactory(c.timestampFn), | ||
} | ||
} | ||
|
||
func (c *ccxtMapper) coinbaseSignFn(base64EncodedSigningKey string) (networking.HeaderFn, error) { | ||
base64DecodedSigningKey, e := base64.StdEncoding.DecodeString(base64EncodedSigningKey) | ||
if e != nil { | ||
return nil, fmt.Errorf("could not decode signing key (%s): %s", base64EncodedSigningKey, e) | ||
} | ||
|
||
// return this inline method casted as a HeaderFn to work as a headerValue | ||
return networking.HeaderFn(func(method string, requestPath string, body string) string { | ||
uppercaseMethod := strings.ToUpper(method) | ||
payload := fmt.Sprintf("%d%s%s%s", c.timestamp, uppercaseMethod, requestPath, body) | ||
|
||
// sign | ||
mac := hmac.New(sha256.New, base64DecodedSigningKey) | ||
mac.Write([]byte(payload)) | ||
signature := mac.Sum(nil) | ||
base64EncodedSignature := base64.StdEncoding.EncodeToString(signature) | ||
|
||
return base64EncodedSignature | ||
}), nil | ||
} | ||
|
||
func (c *ccxtMapper) timestampFn(_ string) (networking.HeaderFn, error) { | ||
return networking.HeaderFn(func(method string, requestPath string, body string) string { | ||
return strconv.FormatInt(c.timestamp, 10) | ||
}), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.