From 8dab3d3c66159faf8a0c9e3d5dec7742124cf482 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 29 Jan 2024 13:46:04 -0600 Subject: [PATCH 1/2] skip Goerli BlobTxType transaction and Mumbai empty block --- changelog.md | 2 + zetaclient/ethrpc/ethrpc.go | 137 ++++++++++++++++++++++ zetaclient/ethrpc/helper.go | 40 +++++++ zetaclient/ethrpc/types.go | 162 ++++++++++++++++++++++++++ zetaclient/evm_client.go | 220 +++++++++++++++++++++++++----------- zetaclient/tss_signer.go | 42 +++---- 6 files changed, 516 insertions(+), 87 deletions(-) create mode 100644 zetaclient/ethrpc/ethrpc.go create mode 100644 zetaclient/ethrpc/helper.go create mode 100644 zetaclient/ethrpc/types.go diff --git a/changelog.md b/changelog.md index edf96c140d..335fa77f04 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ ### Fixes * [1610](https://github.com/zeta-chain/node/issues/1610) - add pending outtx hash to tracker after monitoring for 10 minutes +* [1662](https://github.com/zeta-chain/node/issues/1662) - skip Goerli BlobTxType transactions introduced in Dencun upgrade +* [1663](https://github.com/zeta-chain/node/issues/1663) - skip Mumbai empty block if ethclient sanity check fails ## Version: v12.1.0 diff --git a/zetaclient/ethrpc/ethrpc.go b/zetaclient/ethrpc/ethrpc.go new file mode 100644 index 0000000000..88283254f2 --- /dev/null +++ b/zetaclient/ethrpc/ethrpc.go @@ -0,0 +1,137 @@ +package ethrpc + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// EthError - ethereum error +type EthError struct { + Code int `json:"code"` + Message string `json:"message"` +} + +func (err EthError) Error() string { + return fmt.Sprintf("Error %d (%s)", err.Code, err.Message) +} + +type ethResponse struct { + ID int `json:"id"` + JSONRPC string `json:"jsonrpc"` + Result json.RawMessage `json:"result"` + Error *EthError `json:"error"` +} + +type ethRequest struct { + ID int `json:"id"` + JSONRPC string `json:"jsonrpc"` + Method string `json:"method"` + Params []interface{} `json:"params"` +} + +// EthRPC - Ethereum rpc client +type EthRPC struct { + url string + client *http.Client +} + +// New create new rpc client with given url +func New(url string, options ...func(rpc *EthRPC)) *EthRPC { + rpc := &EthRPC{ + url: url, + client: http.DefaultClient, + } + for _, option := range options { + option(rpc) + } + + return rpc +} + +// NewEthRPC create new rpc client with given url +func NewEthRPC(url string, options ...func(rpc *EthRPC)) *EthRPC { + return New(url, options...) +} + +// URL returns client url +func (rpc *EthRPC) URL() string { + return rpc.url +} + +// Call returns raw response of method call +func (rpc *EthRPC) Call(method string, params ...interface{}) (json.RawMessage, error) { + request := ethRequest{ + ID: 1, + JSONRPC: "2.0", + Method: method, + Params: params, + } + + body, err := json.Marshal(request) + if err != nil { + return nil, err + } + + response, err := rpc.client.Post(rpc.url, "application/json", bytes.NewBuffer(body)) + if response != nil { + defer response.Body.Close() + } + if err != nil { + return nil, err + } + + data, err := io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + resp := new(ethResponse) + if err := json.Unmarshal(data, resp); err != nil { + return nil, err + } + + if resp.Error != nil { + return nil, *resp.Error + } + + return resp.Result, nil + +} + +// RawCall returns raw response of method call (Deprecated) +func (rpc *EthRPC) RawCall(method string, params ...interface{}) (json.RawMessage, error) { + return rpc.Call(method, params...) +} + +func (rpc *EthRPC) getBlock(method string, withTransactions bool, params ...interface{}) (*Block, error) { + result, err := rpc.RawCall(method, params...) + if err != nil { + return nil, err + } + if bytes.Equal(result, []byte("null")) { + return nil, nil + } + + var response proxyBlock + if withTransactions { + response = new(proxyBlockWithTransactions) + } else { + response = new(proxyBlockWithoutTransactions) + } + + err = json.Unmarshal(result, response) + if err != nil { + return nil, err + } + + block := response.toBlock() + return &block, nil +} + +// EthGetBlockByNumber returns information about a block by block number. +func (rpc *EthRPC) EthGetBlockByNumber(number uint64, withTransactions bool) (*Block, error) { + return rpc.getBlock("eth_getBlockByNumber", withTransactions, IntToHex(number), withTransactions) +} diff --git a/zetaclient/ethrpc/helper.go b/zetaclient/ethrpc/helper.go new file mode 100644 index 0000000000..c826bf94cd --- /dev/null +++ b/zetaclient/ethrpc/helper.go @@ -0,0 +1,40 @@ +package ethrpc + +import ( + "fmt" + "math/big" + "strconv" + "strings" +) + +// ParseInt parse hex string value to uint64 +func ParseInt(value string) (uint64, error) { + i, err := strconv.ParseUint(strings.TrimPrefix(value, "0x"), 16, 64) + if err != nil { + return 0, err + } + + return i, nil +} + +// ParseBigInt parse hex string value to big.Int +func ParseBigInt(value string) (big.Int, error) { + i := big.Int{} + _, err := fmt.Sscan(value, &i) + + return i, err +} + +// IntToHex convert int to hexadecimal representation +func IntToHex(i uint64) string { + return fmt.Sprintf("0x%x", i) +} + +// BigToHex covert big.Int to hexadecimal representation +func BigToHex(bigInt big.Int) string { + if bigInt.BitLen() == 0 { + return "0x0" + } + + return "0x" + strings.TrimPrefix(fmt.Sprintf("%x", bigInt.Bytes()), "0") +} diff --git a/zetaclient/ethrpc/types.go b/zetaclient/ethrpc/types.go new file mode 100644 index 0000000000..d756fbfde8 --- /dev/null +++ b/zetaclient/ethrpc/types.go @@ -0,0 +1,162 @@ +package ethrpc + +import ( + "bytes" + "math/big" + "unsafe" +) + +// Transaction - transaction object +type Transaction struct { + Hash string + Nonce int + BlockHash string + BlockNumber *int + TransactionIndex *int + From string + To string + Value big.Int + Gas int + GasPrice big.Int + Input string +} + +// Block - block object +type Block struct { + Number uint64 + Hash string + ParentHash string + Nonce string + Sha3Uncles string + LogsBloom string + TransactionsRoot string + StateRoot string + Miner string + Difficulty big.Int + TotalDifficulty big.Int + ExtraData string + Size int + GasLimit int + GasUsed int + Timestamp int + Uncles []string + Transactions []Transaction +} + +type hexInt uint64 + +func (i *hexInt) UnmarshalJSON(data []byte) error { + result, err := ParseInt(string(bytes.Trim(data, `"`))) + *i = hexInt(result) + + return err +} + +type hexBig big.Int + +func (i *hexBig) UnmarshalJSON(data []byte) error { + result, err := ParseBigInt(string(bytes.Trim(data, `"`))) + *i = hexBig(result) + + return err +} + +type proxyBlock interface { + toBlock() Block +} + +type proxyBlockWithTransactions struct { + Number hexInt `json:"number"` + Hash string `json:"hash"` + ParentHash string `json:"parentHash"` + Nonce string `json:"nonce"` + Sha3Uncles string `json:"sha3Uncles"` + LogsBloom string `json:"logsBloom"` + TransactionsRoot string `json:"transactionsRoot"` + StateRoot string `json:"stateRoot"` + Miner string `json:"miner"` + Difficulty hexBig `json:"difficulty"` + TotalDifficulty hexBig `json:"totalDifficulty"` + ExtraData string `json:"extraData"` + Size hexInt `json:"size"` + GasLimit hexInt `json:"gasLimit"` + GasUsed hexInt `json:"gasUsed"` + Timestamp hexInt `json:"timestamp"` + Uncles []string `json:"uncles"` + Transactions []proxyTransaction `json:"transactions"` +} + +type proxyBlockWithoutTransactions struct { + Number hexInt `json:"number"` + Hash string `json:"hash"` + ParentHash string `json:"parentHash"` + Nonce string `json:"nonce"` + Sha3Uncles string `json:"sha3Uncles"` + LogsBloom string `json:"logsBloom"` + TransactionsRoot string `json:"transactionsRoot"` + StateRoot string `json:"stateRoot"` + Miner string `json:"miner"` + Difficulty hexBig `json:"difficulty"` + TotalDifficulty hexBig `json:"totalDifficulty"` + ExtraData string `json:"extraData"` + Size hexInt `json:"size"` + GasLimit hexInt `json:"gasLimit"` + GasUsed hexInt `json:"gasUsed"` + Timestamp hexInt `json:"timestamp"` + Uncles []string `json:"uncles"` + Transactions []string `json:"transactions"` +} + +func (proxy *proxyBlockWithoutTransactions) toBlock() Block { + block := Block{ + Number: uint64(proxy.Number), + Hash: proxy.Hash, + ParentHash: proxy.ParentHash, + Nonce: proxy.Nonce, + Sha3Uncles: proxy.Sha3Uncles, + LogsBloom: proxy.LogsBloom, + TransactionsRoot: proxy.TransactionsRoot, + StateRoot: proxy.StateRoot, + Miner: proxy.Miner, + Difficulty: big.Int(proxy.Difficulty), + TotalDifficulty: big.Int(proxy.TotalDifficulty), + ExtraData: proxy.ExtraData, + // #nosec G701 - copied file from 3rd library, always in range + Size: int(proxy.Size), + // #nosec G701 - copied file from 3rd library, always in range + GasLimit: int(proxy.GasLimit), + // #nosec G701 - copied file from 3rd library, always in range + GasUsed: int(proxy.GasUsed), + // #nosec G701 - copied file from 3rd library, always in range + Timestamp: int(proxy.Timestamp), + Uncles: proxy.Uncles, + } + + block.Transactions = make([]Transaction, len(proxy.Transactions)) + for i := range proxy.Transactions { + block.Transactions[i] = Transaction{ + Hash: proxy.Transactions[i], + } + } + + return block +} + +type proxyTransaction struct { + Hash string `json:"hash"` + Nonce hexInt `json:"nonce"` + BlockHash string `json:"blockHash"` + BlockNumber *hexInt `json:"blockNumber"` + TransactionIndex *hexInt `json:"transactionIndex"` + From string `json:"from"` + To string `json:"to"` + Value hexBig `json:"value"` + Gas hexInt `json:"gas"` + GasPrice hexBig `json:"gasPrice"` + Input string `json:"input"` +} + +func (proxy *proxyBlockWithTransactions) toBlock() Block { + // #nosec G103 - copied file from 3rd library, should be safe enough + return *(*Block)(unsafe.Pointer(proxy)) +} diff --git a/zetaclient/evm_client.go b/zetaclient/evm_client.go index 36d5ee0570..f745b713d5 100644 --- a/zetaclient/evm_client.go +++ b/zetaclient/evm_client.go @@ -1,6 +1,7 @@ package zetaclient import ( + "bytes" "context" "fmt" "math" @@ -8,6 +9,7 @@ import ( "os" "sort" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -31,6 +33,7 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/ethrpc" metricsPkg "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" "gorm.io/driver/sqlite" @@ -70,6 +73,7 @@ type EVMChainClient struct { *ChainMetrics chain common.Chain evmClient EVMRPCClient + rpcClient *ethrpc.EthRPC // a fallback rpc client KlaytnClient KlaytnRPCClient zetaClient ZetaCoreBridger Tss TSSSigner @@ -92,7 +96,8 @@ type EVMChainClient struct { params observertypes.ChainParams ts *TelemetryServer - BlockCache *lru.Cache + BlockCache *lru.Cache + HeaderCache *lru.Cache } var _ ChainClient = (*EVMChainClient)(nil) @@ -146,12 +151,19 @@ func NewEVMChainClient( return nil, err } ob.evmClient = client + ob.rpcClient = ethrpc.NewEthRPC(evmCfg.Endpoint) + // create block header and block caches ob.BlockCache, err = lru.New(1000) if err != nil { ob.logger.ChainLogger.Error().Err(err).Msg("failed to create block cache") return nil, err } + ob.HeaderCache, err = lru.New(1000) + if err != nil { + ob.logger.ChainLogger.Error().Err(err).Msg("failed to create header cache") + return nil, err + } if ob.chain.IsKlaytnChain() { client, err := Dial(evmCfg.Endpoint) @@ -756,26 +768,6 @@ func (ob *EVMChainClient) checkConfirmedTx(txHash string, nonce uint64) (*ethtyp return nil, nil, false } - // cross-check receipt against the block - block, err := ob.GetBlockByNumberCached(receipt.BlockNumber.Uint64()) - if err != nil { - log.Error().Err(err).Msgf("confirmTxByHash: GetBlockByNumberCached error, txHash %s nonce %d block %d", - txHash, nonce, receipt.BlockNumber) - return nil, nil, false - } - // #nosec G701 non negative value - if receipt.TransactionIndex >= uint(len(block.Transactions())) { - log.Error().Msgf("confirmTxByHash: transaction index %d out of range [0, %d), txHash %s nonce %d block %d", - receipt.TransactionIndex, len(block.Transactions()), txHash, nonce, receipt.BlockNumber) - return nil, nil, false - } - txAtIndex := block.Transactions()[receipt.TransactionIndex] - if txAtIndex.Hash() != transaction.Hash() { - log.Error().Msgf("confirmTxByHash: transaction at index %d has different hash %s, txHash %s nonce %d block %d", - receipt.TransactionIndex, txAtIndex.Hash().Hex(), txHash, nonce, receipt.BlockNumber) - return nil, nil, false - } - // check confirmations if !ob.HasEnoughConfirmations(receipt, ob.GetLastBlockHeight()) { log.Debug().Msgf("confirmTxByHash: txHash %s nonce %d included but not confirmed: receipt block %d, current block %d", @@ -783,9 +775,49 @@ func (ob *EVMChainClient) checkConfirmedTx(txHash string, nonce uint64) (*ethtyp return nil, nil, false } + // cross-check tx inclusion against the block + // Note: a guard for false BlockNumber in receipt. The blob-carrying tx won't come here + err = ob.checkTxInclusion(transaction, receipt.BlockNumber.Uint64(), receipt.TransactionIndex) + if err != nil { + log.Error().Err(err).Msgf("confirmTxByHash: checkTxInclusion error for txHash %s nonce %d", txHash, nonce) + return nil, nil, false + } + return receipt, transaction, true } +// checkTxInclusion returns nil only if tx is included in the block at blockNumber and txIndex +func (ob *EVMChainClient) checkTxInclusion(tx *ethtypes.Transaction, blockNumber uint64, txIndex uint) error { + block, blockRPC, fallBack, _, err := ob.GetBlockByNumberCached(blockNumber) + if err != nil { + return fmt.Errorf("GetBlockByNumberCached error for block %d txHash %s nonce %d: %w", blockNumber, tx.Hash(), tx.Nonce(), err) + } + if !fallBack { + // #nosec G701 non negative value + if txIndex >= uint(len(block.Transactions())) { + return fmt.Errorf("transaction index %d out of range [0, %d), txHash %s nonce %d block %d", + txIndex, len(block.Transactions()), tx.Hash(), tx.Nonce(), blockNumber) + } + txAtIndex := block.Transactions()[txIndex] + if txAtIndex.Hash() != tx.Hash() { + return fmt.Errorf("transaction at index %d has different hash %s, txHash %s nonce %d block %d", + txIndex, txAtIndex.Hash().Hex(), tx.Hash(), tx.Nonce(), blockNumber) + } + } else { // fell back on ETH RPC as ethclient failed to parse the block + // #nosec G701 non negative value + if txIndex >= uint(len(blockRPC.Transactions)) { + return fmt.Errorf("transaction index %d out of range [0, %d), txHash %s nonce %d block %d", + txIndex, len(block.Transactions()), tx.Hash(), tx.Nonce(), blockNumber) + } + txAtIndex := blockRPC.Transactions[txIndex] + if ethcommon.HexToHash(txAtIndex.Hash) != tx.Hash() { + return fmt.Errorf("transaction at index %d has different hash %s, txHash %s nonce %d block %d", + txIndex, txAtIndex.Hash, tx.Hash(), tx.Nonce(), blockNumber) + } + } + return nil +} + // SetLastBlockHeightScanned set last block height scanned (not necessarily caught up with external block; could be slow/paused) func (ob *EVMChainClient) SetLastBlockHeightScanned(height uint64) { atomic.StoreUint64(&ob.lastBlockScanned, height) @@ -863,12 +895,12 @@ func (ob *EVMChainClient) postBlockHeader(tip uint64) error { return fmt.Errorf("postBlockHeader: must post block confirmed block header: %d > %d", bn, tip) } - block, err := ob.GetBlockByNumberCached(bn) + header, err := ob.GetBlockHeaderCached(bn) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("postBlockHeader: error getting block: %d", bn) return err } - headerRLP, err := rlp.EncodeToBytes(block.Header()) + headerRLP, err := rlp.EncodeToBytes(header) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("postBlockHeader: error encoding block header: %d", bn) return err @@ -876,8 +908,8 @@ func (ob *EVMChainClient) postBlockHeader(tip uint64) error { _, err = ob.zetaClient.PostAddBlockHeader( ob.chain.ChainId, - block.Hash().Bytes(), - block.Number().Int64(), + header.Hash().Bytes(), + header.Number.Int64(), common.NewEthereumHeader(headerRLP), ) if err != nil { @@ -1159,48 +1191,39 @@ func (ob *EVMChainClient) observeTssRecvd(startBlock, toBlock uint64, flags obse } // TODO: we can track the total number of 'getBlockByNumber' RPC calls made - block, err := ob.GetBlockByNumberCached(bn) + block, blockRPC, fallBack, skip, err := ob.GetBlockByNumberCached(bn) if err != nil { + if skip { + ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("observeTssRecvd: skip block %d for chain %d", bn, ob.chain.ChainId) + continue + } ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("observeTssRecvd: error getting block %d for chain %d", bn, ob.chain.ChainId) return bn - 1 // we have to re-scan from this block next time } - for _, tx := range block.Transactions() { - if tx.To() == nil { - continue - } - - if *tx.To() == tssAddress { - receipt, err := ob.evmClient.TransactionReceipt(context.Background(), tx.Hash()) - if err != nil { - ob.logger.ExternalChainWatcher.Err(err).Msgf( - "observeTssRecvd: TransactionReceipt error for tx %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) - return bn - 1 // we have to re-scan this block next time - } - if receipt.Status != 1 { // 1: successful, 0: failed - ob.logger.ExternalChainWatcher.Info().Msgf("observeTssRecvd: tx %s chain %d failed; don't act", tx.Hash().Hex(), ob.chain.ChainId) - continue - } - - sender, err := ob.GetTransactionSender(tx, block.Hash(), receipt.TransactionIndex) - if err != nil { - ob.logger.ExternalChainWatcher.Err(err).Msgf( - "observeTssRecvd: GetTransactionSender error for tx %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) - return bn - 1 // we have to re-scan this block next time - } - - msg := ob.GetInboundVoteMsgForTokenSentToTSS(tx, sender, receipt.BlockNumber.Uint64()) - if msg == nil { - continue + if !fallBack { + for _, tx := range block.Transactions() { + if tx.To() != nil && *tx.To() == tssAddress { + if ok := ob.processIntxToTss(tx, bn, block.Hash()); !ok { + return bn - 1 // we have to re-scan this block next time + } } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) - if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( - "observeTssRecvd: error posting to zeta core for tx %s at height %d for chain %d", tx.Hash().Hex(), bn, ob.chain.ChainId) - return bn - 1 // we have to re-scan this block next time - } else if zetaHash != "" { - ob.logger.ExternalChainWatcher.Info().Msgf( - "observeTssRecvd: gas asset deposit detected in tx %s at height %d for chain %d, PostVoteInbound zeta tx: %s ballot %s", - tx.Hash().Hex(), bn, ob.chain.ChainId, zetaHash, ballot) + } + } else { // fell back on ETH RPC as ethclient failed to parse the block + ob.logger.ExternalChainWatcher.Info().Msgf("observeTssRecvd: processing block %d using fallback for chain %d", bn, ob.chain.ChainId) + for _, txRPC := range blockRPC.Transactions { + if ethcommon.HexToAddress(txRPC.To) == tssAddress { + tx, _, err := ob.evmClient.TransactionByHash(context.Background(), ethcommon.HexToHash(txRPC.Hash)) + if err != nil { + if strings.Contains(err.Error(), "transaction type not supported") { + ob.logger.ExternalChainWatcher.Err(err).Msgf( + "observeTssRecvd: transaction type not supported for tx %s chain %d", txRPC.Hash, ob.chain.ChainId) + continue // skip blob-carrying tx to TSS address + } + return bn - 1 // we have to re-scan this block next time + } + if ok := ob.processIntxToTss(tx, blockRPC.Number, ethcommon.HexToHash(blockRPC.Hash)); !ok { + return bn - 1 // we have to re-scan this block next time + } } } } @@ -1209,6 +1232,48 @@ func (ob *EVMChainClient) observeTssRecvd(startBlock, toBlock uint64, flags obse return toBlock } +// processIntxToTss processes the incoming tx to TSS address and posts to zetacore +// returns true if the tx is successfully processed, false otherwise +func (ob *EVMChainClient) processIntxToTss(tx *ethtypes.Transaction, bn uint64, blockHash ethcommon.Hash) bool { + receipt, err := ob.evmClient.TransactionReceipt(context.Background(), tx.Hash()) + if err != nil { + ob.logger.ExternalChainWatcher.Err(err).Msgf( + "processIntxToTss: TransactionReceipt error for tx %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) + return false // we have to re-scan this block next time + } + if receipt.Status != 1 { // 1: successful, 0: failed + ob.logger.ExternalChainWatcher.Info().Msgf("processIntxToTss: tx %s chain %d failed; don't act", tx.Hash().Hex(), ob.chain.ChainId) + return true // skip failed tx + } + if bytes.Equal(tx.Data(), []byte(DonationMessage)) { + ob.logger.ExternalChainWatcher.Info().Msgf( + "processIntxToTss: thank you rich folk for your donation!: %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) + return true // skip donation tx + } + sender, err := ob.GetTransactionSender(tx, blockHash, receipt.TransactionIndex) + if err != nil { + ob.logger.ExternalChainWatcher.Err(err).Msgf( + "processIntxToTss: GetTransactionSender error for tx %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) + return false // we have to re-scan this block next time + } + + msg := ob.GetInboundVoteMsgForTokenSentToTSS(tx, sender, bn) + if msg == nil { + return true // should never happen, always non-nil + } + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) + if err != nil { + ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( + "processIntxToTss: error posting to zeta core for tx %s at height %d for chain %d", tx.Hash().Hex(), bn, ob.chain.ChainId) + return false // we have to re-scan this block next time + } else if zetaHash != "" { + ob.logger.ExternalChainWatcher.Info().Msgf( + "processIntxToTss: gas asset deposit detected in tx %s at height %d for chain %d, PostSend zeta tx: %s ballot %s", + tx.Hash().Hex(), bn, ob.chain.ChainId, zetaHash, ballot) + } + return true +} + func (ob *EVMChainClient) WatchGasPrice() { ob.logger.WatchGasPrice.Info().Msg("WatchGasPrice starting...") err := ob.PostGasPrice() @@ -1411,15 +1476,38 @@ func (ob *EVMChainClient) GetTxID(nonce uint64) string { return fmt.Sprintf("%d-%s-%d", ob.chain.ChainId, tssAddr, nonce) } -func (ob *EVMChainClient) GetBlockByNumberCached(blockNumber uint64) (*ethtypes.Block, error) { +func (ob *EVMChainClient) GetBlockHeaderCached(blockNumber uint64) (*ethtypes.Header, error) { + if header, ok := ob.HeaderCache.Get(blockNumber); ok { + return header.(*ethtypes.Header), nil + } + header, err := ob.evmClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) + if err != nil { + return nil, err + } + ob.HeaderCache.Add(blockNumber, header) + return header, nil +} + +// GetBlockByNumberCached get block by number from cache +// returns block, ethrpc.Block, isFallback, isSkip, error +func (ob *EVMChainClient) GetBlockByNumberCached(blockNumber uint64) (*ethtypes.Block, *ethrpc.Block, bool, bool, error) { if block, ok := ob.BlockCache.Get(blockNumber); ok { - return block.(*ethtypes.Block), nil + return block.(*ethtypes.Block), nil, false, false, nil } block, err := ob.evmClient.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) if err != nil { - return nil, err + if strings.Contains(err.Error(), "block header indicates no transactions") { + return nil, nil, false, true, err // it's ok skip empty block + } else if strings.Contains(err.Error(), "transaction type not supported") { + rpcBlock, err := ob.rpcClient.EthGetBlockByNumber(blockNumber, true) + if err != nil { + return nil, nil, true, false, err // fall back on ethRPC but still fail + } + return nil, rpcBlock, true, false, nil // fall back on ethRPC without error + } + return nil, nil, false, false, err } ob.BlockCache.Add(blockNumber, block) ob.BlockCache.Add(block.Hash(), block) - return block, nil + return block, nil, false, false, nil } diff --git a/zetaclient/tss_signer.go b/zetaclient/tss_signer.go index dd75835024..d1c10e31de 100644 --- a/zetaclient/tss_signer.go +++ b/zetaclient/tss_signer.go @@ -183,7 +183,7 @@ func (tss *TSS) Pubkey() []byte { // Sign signs a digest // digest should be Hashes of some data // NOTE: Specify optionalPubkey to use a different pubkey than the current pubkey set during keygen -func (tss *TSS) Sign(digest []byte, height uint64, nonce uint64, chain *common.Chain, optionalPubKey string) ([65]byte, error) { +func (tss *TSS) Sign(digest []byte, height uint64, _ uint64, _ *common.Chain, optionalPubKey string) ([65]byte, error) { H := digest log.Debug().Msgf("hash of digest is %s", H) @@ -200,14 +200,16 @@ func (tss *TSS) Sign(digest []byte, height uint64, nonce uint64, chain *common.C if ksRes.Status == thorcommon.Fail { log.Warn().Msgf("keysign status FAIL posting blame to core, blaming node(s): %#v", ksRes.Blame.BlameNodes) - digest := hex.EncodeToString(digest) - index := observertypes.GetBlameIndex(chain.ChainId, nonce, digest, height) + //digest := hex.EncodeToString(digest) + //index := observertypes.GetBlameIndex(chain.ChainId, nonce, digest, height) + // + //zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) + //if err != nil { + // log.Error().Err(err).Msg("error sending blame data to core") + // return [65]byte{}, err + //} - zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) - if err != nil { - log.Error().Err(err).Msg("error sending blame data to core") - return [65]byte{}, err - } + //log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) // Increment Blame counter for _, node := range ksRes.Blame.BlameNodes { @@ -218,8 +220,6 @@ func (tss *TSS) Sign(digest []byte, height uint64, nonce uint64, chain *common.C } counter.Inc() } - - log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) } signature := ksRes.Signatures @@ -257,7 +257,7 @@ func (tss *TSS) Sign(digest []byte, height uint64, nonce uint64, chain *common.C // SignBatch is hash of some data // digest should be batch of hashes of some data -func (tss *TSS) SignBatch(digests [][]byte, height uint64, nonce uint64, chain *common.Chain) ([][65]byte, error) { +func (tss *TSS) SignBatch(digests [][]byte, height uint64, _ uint64, _ *common.Chain) ([][65]byte, error) { tssPubkey := tss.CurrentPubkey digestBase64 := make([]string, len(digests)) for i, digest := range digests { @@ -273,14 +273,16 @@ func (tss *TSS) SignBatch(digests [][]byte, height uint64, nonce uint64, chain * if ksRes.Status == thorcommon.Fail { log.Warn().Msg("keysign status FAIL posting blame to core") - digest := combineDigests(digestBase64) - index := observertypes.GetBlameIndex(chain.ChainId, nonce, hex.EncodeToString(digest), height) + //digest := combineDigests(digestBase64) + //index := observertypes.GetBlameIndex(chain.ChainId, nonce, hex.EncodeToString(digest), height) - zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) - if err != nil { - log.Error().Err(err).Msg("error sending blame data to core") - return [][65]byte{}, err - } + //zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) + //if err != nil { + // log.Error().Err(err).Msg("error sending blame data to core") + // return [][65]byte{}, err + //} + // + //log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) // Increment Blame counter for _, node := range ksRes.Blame.BlameNodes { @@ -291,8 +293,6 @@ func (tss *TSS) SignBatch(digests [][]byte, height uint64, nonce uint64, chain * } counter.Inc() } - - log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) } signatures := ksRes.Signatures @@ -580,7 +580,7 @@ func verifySignature(tssPubkey string, signature []keysign.Signature, H []byte) return bytes.Equal(pubkey.Bytes(), compressedPubkey) } -func combineDigests(digestList []string) []byte { +func CombineDigests(digestList []string) []byte { digestConcat := strings.Join(digestList[:], "") digestBytes := chainhash.DoubleHashH([]byte(digestConcat)) return digestBytes.CloneBytes() From 003e851e10e5f76fee146ac76cc4bb564b6c523b Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Tue, 30 Jan 2024 11:30:50 -0600 Subject: [PATCH 2/2] use original ethrpc package and some updates according to suggestions --- go.mod | 5 +- go.sum | 3 + zetaclient/ethrpc/ethrpc.go | 137 ------------------------------ zetaclient/ethrpc/helper.go | 40 --------- zetaclient/ethrpc/types.go | 162 ------------------------------------ zetaclient/evm_client.go | 41 +++++---- zetaclient/tss_signer.go | 51 +++++++----- zetaclient/utils.go | 6 ++ 8 files changed, 69 insertions(+), 376 deletions(-) delete mode 100644 zetaclient/ethrpc/ethrpc.go delete mode 100644 zetaclient/ethrpc/helper.go delete mode 100644 zetaclient/ethrpc/types.go diff --git a/go.mod b/go.mod index aff1f815c3..8341c0bddf 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,10 @@ require ( gorm.io/gorm v1.24.6 ) -require github.com/binance-chain/tss-lib v0.0.0-20201118045712-70b2cb4bf916 +require ( + github.com/binance-chain/tss-lib v0.0.0-20201118045712-70b2cb4bf916 + github.com/onrik/ethrpc v1.2.0 +) require ( github.com/DataDog/zstd v1.5.2 // indirect diff --git a/go.sum b/go.sum index 96ac6b81eb..be10cf47d8 100644 --- a/go.sum +++ b/go.sum @@ -1869,6 +1869,7 @@ github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7Bd github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea/go.mod h1:QMdK4dGB3YhEW2BmA1wgGpPYI3HZy/5gD705PXKUVSg= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= @@ -2358,6 +2359,8 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1 github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onrik/ethrpc v1.2.0 h1:BBcr1iWxW1RBP/eyZfzvSKtGgeqexq5qS0yyf4pmKbc= +github.com/onrik/ethrpc v1.2.0/go.mod h1:uvyqpn8+WbsTgBYfouImgEfpIMb0hR8fWGjwdgPHtFU= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/zetaclient/ethrpc/ethrpc.go b/zetaclient/ethrpc/ethrpc.go deleted file mode 100644 index 88283254f2..0000000000 --- a/zetaclient/ethrpc/ethrpc.go +++ /dev/null @@ -1,137 +0,0 @@ -package ethrpc - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" -) - -// EthError - ethereum error -type EthError struct { - Code int `json:"code"` - Message string `json:"message"` -} - -func (err EthError) Error() string { - return fmt.Sprintf("Error %d (%s)", err.Code, err.Message) -} - -type ethResponse struct { - ID int `json:"id"` - JSONRPC string `json:"jsonrpc"` - Result json.RawMessage `json:"result"` - Error *EthError `json:"error"` -} - -type ethRequest struct { - ID int `json:"id"` - JSONRPC string `json:"jsonrpc"` - Method string `json:"method"` - Params []interface{} `json:"params"` -} - -// EthRPC - Ethereum rpc client -type EthRPC struct { - url string - client *http.Client -} - -// New create new rpc client with given url -func New(url string, options ...func(rpc *EthRPC)) *EthRPC { - rpc := &EthRPC{ - url: url, - client: http.DefaultClient, - } - for _, option := range options { - option(rpc) - } - - return rpc -} - -// NewEthRPC create new rpc client with given url -func NewEthRPC(url string, options ...func(rpc *EthRPC)) *EthRPC { - return New(url, options...) -} - -// URL returns client url -func (rpc *EthRPC) URL() string { - return rpc.url -} - -// Call returns raw response of method call -func (rpc *EthRPC) Call(method string, params ...interface{}) (json.RawMessage, error) { - request := ethRequest{ - ID: 1, - JSONRPC: "2.0", - Method: method, - Params: params, - } - - body, err := json.Marshal(request) - if err != nil { - return nil, err - } - - response, err := rpc.client.Post(rpc.url, "application/json", bytes.NewBuffer(body)) - if response != nil { - defer response.Body.Close() - } - if err != nil { - return nil, err - } - - data, err := io.ReadAll(response.Body) - if err != nil { - return nil, err - } - - resp := new(ethResponse) - if err := json.Unmarshal(data, resp); err != nil { - return nil, err - } - - if resp.Error != nil { - return nil, *resp.Error - } - - return resp.Result, nil - -} - -// RawCall returns raw response of method call (Deprecated) -func (rpc *EthRPC) RawCall(method string, params ...interface{}) (json.RawMessage, error) { - return rpc.Call(method, params...) -} - -func (rpc *EthRPC) getBlock(method string, withTransactions bool, params ...interface{}) (*Block, error) { - result, err := rpc.RawCall(method, params...) - if err != nil { - return nil, err - } - if bytes.Equal(result, []byte("null")) { - return nil, nil - } - - var response proxyBlock - if withTransactions { - response = new(proxyBlockWithTransactions) - } else { - response = new(proxyBlockWithoutTransactions) - } - - err = json.Unmarshal(result, response) - if err != nil { - return nil, err - } - - block := response.toBlock() - return &block, nil -} - -// EthGetBlockByNumber returns information about a block by block number. -func (rpc *EthRPC) EthGetBlockByNumber(number uint64, withTransactions bool) (*Block, error) { - return rpc.getBlock("eth_getBlockByNumber", withTransactions, IntToHex(number), withTransactions) -} diff --git a/zetaclient/ethrpc/helper.go b/zetaclient/ethrpc/helper.go deleted file mode 100644 index c826bf94cd..0000000000 --- a/zetaclient/ethrpc/helper.go +++ /dev/null @@ -1,40 +0,0 @@ -package ethrpc - -import ( - "fmt" - "math/big" - "strconv" - "strings" -) - -// ParseInt parse hex string value to uint64 -func ParseInt(value string) (uint64, error) { - i, err := strconv.ParseUint(strings.TrimPrefix(value, "0x"), 16, 64) - if err != nil { - return 0, err - } - - return i, nil -} - -// ParseBigInt parse hex string value to big.Int -func ParseBigInt(value string) (big.Int, error) { - i := big.Int{} - _, err := fmt.Sscan(value, &i) - - return i, err -} - -// IntToHex convert int to hexadecimal representation -func IntToHex(i uint64) string { - return fmt.Sprintf("0x%x", i) -} - -// BigToHex covert big.Int to hexadecimal representation -func BigToHex(bigInt big.Int) string { - if bigInt.BitLen() == 0 { - return "0x0" - } - - return "0x" + strings.TrimPrefix(fmt.Sprintf("%x", bigInt.Bytes()), "0") -} diff --git a/zetaclient/ethrpc/types.go b/zetaclient/ethrpc/types.go deleted file mode 100644 index d756fbfde8..0000000000 --- a/zetaclient/ethrpc/types.go +++ /dev/null @@ -1,162 +0,0 @@ -package ethrpc - -import ( - "bytes" - "math/big" - "unsafe" -) - -// Transaction - transaction object -type Transaction struct { - Hash string - Nonce int - BlockHash string - BlockNumber *int - TransactionIndex *int - From string - To string - Value big.Int - Gas int - GasPrice big.Int - Input string -} - -// Block - block object -type Block struct { - Number uint64 - Hash string - ParentHash string - Nonce string - Sha3Uncles string - LogsBloom string - TransactionsRoot string - StateRoot string - Miner string - Difficulty big.Int - TotalDifficulty big.Int - ExtraData string - Size int - GasLimit int - GasUsed int - Timestamp int - Uncles []string - Transactions []Transaction -} - -type hexInt uint64 - -func (i *hexInt) UnmarshalJSON(data []byte) error { - result, err := ParseInt(string(bytes.Trim(data, `"`))) - *i = hexInt(result) - - return err -} - -type hexBig big.Int - -func (i *hexBig) UnmarshalJSON(data []byte) error { - result, err := ParseBigInt(string(bytes.Trim(data, `"`))) - *i = hexBig(result) - - return err -} - -type proxyBlock interface { - toBlock() Block -} - -type proxyBlockWithTransactions struct { - Number hexInt `json:"number"` - Hash string `json:"hash"` - ParentHash string `json:"parentHash"` - Nonce string `json:"nonce"` - Sha3Uncles string `json:"sha3Uncles"` - LogsBloom string `json:"logsBloom"` - TransactionsRoot string `json:"transactionsRoot"` - StateRoot string `json:"stateRoot"` - Miner string `json:"miner"` - Difficulty hexBig `json:"difficulty"` - TotalDifficulty hexBig `json:"totalDifficulty"` - ExtraData string `json:"extraData"` - Size hexInt `json:"size"` - GasLimit hexInt `json:"gasLimit"` - GasUsed hexInt `json:"gasUsed"` - Timestamp hexInt `json:"timestamp"` - Uncles []string `json:"uncles"` - Transactions []proxyTransaction `json:"transactions"` -} - -type proxyBlockWithoutTransactions struct { - Number hexInt `json:"number"` - Hash string `json:"hash"` - ParentHash string `json:"parentHash"` - Nonce string `json:"nonce"` - Sha3Uncles string `json:"sha3Uncles"` - LogsBloom string `json:"logsBloom"` - TransactionsRoot string `json:"transactionsRoot"` - StateRoot string `json:"stateRoot"` - Miner string `json:"miner"` - Difficulty hexBig `json:"difficulty"` - TotalDifficulty hexBig `json:"totalDifficulty"` - ExtraData string `json:"extraData"` - Size hexInt `json:"size"` - GasLimit hexInt `json:"gasLimit"` - GasUsed hexInt `json:"gasUsed"` - Timestamp hexInt `json:"timestamp"` - Uncles []string `json:"uncles"` - Transactions []string `json:"transactions"` -} - -func (proxy *proxyBlockWithoutTransactions) toBlock() Block { - block := Block{ - Number: uint64(proxy.Number), - Hash: proxy.Hash, - ParentHash: proxy.ParentHash, - Nonce: proxy.Nonce, - Sha3Uncles: proxy.Sha3Uncles, - LogsBloom: proxy.LogsBloom, - TransactionsRoot: proxy.TransactionsRoot, - StateRoot: proxy.StateRoot, - Miner: proxy.Miner, - Difficulty: big.Int(proxy.Difficulty), - TotalDifficulty: big.Int(proxy.TotalDifficulty), - ExtraData: proxy.ExtraData, - // #nosec G701 - copied file from 3rd library, always in range - Size: int(proxy.Size), - // #nosec G701 - copied file from 3rd library, always in range - GasLimit: int(proxy.GasLimit), - // #nosec G701 - copied file from 3rd library, always in range - GasUsed: int(proxy.GasUsed), - // #nosec G701 - copied file from 3rd library, always in range - Timestamp: int(proxy.Timestamp), - Uncles: proxy.Uncles, - } - - block.Transactions = make([]Transaction, len(proxy.Transactions)) - for i := range proxy.Transactions { - block.Transactions[i] = Transaction{ - Hash: proxy.Transactions[i], - } - } - - return block -} - -type proxyTransaction struct { - Hash string `json:"hash"` - Nonce hexInt `json:"nonce"` - BlockHash string `json:"blockHash"` - BlockNumber *hexInt `json:"blockNumber"` - TransactionIndex *hexInt `json:"transactionIndex"` - From string `json:"from"` - To string `json:"to"` - Value hexBig `json:"value"` - Gas hexInt `json:"gas"` - GasPrice hexBig `json:"gasPrice"` - Input string `json:"input"` -} - -func (proxy *proxyBlockWithTransactions) toBlock() Block { - // #nosec G103 - copied file from 3rd library, should be safe enough - return *(*Block)(unsafe.Pointer(proxy)) -} diff --git a/zetaclient/evm_client.go b/zetaclient/evm_client.go index f745b713d5..8d5b78655e 100644 --- a/zetaclient/evm_client.go +++ b/zetaclient/evm_client.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rlp" lru "github.com/hashicorp/golang-lru" + "github.com/onrik/ethrpc" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -33,7 +34,6 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/ethrpc" metricsPkg "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" "gorm.io/driver/sqlite" @@ -73,7 +73,7 @@ type EVMChainClient struct { *ChainMetrics chain common.Chain evmClient EVMRPCClient - rpcClient *ethrpc.EthRPC // a fallback rpc client + evmClientAlternate *ethrpc.EthRPC // a fallback rpc client KlaytnClient KlaytnRPCClient zetaClient ZetaCoreBridger Tss TSSSigner @@ -96,8 +96,9 @@ type EVMChainClient struct { params observertypes.ChainParams ts *TelemetryServer - BlockCache *lru.Cache - HeaderCache *lru.Cache + blockCache *lru.Cache + blockCacheV3 *lru.Cache // blockCacheV3 caches blocks containing type-3 (BlobTxType) transactions + headerCache *lru.Cache } var _ ChainClient = (*EVMChainClient)(nil) @@ -151,15 +152,20 @@ func NewEVMChainClient( return nil, err } ob.evmClient = client - ob.rpcClient = ethrpc.NewEthRPC(evmCfg.Endpoint) + ob.evmClientAlternate = ethrpc.NewEthRPC(evmCfg.Endpoint) // create block header and block caches - ob.BlockCache, err = lru.New(1000) + ob.blockCache, err = lru.New(1000) if err != nil { ob.logger.ChainLogger.Error().Err(err).Msg("failed to create block cache") return nil, err } - ob.HeaderCache, err = lru.New(1000) + ob.blockCacheV3, err = lru.New(1000) + if err != nil { + ob.logger.ChainLogger.Error().Err(err).Msg("failed to create block cache v3") + return nil, err + } + ob.headerCache, err = lru.New(1000) if err != nil { ob.logger.ChainLogger.Error().Err(err).Msg("failed to create header cache") return nil, err @@ -1221,7 +1227,7 @@ func (ob *EVMChainClient) observeTssRecvd(startBlock, toBlock uint64, flags obse } return bn - 1 // we have to re-scan this block next time } - if ok := ob.processIntxToTss(tx, blockRPC.Number, ethcommon.HexToHash(blockRPC.Hash)); !ok { + if ok := ob.processIntxToTss(tx, bn, ethcommon.HexToHash(blockRPC.Hash)); !ok { return bn - 1 // we have to re-scan this block next time } } @@ -1477,37 +1483,44 @@ func (ob *EVMChainClient) GetTxID(nonce uint64) string { } func (ob *EVMChainClient) GetBlockHeaderCached(blockNumber uint64) (*ethtypes.Header, error) { - if header, ok := ob.HeaderCache.Get(blockNumber); ok { + if header, ok := ob.headerCache.Get(blockNumber); ok { return header.(*ethtypes.Header), nil } header, err := ob.evmClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) if err != nil { return nil, err } - ob.HeaderCache.Add(blockNumber, header) + ob.headerCache.Add(blockNumber, header) return header, nil } // GetBlockByNumberCached get block by number from cache // returns block, ethrpc.Block, isFallback, isSkip, error func (ob *EVMChainClient) GetBlockByNumberCached(blockNumber uint64) (*ethtypes.Block, *ethrpc.Block, bool, bool, error) { - if block, ok := ob.BlockCache.Get(blockNumber); ok { + if block, ok := ob.blockCache.Get(blockNumber); ok { return block.(*ethtypes.Block), nil, false, false, nil } + if block, ok := ob.blockCacheV3.Get(blockNumber); ok { + return nil, block.(*ethrpc.Block), true, false, nil + } block, err := ob.evmClient.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) if err != nil { if strings.Contains(err.Error(), "block header indicates no transactions") { return nil, nil, false, true, err // it's ok skip empty block } else if strings.Contains(err.Error(), "transaction type not supported") { - rpcBlock, err := ob.rpcClient.EthGetBlockByNumber(blockNumber, true) + if blockNumber > math.MaxInt32 { + return nil, nil, true, false, fmt.Errorf("block number %d is too large", blockNumber) + } + // #nosec G701 always in range, checked above + rpcBlock, err := ob.evmClientAlternate.EthGetBlockByNumber(int(blockNumber), true) if err != nil { return nil, nil, true, false, err // fall back on ethRPC but still fail } + ob.blockCacheV3.Add(blockNumber, rpcBlock) return nil, rpcBlock, true, false, nil // fall back on ethRPC without error } return nil, nil, false, false, err } - ob.BlockCache.Add(blockNumber, block) - ob.BlockCache.Add(block.Hash(), block) + ob.blockCache.Add(blockNumber, block) return block, nil, false, false, nil } diff --git a/zetaclient/tss_signer.go b/zetaclient/tss_signer.go index d1c10e31de..2bb59a5550 100644 --- a/zetaclient/tss_signer.go +++ b/zetaclient/tss_signer.go @@ -32,6 +32,10 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/metrics" ) +const ( + envFlagPostBlame = "POST_BLAME" +) + type TSSKey struct { PubkeyInBytes []byte // FIXME: compressed pubkey? PubkeyInBech32 string // FIXME: same above @@ -183,7 +187,7 @@ func (tss *TSS) Pubkey() []byte { // Sign signs a digest // digest should be Hashes of some data // NOTE: Specify optionalPubkey to use a different pubkey than the current pubkey set during keygen -func (tss *TSS) Sign(digest []byte, height uint64, _ uint64, _ *common.Chain, optionalPubKey string) ([65]byte, error) { +func (tss *TSS) Sign(digest []byte, height uint64, nonce uint64, chain *common.Chain, optionalPubKey string) ([65]byte, error) { H := digest log.Debug().Msgf("hash of digest is %s", H) @@ -200,16 +204,17 @@ func (tss *TSS) Sign(digest []byte, height uint64, _ uint64, _ *common.Chain, op if ksRes.Status == thorcommon.Fail { log.Warn().Msgf("keysign status FAIL posting blame to core, blaming node(s): %#v", ksRes.Blame.BlameNodes) - //digest := hex.EncodeToString(digest) - //index := observertypes.GetBlameIndex(chain.ChainId, nonce, digest, height) - // - //zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) - //if err != nil { - // log.Error().Err(err).Msg("error sending blame data to core") - // return [65]byte{}, err - //} - - //log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) + // post blame data if enabled + if IsEnvFlagEnabled(envFlagPostBlame) { + digest := hex.EncodeToString(digest) + index := observertypes.GetBlameIndex(chain.ChainId, nonce, digest, height) + zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) + if err != nil { + log.Error().Err(err).Msg("error sending blame data to core") + return [65]byte{}, err + } + log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) + } // Increment Blame counter for _, node := range ksRes.Blame.BlameNodes { @@ -257,7 +262,7 @@ func (tss *TSS) Sign(digest []byte, height uint64, _ uint64, _ *common.Chain, op // SignBatch is hash of some data // digest should be batch of hashes of some data -func (tss *TSS) SignBatch(digests [][]byte, height uint64, _ uint64, _ *common.Chain) ([][65]byte, error) { +func (tss *TSS) SignBatch(digests [][]byte, height uint64, nonce uint64, chain *common.Chain) ([][65]byte, error) { tssPubkey := tss.CurrentPubkey digestBase64 := make([]string, len(digests)) for i, digest := range digests { @@ -273,16 +278,18 @@ func (tss *TSS) SignBatch(digests [][]byte, height uint64, _ uint64, _ *common.C if ksRes.Status == thorcommon.Fail { log.Warn().Msg("keysign status FAIL posting blame to core") - //digest := combineDigests(digestBase64) - //index := observertypes.GetBlameIndex(chain.ChainId, nonce, hex.EncodeToString(digest), height) - //zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) - //if err != nil { - // log.Error().Err(err).Msg("error sending blame data to core") - // return [][65]byte{}, err - //} - // - //log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) + // post blame data if enabled + if IsEnvFlagEnabled(envFlagPostBlame) { + digest := combineDigests(digestBase64) + index := observertypes.GetBlameIndex(chain.ChainId, nonce, hex.EncodeToString(digest), height) + zetaHash, err := tss.CoreBridge.PostBlameData(&ksRes.Blame, chain.ChainId, index) + if err != nil { + log.Error().Err(err).Msg("error sending blame data to core") + return [][65]byte{}, err + } + log.Info().Msgf("keysign posted blame data tx hash: %s", zetaHash) + } // Increment Blame counter for _, node := range ksRes.Blame.BlameNodes { @@ -580,7 +587,7 @@ func verifySignature(tssPubkey string, signature []keysign.Signature, H []byte) return bytes.Equal(pubkey.Bytes(), compressedPubkey) } -func CombineDigests(digestList []string) []byte { +func combineDigests(digestList []string) []byte { digestConcat := strings.Join(digestList[:], "") digestBytes := chainhash.DoubleHashH([]byte(digestConcat)) return digestBytes.CloneBytes() diff --git a/zetaclient/utils.go b/zetaclient/utils.go index 5a0fcd9ab2..9fa5eab264 100644 --- a/zetaclient/utils.go +++ b/zetaclient/utils.go @@ -9,6 +9,7 @@ import ( "fmt" "math" "math/big" + "os" "strings" "time" @@ -54,6 +55,11 @@ func init() { BtcDepositorFeeMin = DepositorFee(5) // 0.00000745 (5 * 149B / 100000000), the minimum deposit fee in BTC for 5 sat/byte } +func IsEnvFlagEnabled(flag string) bool { + value := os.Getenv(flag) + return value == "true" || value == "1" +} + func PrettyPrintStruct(val interface{}) (string, error) { prettyStruct, err := json.MarshalIndent( val,