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 }