eth_getBlockByNumber impl (#87)
* WIP implement eth_getBlockByNumber * Implemented some missing fields * Added gasLimit and updated previous fields * Implement remaining pieces for eth_getBlock including decoding txs * Add converting transaction objects into function for usability * Clean up code * format block in function for usability * Fixed formatting and cached gasLimit value
This commit is contained in:
parent
1cac4feb4d
commit
4a030335e6
134
rpc/eth_api.go
134
rpc/eth_api.go
@ -5,21 +5,25 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
|
||||||
authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
|
||||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||||
emintkeys "github.com/cosmos/ethermint/keys"
|
emintkeys "github.com/cosmos/ethermint/keys"
|
||||||
"github.com/cosmos/ethermint/rpc/args"
|
"github.com/cosmos/ethermint/rpc/args"
|
||||||
"github.com/cosmos/ethermint/version"
|
"github.com/cosmos/ethermint/version"
|
||||||
"github.com/cosmos/ethermint/x/evm/types"
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
|
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,6 +32,7 @@ type PublicEthAPI struct {
|
|||||||
cliCtx context.CLIContext
|
cliCtx context.CLIContext
|
||||||
key emintcrypto.PrivKeySecp256k1
|
key emintcrypto.PrivKeySecp256k1
|
||||||
nonceLock *AddrLocker
|
nonceLock *AddrLocker
|
||||||
|
gasLimit *int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublicEthAPI creates an instance of the public ETH Web3 API.
|
// NewPublicEthAPI creates an instance of the public ETH Web3 API.
|
||||||
@ -314,13 +319,84 @@ func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) map[string]
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockByNumber returns the block identified by number.
|
// GetBlockByNumber returns the block identified by number.
|
||||||
func (e *PublicEthAPI) GetBlockByNumber(blockNum rpc.BlockNumber, fullTx bool) map[string]interface{} {
|
func (e *PublicEthAPI) GetBlockByNumber(blockNum rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
|
||||||
return nil
|
value := blockNum.Int64()
|
||||||
|
block, err := e.cliCtx.Client.Block(&value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
header := block.BlockMeta.Header
|
||||||
|
|
||||||
|
gasLimit, err := e.getGasLimit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var gasUsed *big.Int
|
||||||
|
var transactions []interface{}
|
||||||
|
|
||||||
|
if fullTx {
|
||||||
|
// Populate full transaction data
|
||||||
|
transactions, gasUsed = convertTransactionsToRPC(e.cliCtx, block.Block.Txs,
|
||||||
|
common.BytesToHash(header.ConsensusHash.Bytes()), uint64(header.Height))
|
||||||
|
} else {
|
||||||
|
// TODO: Gas used not saved and cannot be calculated by hashes
|
||||||
|
// Return slice of transaction hashes
|
||||||
|
transactions = make([]interface{}, len(block.Block.Txs))
|
||||||
|
for i, tx := range block.Block.Txs {
|
||||||
|
transactions[i] = common.BytesToHash(tx.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatBlock(header, block.Block.Size(), gasLimit, gasUsed, transactions), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatBlock(
|
||||||
|
header tmtypes.Header, size int, gasLimit int64,
|
||||||
|
gasUsed *big.Int, transactions []interface{},
|
||||||
|
) map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"number": hexutil.Uint64(header.Height),
|
||||||
|
"hash": hexutil.Bytes(header.ConsensusHash),
|
||||||
|
"parentHash": hexutil.Bytes(header.LastBlockID.Hash),
|
||||||
|
"nonce": nil, // PoW specific
|
||||||
|
"sha3Uncles": nil, // No uncles in Tendermint
|
||||||
|
"logsBloom": "", // TODO: Complete with #55
|
||||||
|
"transactionsRoot": hexutil.Bytes(header.DataHash),
|
||||||
|
"stateRoot": hexutil.Bytes(header.AppHash),
|
||||||
|
"miner": hexutil.Bytes(header.ValidatorsHash),
|
||||||
|
"difficulty": nil,
|
||||||
|
"totalDifficulty": nil,
|
||||||
|
"extraData": nil,
|
||||||
|
"size": hexutil.Uint64(size),
|
||||||
|
"gasLimit": hexutil.Uint64(gasLimit), // Static gas limit
|
||||||
|
"gasUsed": (*hexutil.Big)(gasUsed),
|
||||||
|
"timestamp": hexutil.Uint64(header.Time.Unix()),
|
||||||
|
"transactions": transactions,
|
||||||
|
"uncles": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]interface{}, *big.Int) {
|
||||||
|
transactions := make([]interface{}, len(txs))
|
||||||
|
gasUsed := big.NewInt(0)
|
||||||
|
for i, tx := range txs {
|
||||||
|
var stdTx sdk.Tx
|
||||||
|
err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(tx, &stdTx)
|
||||||
|
ethTx, ok := stdTx.(*types.EthereumTxMsg)
|
||||||
|
if !ok || err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO: Remove gas usage calculation if saving gasUsed per block
|
||||||
|
gasUsed.Add(gasUsed, ethTx.Fee())
|
||||||
|
transactions[i] = newRPCTransaction(ethTx, blockHash, height, uint64(i))
|
||||||
|
}
|
||||||
|
return transactions, gasUsed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction represents a transaction returned to RPC clients.
|
// Transaction represents a transaction returned to RPC clients.
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
BlockHash common.Hash `json:"blockHash"`
|
BlockHash *common.Hash `json:"blockHash"`
|
||||||
BlockNumber *hexutil.Big `json:"blockNumber"`
|
BlockNumber *hexutil.Big `json:"blockNumber"`
|
||||||
From common.Address `json:"from"`
|
From common.Address `json:"from"`
|
||||||
Gas hexutil.Uint64 `json:"gas"`
|
Gas hexutil.Uint64 `json:"gas"`
|
||||||
@ -329,13 +405,40 @@ type Transaction struct {
|
|||||||
Input hexutil.Bytes `json:"input"`
|
Input hexutil.Bytes `json:"input"`
|
||||||
Nonce hexutil.Uint64 `json:"nonce"`
|
Nonce hexutil.Uint64 `json:"nonce"`
|
||||||
To *common.Address `json:"to"`
|
To *common.Address `json:"to"`
|
||||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
|
||||||
Value *hexutil.Big `json:"value"`
|
Value *hexutil.Big `json:"value"`
|
||||||
V *hexutil.Big `json:"v"`
|
V *hexutil.Big `json:"v"`
|
||||||
R *hexutil.Big `json:"r"`
|
R *hexutil.Big `json:"r"`
|
||||||
S *hexutil.Big `json:"s"`
|
S *hexutil.Big `json:"s"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||||
|
// representation, with the given location metadata set (if available).
|
||||||
|
func newRPCTransaction(tx *types.EthereumTxMsg, blockHash common.Hash, blockNumber uint64, index uint64) *Transaction {
|
||||||
|
// Verify signature and retrieve sender address
|
||||||
|
from, _ := tx.VerifySig(tx.ChainID())
|
||||||
|
|
||||||
|
result := &Transaction{
|
||||||
|
From: from,
|
||||||
|
Gas: hexutil.Uint64(tx.Data.GasLimit),
|
||||||
|
GasPrice: (*hexutil.Big)(tx.Data.Price),
|
||||||
|
Hash: tx.Hash(),
|
||||||
|
Input: hexutil.Bytes(tx.Data.Payload),
|
||||||
|
Nonce: hexutil.Uint64(tx.Data.AccountNonce),
|
||||||
|
To: tx.To(),
|
||||||
|
Value: (*hexutil.Big)(tx.Data.Amount),
|
||||||
|
V: (*hexutil.Big)(tx.Data.V),
|
||||||
|
R: (*hexutil.Big)(tx.Data.R),
|
||||||
|
S: (*hexutil.Big)(tx.Data.S),
|
||||||
|
}
|
||||||
|
if blockHash != (common.Hash{}) {
|
||||||
|
result.BlockHash = &blockHash
|
||||||
|
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
|
||||||
|
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// GetTransactionByHash returns the transaction identified by hash.
|
// GetTransactionByHash returns the transaction identified by hash.
|
||||||
func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) *Transaction {
|
func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) *Transaction {
|
||||||
return nil
|
return nil
|
||||||
@ -365,3 +468,22 @@ func (e *PublicEthAPI) GetUncleByBlockHashAndIndex(hash common.Hash, idx hexutil
|
|||||||
func (e *PublicEthAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexutil.Uint) map[string]interface{} {
|
func (e *PublicEthAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexutil.Uint) map[string]interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getGasLimit returns the gas limit per block set in genesis
|
||||||
|
func (e *PublicEthAPI) getGasLimit() (int64, error) {
|
||||||
|
// Retrieve from gasLimit variable cache
|
||||||
|
if e.gasLimit != nil {
|
||||||
|
return *e.gasLimit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query genesis block if hasn't been retrieved yet
|
||||||
|
genesis, err := e.cliCtx.Client.Genesis()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save value to gasLimit cached value
|
||||||
|
gasLimit := genesis.Genesis.ConsensusParams.Block.MaxGas
|
||||||
|
e.gasLimit = &gasLimit
|
||||||
|
return gasLimit, nil
|
||||||
|
}
|
||||||
|
@ -299,6 +299,24 @@ func (msg EthereumTxMsg) Fee() *big.Int {
|
|||||||
return new(big.Int).Mul(msg.Data.Price, new(big.Int).SetUint64(msg.Data.GasLimit))
|
return new(big.Int).Mul(msg.Data.Price, new(big.Int).SetUint64(msg.Data.GasLimit))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChainID returns which chain id this transaction was signed for (if at all)
|
||||||
|
func (msg *EthereumTxMsg) ChainID() *big.Int {
|
||||||
|
return deriveChainID(msg.Data.V)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deriveChainID derives the chain id from the given v parameter
|
||||||
|
func deriveChainID(v *big.Int) *big.Int {
|
||||||
|
if v.BitLen() <= 64 {
|
||||||
|
v := v.Uint64()
|
||||||
|
if v == 27 || v == 28 {
|
||||||
|
return new(big.Int)
|
||||||
|
}
|
||||||
|
return new(big.Int).SetUint64((v - 35) / 2)
|
||||||
|
}
|
||||||
|
v = new(big.Int).Sub(v, big.NewInt(35))
|
||||||
|
return v.Div(v, big.NewInt(2))
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Auxiliary
|
// Auxiliary
|
||||||
|
|
||||||
@ -360,7 +378,7 @@ func recoverEthSig(R, S, Vb *big.Int, sigHash ethcmn.Hash) (ethcmn.Address, erro
|
|||||||
return addr, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopulateFromArgs populates tx message with args (used in RPC API)
|
// GenerateFromArgs populates tx message with args (used in RPC API)
|
||||||
func GenerateFromArgs(args args.SendTxArgs, ctx context.CLIContext) (msg *EthereumTxMsg, err error) {
|
func GenerateFromArgs(args args.SendTxArgs, ctx context.CLIContext) (msg *EthereumTxMsg, err error) {
|
||||||
var nonce uint64
|
var nonce uint64
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user