Modify loading of ethereum blocks
This commit is contained in:
parent
1dbe7f42ba
commit
acb5d63afc
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user