Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func runRunCmd(cmd *cobra.Command, args []string) error {
}

var err error
client, err = ethereum.NewClient(cfg.GethURL, cfg.Params, cfg.SkipGethAdmin)
client, err = ethereum.NewClient(cfg.GethURL, cfg.Params, cfg.SkipGethAdmin, cfg.GethHeaders)
if err != nil {
return fmt.Errorf("%w: cannot initialize ethereum client", err)
}
Expand Down
21 changes: 21 additions & 0 deletions configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"os"
"strconv"
"strings"

"github.com/coinbase/rosetta-ethereum/ethereum"

Expand Down Expand Up @@ -86,6 +87,11 @@ const (
// by hosted node services. When not set, defaults to false.
SkipGethAdminEnv = "SKIP_GETH_ADMIN"

// GethHeadersEnv is an optional environment variable
// of a comma-separated list of key:value pairs to apply
// to geth clients as headers. When not set, defaults to []
GethHeadersEnv = "GETH_HEADERS"

// MiddlewareVersion is the version of rosetta-ethereum.
MiddlewareVersion = "0.0.4"
)
Expand All @@ -100,6 +106,7 @@ type Configuration struct {
Port int
GethArguments string
SkipGethAdmin bool
GethHeaders []*ethereum.HTTPHeader

// Block Reward Data
Params *params.ChainConfig
Expand Down Expand Up @@ -179,6 +186,20 @@ func LoadConfiguration() (*Configuration, error) {
config.SkipGethAdmin = val
}

envGethHeaders := os.Getenv(GethHeadersEnv)
if len(envGethHeaders) > 0 {
headers := strings.Split(envGethHeaders, ",")
headerKVs := make([]*ethereum.HTTPHeader, len(headers))
for i, pair := range headers {
kv := strings.Split(pair, ":")
headerKVs[i] = &ethereum.HTTPHeader{
Key: kv[0],
Value: kv[1],
}
}
config.GethHeaders = headerKVs
}

portValue := os.Getenv(PortEnv)
if len(portValue) == 0 {
return nil, errors.New("PORT must be populated")
Expand Down
17 changes: 17 additions & 0 deletions configuration/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func TestLoadConfiguration(t *testing.T) {
Port string
Geth string
SkipGethAdmin string
GethHeaders string

cfg *Configuration
err error
Expand All @@ -54,6 +55,7 @@ func TestLoadConfiguration(t *testing.T) {
Network: Mainnet,
Port: "1000",
SkipGethAdmin: "FALSE",
GethHeaders: "",
cfg: &Configuration{
Mode: Online,
Network: &types.NetworkIdentifier{
Expand All @@ -66,6 +68,7 @@ func TestLoadConfiguration(t *testing.T) {
GethURL: DefaultGethURL,
GethArguments: ethereum.MainnetGethArguments,
SkipGethAdmin: false,
GethHeaders: nil,
},
},
"all set (mainnet) + geth": {
Expand All @@ -74,6 +77,7 @@ func TestLoadConfiguration(t *testing.T) {
Port: "1000",
Geth: "http://blah",
SkipGethAdmin: "TRUE",
GethHeaders: "X-Auth-Token:12345-ABCDE,X-Api-Version:2",
cfg: &Configuration{
Mode: Online,
Network: &types.NetworkIdentifier{
Expand All @@ -87,6 +91,10 @@ func TestLoadConfiguration(t *testing.T) {
RemoteGeth: true,
GethArguments: ethereum.MainnetGethArguments,
SkipGethAdmin: true,
GethHeaders: []*ethereum.HTTPHeader{
{Key: "X-Auth-Token", Value: "12345-ABCDE"},
{Key: "X-Api-Version", Value: "2"},
},
},
},
"all set (ropsten)": {
Expand All @@ -104,6 +112,7 @@ func TestLoadConfiguration(t *testing.T) {
Port: 1000,
GethURL: DefaultGethURL,
GethArguments: ethereum.RopstenGethArguments,
GethHeaders: nil,
},
},
"all set (rinkeby)": {
Expand All @@ -121,6 +130,7 @@ func TestLoadConfiguration(t *testing.T) {
Port: 1000,
GethURL: DefaultGethURL,
GethArguments: ethereum.RinkebyGethArguments,
GethHeaders: nil,
},
},
"all set (goerli)": {
Expand All @@ -138,13 +148,15 @@ func TestLoadConfiguration(t *testing.T) {
Port: 1000,
GethURL: DefaultGethURL,
GethArguments: ethereum.GoerliGethArguments,
GethHeaders: nil,
},
},
"all set (testnet)": {
Mode: string(Online),
Network: Testnet,
Port: "1000",
SkipGethAdmin: "TRUE",
GethHeaders: "X-Auth-Token:12345-ABCDE,X-Api-Version:2",
cfg: &Configuration{
Mode: Online,
Network: &types.NetworkIdentifier{
Expand All @@ -157,6 +169,10 @@ func TestLoadConfiguration(t *testing.T) {
GethURL: DefaultGethURL,
GethArguments: ethereum.RopstenGethArguments,
SkipGethAdmin: true,
GethHeaders: []*ethereum.HTTPHeader{
{Key: "X-Auth-Token", Value: "12345-ABCDE"},
{Key: "X-Api-Version", Value: "2"},
},
},
},
"invalid mode": {
Expand Down Expand Up @@ -186,6 +202,7 @@ func TestLoadConfiguration(t *testing.T) {
os.Setenv(PortEnv, test.Port)
os.Setenv(GethEnv, test.Geth)
os.Setenv(SkipGethAdminEnv, test.SkipGethAdmin)
os.Setenv(GethHeadersEnv, test.GethHeaders)

cfg, err := LoadConfiguration()
if test.err != nil {
Expand Down
8 changes: 6 additions & 2 deletions ethereum/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,24 @@ type Client struct {
}

// NewClient creates a Client that from the provided url and params.
func NewClient(url string, params *params.ChainConfig, skipAdminCalls bool) (*Client, error) {
func NewClient(url string, params *params.ChainConfig, skipAdminCalls bool, headers []*HTTPHeader) (*Client, error) {
c, err := rpc.DialHTTPWithClient(url, &http.Client{
Timeout: gethHTTPTimeout,
})
if err != nil {
return nil, fmt.Errorf("%w: unable to dial node", err)
}

for _, header := range headers {
c.SetHeader(header.Key, header.Value)
}

tc, err := loadTraceConfig()
if err != nil {
return nil, fmt.Errorf("%w: unable to load trace config", err)
}

g, err := newGraphQLClient(url)
g, err := newGraphQLClient(url, headers)
if err != nil {
return nil, fmt.Errorf("%w: unable to create GraphQL client", err)
}
Expand Down
16 changes: 11 additions & 5 deletions ethereum/graphql_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ const (
// GraphQLClient is a client used to make graphQL
// queries to geth's graphql endpoint.
type GraphQLClient struct {
client *http.Client
url string
client *http.Client
url string
headers []*HTTPHeader
}

// Query makes a query to the graphQL endpoint.
Expand All @@ -56,6 +57,10 @@ func (g *GraphQLClient) Query(ctx context.Context, input string) (string, error)
g.url,
bytes.NewBuffer(jsonValue),
)
for _, header := range g.headers {
request.Header.Set(header.Key, header.Value)
}

if err != nil {
return "", err
}
Expand All @@ -74,7 +79,7 @@ func (g *GraphQLClient) Query(ctx context.Context, input string) (string, error)
return string(data), nil
}

func newGraphQLClient(baseURL string) (*GraphQLClient, error) {
func newGraphQLClient(baseURL string, headers []*HTTPHeader) (*GraphQLClient, error) {
// Compute GraphQL Endpoint
u, err := url.Parse(baseURL)
if err != nil {
Expand All @@ -97,7 +102,8 @@ func newGraphQLClient(baseURL string) (*GraphQLClient, error) {
client.Transport = customTransport

return &GraphQLClient{
client: client,
url: u.String(),
client: client,
url: u.String(),
headers: headers,
}, nil
}
6 changes: 6 additions & 0 deletions ethereum/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ type GraphQL interface {
Query(ctx context.Context, input string) (string, error)
}

// HTTPHeader is key, value pair to be set on the HTTP and GraphQL client.
type HTTPHeader struct {
Key string
Value string
}

// CallType returns a boolean indicating
// if the provided trace type is a call type.
func CallType(t string) bool {
Expand Down