30
vendor/github.com/status-im/status-go/rpc/README.md
generated
vendored
Normal file
30
vendor/github.com/status-im/status-go/rpc/README.md
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# rpc [](https://godoc.org/github.com/status-im/status-go/rpc)
|
||||
Package rpc - JSON-RPC client with custom routing.
|
||||
|
||||
Download:
|
||||
```shell
|
||||
go get github.com/status-im/status-go/rpc
|
||||
```
|
||||
|
||||
* * *
|
||||
Package rpc - JSON-RPC client with custom routing.
|
||||
|
||||
Package rpc implements status-go JSON-RPC client and handles
|
||||
requests to different endpoints: upstream or local node.
|
||||
|
||||
Every JSON-RPC request coming from either JS code or any other part
|
||||
of status-go should use this package to be handled and routed properly.
|
||||
|
||||
Routing rules are following:
|
||||
|
||||
- if Upstream is disabled, everything is routed to local ethereum-go node
|
||||
- otherwise, some requests (from the list, see below) are routed to upstream, others - locally.
|
||||
|
||||
List of methods to be routed is currently available here: https://docs.google.com/spreadsheets/d/1N1nuzVN5tXoDmzkBLeC9_mwIlVH8DGF7YD2XwxA8BAE/edit#gid=0
|
||||
|
||||
Note, upon creation of a new client, it ok to be offline - client will keep trying to reconnect in background.
|
||||
|
||||
|
||||
|
||||
* * *
|
||||
Automatically generated by [autoreadme](https://github.com/jimmyfrasche/autoreadme) on 2017.09.18
|
||||
220
vendor/github.com/status-im/status-go/rpc/call_raw.go
generated
vendored
Normal file
220
vendor/github.com/status-im/status-go/rpc/call_raw.go
generated
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonrpcVersion = "2.0"
|
||||
errInvalidMessageCode = -32700 // from go-ethereum/rpc/errors.go
|
||||
)
|
||||
|
||||
// for JSON-RPC responses obtained via CallRaw(), we have no way
|
||||
// to know ID field from actual response. web3.js (primary and
|
||||
// only user of CallRaw()) will validate response by checking
|
||||
// ID field for being a number:
|
||||
// https://github.com/ethereum/web3.js/blob/develop/lib/web3/jsonrpc.js#L66
|
||||
// thus, we will use zero ID as a workaround of this limitation
|
||||
var defaultMsgID = json.RawMessage(`0`)
|
||||
|
||||
// CallRaw performs a JSON-RPC call with already crafted JSON-RPC body. It
|
||||
// returns string in JSON format with response (successul or error).
|
||||
func (c *Client) CallRaw(body string) string {
|
||||
ctx := context.Background()
|
||||
return c.callRawContext(ctx, json.RawMessage(body))
|
||||
}
|
||||
|
||||
// jsonrpcMessage represents JSON-RPC message
|
||||
type jsonrpcMessage struct {
|
||||
Version string `json:"jsonrpc"`
|
||||
ID json.RawMessage `json:"id"`
|
||||
}
|
||||
|
||||
type jsonrpcRequest struct {
|
||||
jsonrpcMessage
|
||||
ChainID uint64 `json:"chainId"`
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
type jsonrpcSuccessfulResponse struct {
|
||||
jsonrpcMessage
|
||||
Result json.RawMessage `json:"result"`
|
||||
}
|
||||
|
||||
type jsonrpcErrorResponse struct {
|
||||
jsonrpcMessage
|
||||
Error jsonError `json:"error"`
|
||||
}
|
||||
|
||||
// jsonError represents Error message for JSON-RPC responses.
|
||||
type jsonError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// callRawContext performs a JSON-RPC call with already crafted JSON-RPC body and
|
||||
// given context. It returns string in JSON format with response (successful or error).
|
||||
//
|
||||
// TODO(divan): this function exists for compatibility and uses default
|
||||
// go-ethereum's RPC client under the hood. It adds some unnecessary overhead
|
||||
// by first marshalling JSON string into object to use with normal Call,
|
||||
// which is then umarshalled back to the same JSON. The same goes with response.
|
||||
// This is waste of CPU and memory and should be avoided if possible,
|
||||
// either by changing exported API (provide only Call, not CallRaw) or
|
||||
// refactoring go-ethereum's client to allow using raw JSON directly.
|
||||
func (c *Client) callRawContext(ctx context.Context, body json.RawMessage) string {
|
||||
if isBatch(body) {
|
||||
return c.callBatchMethods(ctx, body)
|
||||
}
|
||||
|
||||
return c.callSingleMethod(ctx, body)
|
||||
}
|
||||
|
||||
// callBatchMethods handles batched JSON-RPC requests, calling each of
|
||||
// individual requests one by one and constructing proper batched response.
|
||||
//
|
||||
// See http://www.jsonrpc.org/specification#batch for details.
|
||||
//
|
||||
// We can't use gethtrpc.BatchCall here, because each call should go through
|
||||
// our routing logic and router to corresponding destination.
|
||||
func (c *Client) callBatchMethods(ctx context.Context, msgs json.RawMessage) string {
|
||||
var requests []json.RawMessage
|
||||
|
||||
err := json.Unmarshal(msgs, &requests)
|
||||
if err != nil {
|
||||
return newErrorResponse(errInvalidMessageCode, err, defaultMsgID)
|
||||
}
|
||||
|
||||
// run all methods sequentially, this seems to be main
|
||||
// objective to use batched requests.
|
||||
// See: https://github.com/ethereum/wiki/wiki/JavaScript-API#batch-requests
|
||||
responses := make([]json.RawMessage, len(requests))
|
||||
for i := range requests {
|
||||
resp := c.callSingleMethod(ctx, requests[i])
|
||||
responses[i] = json.RawMessage(resp)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(responses)
|
||||
if err != nil {
|
||||
c.log.Error("Failed to marshal batch responses:", "error", err)
|
||||
return newErrorResponse(errInvalidMessageCode, err, defaultMsgID)
|
||||
}
|
||||
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// callSingleMethod executes single JSON-RPC message and constructs proper response.
|
||||
func (c *Client) callSingleMethod(ctx context.Context, msg json.RawMessage) string {
|
||||
// unmarshal JSON body into json-rpc request
|
||||
chainID, method, params, id, err := methodAndParamsFromBody(msg)
|
||||
if err != nil {
|
||||
return newErrorResponse(errInvalidMessageCode, err, id)
|
||||
}
|
||||
|
||||
if chainID == 0 {
|
||||
chainID = c.UpstreamChainID
|
||||
}
|
||||
|
||||
// route and execute
|
||||
var result json.RawMessage
|
||||
err = c.CallContext(ctx, &result, chainID, method, params...)
|
||||
|
||||
// as we have to return original JSON, we have to
|
||||
// analyze returned error and reconstruct original
|
||||
// JSON error response.
|
||||
if err != nil && err != gethrpc.ErrNoResult {
|
||||
if er, ok := err.(gethrpc.Error); ok {
|
||||
return newErrorResponse(er.ErrorCode(), err, id)
|
||||
}
|
||||
|
||||
return newErrorResponse(errInvalidMessageCode, err, id)
|
||||
}
|
||||
|
||||
// finally, marshal answer
|
||||
return newSuccessResponse(result, id)
|
||||
}
|
||||
|
||||
// methodAndParamsFromBody extracts Method and Params of
|
||||
// JSON-RPC body into values ready to use with ethereum-go's
|
||||
// RPC client Call() function. A lot of empty interface usage is
|
||||
// due to the underlying code design :/
|
||||
func methodAndParamsFromBody(body json.RawMessage) (uint64, string, []interface{}, json.RawMessage, error) {
|
||||
msg, err := unmarshalMessage(body)
|
||||
if err != nil {
|
||||
return 0, "", nil, nil, err
|
||||
}
|
||||
params := []interface{}{}
|
||||
if msg.Params != nil {
|
||||
err = json.Unmarshal(msg.Params, ¶ms)
|
||||
if err != nil {
|
||||
return 0, "", nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return msg.ChainID, msg.Method, params, msg.ID, nil
|
||||
}
|
||||
|
||||
// unmarshalMessage tries to unmarshal JSON-RPC message.
|
||||
func unmarshalMessage(body json.RawMessage) (*jsonrpcRequest, error) {
|
||||
var msg jsonrpcRequest
|
||||
err := json.Unmarshal(body, &msg)
|
||||
return &msg, err
|
||||
}
|
||||
|
||||
func newSuccessResponse(result json.RawMessage, id json.RawMessage) string {
|
||||
if id == nil {
|
||||
id = defaultMsgID
|
||||
}
|
||||
|
||||
msg := &jsonrpcSuccessfulResponse{
|
||||
jsonrpcMessage: jsonrpcMessage{
|
||||
ID: id,
|
||||
Version: jsonrpcVersion,
|
||||
},
|
||||
Result: result,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return newErrorResponse(errInvalidMessageCode, err, id)
|
||||
}
|
||||
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func newErrorResponse(code int, err error, id json.RawMessage) string {
|
||||
if id == nil {
|
||||
id = defaultMsgID
|
||||
}
|
||||
|
||||
errMsg := &jsonrpcErrorResponse{
|
||||
jsonrpcMessage: jsonrpcMessage{
|
||||
ID: id,
|
||||
Version: jsonrpcVersion,
|
||||
},
|
||||
Error: jsonError{
|
||||
Code: code,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(errMsg)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// isBatch returns true when the first non-whitespace characters is '['
|
||||
// code from go-ethereum's rpc client (rpc/client.go)
|
||||
func isBatch(msg json.RawMessage) bool {
|
||||
for _, c := range msg {
|
||||
// skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt)
|
||||
if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d {
|
||||
continue
|
||||
}
|
||||
return c == '['
|
||||
}
|
||||
return false
|
||||
}
|
||||
906
vendor/github.com/status-im/status-go/rpc/chain/client.go
generated
vendored
Normal file
906
vendor/github.com/status-im/status-go/rpc/chain/client.go
generated
vendored
Normal file
@@ -0,0 +1,906 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/afex/hystrix-go/hystrix"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/status-im/status-go/services/rpcstats"
|
||||
)
|
||||
|
||||
type FeeHistory struct {
|
||||
BaseFeePerGas []string `json:"baseFeePerGas"`
|
||||
}
|
||||
|
||||
type BatchCallClient interface {
|
||||
BatchCallContext(ctx context.Context, b []rpc.BatchElem) error
|
||||
}
|
||||
|
||||
type ClientInterface interface {
|
||||
BatchCallContext(ctx context.Context, b []rpc.BatchElem) error
|
||||
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
||||
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||
BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
|
||||
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
|
||||
FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error)
|
||||
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||
FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*FullTransaction, error)
|
||||
GetBaseFeeFromBlock(blockNumber *big.Int) (string, error)
|
||||
NetworkID() uint64
|
||||
ToBigInt() *big.Int
|
||||
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
|
||||
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||
CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
|
||||
GetWalletNotifier() func(chainId uint64, message string)
|
||||
SetWalletNotifier(notifier func(chainId uint64, message string))
|
||||
TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error)
|
||||
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
|
||||
BlockNumber(ctx context.Context) (uint64, error)
|
||||
SetIsConnected(value bool)
|
||||
GetIsConnected() bool
|
||||
bind.ContractCaller
|
||||
bind.ContractTransactor
|
||||
bind.ContractFilterer
|
||||
}
|
||||
|
||||
type ClientWithFallback struct {
|
||||
ChainID uint64
|
||||
main *ethclient.Client
|
||||
fallback *ethclient.Client
|
||||
|
||||
mainRPC *rpc.Client
|
||||
fallbackRPC *rpc.Client
|
||||
|
||||
WalletNotifier func(chainId uint64, message string)
|
||||
|
||||
IsConnected bool
|
||||
IsConnectedLock sync.RWMutex
|
||||
LastCheckedAt int64
|
||||
}
|
||||
|
||||
// Don't mark connection as failed if we get one of these errors
|
||||
var propagateErrors = []error{
|
||||
vm.ErrOutOfGas,
|
||||
vm.ErrCodeStoreOutOfGas,
|
||||
vm.ErrDepth,
|
||||
vm.ErrInsufficientBalance,
|
||||
vm.ErrContractAddressCollision,
|
||||
vm.ErrExecutionReverted,
|
||||
vm.ErrMaxCodeSizeExceeded,
|
||||
vm.ErrInvalidJump,
|
||||
vm.ErrWriteProtection,
|
||||
vm.ErrReturnDataOutOfBounds,
|
||||
vm.ErrGasUintOverflow,
|
||||
vm.ErrInvalidCode,
|
||||
vm.ErrNonceUintOverflow,
|
||||
|
||||
// Used by balance history to check state
|
||||
ethereum.NotFound,
|
||||
bind.ErrNoCode,
|
||||
}
|
||||
|
||||
type CommandResult struct {
|
||||
res1 any
|
||||
res2 any
|
||||
vmError error
|
||||
}
|
||||
|
||||
func NewSimpleClient(main *rpc.Client, chainID uint64) *ClientWithFallback {
|
||||
hystrix.ConfigureCommand(fmt.Sprintf("ethClient_%d", chainID), hystrix.CommandConfig{
|
||||
Timeout: 10000,
|
||||
MaxConcurrentRequests: 100,
|
||||
SleepWindow: 300000,
|
||||
ErrorPercentThreshold: 25,
|
||||
})
|
||||
|
||||
return &ClientWithFallback{
|
||||
ChainID: chainID,
|
||||
main: ethclient.NewClient(main),
|
||||
fallback: nil,
|
||||
mainRPC: main,
|
||||
fallbackRPC: nil,
|
||||
IsConnected: true,
|
||||
LastCheckedAt: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient(main, fallback *rpc.Client, chainID uint64) *ClientWithFallback {
|
||||
hystrix.ConfigureCommand(fmt.Sprintf("ethClient_%d", chainID), hystrix.CommandConfig{
|
||||
Timeout: 20000,
|
||||
MaxConcurrentRequests: 100,
|
||||
SleepWindow: 300000,
|
||||
ErrorPercentThreshold: 25,
|
||||
})
|
||||
|
||||
var fallbackEthClient *ethclient.Client
|
||||
if fallback != nil {
|
||||
fallbackEthClient = ethclient.NewClient(fallback)
|
||||
}
|
||||
return &ClientWithFallback{
|
||||
ChainID: chainID,
|
||||
main: ethclient.NewClient(main),
|
||||
fallback: fallbackEthClient,
|
||||
mainRPC: main,
|
||||
fallbackRPC: fallback,
|
||||
IsConnected: true,
|
||||
LastCheckedAt: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) Close() {
|
||||
c.main.Close()
|
||||
if c.fallback != nil {
|
||||
c.fallback.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func isVMError(err error) bool {
|
||||
if strings.HasPrefix(err.Error(), "execution reverted") {
|
||||
return true
|
||||
}
|
||||
for _, vmError := range propagateErrors {
|
||||
if err == vmError {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SetIsConnected(value bool) {
|
||||
c.IsConnectedLock.Lock()
|
||||
defer c.IsConnectedLock.Unlock()
|
||||
c.LastCheckedAt = time.Now().Unix()
|
||||
if !value {
|
||||
if c.IsConnected {
|
||||
if c.WalletNotifier != nil {
|
||||
c.WalletNotifier(c.ChainID, "down")
|
||||
}
|
||||
c.IsConnected = false
|
||||
}
|
||||
|
||||
} else {
|
||||
if !c.IsConnected {
|
||||
c.IsConnected = true
|
||||
if c.WalletNotifier != nil {
|
||||
c.WalletNotifier(c.ChainID, "up")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) GetIsConnected() bool {
|
||||
c.IsConnectedLock.RLock()
|
||||
defer c.IsConnectedLock.RUnlock()
|
||||
return c.IsConnected
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) makeCallNoReturn(main func() error, fallback func() error) error {
|
||||
resultChan := make(chan CommandResult, 1)
|
||||
c.LastCheckedAt = time.Now().Unix()
|
||||
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
|
||||
err := main()
|
||||
if err != nil {
|
||||
if isVMError(err) {
|
||||
resultChan <- CommandResult{vmError: err}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
c.SetIsConnected(true)
|
||||
resultChan <- CommandResult{}
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
if c.fallback == nil {
|
||||
c.SetIsConnected(false)
|
||||
return err
|
||||
}
|
||||
|
||||
err = fallback()
|
||||
if err != nil {
|
||||
if isVMError(err) {
|
||||
resultChan <- CommandResult{vmError: err}
|
||||
return nil
|
||||
}
|
||||
c.SetIsConnected(false)
|
||||
return err
|
||||
}
|
||||
resultChan <- CommandResult{}
|
||||
return nil
|
||||
})
|
||||
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
if result.vmError != nil {
|
||||
return result.vmError
|
||||
}
|
||||
return nil
|
||||
case err := <-errChan:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) makeCallSingleReturn(main func() (any, error), fallback func() (any, error), toggleIsConnected bool) (any, error) {
|
||||
resultChan := make(chan CommandResult, 1)
|
||||
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
|
||||
res, err := main()
|
||||
if err != nil {
|
||||
if isVMError(err) {
|
||||
resultChan <- CommandResult{vmError: err}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if toggleIsConnected {
|
||||
c.SetIsConnected(true)
|
||||
}
|
||||
resultChan <- CommandResult{res1: res}
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
if c.fallback == nil {
|
||||
if toggleIsConnected {
|
||||
c.SetIsConnected(false)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := fallback()
|
||||
if err != nil {
|
||||
if isVMError(err) {
|
||||
resultChan <- CommandResult{vmError: err}
|
||||
return nil
|
||||
}
|
||||
if toggleIsConnected {
|
||||
c.SetIsConnected(false)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if toggleIsConnected {
|
||||
c.SetIsConnected(true)
|
||||
}
|
||||
resultChan <- CommandResult{res1: res}
|
||||
return nil
|
||||
})
|
||||
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
if result.vmError != nil {
|
||||
return nil, result.vmError
|
||||
}
|
||||
return result.res1, nil
|
||||
case err := <-errChan:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) makeCallDoubleReturn(main func() (any, any, error), fallback func() (any, any, error)) (any, any, error) {
|
||||
resultChan := make(chan CommandResult, 1)
|
||||
c.LastCheckedAt = time.Now().Unix()
|
||||
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
|
||||
a, b, err := main()
|
||||
if err != nil {
|
||||
if isVMError(err) {
|
||||
resultChan <- CommandResult{vmError: err}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
c.SetIsConnected(true)
|
||||
resultChan <- CommandResult{res1: a, res2: b}
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
if c.fallback == nil {
|
||||
c.SetIsConnected(false)
|
||||
return err
|
||||
}
|
||||
|
||||
a, b, err := fallback()
|
||||
if err != nil {
|
||||
if isVMError(err) {
|
||||
resultChan <- CommandResult{vmError: err}
|
||||
return nil
|
||||
}
|
||||
c.SetIsConnected(false)
|
||||
return err
|
||||
}
|
||||
c.SetIsConnected(true)
|
||||
resultChan <- CommandResult{res1: a, res2: b}
|
||||
return nil
|
||||
})
|
||||
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
if result.vmError != nil {
|
||||
return nil, nil, result.vmError
|
||||
}
|
||||
return result.res1, result.res2, nil
|
||||
case err := <-errChan:
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
rpcstats.CountCall("eth_BlockByHash")
|
||||
|
||||
block, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BlockByHash(ctx, hash) },
|
||||
func() (any, error) { return c.fallback.BlockByHash(ctx, hash) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return block.(*types.Block), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||
rpcstats.CountCall("eth_BlockByNumber")
|
||||
block, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BlockByNumber(ctx, number) },
|
||||
func() (any, error) { return c.fallback.BlockByNumber(ctx, number) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return block.(*types.Block), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BlockNumber(ctx context.Context) (uint64, error) {
|
||||
rpcstats.CountCall("eth_BlockNumber")
|
||||
|
||||
number, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BlockNumber(ctx) },
|
||||
func() (any, error) { return c.fallback.BlockNumber(ctx) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return number.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PeerCount(ctx context.Context) (uint64, error) {
|
||||
rpcstats.CountCall("eth_PeerCount")
|
||||
|
||||
peerCount, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PeerCount(ctx) },
|
||||
func() (any, error) { return c.fallback.PeerCount(ctx) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return peerCount.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||
rpcstats.CountCall("eth_HeaderByHash")
|
||||
header, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.HeaderByHash(ctx, hash) },
|
||||
func() (any, error) { return c.fallback.HeaderByHash(ctx, hash) },
|
||||
false,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return header.(*types.Header), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||
rpcstats.CountCall("eth_HeaderByNumber")
|
||||
header, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.HeaderByNumber(ctx, number) },
|
||||
func() (any, error) { return c.fallback.HeaderByNumber(ctx, number) },
|
||||
false,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return header.(*types.Header), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
|
||||
rpcstats.CountCall("eth_TransactionByHash")
|
||||
|
||||
tx, isPending, err := c.makeCallDoubleReturn(
|
||||
func() (any, any, error) { return c.main.TransactionByHash(ctx, hash) },
|
||||
func() (any, any, error) { return c.fallback.TransactionByHash(ctx, hash) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return tx.(*types.Transaction), isPending.(bool), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) {
|
||||
rpcstats.CountCall("eth_TransactionSender")
|
||||
|
||||
address, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionSender(ctx, tx, block, index) },
|
||||
func() (any, error) { return c.fallback.TransactionSender(ctx, tx, block, index) },
|
||||
true,
|
||||
)
|
||||
|
||||
return address.(common.Address), err
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
||||
rpcstats.CountCall("eth_TransactionCount")
|
||||
|
||||
count, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionCount(ctx, blockHash) },
|
||||
func() (any, error) { return c.fallback.TransactionCount(ctx, blockHash) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count.(uint), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
||||
rpcstats.CountCall("eth_TransactionInBlock")
|
||||
|
||||
transactions, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionInBlock(ctx, blockHash, index) },
|
||||
func() (any, error) { return c.fallback.TransactionInBlock(ctx, blockHash, index) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transactions.(*types.Transaction), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||
rpcstats.CountCall("eth_TransactionReceipt")
|
||||
|
||||
receipt, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionReceipt(ctx, txHash) },
|
||||
func() (any, error) { return c.fallback.TransactionReceipt(ctx, txHash) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return receipt.(*types.Receipt), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
|
||||
rpcstats.CountCall("eth_SyncProgress")
|
||||
|
||||
progress, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SyncProgress(ctx) },
|
||||
func() (any, error) { return c.fallback.SyncProgress(ctx) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return progress.(*ethereum.SyncProgress), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
|
||||
rpcstats.CountCall("eth_SubscribeNewHead")
|
||||
|
||||
sub, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SubscribeNewHead(ctx, ch) },
|
||||
func() (any, error) { return c.fallback.SubscribeNewHead(ctx, ch) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sub.(ethereum.Subscription), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) NetworkID() uint64 {
|
||||
return c.ChainID
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_BalanceAt")
|
||||
|
||||
balance, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BalanceAt(ctx, account, blockNumber) },
|
||||
func() (any, error) { return c.fallback.BalanceAt(ctx, account, blockNumber) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return balance.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_StorageAt")
|
||||
|
||||
storage, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.StorageAt(ctx, account, key, blockNumber) },
|
||||
func() (any, error) { return c.fallback.StorageAt(ctx, account, key, blockNumber) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storage.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_CodeAt")
|
||||
|
||||
code, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.CodeAt(ctx, account, blockNumber) },
|
||||
func() (any, error) { return c.fallback.CodeAt(ctx, account, blockNumber) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return code.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
rpcstats.CountCall("eth_NonceAt")
|
||||
|
||||
nonce, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.NonceAt(ctx, account, blockNumber) },
|
||||
func() (any, error) { return c.fallback.NonceAt(ctx, account, blockNumber) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nonce.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
|
||||
rpcstats.CountCall("eth_FilterLogs")
|
||||
|
||||
logs, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.FilterLogs(ctx, q) },
|
||||
func() (any, error) { return c.fallback.FilterLogs(ctx, q) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logs.([]types.Log), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
||||
rpcstats.CountCall("eth_SubscribeFilterLogs")
|
||||
|
||||
sub, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SubscribeFilterLogs(ctx, q, ch) },
|
||||
func() (any, error) { return c.fallback.SubscribeFilterLogs(ctx, q, ch) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sub.(ethereum.Subscription), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_PendingBalanceAt")
|
||||
|
||||
balance, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingBalanceAt(ctx, account) },
|
||||
func() (any, error) { return c.fallback.PendingBalanceAt(ctx, account) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return balance.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_PendingStorageAt")
|
||||
|
||||
storage, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingStorageAt(ctx, account, key) },
|
||||
func() (any, error) { return c.fallback.PendingStorageAt(ctx, account, key) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storage.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_PendingCodeAt")
|
||||
|
||||
code, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingCodeAt(ctx, account) },
|
||||
func() (any, error) { return c.fallback.PendingCodeAt(ctx, account) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return code.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||
rpcstats.CountCall("eth_PendingNonceAt")
|
||||
|
||||
nonce, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingNonceAt(ctx, account) },
|
||||
func() (any, error) { return c.fallback.PendingNonceAt(ctx, account) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nonce.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||
rpcstats.CountCall("eth_PendingTransactionCount")
|
||||
|
||||
count, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingTransactionCount(ctx) },
|
||||
func() (any, error) { return c.fallback.PendingTransactionCount(ctx) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count.(uint), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_CallContract_" + msg.To.String())
|
||||
|
||||
data, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.CallContract(ctx, msg, blockNumber) },
|
||||
func() (any, error) { return c.fallback.CallContract(ctx, msg, blockNumber) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_CallContractAtHash")
|
||||
|
||||
data, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.CallContractAtHash(ctx, msg, blockHash) },
|
||||
func() (any, error) { return c.fallback.CallContractAtHash(ctx, msg, blockHash) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_PendingCallContract")
|
||||
|
||||
data, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingCallContract(ctx, msg) },
|
||||
func() (any, error) { return c.fallback.PendingCallContract(ctx, msg) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_SuggestGasPrice")
|
||||
|
||||
gasPrice, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SuggestGasPrice(ctx) },
|
||||
func() (any, error) { return c.fallback.SuggestGasPrice(ctx) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gasPrice.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_SuggestGasTipCap")
|
||||
|
||||
tip, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SuggestGasTipCap(ctx) },
|
||||
func() (any, error) { return c.fallback.SuggestGasTipCap(ctx) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tip.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) {
|
||||
rpcstats.CountCall("eth_FeeHistory")
|
||||
|
||||
feeHistory, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) },
|
||||
func() (any, error) { return c.fallback.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return feeHistory.(*ethereum.FeeHistory), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
|
||||
rpcstats.CountCall("eth_EstimateGas")
|
||||
|
||||
estimate, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.EstimateGas(ctx, msg) },
|
||||
func() (any, error) { return c.fallback.EstimateGas(ctx, msg) },
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return estimate.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||
rpcstats.CountCall("eth_SendTransaction")
|
||||
|
||||
return c.makeCallNoReturn(
|
||||
func() error { return c.main.SendTransaction(ctx, tx) },
|
||||
func() error { return c.fallback.SendTransaction(ctx, tx) },
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
rpcstats.CountCall("eth_CallContext")
|
||||
|
||||
return c.makeCallNoReturn(
|
||||
func() error { return c.mainRPC.CallContext(ctx, result, method, args...) },
|
||||
func() error { return c.fallbackRPC.CallContext(ctx, result, method, args...) },
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
|
||||
rpcstats.CountCall("eth_BatchCallContext")
|
||||
|
||||
return c.makeCallNoReturn(
|
||||
func() error { return c.mainRPC.BatchCallContext(ctx, b) },
|
||||
func() error { return c.fallbackRPC.BatchCallContext(ctx, b) },
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) ToBigInt() *big.Int {
|
||||
return big.NewInt(int64(c.ChainID))
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) GetBaseFeeFromBlock(blockNumber *big.Int) (string, error) {
|
||||
rpcstats.CountCall("eth_GetBaseFeeFromBlock")
|
||||
var feeHistory FeeHistory
|
||||
err := c.mainRPC.Call(&feeHistory, "eth_feeHistory", "0x1", (*hexutil.Big)(blockNumber), nil)
|
||||
if err != nil {
|
||||
if err.Error() == "the method eth_feeHistory does not exist/is not available" {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
var baseGasFee string = ""
|
||||
if len(feeHistory.BaseFeePerGas) > 0 {
|
||||
baseGasFee = feeHistory.BaseFeePerGas[0]
|
||||
}
|
||||
|
||||
return baseGasFee, err
|
||||
}
|
||||
|
||||
// go-ethereum's `Transaction` items drop the blkHash obtained during the RPC call.
|
||||
// This function preserves the additional data. This is the cheapest way to obtain
|
||||
// the block hash for a given block number.
|
||||
func (c *ClientWithFallback) FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*FullTransaction, error) {
|
||||
rpcstats.CountCall("eth_FullTransactionByBlockNumberAndIndex")
|
||||
|
||||
tx, err := c.makeCallSingleReturn(
|
||||
func() (any, error) {
|
||||
return callFullTransactionByBlockNumberAndIndex(ctx, c.mainRPC, blockNumber, index)
|
||||
},
|
||||
func() (any, error) {
|
||||
return callFullTransactionByBlockNumberAndIndex(ctx, c.fallbackRPC, blockNumber, index)
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tx.(*FullTransaction), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) GetWalletNotifier() func(chainId uint64, message string) {
|
||||
return c.WalletNotifier
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SetWalletNotifier(notifier func(chainId uint64, message string)) {
|
||||
c.WalletNotifier = notifier
|
||||
}
|
||||
108
vendor/github.com/status-im/status-go/rpc/chain/rpc.go
generated
vendored
Normal file
108
vendor/github.com/status-im/status-go/rpc/chain/rpc.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// The code below is mostly copied from go-ethereum/ethclient (see TransactionInBlock), to keep the exact same behavior as the
|
||||
// normal Transaction items, but exposing the additional data obtained in the `rpcTransaction` struct`.
|
||||
// Unfortunately, the functions and classes used are not exposed outside of the package.
|
||||
type FullTransaction struct {
|
||||
Tx *types.Transaction
|
||||
TxExtraInfo
|
||||
}
|
||||
|
||||
type TxExtraInfo struct {
|
||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||
From *common.Address `json:"from,omitempty"`
|
||||
}
|
||||
|
||||
func callFullTransactionByBlockNumberAndIndex(ctx context.Context, rpc *rpc.Client, number *big.Int, index uint) (*FullTransaction, error) {
|
||||
var json *FullTransaction
|
||||
err := rpc.CallContext(ctx, &json, "eth_getTransactionByBlockNumberAndIndex", toBlockNumArg(number), hexutil.Uint64(index))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if json == nil {
|
||||
return nil, ethereum.NotFound
|
||||
} else if _, r, _ := json.Tx.RawSignatureValues(); r == nil {
|
||||
return nil, fmt.Errorf("server returned transaction without signature")
|
||||
}
|
||||
if json.From != nil && json.BlockHash != nil {
|
||||
setSenderFromServer(json.Tx, *json.From, *json.BlockHash)
|
||||
}
|
||||
return json, nil
|
||||
}
|
||||
|
||||
func (tx *FullTransaction) UnmarshalJSON(msg []byte) error {
|
||||
if err := json.Unmarshal(msg, &tx.Tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(msg, &tx.TxExtraInfo)
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
}
|
||||
pending := big.NewInt(-1)
|
||||
if number.Cmp(pending) == 0 {
|
||||
return "pending"
|
||||
}
|
||||
finalized := big.NewInt(int64(rpc.FinalizedBlockNumber))
|
||||
if number.Cmp(finalized) == 0 {
|
||||
return "finalized"
|
||||
}
|
||||
safe := big.NewInt(int64(rpc.SafeBlockNumber))
|
||||
if number.Cmp(safe) == 0 {
|
||||
return "safe"
|
||||
}
|
||||
return hexutil.EncodeBig(number)
|
||||
}
|
||||
|
||||
type senderFromServer struct {
|
||||
addr common.Address
|
||||
blockhash common.Hash
|
||||
}
|
||||
|
||||
var errNotCached = errors.New("sender not cached")
|
||||
|
||||
//nolint:errcheck
|
||||
func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) {
|
||||
// Use types.Sender for side-effect to store our signer into the cache.
|
||||
types.Sender(&senderFromServer{addr, block}, tx)
|
||||
}
|
||||
|
||||
func (s *senderFromServer) Equal(other types.Signer) bool {
|
||||
os, ok := other.(*senderFromServer)
|
||||
return ok && os.blockhash == s.blockhash
|
||||
}
|
||||
|
||||
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) {
|
||||
if s.addr == (common.Address{}) {
|
||||
return common.Address{}, errNotCached
|
||||
}
|
||||
return s.addr, nil
|
||||
}
|
||||
|
||||
func (s *senderFromServer) ChainID() *big.Int {
|
||||
panic("can't sign with senderFromServer")
|
||||
}
|
||||
func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash {
|
||||
panic("can't sign with senderFromServer")
|
||||
}
|
||||
func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
panic("can't sign with senderFromServer")
|
||||
}
|
||||
352
vendor/github.com/status-im/status-go/rpc/client.go
generated
vendored
Normal file
352
vendor/github.com/status-im/status-go/rpc/client.go
generated
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/rpc/network"
|
||||
"github.com/status-im/status-go/services/rpcstats"
|
||||
"github.com/status-im/status-go/services/wallet/common"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultCallTimeout is a default timeout for an RPC call
|
||||
DefaultCallTimeout = time.Minute
|
||||
)
|
||||
|
||||
// List of RPC client errors.
|
||||
var (
|
||||
ErrMethodNotFound = fmt.Errorf("The method does not exist/is not available")
|
||||
)
|
||||
|
||||
// Handler defines handler for RPC methods.
|
||||
type Handler func(context.Context, uint64, ...interface{}) (interface{}, error)
|
||||
|
||||
type ClientInterface interface {
|
||||
AbstractEthClient(chainID common.ChainID) (chain.BatchCallClient, error)
|
||||
}
|
||||
|
||||
// Client represents RPC client with custom routing
|
||||
// scheme. It automatically decides where RPC call
|
||||
// goes - Upstream or Local node.
|
||||
type Client struct {
|
||||
sync.RWMutex
|
||||
|
||||
upstreamEnabled bool
|
||||
upstreamURL string
|
||||
UpstreamChainID uint64
|
||||
|
||||
local *gethrpc.Client
|
||||
upstream chain.ClientInterface
|
||||
rpcClientsMutex sync.RWMutex
|
||||
rpcClients map[uint64]chain.ClientInterface
|
||||
|
||||
router *router
|
||||
NetworkManager *network.Manager
|
||||
|
||||
handlersMx sync.RWMutex // mx guards handlers
|
||||
handlers map[string]Handler // locally registered handlers
|
||||
log log.Logger
|
||||
|
||||
walletNotifier func(chainID uint64, message string)
|
||||
}
|
||||
|
||||
// Is initialized in a build-tag-dependent module
|
||||
var verifProxyInitFn func(c *Client)
|
||||
|
||||
// NewClient initializes Client and tries to connect to both,
|
||||
// upstream and local node.
|
||||
//
|
||||
// Client is safe for concurrent use and will automatically
|
||||
// reconnect to the server if connection is lost.
|
||||
func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.UpstreamRPCConfig, networks []params.Network, db *sql.DB) (*Client, error) {
|
||||
var err error
|
||||
|
||||
log := log.New("package", "status-go/rpc.Client")
|
||||
networkManager := network.NewManager(db)
|
||||
err = networkManager.Init(networks)
|
||||
if err != nil {
|
||||
log.Error("Network manager failed to initialize", "error", err)
|
||||
}
|
||||
|
||||
c := Client{
|
||||
local: client,
|
||||
NetworkManager: networkManager,
|
||||
handlers: make(map[string]Handler),
|
||||
rpcClients: make(map[uint64]chain.ClientInterface),
|
||||
log: log,
|
||||
}
|
||||
|
||||
if upstream.Enabled {
|
||||
c.UpstreamChainID = upstreamChainID
|
||||
c.upstreamEnabled = upstream.Enabled
|
||||
c.upstreamURL = upstream.URL
|
||||
upstreamClient, err := gethrpc.Dial(c.upstreamURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial upstream server: %s", err)
|
||||
}
|
||||
c.upstream = chain.NewSimpleClient(upstreamClient, upstreamChainID)
|
||||
}
|
||||
|
||||
c.router = newRouter(c.upstreamEnabled)
|
||||
|
||||
if verifProxyInitFn != nil {
|
||||
verifProxyInitFn(&c)
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetWalletNotifier(notifier func(chainID uint64, message string)) {
|
||||
c.walletNotifier = notifier
|
||||
}
|
||||
|
||||
func (c *Client) getClientUsingCache(chainID uint64) (chain.ClientInterface, error) {
|
||||
c.rpcClientsMutex.Lock()
|
||||
defer c.rpcClientsMutex.Unlock()
|
||||
if rpcClient, ok := c.rpcClients[chainID]; ok {
|
||||
if rpcClient.GetWalletNotifier() == nil {
|
||||
rpcClient.SetWalletNotifier(c.walletNotifier)
|
||||
}
|
||||
return rpcClient, nil
|
||||
}
|
||||
|
||||
network := c.NetworkManager.Find(chainID)
|
||||
if network == nil {
|
||||
if c.UpstreamChainID == chainID {
|
||||
return c.upstream, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not find network: %d", chainID)
|
||||
}
|
||||
|
||||
rpcClient, err := gethrpc.Dial(network.RPCURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial upstream server: %s", err)
|
||||
}
|
||||
|
||||
var rpcFallbackClient *gethrpc.Client
|
||||
if len(network.FallbackURL) > 0 {
|
||||
rpcFallbackClient, err = gethrpc.Dial(network.FallbackURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial upstream server: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
client := chain.NewClient(rpcClient, rpcFallbackClient, chainID)
|
||||
client.WalletNotifier = c.walletNotifier
|
||||
c.rpcClients[chainID] = client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Ethclient returns ethclient.Client per chain
|
||||
func (c *Client) EthClient(chainID uint64) (chain.ClientInterface, error) {
|
||||
client, err := c.getClientUsingCache(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// AbstractEthClient returns a partial abstraction used by new components for testing purposes
|
||||
func (c *Client) AbstractEthClient(chainID common.ChainID) (chain.BatchCallClient, error) {
|
||||
client, err := c.getClientUsingCache(uint64(chainID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Client) EthClients(chainIDs []uint64) (map[uint64]chain.ClientInterface, error) {
|
||||
clients := make(map[uint64]chain.ClientInterface, 0)
|
||||
for _, chainID := range chainIDs {
|
||||
client, err := c.getClientUsingCache(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clients[chainID] = client
|
||||
}
|
||||
|
||||
return clients, nil
|
||||
}
|
||||
|
||||
// SetClient strictly for testing purposes
|
||||
func (c *Client) SetClient(chainID uint64, client chain.ClientInterface) {
|
||||
c.rpcClientsMutex.Lock()
|
||||
defer c.rpcClientsMutex.Unlock()
|
||||
c.rpcClients[chainID] = client
|
||||
}
|
||||
|
||||
// UpdateUpstreamURL changes the upstream RPC client URL, if the upstream is enabled.
|
||||
func (c *Client) UpdateUpstreamURL(url string) error {
|
||||
if c.upstream == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rpcClient, err := gethrpc.Dial(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
c.upstream = chain.NewSimpleClient(rpcClient, c.UpstreamChainID)
|
||||
c.upstreamURL = url
|
||||
c.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Call performs a JSON-RPC call with the given arguments and unmarshals into
|
||||
// result if no error occurred.
|
||||
//
|
||||
// The result must be a pointer so that package json can unmarshal into it. You
|
||||
// can also pass nil, in which case the result is ignored.
|
||||
//
|
||||
// It uses custom routing scheme for calls.
|
||||
func (c *Client) Call(result interface{}, chainID uint64, method string, args ...interface{}) error {
|
||||
ctx := context.Background()
|
||||
return c.CallContext(ctx, result, chainID, method, args...)
|
||||
}
|
||||
|
||||
// CallContext performs a JSON-RPC call with the given arguments. If the context is
|
||||
// canceled before the call has successfully returned, CallContext returns immediately.
|
||||
//
|
||||
// The result must be a pointer so that package json can unmarshal into it. You
|
||||
// can also pass nil, in which case the result is ignored.
|
||||
//
|
||||
// It uses custom routing scheme for calls.
|
||||
// If there are any local handlers registered for this call, they will handle it.
|
||||
func (c *Client) CallContext(ctx context.Context, result interface{}, chainID uint64, method string, args ...interface{}) error {
|
||||
rpcstats.CountCall(method)
|
||||
if c.router.routeBlocked(method) {
|
||||
return ErrMethodNotFound
|
||||
}
|
||||
|
||||
// check locally registered handlers first
|
||||
if handler, ok := c.handler(method); ok {
|
||||
return c.callMethod(ctx, result, chainID, handler, args...)
|
||||
}
|
||||
|
||||
return c.CallContextIgnoringLocalHandlers(ctx, result, chainID, method, args...)
|
||||
}
|
||||
|
||||
// CallContextIgnoringLocalHandlers performs a JSON-RPC call with the given
|
||||
// arguments.
|
||||
//
|
||||
// If there are local handlers registered for this call, they would
|
||||
// be ignored. It is useful if the call is happening from within a local
|
||||
// handler itself.
|
||||
// Upstream calls routing will be used anyway.
|
||||
func (c *Client) CallContextIgnoringLocalHandlers(ctx context.Context, result interface{}, chainID uint64, method string, args ...interface{}) error {
|
||||
if c.router.routeBlocked(method) {
|
||||
return ErrMethodNotFound
|
||||
}
|
||||
|
||||
if c.router.routeRemote(method) {
|
||||
client, err := c.getClientUsingCache(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.CallContext(ctx, result, method, args...)
|
||||
}
|
||||
|
||||
if c.local == nil {
|
||||
c.log.Warn("Local JSON-RPC endpoint missing", "method", method)
|
||||
return errors.New("missing local JSON-RPC endpoint")
|
||||
}
|
||||
return c.local.CallContext(ctx, result, method, args...)
|
||||
}
|
||||
|
||||
// RegisterHandler registers local handler for specific RPC method.
|
||||
//
|
||||
// If method is registered, it will be executed with given handler and
|
||||
// never routed to the upstream or local servers.
|
||||
func (c *Client) RegisterHandler(method string, handler Handler) {
|
||||
c.handlersMx.Lock()
|
||||
defer c.handlersMx.Unlock()
|
||||
|
||||
c.handlers[method] = handler
|
||||
}
|
||||
|
||||
// UnregisterHandler removes a previously registered handler.
|
||||
func (c *Client) UnregisterHandler(method string) {
|
||||
c.handlersMx.Lock()
|
||||
defer c.handlersMx.Unlock()
|
||||
|
||||
delete(c.handlers, method)
|
||||
}
|
||||
|
||||
// callMethod calls registered RPC handler with given args and pointer to result.
|
||||
// It handles proper params and result converting
|
||||
//
|
||||
// TODO(divan): use cancellation via context here?
|
||||
func (c *Client) callMethod(ctx context.Context, result interface{}, chainID uint64, handler Handler, args ...interface{}) error {
|
||||
response, err := handler(ctx, chainID, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if result is nil, just ignore result -
|
||||
// the same way as gethrpc.CallContext() caller would expect
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return setResultFromRPCResponse(result, response)
|
||||
}
|
||||
|
||||
// handler is a concurrently safe method to get registered handler by name.
|
||||
func (c *Client) handler(method string) (Handler, bool) {
|
||||
c.handlersMx.RLock()
|
||||
defer c.handlersMx.RUnlock()
|
||||
handler, ok := c.handlers[method]
|
||||
return handler, ok
|
||||
}
|
||||
|
||||
// setResultFromRPCResponse tries to set result value from response using reflection
|
||||
// as concrete types are unknown.
|
||||
func setResultFromRPCResponse(result, response interface{}) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("invalid result type: %s", r)
|
||||
}
|
||||
}()
|
||||
|
||||
responseValue := reflect.ValueOf(response)
|
||||
|
||||
// If it is called via CallRaw, result has type json.RawMessage and
|
||||
// we should marshal the response before setting it.
|
||||
// Otherwise, it is called with CallContext and result is of concrete type,
|
||||
// thus we should try to set it as it is.
|
||||
// If response type and result type are incorrect, an error should be returned.
|
||||
// TODO(divan): add additional checks for result underlying value, if needed:
|
||||
// some example: https://golang.org/src/encoding/json/decode.go#L596
|
||||
switch reflect.ValueOf(result).Elem().Type() {
|
||||
case reflect.TypeOf(json.RawMessage{}), reflect.TypeOf([]byte{}):
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responseValue = reflect.ValueOf(data)
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(result).Elem()
|
||||
if !value.CanSet() {
|
||||
return errors.New("can't assign value to result")
|
||||
}
|
||||
value.Set(responseValue)
|
||||
|
||||
return nil
|
||||
}
|
||||
21
vendor/github.com/status-im/status-go/rpc/doc.go
generated
vendored
Normal file
21
vendor/github.com/status-im/status-go/rpc/doc.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Package rpc - JSON-RPC client with custom routing.
|
||||
|
||||
Package rpc implements status-go JSON-RPC client and handles
|
||||
requests to different endpoints: upstream or local node.
|
||||
|
||||
Every JSON-RPC request coming from either JS code or any other part
|
||||
of status-go should use this package to be handled and routed properly.
|
||||
|
||||
Routing rules are following:
|
||||
|
||||
- if Upstream is disabled, everything is routed to local ethereum-go node
|
||||
- otherwise, some requests (from the list, see below) are routed to upstream, others - locally.
|
||||
|
||||
List of methods to be routed is currently available here: https://docs.google.com/spreadsheets/d/1N1nuzVN5tXoDmzkBLeC9_mwIlVH8DGF7YD2XwxA8BAE/edit#gid=0
|
||||
|
||||
Note, upon creation of a new client, it ok to be offline - client will keep trying to reconnect in background.
|
||||
*/
|
||||
package rpc
|
||||
|
||||
//go:generate autoreadme -f
|
||||
326
vendor/github.com/status-im/status-go/rpc/network/network.go
generated
vendored
Normal file
326
vendor/github.com/status-im/status-go/rpc/network/network.go
generated
vendored
Normal file
@@ -0,0 +1,326 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
)
|
||||
|
||||
var SepoliaChainIDs = []uint64{11155111, 421614, 11155420}
|
||||
|
||||
var GoerliChainIDs = []uint64{5, 421613, 420}
|
||||
|
||||
type CombinedNetwork struct {
|
||||
Prod *params.Network
|
||||
Test *params.Network
|
||||
}
|
||||
|
||||
const baseQuery = "SELECT chain_id, chain_name, rpc_url, original_rpc_url, fallback_url, original_fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name, related_chain_id FROM networks"
|
||||
|
||||
func newNetworksQuery() *networksQuery {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.WriteString(baseQuery)
|
||||
return &networksQuery{buf: buf}
|
||||
}
|
||||
|
||||
type networksQuery struct {
|
||||
buf *bytes.Buffer
|
||||
args []interface{}
|
||||
added bool
|
||||
}
|
||||
|
||||
func (nq *networksQuery) andOrWhere() {
|
||||
if nq.added {
|
||||
nq.buf.WriteString(" AND")
|
||||
} else {
|
||||
nq.buf.WriteString(" WHERE")
|
||||
}
|
||||
}
|
||||
|
||||
func (nq *networksQuery) filterEnabled(enabled bool) *networksQuery {
|
||||
nq.andOrWhere()
|
||||
nq.added = true
|
||||
nq.buf.WriteString(" enabled = ?")
|
||||
nq.args = append(nq.args, enabled)
|
||||
return nq
|
||||
}
|
||||
|
||||
func (nq *networksQuery) filterChainID(chainID uint64) *networksQuery {
|
||||
nq.andOrWhere()
|
||||
nq.added = true
|
||||
nq.buf.WriteString(" chain_id = ?")
|
||||
nq.args = append(nq.args, chainID)
|
||||
return nq
|
||||
}
|
||||
|
||||
func (nq *networksQuery) exec(db *sql.DB) ([]*params.Network, error) {
|
||||
rows, err := db.Query(nq.buf.String(), nq.args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []*params.Network
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
network := params.Network{}
|
||||
err := rows.Scan(
|
||||
&network.ChainID, &network.ChainName, &network.RPCURL, &network.OriginalRPCURL, &network.FallbackURL, &network.OriginalFallbackURL,
|
||||
&network.BlockExplorerURL, &network.IconURL, &network.NativeCurrencyName, &network.NativeCurrencySymbol,
|
||||
&network.NativeCurrencyDecimals, &network.IsTest, &network.Layer, &network.Enabled, &network.ChainColor, &network.ShortName,
|
||||
&network.RelatedChainID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = append(res, &network)
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
db *sql.DB
|
||||
configuredNetworks []params.Network
|
||||
accountsDB *accounts.Database
|
||||
}
|
||||
|
||||
func NewManager(db *sql.DB) *Manager {
|
||||
accountsDB, err := accounts.NewDB(db)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &Manager{
|
||||
db: db,
|
||||
accountsDB: accountsDB,
|
||||
}
|
||||
}
|
||||
|
||||
func find(chainID uint64, networks []params.Network) int {
|
||||
for i := range networks {
|
||||
if networks[i].ChainID == chainID {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (nm *Manager) Init(networks []params.Network) error {
|
||||
if networks == nil {
|
||||
return nil
|
||||
}
|
||||
nm.configuredNetworks = networks
|
||||
|
||||
var errors string
|
||||
currentNetworks, _ := nm.Get(false)
|
||||
|
||||
// Delete networks which are not supported any more
|
||||
for i := range currentNetworks {
|
||||
if find(currentNetworks[i].ChainID, networks) == -1 {
|
||||
err := nm.Delete(currentNetworks[i].ChainID)
|
||||
if err != nil {
|
||||
errors += fmt.Sprintf("error deleting network with ChainID: %d, %s", currentNetworks[i].ChainID, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new networks and update related chain id for the old ones
|
||||
for i := range networks {
|
||||
found := false
|
||||
networks[i].OriginalRPCURL = networks[i].RPCURL
|
||||
networks[i].OriginalFallbackURL = networks[i].FallbackURL
|
||||
|
||||
for j := range currentNetworks {
|
||||
if currentNetworks[j].ChainID == networks[i].ChainID {
|
||||
found = true
|
||||
if currentNetworks[j].RelatedChainID != networks[i].RelatedChainID {
|
||||
// Update fallback_url if it's different
|
||||
err := nm.UpdateRelatedChainID(currentNetworks[j].ChainID, networks[i].RelatedChainID)
|
||||
if err != nil {
|
||||
errors += fmt.Sprintf("error updating network fallback_url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if networks[i].OriginalRPCURL != currentNetworks[j].OriginalRPCURL && currentNetworks[j].RPCURL == currentNetworks[j].OriginalRPCURL {
|
||||
err := nm.updateRPCURL(networks[i].ChainID, networks[i].OriginalRPCURL)
|
||||
if err != nil {
|
||||
errors += fmt.Sprintf("error updating rpc url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if networks[i].OriginalFallbackURL != currentNetworks[j].OriginalFallbackURL && currentNetworks[j].FallbackURL == currentNetworks[j].OriginalFallbackURL {
|
||||
err := nm.updateFallbackURL(networks[i].ChainID, networks[i].OriginalFallbackURL)
|
||||
if err != nil {
|
||||
errors += fmt.Sprintf("error updating rpc url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
err := nm.updateOriginalURLs(networks[i].ChainID, networks[i].OriginalRPCURL, networks[i].OriginalFallbackURL)
|
||||
if err != nil {
|
||||
errors += fmt.Sprintf("error updating network original url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// Insert new network
|
||||
err := nm.Upsert(&networks[i])
|
||||
if err != nil {
|
||||
errors += fmt.Sprintf("error inserting network with ChainID: %d, %s", networks[i].ChainID, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nm *Manager) Upsert(network *params.Network) error {
|
||||
_, err := nm.db.Exec(
|
||||
"INSERT OR REPLACE INTO networks (chain_id, chain_name, rpc_url, original_rpc_url, fallback_url, original_fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name, related_chain_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
network.ChainID, network.ChainName, network.RPCURL, network.OriginalRPCURL, network.FallbackURL, network.OriginalFallbackURL, network.BlockExplorerURL, network.IconURL,
|
||||
network.NativeCurrencyName, network.NativeCurrencySymbol, network.NativeCurrencyDecimals,
|
||||
network.IsTest, network.Layer, network.Enabled, network.ChainColor, network.ShortName,
|
||||
network.RelatedChainID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) Delete(chainID uint64) error {
|
||||
_, err := nm.db.Exec("DELETE FROM networks WHERE chain_id = ?", chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) UpdateRelatedChainID(chainID uint64, relatedChainID uint64) error {
|
||||
_, err := nm.db.Exec(`UPDATE networks SET related_chain_id = ? WHERE chain_id = ?`, relatedChainID, chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) updateRPCURL(chainID uint64, rpcURL string) error {
|
||||
_, err := nm.db.Exec(`UPDATE networks SET rpc_url = ? WHERE chain_id = ?`, rpcURL, chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) updateFallbackURL(chainID uint64, fallbackURL string) error {
|
||||
_, err := nm.db.Exec(`UPDATE networks SET fallback_url = ? WHERE chain_id = ?`, fallbackURL, chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) updateOriginalURLs(chainID uint64, originalRPCURL, OriginalFallbackURL string) error {
|
||||
_, err := nm.db.Exec(`UPDATE networks SET original_rpc_url = ?, original_fallback_url = ? WHERE chain_id = ?`, originalRPCURL, OriginalFallbackURL, chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) Find(chainID uint64) *params.Network {
|
||||
networks, err := newNetworksQuery().filterChainID(chainID).exec(nm.db)
|
||||
if len(networks) != 1 || err != nil {
|
||||
return nil
|
||||
}
|
||||
return networks[0]
|
||||
}
|
||||
|
||||
func (nm *Manager) GetAll() ([]*params.Network, error) {
|
||||
query := newNetworksQuery()
|
||||
return query.exec(nm.db)
|
||||
}
|
||||
|
||||
func (nm *Manager) Get(onlyEnabled bool) ([]*params.Network, error) {
|
||||
isSepoliaEnabled, err := nm.accountsDB.GetIsSepoliaEnabled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := newNetworksQuery()
|
||||
if onlyEnabled {
|
||||
query.filterEnabled(true)
|
||||
}
|
||||
|
||||
networks, err := query.exec(nm.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []*params.Network
|
||||
for _, network := range networks {
|
||||
if !isSepoliaEnabled {
|
||||
found := false
|
||||
for _, chainID := range SepoliaChainIDs {
|
||||
if network.ChainID == chainID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if isSepoliaEnabled {
|
||||
found := false
|
||||
|
||||
for _, chainID := range GoerliChainIDs {
|
||||
if network.ChainID == chainID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
results = append(results, network)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (nm *Manager) GetCombinedNetworks() ([]*CombinedNetwork, error) {
|
||||
networks, err := nm.Get(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var combinedNetworks []*CombinedNetwork
|
||||
for _, network := range networks {
|
||||
found := false
|
||||
for _, n := range combinedNetworks {
|
||||
if (n.Test != nil && (network.ChainID == n.Test.RelatedChainID || n.Test.ChainID == network.RelatedChainID)) || (n.Prod != nil && (network.ChainID == n.Prod.RelatedChainID || n.Prod.ChainID == network.RelatedChainID)) {
|
||||
found = true
|
||||
if network.IsTest {
|
||||
n.Test = network
|
||||
break
|
||||
} else {
|
||||
n.Prod = network
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
|
||||
newCombined := &CombinedNetwork{}
|
||||
if network.IsTest {
|
||||
newCombined.Test = network
|
||||
} else {
|
||||
newCombined.Prod = network
|
||||
}
|
||||
combinedNetworks = append(combinedNetworks, newCombined)
|
||||
}
|
||||
|
||||
return combinedNetworks, nil
|
||||
}
|
||||
|
||||
func (nm *Manager) GetConfiguredNetworks() []params.Network {
|
||||
return nm.configuredNetworks
|
||||
}
|
||||
|
||||
func (nm *Manager) GetTestNetworksEnabled() (result bool, err error) {
|
||||
return nm.accountsDB.GetTestNetworksEnabled()
|
||||
}
|
||||
115
vendor/github.com/status-im/status-go/rpc/route.go
generated
vendored
Normal file
115
vendor/github.com/status-im/status-go/rpc/route.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package rpc
|
||||
|
||||
// router implements logic for routing
|
||||
// JSON-RPC requests either to Upstream or
|
||||
// Local node.
|
||||
type router struct {
|
||||
methods map[string]bool
|
||||
blockedMethods map[string]struct{}
|
||||
upstreamEnabled bool
|
||||
}
|
||||
|
||||
// newRouter inits new router.
|
||||
func newRouter(upstreamEnabled bool) *router {
|
||||
r := &router{
|
||||
methods: make(map[string]bool),
|
||||
blockedMethods: make(map[string]struct{}),
|
||||
upstreamEnabled: upstreamEnabled,
|
||||
}
|
||||
|
||||
for _, m := range remoteMethods {
|
||||
r.methods[m] = true
|
||||
}
|
||||
|
||||
for _, m := range blockedMethods {
|
||||
r.blockedMethods[m] = struct{}{}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// routeRemote returns true if given method should be routed to the remote node
|
||||
func (r *router) routeRemote(method string) bool {
|
||||
if !r.upstreamEnabled {
|
||||
return false
|
||||
}
|
||||
|
||||
// else check route using the methods list
|
||||
return r.methods[method]
|
||||
}
|
||||
|
||||
func (r *router) routeBlocked(method string) bool {
|
||||
_, ok := r.blockedMethods[method]
|
||||
return ok
|
||||
}
|
||||
|
||||
// blockedMethods is a list of dangerous or having security implications JSON-RPC methods
|
||||
// that are not allowed to be called.
|
||||
var blockedMethods = [...]string{
|
||||
"shh_getPrivateKey",
|
||||
}
|
||||
|
||||
// BlockedMethods returns a list of methods that are not allowed to be called.
|
||||
// A copy of a slice is returned in order to prevent from changing it from outside.
|
||||
func BlockedMethods() []string {
|
||||
return append([]string(nil), blockedMethods[:]...)
|
||||
}
|
||||
|
||||
// remoteMethods contains methods that should be routed to
|
||||
// the upstream node; the rest is considered to be routed to
|
||||
// the local node.
|
||||
// A list of supported methods:
|
||||
//
|
||||
// curl --include \
|
||||
// --header "Content-Type: application/json" \
|
||||
// --header "Accept: application/json" 'https://api.infura.io/v1/jsonrpc/ropsten/methods'
|
||||
//
|
||||
// Although it's tempting to only list methods coming to the local node as there're fewer of them
|
||||
// but it's deceptive: we want to ensure that only known requests leave our zone of responsibility.
|
||||
// Also, we want new requests in newer Geth versions not to be accidentally routed to the upstream.
|
||||
// The list of methods: https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
var remoteMethods = [...]string{
|
||||
"eth_protocolVersion",
|
||||
"eth_syncing",
|
||||
"eth_coinbase",
|
||||
"eth_mining",
|
||||
"eth_hashrate",
|
||||
"eth_gasPrice",
|
||||
"eth_maxPriorityFeePerGas",
|
||||
"eth_feeHistory",
|
||||
//"eth_accounts", // due to sub-accounts handling
|
||||
"eth_blockNumber",
|
||||
"eth_getBalance",
|
||||
"eth_getStorageAt",
|
||||
"eth_getTransactionCount",
|
||||
"eth_getBlockTransactionCountByHash",
|
||||
"eth_getBlockTransactionCountByNumber",
|
||||
"eth_getUncleCountByBlockHash",
|
||||
"eth_getUncleCountByBlockNumber",
|
||||
"eth_getCode",
|
||||
//"eth_sign", // only the local node has an injected account to sign the payload with
|
||||
//"eth_sendTransaction", // we handle this specially calling eth_estimateGas, signing it locally and sending eth_sendRawTransaction afterwards
|
||||
"eth_sendRawTransaction",
|
||||
"eth_call",
|
||||
"eth_estimateGas",
|
||||
"eth_getBlockByHash",
|
||||
"eth_getBlockByNumber",
|
||||
"eth_getTransactionByHash",
|
||||
"eth_getTransactionByBlockHashAndIndex",
|
||||
"eth_getTransactionByBlockNumberAndIndex",
|
||||
"eth_getTransactionReceipt",
|
||||
"eth_getUncleByBlockHashAndIndex",
|
||||
"eth_getUncleByBlockNumberAndIndex",
|
||||
//"eth_getCompilers", // goes to the local because there's no need to send it anywhere
|
||||
//"eth_compileLLL", // goes to the local because there's no need to send it anywhere
|
||||
//"eth_compileSolidity", // goes to the local because there's no need to send it anywhere
|
||||
//"eth_compileSerpent", // goes to the local because there's no need to send it anywhere
|
||||
|
||||
"eth_getLogs",
|
||||
"eth_getWork",
|
||||
"eth_submitWork",
|
||||
"eth_submitHashrate",
|
||||
"net_version",
|
||||
"net_peerCount",
|
||||
"net_listening",
|
||||
}
|
||||
72
vendor/github.com/status-im/status-go/rpc/verif_proxy.go
generated
vendored
Normal file
72
vendor/github.com/status-im/status-go/rpc/verif_proxy.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
//go:build nimbus_light_client
|
||||
// +build nimbus_light_client
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
proxy "github.com/siphiuel/lc-proxy-wrapper"
|
||||
|
||||
"github.com/status-im/status-go/params"
|
||||
)
|
||||
|
||||
type VerifProxy struct {
|
||||
config *proxy.Config
|
||||
client *gethrpc.Client
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func init() {
|
||||
verifProxyInitFn = func(c *Client) {
|
||||
ctx := context.Background()
|
||||
var testConfig = proxy.Config{
|
||||
Eth2Network: "mainnet",
|
||||
TrustedBlockRoot: "0xc5182cdb750fe088138b0d475683cda26a96befc24de16fb17bcf49d9cadf2f7",
|
||||
Web3Url: c.upstreamURL,
|
||||
RpcAddress: "127.0.0.1",
|
||||
RpcPort: 8545,
|
||||
LogLevel: "INFO",
|
||||
}
|
||||
proxy.StartLightClient(ctx, &testConfig)
|
||||
verifProxy, err := newVerifProxy(&testConfig, c.log)
|
||||
if err != nil {
|
||||
c.RegisterHandler(
|
||||
params.BalanceMethodName,
|
||||
func(ctx context.Context, v uint64, params ...interface{}) (interface{}, error) {
|
||||
addr := params[0].(common.Address)
|
||||
return verifProxy.GetBalance(ctx, addr)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newVerifProxy(cfg *proxy.Config, log log.Logger) (*VerifProxy, error) {
|
||||
endpoint := "http://" + cfg.RpcAddress + ":" + fmt.Sprint(cfg.RpcPort)
|
||||
client, err := gethrpc.DialHTTP(endpoint)
|
||||
if err != nil {
|
||||
log.Error("Error when creating VerifProxy client", err)
|
||||
return nil, err
|
||||
}
|
||||
proxy := &VerifProxy{cfg, client, log}
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
func (p *VerifProxy) GetBalance(ctx context.Context, address common.Address) (interface{}, error) {
|
||||
var result hexutil.Big
|
||||
err := p.client.CallContext(ctx, &result, "eth_getBalance", address, "latest")
|
||||
if err != nil {
|
||||
p.log.Error("Error when invoking GetBalance", err)
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user