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"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
"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 {
AccountNonce string `json:"nonce" gencodec:"required"`
GasPrice string `json:"gasPrice" gencodec:"required"`
GasLimit string `json:"gas" gencodec:"required"`
AccountNonce string `json:"nonce"`
GasPrice string `json:"gasPrice"`
GasLimit string `json:"gas"`
To string `json:"to" rlp:"nil"` // nil means contract creation
Value string `json:"value" gencodec:"required"`
Payload string `json:"input" gencodec:"required"`
Value string `json:"value"`
Payload string `json:"input"`
Hash ethcommon.Hash `json:"hash" rlp:"-"`
BlockNumber string `json:"blockNumber"`
BlockHash *ethcommon.Hash `json:"blockHash,omitempty"`
From string `json:"from"`
TransactionIndex string `json:"transactionIndex"`
// Signature values - ignored
// V string `json:"v" gencodec:"required"`
// R string `json:"r" gencodec:"required"`
// S string `json:"s" gencodec:"required"`
// V string `json:"v"`
// R string `json:"r"`
// S string `json:"s"`
}
type rpcLog struct {
Address ethcommon.Address `json:"address" gencodec:"required"`
Topics []string `json:"topics" gencodec:"required"`
Data string `json:"data" gencodec:"required"`
Address ethcommon.Address `json:"address"`
Topics []string `json:"topics"`
Data string `json:"data"`
}
type rpcReceipt struct {
GasUsed string `json:"gasUsed" gencodec:"required"`
GasUsed string `json:"gasUsed"`
Status string `json:"status"`
Logs []*rpcLog `json:"logs" gencodec:"required"`
Logs []*rpcLog `json:"logs"`
}
type completeTransaction struct {
@ -62,14 +70,12 @@ type completeTransaction struct {
Receipt *rpcReceipt `json:"receipt,omitempty"`
}
type rpcBlock struct {
Hash ethcommon.Hash `json:"hash"`
Size string `json:"size"`
type rpcBlockTransactions struct {
Transactions []rpcTransaction `json:"transactions"`
}
func ethHashToHash(h ethcommon.Hash) string {
return h.Hex()
type rpcBlockTxids struct {
Transactions []string `json:"transactions"`
}
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) {
txid := ethHashToHash(tx.Hash)
txid := tx.Hash.Hex()
var (
fa, ta []string
err error
@ -106,9 +112,6 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, bloc
}
tx.BlockHash = bh
h = hex.EncodeToString(b)
if receipt != nil {
glog.Info(tx.Hash.Hex(), ": ", h)
}
}
vs, err := hexutil.DecodeBig(tx.Value)
if err != nil {

View File

@ -12,7 +12,6 @@ import (
ethereum "github.com/ethereum/go-ethereum"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
@ -287,7 +286,7 @@ func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) {
}
rv := &bchain.ChainInfo{
Blocks: int(h.Number.Int64()),
Bestblockhash: ethHashToHash(h.Hash()),
Bestblockhash: h.Hash().Hex(),
Difficulty: h.Difficulty.String(),
Version: ver,
ProtocolVersion: protocol,
@ -329,7 +328,7 @@ func (b *EthereumRPC) GetBestBlockHash() (string, error) {
if err != nil {
return "", err
}
return ethHashToHash(h.Hash()), nil
return h.Hash().Hex(), nil
}
// GetBestBlockHeight returns height of the tip of the best-block-chain
@ -338,7 +337,6 @@ func (b *EthereumRPC) GetBestBlockHeight() (uint32, error) {
if err != nil {
return 0, err
}
// TODO - can it grow over 2^32 ?
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 ethHashToHash(h.Hash()), nil
return h.Hash().Hex(), nil
}
func (b *EthereumRPC) ethHeaderToBlockHeader(h *ethtypes.Header) (*bchain.BlockHeader, error) {
hn := h.Number.Uint64()
c, err := b.computeConfirmations(hn)
func (b *EthereumRPC) ethHeaderToBlockHeader(h *rpcHeader) (*bchain.BlockHeader, error) {
height, err := ethNumber(h.Number)
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 {
return nil, err
}
return &bchain.BlockHeader{
Hash: ethHashToHash(h.Hash()),
Height: uint32(hn),
Hash: h.Hash.Hex(),
Height: uint32(height),
Confirmations: int(c),
Time: int64(h.Time.Uint64()),
// Next
// Prev
Time: time,
Size: int(size),
}, nil
}
// GetBlockHeader returns header of block with given hash
func (b *EthereumRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
h, err := b.client.HeaderByHash(ctx, ethcommon.HexToHash(hash))
raw, err := b.getBlockRaw(hash, 0, false)
if err != nil {
if err == ethereum.NotFound {
return nil, bchain.ErrBlockNotFound
}
return nil, err
}
var h rpcHeader
if err := json.Unmarshal(raw, &h); err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
return b.ethHeaderToBlockHeader(h)
return b.ethHeaderToBlockHeader(&h)
}
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
}
// 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) {
func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (json.RawMessage, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
var raw json.RawMessage
var err error
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 {
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", fmt.Sprintf("%#x", height), true)
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", fmt.Sprintf("%#x", height), fullTxs)
}
if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
} else if len(raw) == 0 {
return nil, bchain.ErrBlockNotFound
}
// Decode header and transactions.
var head *ethtypes.Header
var body rpcBlock
return raw, nil
}
// 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 {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
}
if head == nil {
return nil, bchain.ErrBlockNotFound
}
var body rpcBlockTransactions
if err := json.Unmarshal(raw, &body); err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
}
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server.
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)
bbh, err := b.ethHeaderToBlockHeader(&head)
if err != nil {
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
btxs := make([]bchain.Tx, len(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 {
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
func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
// TODO - implement
return nil, errors.New("Not implemented yet")
raw, err := b.getBlockRaw(hash, 0, false)
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.
@ -484,12 +502,6 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
} else if tx == nil {
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
if tx.BlockNumber == "" {
// mempool tx
@ -499,8 +511,18 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
}
} else {
// 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 {
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)
}
var receipt rpcReceipt
@ -516,7 +538,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if err != nil {
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 {
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
}
type rpcMempoolBlock struct {
Transactions []string `json:"transactions"`
}
// GetMempool returns transactions in mempool
func (b *EthereumRPC) GetMempool() ([]string, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
var raw json.RawMessage
var err error
err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", "pending", false)
raw, err := b.getBlockRaw("pending", 0, false)
if err != nil {
return nil, err
} else if len(raw) == 0 {
}
if len(raw) == 0 {
return nil, bchain.ErrBlockNotFound
}
var body rpcMempoolBlock
var body rpcBlockTxids
if err := json.Unmarshal(raw, &body); err != nil {
return nil, err
}