github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/vent/chain/ethereum/ethereum.go (about)

     1  package ethereum
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/hyperledger/burrow/binary"
    12  	"github.com/hyperledger/burrow/crypto"
    13  	"github.com/hyperledger/burrow/encoding/web3hex"
    14  	"github.com/hyperledger/burrow/event"
    15  	"github.com/hyperledger/burrow/event/query"
    16  	"github.com/hyperledger/burrow/execution/errors"
    17  	"github.com/hyperledger/burrow/execution/exec"
    18  	"github.com/hyperledger/burrow/logging"
    19  	"github.com/hyperledger/burrow/rpc/rpcevents"
    20  	"github.com/hyperledger/burrow/rpc/web3/ethclient"
    21  	"github.com/hyperledger/burrow/vent/chain"
    22  	"github.com/hyperledger/burrow/vent/types"
    23  	"google.golang.org/grpc/connectivity"
    24  )
    25  
    26  const Scope = "Ethereum"
    27  
    28  type Chain struct {
    29  	client         ThrottleClient
    30  	filter         *chain.Filter
    31  	chainID        string
    32  	version        string
    33  	consumerConfig *chain.BlockConsumerConfig
    34  	logger         *logging.Logger
    35  }
    36  
    37  var _ chain.Chain = (*Chain)(nil)
    38  
    39  type EthClient interface {
    40  	GetLogs(filter *ethclient.Filter) ([]*ethclient.EthLog, error)
    41  	BlockNumber() (uint64, error)
    42  	GetBlockByNumber(height string) (*ethclient.Block, error)
    43  	NetVersion() (string, error)
    44  	Web3ClientVersion() (string, error)
    45  	Syncing() (bool, error)
    46  }
    47  
    48  // We rely on this failing if the chain is not an Ethereum Chain
    49  func New(client EthClient, filter *chain.Filter, consumerConfig *chain.BlockConsumerConfig,
    50  	logger *logging.Logger) (*Chain, error) {
    51  	logger = logger.WithScope(Scope)
    52  	throttleClient := NewThrottleClient(client, consumerConfig.MaxRequests, consumerConfig.TimeBase, logger)
    53  	chainID, err := throttleClient.NetVersion()
    54  	if err != nil {
    55  		return nil, fmt.Errorf("could not get Ethereum ChainID: %w", err)
    56  	}
    57  	version, err := throttleClient.Web3ClientVersion()
    58  	if err != nil {
    59  		return nil, fmt.Errorf("could not get Ethereum node version: %w", err)
    60  	}
    61  	return &Chain{
    62  		client:         throttleClient,
    63  		filter:         filter,
    64  		chainID:        chainID,
    65  		version:        version,
    66  		consumerConfig: consumerConfig,
    67  		logger:         logger,
    68  	}, nil
    69  }
    70  
    71  func (c *Chain) StatusMessage(ctx context.Context, lastProcessedHeight uint64) []interface{} {
    72  	// TODO: more info is available from web3
    73  	return []interface{}{
    74  		"msg", "status",
    75  		"chain_type", "Ethereum",
    76  		"last_processed_height", lastProcessedHeight,
    77  	}
    78  }
    79  
    80  func (c *Chain) GetABI(ctx context.Context, address crypto.Address) (string, error) {
    81  	// Unsupported by Ethereum
    82  	return "", nil
    83  }
    84  
    85  func (c *Chain) GetVersion() string {
    86  	return c.version
    87  }
    88  
    89  func (c *Chain) GetChainID() string {
    90  	return c.chainID
    91  }
    92  
    93  func (c *Chain) ConsumeBlocks(ctx context.Context, in *rpcevents.BlockRange, consumer func(chain.Block) error) error {
    94  	return Consume(c.client, c.filter, in, c.consumerConfig, c.logger, consumer)
    95  }
    96  
    97  func (c *Chain) Connectivity() connectivity.State {
    98  	// TODO: better connectivity information
    99  	_, err := c.client.Syncing()
   100  	if err != nil {
   101  		return connectivity.TransientFailure
   102  	}
   103  	return connectivity.Ready
   104  }
   105  
   106  func (c *Chain) Close() error {
   107  	// just a http.Client - nothing to free
   108  	return nil
   109  }
   110  
   111  type Block struct {
   112  	client       EthClient
   113  	Height       uint64
   114  	Transactions []chain.Transaction
   115  }
   116  
   117  func newBlock(client EthClient, log *Event) *Block {
   118  	return &Block{
   119  		client:       client,
   120  		Height:       log.Height,
   121  		Transactions: []chain.Transaction{NewEthereumTransaction(log)},
   122  	}
   123  }
   124  
   125  var _ chain.Block = (*Block)(nil)
   126  
   127  func (b *Block) GetHeight() uint64 {
   128  	return b.Height
   129  }
   130  
   131  func (b *Block) GetTxs() []chain.Transaction {
   132  	return b.Transactions
   133  }
   134  
   135  func (b *Block) GetMetadata(columns types.SQLColumnNames) (map[string]interface{}, error) {
   136  	block, err := b.client.GetBlockByNumber(web3hex.Encoder.Uint64(b.Height))
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	d := new(web3hex.Decoder)
   141  	blockHeader, err := json.Marshal(block)
   142  	if err != nil {
   143  		return nil, fmt.Errorf("could not serialise block header: %w", err)
   144  	}
   145  	return map[string]interface{}{
   146  		columns.Height:      strconv.FormatUint(b.Height, 10),
   147  		columns.TimeStamp:   time.Unix(d.Int64(block.Timestamp), 0),
   148  		columns.BlockHeader: string(blockHeader),
   149  	}, d.Err()
   150  }
   151  
   152  func (b *Block) appendTransaction(log *Event) {
   153  	b.Transactions = append(b.Transactions, &Transaction{
   154  		Index:  uint64(len(b.Transactions)),
   155  		Hash:   log.TransactionHash,
   156  		Events: []chain.Event{log},
   157  	})
   158  }
   159  
   160  func (b *Block) appendEvent(log *Event) {
   161  	tx := b.Transactions[len(b.Transactions)-1].(*Transaction)
   162  	log.Index = uint64(len(tx.Events))
   163  	tx.Events = append(tx.Events, log)
   164  }
   165  
   166  type Transaction struct {
   167  	Height uint64
   168  	Index  uint64
   169  	Hash   binary.HexBytes
   170  	Events []chain.Event
   171  }
   172  
   173  func NewEthereumTransaction(log *Event) *Transaction {
   174  	return &Transaction{
   175  		Height: log.Height,
   176  		Index:  0,
   177  		Hash:   log.TransactionHash,
   178  		Events: []chain.Event{log},
   179  	}
   180  }
   181  
   182  func (tx *Transaction) GetHash() binary.HexBytes {
   183  	return tx.Hash
   184  }
   185  
   186  func (tx *Transaction) GetIndex() uint64 {
   187  	return tx.Index
   188  }
   189  
   190  func (tx *Transaction) GetEvents() []chain.Event {
   191  	return tx.Events
   192  }
   193  
   194  func (tx *Transaction) GetException() *errors.Exception {
   195  	// Ethereum does not retain an log from reverted transactions
   196  	return nil
   197  }
   198  
   199  func (tx *Transaction) GetOrigin() *chain.Origin {
   200  	// Origin refers to a previous dumped chain which is not a concept in Ethereum
   201  	return nil
   202  }
   203  
   204  func (tx *Transaction) GetMetadata(columns types.SQLColumnNames) (map[string]interface{}, error) {
   205  	return map[string]interface{}{
   206  		columns.Height:  tx.Height,
   207  		columns.TxHash:  tx.Hash.String(),
   208  		columns.TxIndex: tx.Index,
   209  		columns.TxType:  exec.TypeLog.String(),
   210  	}, nil
   211  }
   212  
   213  var _ chain.Transaction = (*Transaction)(nil)
   214  
   215  type Event struct {
   216  	exec.LogEvent
   217  	Height uint64
   218  	// Index of event in entire block (what ethereum provides us with
   219  	IndexInBlock uint64
   220  	// Index of event in transaction
   221  	Index           uint64
   222  	TransactionHash binary.HexBytes
   223  }
   224  
   225  var _ chain.Event = (*Event)(nil)
   226  
   227  func newEvent(log *ethclient.EthLog) (*Event, error) {
   228  	d := new(web3hex.Decoder)
   229  	topics := make([]binary.Word256, len(log.Topics))
   230  	for i, t := range log.Topics {
   231  		topics[i] = binary.LeftPadWord256(d.Bytes(t))
   232  	}
   233  	txHash := d.Bytes(log.TransactionHash)
   234  	return &Event{
   235  		LogEvent: exec.LogEvent{
   236  			Topics:  topics,
   237  			Address: d.Address(log.Address),
   238  			Data:    d.Bytes(log.Data),
   239  		},
   240  		Height:          d.Uint64(log.BlockNumber),
   241  		IndexInBlock:    d.Uint64(log.LogIndex),
   242  		TransactionHash: txHash,
   243  	}, d.Err()
   244  }
   245  
   246  func (ev *Event) GetIndex() uint64 {
   247  	return ev.Index
   248  }
   249  
   250  func (ev *Event) GetTransactionHash() binary.HexBytes {
   251  	return ev.TransactionHash
   252  }
   253  
   254  func (ev *Event) GetAddress() crypto.Address {
   255  	return ev.Address
   256  }
   257  
   258  func (ev *Event) GetTopics() []binary.Word256 {
   259  	return ev.Topics
   260  }
   261  
   262  func (ev *Event) GetData() []byte {
   263  	return ev.Data
   264  }
   265  
   266  func (ev *Event) Get(key string) (value interface{}, ok bool) {
   267  	switch key {
   268  	case event.EventTypeKey:
   269  		return exec.TypeLog, true
   270  	}
   271  	v, ok := ev.LogEvent.Get(key)
   272  	if ok {
   273  		return v, ok
   274  	}
   275  	return query.GetReflect(reflect.ValueOf(ev), key)
   276  }