Files
fd38715c6c change tracer response type (#469)
* change tracer response type

* convert from raw struct to type

* fix typo

* nits

* reduce diff

* lint  nit

Co-authored-by: dhrubabasu <[email protected]>
2022-05-09 12:08:43 -06:00

207 lines
4.5 KiB
Go

package modelsc
import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"strings"
"sync"
"time"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/eth/tracers"
"github.com/ava-labs/coreth/ethclient"
"github.com/ava-labs/coreth/interfaces"
"github.com/ava-labs/coreth/rpc"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
var ErrNotFound = errors.New("block not found")
type Block struct {
Header types.Header `json:"header"`
Uncles []types.Header `json:"uncles"`
TxsBytes *[][]byte `json:"txs,omitempty"`
Version uint32 `json:"version"`
BlockExtraData []byte `json:"blockExtraData"`
Txs []types.Transaction `json:"transactions,omitempty"`
}
type Call struct {
Type string `json:"type"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Value *hexutil.Big `json:"value"`
GasUsed *hexutil.Big `json:"gasUsed"`
Revert bool `json:"revert"`
Error string `json:"error,omitempty"`
Calls []*Call `json:"calls,omitempty"`
}
func New(bl *types.Block) (*Block, error) {
var cblock Block
cblock.Version = bl.Version()
cblock.BlockExtraData = bl.ExtData()
if cblock.BlockExtraData != nil {
if len(cblock.BlockExtraData) == 0 {
cblock.BlockExtraData = nil
}
}
var h = bl.Header()
if h != nil {
cblock.Header = *h
}
for _, u := range bl.Uncles() {
if u == nil {
continue
}
cblock.Uncles = append(cblock.Uncles, *u)
}
for _, t := range bl.Transactions() {
cblock.Txs = append(cblock.Txs, *t)
}
return &cblock, nil
}
func Marshal(bl *types.Block) ([]byte, error) {
b, err := New(bl)
if err != nil {
return nil, err
}
if b == nil {
return nil, fmt.Errorf("invalid block")
}
return json.Marshal(b)
}
func Unmarshal(data []byte) (*Block, error) {
var block Block
err := json.Unmarshal(data, &block)
if err != nil {
return nil, err
}
if block.TxsBytes != nil && len(*block.TxsBytes) != 0 {
// convert the tx bytes into transactions.
for _, t := range *block.TxsBytes {
var tr types.Transaction
err := tr.UnmarshalJSON(t)
if err != nil {
return nil, err
}
block.Txs = append(block.Txs, tr)
}
}
return &block, err
}
type TransactionTrace struct {
Hash string `json:"hash"`
Idx uint32 `json:"idx"`
Trace []byte `json:"trace"`
}
type Client struct {
rpcClient *rpc.Client
ethClient ethclient.Client
lock sync.Mutex
}
func NewClient(url string) (*Client, error) {
rc, err := rpc.Dial(url)
if err != nil {
return nil, err
}
cl := &Client{}
cl.rpcClient = rc
cl.ethClient = ethclient.NewClient(rc)
return cl, nil
}
func (c *Client) Latest(rpcTimeout time.Duration) (*big.Int, error) {
c.lock.Lock()
defer c.lock.Unlock()
ctx, cancelCTX := context.WithTimeout(context.Background(), rpcTimeout)
defer cancelCTX()
bl, err := c.ethClient.BlockNumber(ctx)
if err != nil {
return nil, err
}
return big.NewInt(0).SetUint64(bl), nil
}
func (c *Client) Close() {
c.rpcClient.Close()
}
type BlockContainer struct {
Block *types.Block
Traces []*TransactionTrace
Logs []*types.Log
}
func (c *Client) ReadBlock(blockNumber *big.Int, rpcTimeout time.Duration) (*BlockContainer, error) {
c.lock.Lock()
defer c.lock.Unlock()
ctx, cancelCTX := context.WithTimeout(context.Background(), rpcTimeout)
defer cancelCTX()
bl, err := c.ethClient.BlockByNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
var (
tracer = "callTracer"
tracerTimeout = "180s"
)
txTraces := make([]*TransactionTrace, 0, len(bl.Transactions()))
for _, tx := range bl.Transactions() {
txh := tx.Hash().Hex()
if !strings.HasPrefix(txh, "0x") {
txh = "0x" + txh
}
var results Call
args := &tracers.TraceConfig{
Timeout: &tracerTimeout,
Tracer: &tracer,
}
if err := c.rpcClient.CallContext(ctx, &results, "debug_traceTransaction", txh, args); err != nil {
return nil, err
}
for ipos, result := range results.Calls {
traceBits, err := json.Marshal(result)
if err != nil {
return nil, err
}
txTraces = append(txTraces,
&TransactionTrace{
Hash: txh,
Idx: uint32(ipos),
Trace: traceBits,
},
)
}
}
blhash := bl.Hash()
fq := interfaces.FilterQuery{BlockHash: &blhash}
fls, err := c.ethClient.FilterLogs(ctx, fq)
if err != nil {
return nil, err
}
flrs := make([]*types.Log, 0, len(fls))
for _, fl := range fls {
flcopy := fl
flrs = append(flrs, &flcopy)
}
return &BlockContainer{Block: bl, Traces: txTraces, Logs: flrs}, nil
}