github.com/elastos/Elastos.ELA.SideChain.ETH@v0.2.2/chainbridge-core/chains/evm/evmclient/evm-client.go (about)

     1  // Copyright 2020 ChainSafe Systems
     2  // SPDX-License-Identifier: LGPL-3.0-only
     3  
     4  package evmclient
     5  
     6  import (
     7  	"context"
     8  	"crypto/ecdsa"
     9  	"encoding/json"
    10  	"errors"
    11  	"math/big"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/elastos/Elastos.ELA.SideChain.ESC"
    16  	"github.com/elastos/Elastos.ELA.SideChain.ESC/accounts/abi"
    17  	"github.com/elastos/Elastos.ELA.SideChain.ESC/chainbridge-core/bridgelog"
    18  	"github.com/elastos/Elastos.ELA.SideChain.ESC/chainbridge-core/config"
    19  	"github.com/elastos/Elastos.ELA.SideChain.ESC/chainbridge-core/crypto/secp256k1"
    20  	"github.com/elastos/Elastos.ELA.SideChain.ESC/chainbridge-core/engine"
    21  	"github.com/elastos/Elastos.ELA.SideChain.ESC/chainbridge-core/keystore"
    22  	"github.com/elastos/Elastos.ELA.SideChain.ESC/chainbridge-core/relayer"
    23  	"github.com/elastos/Elastos.ELA.SideChain.ESC/chainbridge_abi"
    24  	"github.com/elastos/Elastos.ELA.SideChain.ESC/common"
    25  	"github.com/elastos/Elastos.ELA.SideChain.ESC/common/hexutil"
    26  	"github.com/elastos/Elastos.ELA.SideChain.ESC/core/types"
    27  	"github.com/elastos/Elastos.ELA.SideChain.ESC/crypto"
    28  	"github.com/elastos/Elastos.ELA.SideChain.ESC/ethclient"
    29  	"github.com/elastos/Elastos.ELA.SideChain.ESC/internal/ethapi"
    30  	"github.com/elastos/Elastos.ELA.SideChain.ESC/log"
    31  	"github.com/elastos/Elastos.ELA.SideChain.ESC/rpc"
    32  )
    33  
    34  type EVMClient struct {
    35  	*ethclient.Client
    36  	rpClient  *rpc.Client
    37  	nonceLock sync.Mutex
    38  	config    *config.GeneralChainConfig
    39  	nonce     *big.Int
    40  
    41  	engine            engine.ESCEngine
    42  	updateArbitersABI abi.ABI
    43  }
    44  
    45  type CommonTransaction interface {
    46  	// Hash returns the transaction hash.
    47  	Hash() common.Hash
    48  	// Returns signed transaction by provided private key
    49  	RawWithSignature(key *ecdsa.PrivateKey, chainID *big.Int) ([]byte, error)
    50  }
    51  
    52  func NewEVMClient(engine engine.ESCEngine) *EVMClient {
    53  	abi, err := chainbridge_abi.UpdateArbiterABI()
    54  	if err != nil {
    55  		bridgelog.Error("UpdateArbiterABI failed", "error", err)
    56  		return nil
    57  	}
    58  	client := &EVMClient{engine: engine, updateArbitersABI: abi}
    59  	return client
    60  }
    61  
    62  func (c *EVMClient) Configurate(generalConfig *config.GeneralChainConfig, accountPath, password string) error {
    63  	if generalConfig == nil {
    64  		return errors.New("chain config is nil")
    65  	}
    66  	c.config = generalConfig
    67  	if len(generalConfig.KeystorePath) > 0 {
    68  		accountPath = generalConfig.KeystorePath
    69  	}
    70  	kp, err := keystore.KeypairFromAddress(keystore.EthChain, accountPath, []byte(password), generalConfig.Insecure)
    71  	if err == nil {
    72  		krp := kp.(*secp256k1.Keypair)
    73  		c.config.Kp = krp
    74  	}
    75  	rpcClient, err := rpc.DialContext(context.TODO(), generalConfig.Endpoint)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	c.Client = ethclient.NewClient(rpcClient)
    80  	c.rpClient = rpcClient
    81  
    82  	if generalConfig.LatestBlock {
    83  		curr, err := c.LatestBlock()
    84  		if err != nil {
    85  			return err
    86  		}
    87  		generalConfig.Opts.StartBlock = curr.Uint64()
    88  	}
    89  	return nil
    90  }
    91  
    92  type headerNumber struct {
    93  	Number *big.Int `json:"number"           gencodec:"required"`
    94  }
    95  
    96  func (h *headerNumber) UnmarshalJSON(input []byte) error {
    97  	type headerNumber struct {
    98  		Number *hexutil.Big `json:"number" gencodec:"required"`
    99  	}
   100  	var dec headerNumber
   101  	if err := json.Unmarshal(input, &dec); err != nil {
   102  		return err
   103  	}
   104  	if dec.Number == nil {
   105  		return errors.New("missing required field 'number' for Header")
   106  	}
   107  	h.Number = (*big.Int)(dec.Number)
   108  	return nil
   109  }
   110  
   111  // LatestBlock returns the latest block from the current chain
   112  func (c *EVMClient) LatestBlock() (*big.Int, error) {
   113  	var head *headerNumber
   114  
   115  	err := c.rpClient.CallContext(context.Background(), &head, "eth_getBlockByNumber", toBlockNumArg(nil), false)
   116  	if err == nil && head == nil {
   117  		err = ethereum.NotFound
   118  		return nil, err
   119  	}
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return head.Number, err
   124  }
   125  
   126  // LatestBlock returns the latest block from the current chain
   127  func (c *EVMClient) PendingTransaction() ([]ethapi.RPCTransaction, error) {
   128  	var pendingTx []ethapi.RPCTransaction
   129  
   130  	err := c.rpClient.CallContext(context.Background(), &pendingTx, "eth_pendingTransactions")
   131  	return pendingTx, err
   132  }
   133  
   134  func (c *EVMClient) CurrentBlock() (*types.Block, error) {
   135  	return c.Client.BlockByNumber(context.Background(), nil)
   136  }
   137  
   138  func (c *EVMClient) Engine() engine.ESCEngine {
   139  	return c.engine
   140  }
   141  
   142  func (c *EVMClient) GetClientAddress() common.Address {
   143  	return common.HexToAddress(c.config.Kp.Address())
   144  }
   145  
   146  // SendRawTransaction accepts rlp-encode of signed transaction and sends it via RPC call
   147  func (c *EVMClient) SendRawTransaction(ctx context.Context, tx []byte) error {
   148  	return c.rpClient.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(tx))
   149  }
   150  
   151  func (c *EVMClient) CallContract(ctx context.Context, callArgs map[string]interface{}, blockNumber *big.Int) ([]byte, error) {
   152  	var hex hexutil.Bytes
   153  	err := c.rpClient.CallContext(ctx, &hex, "eth_call", callArgs, toBlockNumArg(blockNumber))
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	return hex, nil
   158  }
   159  
   160  func (c *EVMClient) PendingCallContract(ctx context.Context, callArgs map[string]interface{}) ([]byte, error) {
   161  	var hex hexutil.Bytes
   162  	err := c.rpClient.CallContext(ctx, &hex, "eth_call", callArgs, "pending")
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	return hex, nil
   167  }
   168  
   169  func (c *EVMClient) SignAndSendTransaction(ctx context.Context, tx CommonTransaction) (common.Hash, error) {
   170  	id, err := c.ChainID(ctx)
   171  	if err != nil {
   172  		return common.Hash{}, err
   173  	}
   174  	rawTX, err := tx.RawWithSignature(c.config.Kp.PrivateKey(), id)
   175  	if err != nil {
   176  		return common.Hash{}, err
   177  	}
   178  
   179  	err = c.SendRawTransaction(ctx, rawTX)
   180  	if err != nil {
   181  		log.Error("send account", "account", c.config.Kp.Address())
   182  		return common.Hash{}, err
   183  	}
   184  	return tx.Hash(), nil
   185  }
   186  
   187  func (c *EVMClient) LockNonce() {
   188  	c.nonceLock.Lock()
   189  }
   190  
   191  func (c *EVMClient) UnlockNonce() {
   192  	c.nonceLock.Unlock()
   193  }
   194  
   195  func (c *EVMClient) GetNonce() (*big.Int, error) {
   196  	var err error
   197  	for i := 0; i <= 10; i++ {
   198  		nonce, err := c.PendingNonceAt(context.Background(), c.config.Kp.CommonAddress())
   199  		if err != nil {
   200  			time.Sleep(1)
   201  			continue
   202  		}
   203  		c.nonce = big.NewInt(0).SetUint64(nonce)
   204  		return c.nonce, nil
   205  	}
   206  	return nil, err
   207  }
   208  
   209  func (c *EVMClient) GasPrice() (*big.Int, error) {
   210  	gasPrice, err := c.SafeEstimateGasPrice(context.TODO())
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	return gasPrice, nil
   215  }
   216  
   217  func (c *EVMClient) SafeEstimateGasPrice(ctx context.Context) (*big.Int, error) {
   218  	suggestedGasPrice, err := c.SuggestGasPrice(context.TODO())
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	gasPrice := multiplyGasPrice(suggestedGasPrice, big.NewFloat(c.config.Opts.GasMultiplier))
   224  
   225  	// Check we aren't exceeding our limit
   226  
   227  	if gasPrice.Cmp(big.NewInt(0).SetUint64(c.config.Opts.MaxGasPrice)) == 1 {
   228  		return big.NewInt(0).SetUint64(c.config.Opts.MaxGasPrice), nil
   229  	} else {
   230  		return gasPrice, nil
   231  	}
   232  }
   233  
   234  func (c *EVMClient) EstimateGasLimit(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
   235  	gasLimit, err := c.EstimateGas(ctx, msg)
   236  	if err != nil {
   237  		return 0, err
   238  	}
   239  	gas := multiplyGasPrice(big.NewInt(0).SetUint64(gasLimit), big.NewFloat(c.config.Opts.GasMultiplier))
   240  	if gas.Cmp(big.NewInt(0).SetUint64(c.config.Opts.GasLimit)) == 1 {
   241  		return big.NewInt(0).SetUint64(c.config.Opts.GasLimit).Uint64(), nil
   242  	}
   243  	return gas.Uint64(), nil
   244  }
   245  
   246  const (
   247  	SetAbiterList string = "SetArbiterList(uint256)"
   248  )
   249  
   250  func (c *EVMClient) FetchUpdateArbitersLogs(ctx context.Context, contractAddress common.Address, startBlock *big.Int, endBlock *big.Int) ([]*relayer.SetArbiterListMsg, error) {
   251  	logs, err := c.FilterLogs(ctx, buildQuery(contractAddress, SetAbiterList, startBlock, endBlock))
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	depositLogs := make([]*relayer.SetArbiterListMsg, 0)
   256  	for _, l := range logs {
   257  		record := new(relayer.SetArbiterListMsg)
   258  		err = c.updateArbitersABI.Unpack(record, "SetArbiterList", l.Data)
   259  		if err != nil {
   260  			return depositLogs, errors.New("SetAbiterList record resolved error:" + err.Error())
   261  		}
   262  		depositLogs = append(depositLogs, record)
   263  	}
   264  	return depositLogs, nil
   265  }
   266  
   267  func multiplyGasPrice(gasEstimate *big.Int, gasMultiplier *big.Float) *big.Int {
   268  
   269  	gasEstimateFloat := new(big.Float).SetInt(gasEstimate)
   270  
   271  	result := gasEstimateFloat.Mul(gasEstimateFloat, gasMultiplier)
   272  
   273  	gasPrice := new(big.Int)
   274  
   275  	result.Int(gasPrice)
   276  
   277  	return gasPrice
   278  }
   279  
   280  func toBlockNumArg(number *big.Int) string {
   281  	if number == nil {
   282  		return "latest"
   283  	}
   284  	return hexutil.EncodeBig(number)
   285  }
   286  
   287  // buildQuery constructs a query for the bridgeContract by hashing sig to get the event topic
   288  func buildQuery(contract common.Address, sig string, startBlock *big.Int, endBlock *big.Int) ethereum.FilterQuery {
   289  	query := ethereum.FilterQuery{
   290  		FromBlock: startBlock,
   291  		ToBlock:   endBlock,
   292  		Addresses: []common.Address{contract},
   293  		Topics: [][]common.Hash{
   294  			{crypto.Keccak256Hash([]byte(sig))},
   295  		},
   296  	}
   297  	return query
   298  }
   299  
   300  func (c *EVMClient) GetConfig() *config.GeneralChainConfig {
   301  	return c.config
   302  }
   303  
   304  func (c *EVMClient) IsContractAddress(address string) bool {
   305  	code, err := c.CodeAt(context.TODO(), common.HexToAddress(address), nil)
   306  	if err != nil || len(code) == 0 {
   307  		return false
   308  	}
   309  	return true
   310  }