Modify loading of ethereum blocks

This commit is contained in:
Martin Boehm 2018-11-14 12:13:36 +01:00
parent 1dbe7f42ba
commit acb5d63afc
2 changed files with 108 additions and 90 deletions

View File

@ -9,7 +9,6 @@ import (
ethcommon "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/glog"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/juju/errors" "github.com/juju/errors"
) )
@ -27,34 +26,43 @@ func NewEthereumParser() *EthereumParser {
}} }}
} }
type rpcHeader struct {
Hash ethcommon.Hash `json:"hash"`
Difficulty string `json:"difficulty"`
Number string `json:"number"`
Time string `json:"timestamp"`
Size string `json:"size"`
Nonce string `json:"nonce"`
}
type rpcTransaction struct { type rpcTransaction struct {
AccountNonce string `json:"nonce" gencodec:"required"` AccountNonce string `json:"nonce"`
GasPrice string `json:"gasPrice" gencodec:"required"` GasPrice string `json:"gasPrice"`
GasLimit string `json:"gas" gencodec:"required"` GasLimit string `json:"gas"`
To string `json:"to" rlp:"nil"` // nil means contract creation To string `json:"to" rlp:"nil"` // nil means contract creation
Value string `json:"value" gencodec:"required"` Value string `json:"value"`
Payload string `json:"input" gencodec:"required"` Payload string `json:"input"`
Hash ethcommon.Hash `json:"hash" rlp:"-"` Hash ethcommon.Hash `json:"hash" rlp:"-"`
BlockNumber string `json:"blockNumber"` BlockNumber string `json:"blockNumber"`
BlockHash *ethcommon.Hash `json:"blockHash,omitempty"` BlockHash *ethcommon.Hash `json:"blockHash,omitempty"`
From string `json:"from"` From string `json:"from"`
TransactionIndex string `json:"transactionIndex"` TransactionIndex string `json:"transactionIndex"`
// Signature values - ignored // Signature values - ignored
// V string `json:"v" gencodec:"required"` // V string `json:"v"`
// R string `json:"r" gencodec:"required"` // R string `json:"r"`
// S string `json:"s" gencodec:"required"` // S string `json:"s"`
} }
type rpcLog struct { type rpcLog struct {
Address ethcommon.Address `json:"address" gencodec:"required"` Address ethcommon.Address `json:"address"`
Topics []string `json:"topics" gencodec:"required"` Topics []string `json:"topics"`
Data string `json:"data" gencodec:"required"` Data string `json:"data"`
} }
type rpcReceipt struct { type rpcReceipt struct {
GasUsed string `json:"gasUsed" gencodec:"required"` GasUsed string `json:"gasUsed"`
Status string `json:"status"` Status string `json:"status"`
Logs []*rpcLog `json:"logs" gencodec:"required"` Logs []*rpcLog `json:"logs"`
} }
type completeTransaction struct { type completeTransaction struct {
@ -62,14 +70,12 @@ type completeTransaction struct {
Receipt *rpcReceipt `json:"receipt,omitempty"` Receipt *rpcReceipt `json:"receipt,omitempty"`
} }
type rpcBlock struct { type rpcBlockTransactions struct {
Hash ethcommon.Hash `json:"hash"`
Size string `json:"size"`
Transactions []rpcTransaction `json:"transactions"` Transactions []rpcTransaction `json:"transactions"`
} }
func ethHashToHash(h ethcommon.Hash) string { type rpcBlockTxids struct {
return h.Hex() Transactions []string `json:"transactions"`
} }
func ethNumber(n string) (int64, error) { func ethNumber(n string) (int64, error) {
@ -80,7 +86,7 @@ func ethNumber(n string) (int64, error) {
} }
func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, blocktime int64, confirmations uint32, marshallHex bool) (*bchain.Tx, error) { func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, blocktime int64, confirmations uint32, marshallHex bool) (*bchain.Tx, error) {
txid := ethHashToHash(tx.Hash) txid := tx.Hash.Hex()
var ( var (
fa, ta []string fa, ta []string
err error err error
@ -106,9 +112,6 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, bloc
} }
tx.BlockHash = bh tx.BlockHash = bh
h = hex.EncodeToString(b) h = hex.EncodeToString(b)
if receipt != nil {
glog.Info(tx.Hash.Hex(), ": ", h)
}
} }
vs, err := hexutil.DecodeBig(tx.Value) vs, err := hexutil.DecodeBig(tx.Value)
if err != nil { if err != nil {

View File

@ -12,7 +12,6 @@ import (
ethereum "github.com/ethereum/go-ethereum" ethereum "github.com/ethereum/go-ethereum"
ethcommon "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
@ -287,7 +286,7 @@ func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) {
} }
rv := &bchain.ChainInfo{ rv := &bchain.ChainInfo{
Blocks: int(h.Number.Int64()), Blocks: int(h.Number.Int64()),
Bestblockhash: ethHashToHash(h.Hash()), Bestblockhash: h.Hash().Hex(),
Difficulty: h.Difficulty.String(), Difficulty: h.Difficulty.String(),
Version: ver, Version: ver,
ProtocolVersion: protocol, ProtocolVersion: protocol,
@ -329,7 +328,7 @@ func (b *EthereumRPC) GetBestBlockHash() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return ethHashToHash(h.Hash()), nil return h.Hash().Hex(), nil
} }
// GetBestBlockHeight returns height of the tip of the best-block-chain // GetBestBlockHeight returns height of the tip of the best-block-chain
@ -338,7 +337,6 @@ func (b *EthereumRPC) GetBestBlockHeight() (uint32, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
// TODO - can it grow over 2^32 ?
return uint32(h.Number.Uint64()), nil return uint32(h.Number.Uint64()), nil
} }
@ -355,37 +353,46 @@ func (b *EthereumRPC) GetBlockHash(height uint32) (string, error) {
} }
return "", errors.Annotatef(err, "height %v", height) return "", errors.Annotatef(err, "height %v", height)
} }
return ethHashToHash(h.Hash()), nil return h.Hash().Hex(), nil
} }
func (b *EthereumRPC) ethHeaderToBlockHeader(h *ethtypes.Header) (*bchain.BlockHeader, error) { func (b *EthereumRPC) ethHeaderToBlockHeader(h *rpcHeader) (*bchain.BlockHeader, error) {
hn := h.Number.Uint64() height, err := ethNumber(h.Number)
c, err := b.computeConfirmations(hn) if err != nil {
return nil, err
}
c, err := b.computeConfirmations(uint64(height))
if err != nil {
return nil, err
}
time, err := ethNumber(h.Time)
if err != nil {
return nil, err
}
size, err := ethNumber(h.Size)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &bchain.BlockHeader{ return &bchain.BlockHeader{
Hash: ethHashToHash(h.Hash()), Hash: h.Hash.Hex(),
Height: uint32(hn), Height: uint32(height),
Confirmations: int(c), Confirmations: int(c),
Time: int64(h.Time.Uint64()), Time: time,
// Next Size: int(size),
// Prev
}, nil }, nil
} }
// GetBlockHeader returns header of block with given hash // GetBlockHeader returns header of block with given hash
func (b *EthereumRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) { func (b *EthereumRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout) raw, err := b.getBlockRaw(hash, 0, false)
defer cancel()
h, err := b.client.HeaderByHash(ctx, ethcommon.HexToHash(hash))
if err != nil { if err != nil {
if err == ethereum.NotFound { return nil, err
return nil, bchain.ErrBlockNotFound }
} var h rpcHeader
if err := json.Unmarshal(raw, &h); err != nil {
return nil, errors.Annotatef(err, "hash %v", hash) return nil, errors.Annotatef(err, "hash %v", hash)
} }
return b.ethHeaderToBlockHeader(h) return b.ethHeaderToBlockHeader(&h)
} }
func (b *EthereumRPC) computeConfirmations(n uint64) (uint32, error) { func (b *EthereumRPC) computeConfirmations(n uint64) (uint32, error) {
@ -398,56 +405,50 @@ func (b *EthereumRPC) computeConfirmations(n uint64) (uint32, error) {
return uint32(bn - n + 1), nil return uint32(bn - n + 1), nil
} }
// GetBlock returns block with given hash or height, hash has precedence if both passed func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (json.RawMessage, error) {
func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout) ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel() defer cancel()
var raw json.RawMessage var raw json.RawMessage
var err error var err error
if hash != "" { if hash != "" {
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByHash", ethcommon.HexToHash(hash), true) if hash == "pending" {
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", hash, fullTxs)
} else {
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByHash", ethcommon.HexToHash(hash), fullTxs)
}
} else { } else {
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", fmt.Sprintf("%#x", height), fullTxs)
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", fmt.Sprintf("%#x", height), true)
} }
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
} else if len(raw) == 0 { } else if len(raw) == 0 {
return nil, bchain.ErrBlockNotFound return nil, bchain.ErrBlockNotFound
} }
// Decode header and transactions. return raw, nil
var head *ethtypes.Header }
var body rpcBlock
// GetBlock returns block with given hash or height, hash has precedence if both passed
func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
raw, err := b.getBlockRaw(hash, height, true)
if err != nil {
return nil, err
}
var head rpcHeader
if err := json.Unmarshal(raw, &head); err != nil { if err := json.Unmarshal(raw, &head); err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
} }
if head == nil { var body rpcBlockTransactions
return nil, bchain.ErrBlockNotFound
}
if err := json.Unmarshal(raw, &body); err != nil { if err := json.Unmarshal(raw, &body); err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
} }
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server. bbh, err := b.ethHeaderToBlockHeader(&head)
if head.TxHash == ethtypes.EmptyRootHash && len(body.Transactions) > 0 {
return nil, errors.Annotatef(fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions"), "hash %v, height %v", hash, height)
}
if head.TxHash != ethtypes.EmptyRootHash && len(body.Transactions) == 0 {
return nil, errors.Annotatef(fmt.Errorf("server returned empty transaction list but block header indicates transactions"), "hash %v, height %v", hash, height)
}
bbh, err := b.ethHeaderToBlockHeader(head)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
} }
bigSize, err := hexutil.DecodeBig(body.Size)
if err != nil {
glog.Error("invalid size of block ", body.Hash, ": ", body.Size)
} else {
bbh.Size = int(bigSize.Int64())
}
// TODO - get ERC20 events // TODO - get ERC20 events
btxs := make([]bchain.Tx, len(body.Transactions)) btxs := make([]bchain.Tx, len(body.Transactions))
for i, tx := range body.Transactions { for i, tx := range body.Transactions {
btx, err := b.Parser.ethTxToTx(&tx, nil, int64(head.Time.Uint64()), uint32(bbh.Confirmations), false) btx, err := b.Parser.ethTxToTx(&tx, nil, bbh.Time, uint32(bbh.Confirmations), false)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash.String()) return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash.String())
} }
@ -462,8 +463,25 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
// GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids // GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) { func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
// TODO - implement raw, err := b.getBlockRaw(hash, 0, false)
return nil, errors.New("Not implemented yet") if err != nil {
return nil, err
}
var head rpcHeader
var txs rpcBlockTxids
if err := json.Unmarshal(raw, &head); err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if err = json.Unmarshal(raw, &txs); err != nil {
return nil, err
}
bch, err := b.ethHeaderToBlockHeader(&head)
return &bchain.BlockInfo{
BlockHeader: *bch,
Difficulty: json.Number(head.Difficulty),
Nonce: json.Number(head.Nonce),
Txids: txs.Transactions,
}, nil
} }
// GetTransactionForMempool returns a transaction by the transaction ID. // GetTransactionForMempool returns a transaction by the transaction ID.
@ -484,12 +502,6 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
} else if tx == nil { } else if tx == nil {
return nil, ethereum.NotFound return nil, ethereum.NotFound
} }
// else if tx.R == "" {
// if !b.isETC {
// return nil, errors.Annotatef(fmt.Errorf("server returned transaction without signature"), "txid %v", txid)
// }
// glog.Warning("server returned transaction without signature, txid ", txid)
// }
var btx *bchain.Tx var btx *bchain.Tx
if tx.BlockNumber == "" { if tx.BlockNumber == "" {
// mempool tx // mempool tx
@ -499,8 +511,18 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
} }
} else { } else {
// non mempool tx - we must read the block header to get the block time // non mempool tx - we must read the block header to get the block time
h, err := b.client.HeaderByHash(ctx, *tx.BlockHash) raw, err := b.getBlockRaw(tx.BlockHash.Hex(), 0, false)
if err != nil { if err != nil {
return nil, err
}
var ht struct {
Time string `json:"timestamp"`
}
if err := json.Unmarshal(raw, &ht); err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
var time int64
if time, err = ethNumber(ht.Time); err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
var receipt rpcReceipt var receipt rpcReceipt
@ -516,7 +538,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
btx, err = b.Parser.ethTxToTx(tx, &receipt, h.Time.Int64(), confirmations, true) btx, err = b.Parser.ethTxToTx(tx, &receipt, time, confirmations, true)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
@ -541,23 +563,16 @@ func (b *EthereumRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, er
return json.RawMessage(m), err return json.RawMessage(m), err
} }
type rpcMempoolBlock struct {
Transactions []string `json:"transactions"`
}
// GetMempool returns transactions in mempool // GetMempool returns transactions in mempool
func (b *EthereumRPC) GetMempool() ([]string, error) { func (b *EthereumRPC) GetMempool() ([]string, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout) raw, err := b.getBlockRaw("pending", 0, false)
defer cancel()
var raw json.RawMessage
var err error
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", "pending", false)
if err != nil { if err != nil {
return nil, err return nil, err
} else if len(raw) == 0 { }
if len(raw) == 0 {
return nil, bchain.ErrBlockNotFound return nil, bchain.ErrBlockNotFound
} }
var body rpcMempoolBlock var body rpcBlockTxids
if err := json.Unmarshal(raw, &body); err != nil { if err := json.Unmarshal(raw, &body); err != nil {
return nil, err return nil, err
} }