Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: skip Goerli BlobTxType transaction and Mumbai empty block #1665

Merged
merged 4 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
137 changes: 137 additions & 0 deletions zetaclient/ethrpc/ethrpc.go
Original file line number Diff line number Diff line change
@@ -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)
}
40 changes: 40 additions & 0 deletions zetaclient/ethrpc/helper.go
Original file line number Diff line number Diff line change
@@ -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")
}
162 changes: 162 additions & 0 deletions zetaclient/ethrpc/types.go
Original file line number Diff line number Diff line change
@@ -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))
}
Loading
Loading