forked from cerc-io/laconicd-deprecated
more fixes
This commit is contained in:
@@ -1,38 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// AddrLocker is a mutex structure used to avoid querying outdated account data
|
||||
type AddrLocker struct {
|
||||
mu sync.Mutex
|
||||
locks map[common.Address]*sync.Mutex
|
||||
}
|
||||
|
||||
// lock returns the lock of the given address.
|
||||
func (l *AddrLocker) lock(address common.Address) *sync.Mutex {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.locks == nil {
|
||||
l.locks = make(map[common.Address]*sync.Mutex)
|
||||
}
|
||||
if _, ok := l.locks[address]; !ok {
|
||||
l.locks[address] = new(sync.Mutex)
|
||||
}
|
||||
return l.locks[address]
|
||||
}
|
||||
|
||||
// LockAddr locks an account's mutex. This is used to prevent another tx getting the
|
||||
// same nonce until the lock is released. The mutex prevents the (an identical nonce) from
|
||||
// being read again during the time that the first transaction is being signed.
|
||||
func (l *AddrLocker) LockAddr(address common.Address) {
|
||||
l.lock(address).Lock()
|
||||
}
|
||||
|
||||
// UnlockAddr unlocks the mutex of the given account.
|
||||
func (l *AddrLocker) UnlockAddr(address common.Address) {
|
||||
l.lock(address).Unlock()
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// BlockNumber represents decoding hex string to block values
|
||||
type BlockNumber int64
|
||||
|
||||
const (
|
||||
// LatestBlockNumber mapping from "latest" to 0 for tm query
|
||||
LatestBlockNumber = BlockNumber(0)
|
||||
|
||||
// EarliestBlockNumber mapping from "earliest" to 1 for tm query (earliest query not supported)
|
||||
EarliestBlockNumber = BlockNumber(1)
|
||||
|
||||
// PendingBlockNumber mapping from "pending" to -1 for tm query
|
||||
PendingBlockNumber = BlockNumber(-1)
|
||||
)
|
||||
|
||||
// NewBlockNumber creates a new BlockNumber instance.
|
||||
func NewBlockNumber(n *big.Int) BlockNumber {
|
||||
return BlockNumber(n.Int64())
|
||||
}
|
||||
|
||||
// ContextWithHeight wraps a context with the a gRPC block height header. If the provided height is
|
||||
// 0 or -1, it will return an empty context and the gRPC query will use the latest block height for querying.
|
||||
func ContextWithHeight(height int64) context.Context {
|
||||
if height == LatestBlockNumber.Int64() || height == PendingBlockNumber.Int64() {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
return metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, fmt.Sprintf("%d", height))
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
|
||||
// - "latest", "earliest" or "pending" as string arguments
|
||||
// - the block number
|
||||
// Returned errors:
|
||||
// - an invalid block number error when the given argument isn't a known strings
|
||||
// - an out of range error when the given block number is either too little or too large
|
||||
func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
||||
input := strings.TrimSpace(string(data))
|
||||
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
|
||||
input = input[1 : len(input)-1]
|
||||
}
|
||||
|
||||
switch input {
|
||||
case "earliest":
|
||||
*bn = EarliestBlockNumber
|
||||
return nil
|
||||
case "latest":
|
||||
*bn = LatestBlockNumber
|
||||
return nil
|
||||
case "pending":
|
||||
*bn = PendingBlockNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
blckNum, err := hexutil.DecodeUint64(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blckNum > math.MaxInt64 {
|
||||
return fmt.Errorf("blocknumber too high")
|
||||
}
|
||||
|
||||
*bn = BlockNumber(blckNum)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int64 converts block number to primitive type
|
||||
func (bn BlockNumber) Int64() int64 {
|
||||
return int64(bn)
|
||||
}
|
||||
|
||||
// TmHeight is a util function used for the Tendermint RPC client. It returns
|
||||
// nil if the block number is "latest". Otherwise, it returns the pointer of the
|
||||
// int64 value of the height.
|
||||
func (bn BlockNumber) TmHeight() *int64 {
|
||||
if bn == LatestBlockNumber {
|
||||
return nil
|
||||
}
|
||||
height := bn.Int64()
|
||||
return &height
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/proto/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx"
|
||||
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
// QueryClient defines a gRPC Client used for:
|
||||
// - Transaction simulation
|
||||
// - EVM module queries
|
||||
type QueryClient struct {
|
||||
tx.ServiceClient
|
||||
evmtypes.QueryClient
|
||||
}
|
||||
|
||||
// NewQueryClient creates a new gRPC query client
|
||||
func NewQueryClient(clientCtx client.Context) *QueryClient { // nolint: interfacer
|
||||
return &QueryClient{
|
||||
ServiceClient: tx.NewServiceClient(clientCtx),
|
||||
QueryClient: evmtypes.NewQueryClient(clientCtx),
|
||||
}
|
||||
}
|
||||
|
||||
// GetProof performs an ABCI query with the given key and returns a merkle proof. The desired
|
||||
// tendermint height to perform the query should be set in the client context. The query will be
|
||||
// performed at one below this height (at the IAVL version) in order to obtain the correct merkle
|
||||
// proof. Proof queries at height less than or equal to 2 are not supported.
|
||||
// Issue: https://github.com/cosmos/cosmos-sdk/issues/6567
|
||||
func (QueryClient) GetProof(clientCtx client.Context, storeKey string, key []byte) ([]byte, *crypto.ProofOps, error) {
|
||||
height := clientCtx.Height
|
||||
// ABCI queries at height less than or equal to 2 are not supported.
|
||||
// Base app does not support queries for height less than or equal to 1.
|
||||
// Therefore, a query at height 2 would be equivalent to a query at height 3
|
||||
if height <= 2 {
|
||||
return nil, nil, fmt.Errorf("proof queries at height <= 2 are not supported")
|
||||
}
|
||||
|
||||
// Use the IAVL height if a valid tendermint height is passed in.
|
||||
height--
|
||||
|
||||
abciReq := abci.RequestQuery{
|
||||
Path: fmt.Sprintf("store/%s/key", storeKey),
|
||||
Data: key,
|
||||
Height: height,
|
||||
Prove: true,
|
||||
}
|
||||
|
||||
abciRes, err := clientCtx.QueryABCI(abciReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return abciRes.Value, abciRes.ProofOps, nil
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// Copied the Account and StorageResult types since they are registered under an
|
||||
// internal pkg on geth.
|
||||
|
||||
// AccountResult struct for account proof
|
||||
type AccountResult struct {
|
||||
Address common.Address `json:"address"`
|
||||
AccountProof []string `json:"accountProof"`
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
CodeHash common.Hash `json:"codeHash"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
StorageHash common.Hash `json:"storageHash"`
|
||||
StorageProof []StorageResult `json:"storageProof"`
|
||||
}
|
||||
|
||||
// StorageResult defines the format for storage proof return
|
||||
type StorageResult struct {
|
||||
Key string `json:"key"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Proof []string `json:"proof"`
|
||||
}
|
||||
|
||||
// Transaction represents a transaction returned to RPC clients.
|
||||
type Transaction struct {
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber"`
|
||||
From common.Address `json:"from"`
|
||||
Gas hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
To *common.Address `json:"to"`
|
||||
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
}
|
||||
|
||||
// SendTxArgs represents the arguments to submit a new transaction into the transaction pool.
|
||||
// Duplicate struct definition since geth struct is in internal package
|
||||
// Ref: https://github.com/ethereum/go-ethereum/blob/release/1.9/internal/ethapi/api.go#L1346
|
||||
type SendTxArgs struct {
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
// We accept "data" and "input" for backwards-compatibility reasons. "input" is the
|
||||
// newer name and should be preferred by clients.
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
Input *hexutil.Bytes `json:"input"`
|
||||
}
|
||||
|
||||
// CallArgs represents the arguments for a call.
|
||||
type CallArgs struct {
|
||||
From *common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
}
|
||||
|
||||
// Account indicates the overriding fields of account during the execution of
|
||||
// a message call.
|
||||
// NOTE: state and stateDiff can't be specified at the same time. If state is
|
||||
// set, message execution will only use the data in the given state. Otherwise
|
||||
// if statDiff is set, all diff will be applied first and then execute the call
|
||||
// message.
|
||||
type Account struct {
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
Code *hexutil.Bytes `json:"code"`
|
||||
Balance **hexutil.Big `json:"balance"`
|
||||
State *map[common.Hash]common.Hash `json:"state"`
|
||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// RawTxToEthTx returns a evm MsgEthereum transaction from raw tx bytes.
|
||||
func RawTxToEthTx(clientCtx client.Context, bz []byte) (*evmtypes.MsgEthereumTx, error) {
|
||||
tx, err := clientCtx.TxConfig.TxDecoder()(bz)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
|
||||
ethTx, ok := tx.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid transaction type %T, expected %T", tx, evmtypes.MsgEthereumTx{})
|
||||
}
|
||||
return ethTx, nil
|
||||
}
|
||||
|
||||
// NewTransaction returns a transaction that will serialize to the RPC
|
||||
// representation, with the given location metadata set (if available).
|
||||
func NewTransaction(tx *evmtypes.MsgEthereumTx, txHash, blockHash common.Hash, blockNumber, index uint64) (*Transaction, error) {
|
||||
// Verify signature and retrieve sender address
|
||||
from, err := tx.VerifySig(tx.ChainID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpcTx := &Transaction{
|
||||
From: from,
|
||||
Gas: hexutil.Uint64(tx.Data.GasLimit),
|
||||
GasPrice: (*hexutil.Big)(tx.Data.Price.BigInt()),
|
||||
Hash: txHash,
|
||||
Input: hexutil.Bytes(tx.Data.Payload),
|
||||
Nonce: hexutil.Uint64(tx.Data.AccountNonce),
|
||||
To: tx.To(),
|
||||
Value: (*hexutil.Big)(tx.Data.Amount.BigInt()),
|
||||
V: (*hexutil.Big)(new(big.Int).SetBytes(tx.Data.V)),
|
||||
R: (*hexutil.Big)(new(big.Int).SetBytes(tx.Data.R)),
|
||||
S: (*hexutil.Big)(new(big.Int).SetBytes(tx.Data.S)),
|
||||
}
|
||||
|
||||
if blockHash != (common.Hash{}) {
|
||||
rpcTx.BlockHash = &blockHash
|
||||
rpcTx.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
|
||||
rpcTx.TransactionIndex = (*hexutil.Uint64)(&index)
|
||||
}
|
||||
|
||||
return rpcTx, nil
|
||||
}
|
||||
|
||||
// EthBlockFromTendermint returns a JSON-RPC compatible Ethereum blockfrom a given Tendermint block.
|
||||
func EthBlockFromTendermint(clientCtx client.Context, queryClient *QueryClient, block *tmtypes.Block) (map[string]interface{}, error) {
|
||||
gasLimit, err := BlockMaxGasFromConsensusParams(context.Background(), clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions, gasUsed, err := EthTransactionsFromTendermint(clientCtx, block.Txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &evmtypes.QueryBlockBloomRequest{}
|
||||
|
||||
// use height 0 for querying the latest block height
|
||||
res, err := queryClient.BlockBloom(ContextWithHeight(0), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bloom := ethtypes.BytesToBloom(res.Bloom)
|
||||
|
||||
return FormatBlock(block.Header, block.Size(), block.Hash(), gasLimit, gasUsed, transactions, bloom), nil
|
||||
}
|
||||
|
||||
// EthHeaderFromTendermint is an util function that returns an Ethereum Header
|
||||
// from a tendermint Header.
|
||||
func EthHeaderFromTendermint(header tmtypes.Header) *ethtypes.Header {
|
||||
return ðtypes.Header{
|
||||
ParentHash: common.BytesToHash(header.LastBlockID.Hash.Bytes()),
|
||||
UncleHash: common.Hash{},
|
||||
Coinbase: common.Address{},
|
||||
Root: common.BytesToHash(header.AppHash),
|
||||
TxHash: common.BytesToHash(header.DataHash),
|
||||
ReceiptHash: common.Hash{},
|
||||
Difficulty: nil,
|
||||
Number: big.NewInt(header.Height),
|
||||
Time: uint64(header.Time.Unix()),
|
||||
Extra: nil,
|
||||
MixDigest: common.Hash{},
|
||||
Nonce: ethtypes.BlockNonce{},
|
||||
}
|
||||
}
|
||||
|
||||
// EthTransactionsFromTendermint returns a slice of ethereum transaction hashes and the total gas usage from a set of
|
||||
// tendermint block transactions.
|
||||
func EthTransactionsFromTendermint(clientCtx client.Context, txs []tmtypes.Tx) ([]common.Hash, *big.Int, error) {
|
||||
transactionHashes := []common.Hash{}
|
||||
gasUsed := big.NewInt(0)
|
||||
|
||||
for _, tx := range txs {
|
||||
ethTx, err := RawTxToEthTx(clientCtx, tx)
|
||||
if err != nil {
|
||||
// continue to next transaction in case it's not a MsgEthereumTx
|
||||
continue
|
||||
}
|
||||
// TODO: Remove gas usage calculation if saving gasUsed per block
|
||||
gasUsed.Add(gasUsed, big.NewInt(int64(ethTx.GetGas())))
|
||||
transactionHashes = append(transactionHashes, common.BytesToHash(tx.Hash()))
|
||||
}
|
||||
|
||||
return transactionHashes, gasUsed, nil
|
||||
}
|
||||
|
||||
// BlockMaxGasFromConsensusParams returns the gas limit for the latest block from the chain consensus params.
|
||||
func BlockMaxGasFromConsensusParams(ctx context.Context, clientCtx client.Context) (int64, error) {
|
||||
resConsParams, err := clientCtx.Client.ConsensusParams(ctx, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
gasLimit := resConsParams.ConsensusParams.Block.MaxGas
|
||||
if gasLimit == -1 {
|
||||
// Sets gas limit to max uint32 to not error with javascript dev tooling
|
||||
// This -1 value indicating no block gas limit is set to max uint64 with geth hexutils
|
||||
// which errors certain javascript dev tooling which only supports up to 53 bits
|
||||
gasLimit = int64(^uint32(0))
|
||||
}
|
||||
|
||||
return gasLimit, nil
|
||||
}
|
||||
|
||||
// FormatBlock creates an ethereum block from a tendermint header and ethereum-formatted
|
||||
// transactions.
|
||||
func FormatBlock(
|
||||
header tmtypes.Header, size int, curBlockHash tmbytes.HexBytes, gasLimit int64,
|
||||
gasUsed *big.Int, transactions interface{}, bloom ethtypes.Bloom,
|
||||
) map[string]interface{} {
|
||||
if len(header.DataHash) == 0 {
|
||||
header.DataHash = tmbytes.HexBytes(common.Hash{}.Bytes())
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"number": hexutil.Uint64(header.Height),
|
||||
"hash": hexutil.Bytes(curBlockHash),
|
||||
"parentHash": hexutil.Bytes(header.LastBlockID.Hash),
|
||||
"nonce": hexutil.Uint64(0), // PoW specific
|
||||
"sha3Uncles": common.Hash{}, // No uncles in Tendermint
|
||||
"logsBloom": bloom,
|
||||
"transactionsRoot": hexutil.Bytes(header.DataHash),
|
||||
"stateRoot": hexutil.Bytes(header.AppHash),
|
||||
"miner": common.Address{},
|
||||
"mixHash": common.Hash{},
|
||||
"difficulty": 0,
|
||||
"totalDifficulty": 0,
|
||||
"extraData": hexutil.Uint64(0),
|
||||
"size": hexutil.Uint64(size),
|
||||
"gasLimit": hexutil.Uint64(gasLimit), // Static gas limit
|
||||
"gasUsed": (*hexutil.Big)(gasUsed),
|
||||
"timestamp": hexutil.Uint64(header.Time.Unix()),
|
||||
"transactions": transactions.([]common.Hash),
|
||||
"uncles": []string{},
|
||||
"receiptsRoot": common.Hash{},
|
||||
}
|
||||
}
|
||||
|
||||
// GetKeyByAddress returns the private key matching the given address. If not found it returns false.
|
||||
func GetKeyByAddress(keys []ethsecp256k1.PrivKey, address common.Address) (key *ethsecp256k1.PrivKey, exist bool) {
|
||||
for _, key := range keys {
|
||||
if bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) {
|
||||
return &key, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// BuildEthereumTx builds and signs a Cosmos transaction from a MsgEthereumTx and returns the tx
|
||||
func BuildEthereumTx(
|
||||
clientCtx client.Context,
|
||||
msgs []sdk.Msg,
|
||||
accNumber, seq, gasLimit uint64,
|
||||
fees sdk.Coins,
|
||||
privKey cryptotypes.PrivKey,
|
||||
) ([]byte, error) {
|
||||
signMode := clientCtx.TxConfig.SignModeHandler().DefaultMode()
|
||||
signerData := authsigning.SignerData{
|
||||
ChainID: clientCtx.ChainID,
|
||||
AccountNumber: accNumber,
|
||||
Sequence: seq,
|
||||
}
|
||||
|
||||
// Create a TxBuilder
|
||||
txBuilder := clientCtx.TxConfig.NewTxBuilder()
|
||||
if err := txBuilder.SetMsgs(msgs...); err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
txBuilder.SetFeeAmount(fees)
|
||||
txBuilder.SetGasLimit(gasLimit)
|
||||
|
||||
// sign with the private key
|
||||
sigV2, err := tx.SignWithPrivKey(
|
||||
signMode, signerData,
|
||||
txBuilder, privKey, clientCtx.TxConfig, seq,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := txBuilder.SetSignatures(sigV2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBytes, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txBytes, nil
|
||||
}
|
||||
|
||||
// GetBlockCumulativeGas returns the cumulative gas used on a block up to a given
|
||||
// transaction index. The returned gas used includes the gas from both the SDK and
|
||||
// EVM module transactions.
|
||||
func GetBlockCumulativeGas(clientCtx client.Context, block *tmtypes.Block, idx int) uint64 {
|
||||
var gasUsed uint64
|
||||
txDecoder := clientCtx.TxConfig.TxDecoder()
|
||||
|
||||
for i := 0; i < idx && i < len(block.Txs); i++ {
|
||||
txi, err := txDecoder(block.Txs[i])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch tx := txi.(type) {
|
||||
case *evmtypes.MsgEthereumTx:
|
||||
gasUsed += tx.GetGas()
|
||||
case sdk.FeeTx:
|
||||
gasUsed += tx.GetGas()
|
||||
}
|
||||
}
|
||||
return gasUsed
|
||||
}
|
||||
Reference in New Issue
Block a user