github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/avalanche/avalancherpc.go (about)

     1  package avalanche
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/url"
     8  
     9  	jsontypes "github.com/ava-labs/avalanchego/utils/json"
    10  	"github.com/ava-labs/coreth/core/types"
    11  	"github.com/ava-labs/coreth/ethclient"
    12  	"github.com/ava-labs/coreth/interfaces"
    13  	"github.com/ava-labs/coreth/rpc"
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/common/hexutil"
    16  	"github.com/golang/glog"
    17  	"github.com/juju/errors"
    18  	"github.com/trezor/blockbook/bchain"
    19  	"github.com/trezor/blockbook/bchain/coins/eth"
    20  )
    21  
    22  const (
    23  	// MainNet is production network
    24  	MainNet eth.Network = 43114
    25  )
    26  
    27  // AvalancheRPC is an interface to JSON-RPC avalanche service.
    28  type AvalancheRPC struct {
    29  	*eth.EthereumRPC
    30  	info *rpc.Client
    31  }
    32  
    33  // NewAvalancheRPC returns new AvalancheRPC instance.
    34  func NewAvalancheRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
    35  	c, err := eth.NewEthereumRPC(config, pushHandler)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	s := &AvalancheRPC{
    41  		EthereumRPC: c.(*eth.EthereumRPC),
    42  	}
    43  
    44  	return s, nil
    45  }
    46  
    47  // Initialize avalanche rpc interface
    48  func (b *AvalancheRPC) Initialize() error {
    49  	b.OpenRPC = func(url string) (bchain.EVMRPCClient, bchain.EVMClient, error) {
    50  		r, err := rpc.Dial(url)
    51  		if err != nil {
    52  			return nil, nil, err
    53  		}
    54  		rc := &AvalancheRPCClient{Client: r}
    55  		c := &AvalancheClient{Client: ethclient.NewClient(r)}
    56  		return rc, c, nil
    57  	}
    58  
    59  	rpcClient, client, err := b.OpenRPC(b.ChainConfig.RPCURL)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	rpcUrl, err := url.Parse(b.ChainConfig.RPCURL)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	scheme := "http"
    70  	if rpcUrl.Scheme == "wss" || rpcUrl.Scheme == "https" {
    71  		scheme = "https"
    72  	}
    73  
    74  	infoClient, err := rpc.DialHTTP(fmt.Sprintf("%s://%s/ext/info", scheme, rpcUrl.Host))
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	// set chain specific
    80  	b.Client = client
    81  	b.RPC = rpcClient
    82  	b.info = infoClient
    83  	b.MainNetChainID = MainNet
    84  	b.NewBlock = &AvalancheNewBlock{channel: make(chan *types.Header)}
    85  	b.NewTx = &AvalancheNewTx{channel: make(chan common.Hash)}
    86  
    87  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
    88  	defer cancel()
    89  
    90  	id, err := b.Client.NetworkID(ctx)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	// parameters for getInfo request
    96  	switch eth.Network(id.Uint64()) {
    97  	case MainNet:
    98  		b.Testnet = false
    99  		b.Network = "livenet"
   100  	default:
   101  		return errors.Errorf("Unknown network id %v", id)
   102  	}
   103  
   104  	glog.Info("rpc: block chain ", b.Network)
   105  
   106  	return nil
   107  }
   108  
   109  // GetChainInfo returns information about the connected backend
   110  func (b *AvalancheRPC) GetChainInfo() (*bchain.ChainInfo, error) {
   111  	ci, err := b.EthereumRPC.GetChainInfo()
   112  	if err != nil {
   113  		fmt.Println(err)
   114  		return nil, err
   115  	}
   116  
   117  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   118  	defer cancel()
   119  
   120  	var v struct {
   121  		Version            string            `json:"version"`
   122  		DatabaseVersion    string            `json:"databaseVersion"`
   123  		RPCProtocolVersion jsontypes.Uint32  `json:"rpcProtocolVersion"`
   124  		GitCommit          string            `json:"gitCommit"`
   125  		VMVersions         map[string]string `json:"vmVersions"`
   126  	}
   127  
   128  	if err := b.info.CallContext(ctx, &v, "info.getNodeVersion"); err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	if avm, ok := v.VMVersions["avm"]; ok {
   133  		ci.Version = avm
   134  	}
   135  
   136  	return ci, nil
   137  }
   138  
   139  // EthereumTypeEstimateGas returns estimation of gas consumption for given transaction parameters
   140  func (b *AvalancheRPC) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) {
   141  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   142  	defer cancel()
   143  	msg := interfaces.CallMsg{}
   144  	if s, ok := eth.GetStringFromMap("from", params); ok && len(s) > 0 {
   145  		msg.From = common.HexToAddress(s)
   146  	}
   147  	if s, ok := eth.GetStringFromMap("to", params); ok && len(s) > 0 {
   148  		a := common.HexToAddress(s)
   149  		msg.To = &a
   150  	}
   151  	if s, ok := eth.GetStringFromMap("data", params); ok && len(s) > 0 {
   152  		msg.Data = common.FromHex(s)
   153  	}
   154  	if s, ok := eth.GetStringFromMap("value", params); ok && len(s) > 0 {
   155  		msg.Value, _ = hexutil.DecodeBig(s)
   156  	}
   157  	if s, ok := eth.GetStringFromMap("gas", params); ok && len(s) > 0 {
   158  		msg.Gas, _ = hexutil.DecodeUint64(s)
   159  	}
   160  	if s, ok := eth.GetStringFromMap("gasPrice", params); ok && len(s) > 0 {
   161  		msg.GasPrice, _ = hexutil.DecodeBig(s)
   162  	}
   163  	return b.Client.EstimateGas(ctx, msg)
   164  }