filters: begin implementation (#230)
* adds Filter type and related methods * updates PublicFilterAPI to include backend, filter mapping * stub out filter related eth_ functions
This commit is contained in:
parent
da9157e406
commit
35e7a98ab2
20
rpc/apis.go
20
rpc/apis.go
@ -8,36 +8,42 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const Web3Namespace = "web3"
|
||||
const EthNamespace = "eth"
|
||||
const PersonalNamespace = "personal"
|
||||
const NetNamespace = "net"
|
||||
|
||||
// GetRPCAPIs returns the list of all APIs
|
||||
func GetRPCAPIs(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) []rpc.API {
|
||||
nonceLock := new(AddrLocker)
|
||||
backend := NewEthermintBackend(cliCtx)
|
||||
return []rpc.API{
|
||||
{
|
||||
Namespace: "web3",
|
||||
Namespace: Web3Namespace,
|
||||
Version: "1.0",
|
||||
Service: NewPublicWeb3API(),
|
||||
Public: true,
|
||||
},
|
||||
{
|
||||
Namespace: "eth",
|
||||
Namespace: EthNamespace,
|
||||
Version: "1.0",
|
||||
Service: NewPublicEthAPI(cliCtx, nonceLock, key),
|
||||
Service: NewPublicEthAPI(cliCtx, backend, nonceLock, key),
|
||||
Public: true,
|
||||
},
|
||||
{
|
||||
Namespace: "personal",
|
||||
Namespace: PersonalNamespace,
|
||||
Version: "1.0",
|
||||
Service: NewPersonalEthAPI(cliCtx, nonceLock),
|
||||
Public: false,
|
||||
},
|
||||
{
|
||||
Namespace: "eth",
|
||||
Namespace: EthNamespace,
|
||||
Version: "1.0",
|
||||
Service: NewPublicFilterAPI(cliCtx),
|
||||
Service: NewPublicFilterAPI(cliCtx, backend),
|
||||
Public: true,
|
||||
},
|
||||
{
|
||||
Namespace: "net",
|
||||
Namespace: NetNamespace,
|
||||
Version: "1.0",
|
||||
Service: NewPublicNetAPI(cliCtx),
|
||||
Public: true,
|
||||
|
180
rpc/backend.go
Normal file
180
rpc/backend.go
Normal file
@ -0,0 +1,180 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/ethermint/x/evm"
|
||||
"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"
|
||||
)
|
||||
|
||||
// Backend implements the functionality needed to filter changes.
|
||||
// Implemented by EthermintBackend.
|
||||
type Backend interface {
|
||||
// Used by block filter; also used for polling
|
||||
BlockNumber() (hexutil.Uint64, error)
|
||||
GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error)
|
||||
getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error)
|
||||
getGasLimit() (int64, error)
|
||||
|
||||
// Used by pending transaction filter
|
||||
PendingTransactions() ([]*Transaction, error)
|
||||
|
||||
// Used by log filter
|
||||
GetTxLogs(txHash common.Hash) ([]*ethtypes.Log, error)
|
||||
// TODO: Bloom methods
|
||||
}
|
||||
|
||||
// EmintBackend implements Backend
|
||||
type EthermintBackend struct {
|
||||
cliCtx context.CLIContext
|
||||
gasLimit int64
|
||||
}
|
||||
|
||||
func NewEthermintBackend(cliCtx context.CLIContext) *EthermintBackend {
|
||||
return &EthermintBackend{
|
||||
cliCtx: cliCtx,
|
||||
gasLimit: int64(^uint32(0)),
|
||||
}
|
||||
}
|
||||
|
||||
// BlockNumber returns the current block number.
|
||||
func (e *EthermintBackend) BlockNumber() (hexutil.Uint64, error) {
|
||||
res, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/blockNumber", types.ModuleName), nil)
|
||||
if err != nil {
|
||||
return hexutil.Uint64(0), err
|
||||
}
|
||||
|
||||
var out types.QueryResBlockNumber
|
||||
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||
return hexutil.Uint64(out.Number), nil
|
||||
}
|
||||
|
||||
// GetBlockByNumber returns the block identified by number.
|
||||
func (e *EthermintBackend) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error) {
|
||||
value := blockNum.Int64()
|
||||
return e.getEthBlockByNumber(value, fullTx)
|
||||
}
|
||||
|
||||
func (e *EthermintBackend) getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error) {
|
||||
// Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014)
|
||||
var blkNumPtr *int64
|
||||
if height != 0 {
|
||||
blkNumPtr = &height
|
||||
}
|
||||
|
||||
block, err := e.cliCtx.Client.Block(blkNumPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := block.Block.Header
|
||||
|
||||
gasLimit, err := e.getGasLimit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
gasUsed *big.Int
|
||||
transactions []interface{}
|
||||
)
|
||||
|
||||
if fullTx {
|
||||
// Populate full transaction data
|
||||
transactions, gasUsed, err = convertTransactionsToRPC(
|
||||
e.cliCtx, block.Block.Txs, common.BytesToHash(header.Hash()), uint64(header.Height),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} 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())
|
||||
}
|
||||
}
|
||||
|
||||
res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryLogsBloom, strconv.FormatInt(block.Block.Height, 10)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out types.QueryBloomFilter
|
||||
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||
|
||||
return formatBlock(header, block.Block.Size(), gasLimit, gasUsed, transactions, out.Bloom), nil
|
||||
}
|
||||
|
||||
// getGasLimit returns the gas limit per block set in genesis
|
||||
func (e *EthermintBackend) getGasLimit() (int64, error) {
|
||||
// Retrieve from gasLimit variable cache
|
||||
if e.gasLimit != -1 {
|
||||
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
|
||||
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))
|
||||
}
|
||||
e.gasLimit = gasLimit
|
||||
return gasLimit, nil
|
||||
}
|
||||
|
||||
// GetTxLogs returns the logs given a transaction hash.
|
||||
func (e *EthermintBackend) GetTxLogs(txHash common.Hash) ([]*ethtypes.Log, error) {
|
||||
// do we need to use the block height somewhere?
|
||||
ctx := e.cliCtx
|
||||
res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryTxLogs, txHash.Hex()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out types.QueryETHLogs
|
||||
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||
return out.Logs, nil
|
||||
}
|
||||
|
||||
// PendingTransactions returns the transactions that are in the transaction pool
|
||||
// and have a from address that is one of the accounts this node manages.
|
||||
func (e *EthermintBackend) PendingTransactions() ([]*Transaction, error) {
|
||||
pendingTxs, err := e.cliCtx.Client.UnconfirmedTxs(100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions := make([]*Transaction, 0, 100)
|
||||
for _, tx := range pendingTxs.Txs {
|
||||
ethTx, err := bytesToEthTx(e.cliCtx, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// * Should check signer and reference against accounts the node manages in future
|
||||
rpcTx, err := newRPCTransaction(*ethTx, common.Hash{}, nil, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions = append(transactions, rpcTx)
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
126
rpc/eth_api.go
126
rpc/eth_api.go
@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
@ -40,18 +39,19 @@ import (
|
||||
// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
||||
type PublicEthAPI struct {
|
||||
cliCtx context.CLIContext
|
||||
backend Backend
|
||||
key emintcrypto.PrivKeySecp256k1
|
||||
nonceLock *AddrLocker
|
||||
keybaseLock sync.Mutex
|
||||
gasLimit *int64
|
||||
}
|
||||
|
||||
// NewPublicEthAPI creates an instance of the public ETH Web3 API.
|
||||
func NewPublicEthAPI(cliCtx context.CLIContext, nonceLock *AddrLocker,
|
||||
func NewPublicEthAPI(cliCtx context.CLIContext, backend Backend, nonceLock *AddrLocker,
|
||||
key emintcrypto.PrivKeySecp256k1) *PublicEthAPI {
|
||||
|
||||
return &PublicEthAPI{
|
||||
cliCtx: cliCtx,
|
||||
backend: backend,
|
||||
key: key,
|
||||
nonceLock: nonceLock,
|
||||
}
|
||||
@ -132,14 +132,7 @@ func (e *PublicEthAPI) Accounts() ([]common.Address, error) {
|
||||
|
||||
// BlockNumber returns the current block number.
|
||||
func (e *PublicEthAPI) BlockNumber() (hexutil.Uint64, error) {
|
||||
res, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/blockNumber", types.ModuleName), nil)
|
||||
if err != nil {
|
||||
return hexutil.Uint64(0), err
|
||||
}
|
||||
|
||||
var out types.QueryResBlockNumber
|
||||
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||
return hexutil.Uint64(out.Number), nil
|
||||
return e.backend.BlockNumber()
|
||||
}
|
||||
|
||||
// GetBalance returns the provided account's balance up to the provided block number.
|
||||
@ -229,7 +222,7 @@ func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum BlockNumber) hexutil.
|
||||
// GetCode returns the contract code at the given address and block number.
|
||||
func (e *PublicEthAPI) GetCode(address common.Address, blockNumber BlockNumber) (hexutil.Bytes, error) {
|
||||
ctx := e.cliCtx.WithHeight(blockNumber.Int64())
|
||||
res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/code/%s", types.ModuleName, address.Hex()), nil)
|
||||
res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryCode, address.Hex()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -239,6 +232,11 @@ func (e *PublicEthAPI) GetCode(address common.Address, blockNumber BlockNumber)
|
||||
return out.Code, nil
|
||||
}
|
||||
|
||||
// GetTxLogs returns the logs given a transaction hash.
|
||||
func (e *PublicEthAPI) GetTxLogs(txHash common.Hash) ([]*ethtypes.Log, error) {
|
||||
return e.backend.GetTxLogs(txHash)
|
||||
}
|
||||
|
||||
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
||||
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
|
||||
// TODO: Change this functionality to find an unlocked account by address
|
||||
@ -480,64 +478,12 @@ func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) (map[string
|
||||
|
||||
var out types.QueryResBlockNumber
|
||||
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||
return e.getEthBlockByNumber(out.Number, fullTx)
|
||||
return e.backend.getEthBlockByNumber(out.Number, fullTx)
|
||||
}
|
||||
|
||||
// GetBlockByNumber returns the block identified by number.
|
||||
func (e *PublicEthAPI) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error) {
|
||||
value := blockNum.Int64()
|
||||
return e.getEthBlockByNumber(value, fullTx)
|
||||
}
|
||||
|
||||
func (e *PublicEthAPI) getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error) {
|
||||
// Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014)
|
||||
var blkNumPtr *int64
|
||||
if height != 0 {
|
||||
blkNumPtr = &height
|
||||
}
|
||||
|
||||
block, err := e.cliCtx.Client.Block(blkNumPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := block.Block.Header
|
||||
|
||||
gasLimit, err := e.getGasLimit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
gasUsed *big.Int
|
||||
transactions []interface{}
|
||||
)
|
||||
|
||||
if fullTx {
|
||||
// Populate full transaction data
|
||||
transactions, gasUsed, err = convertTransactionsToRPC(
|
||||
e.cliCtx, block.Block.Txs, common.BytesToHash(header.Hash()), uint64(header.Height),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} 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())
|
||||
}
|
||||
}
|
||||
|
||||
res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryLogsBloom, strconv.FormatInt(block.Block.Height, 10)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out types.QueryBloomFilter
|
||||
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||
|
||||
return formatBlock(header, block.Block.Size(), gasLimit, gasUsed, transactions, out.Bloom), nil
|
||||
return e.backend.GetBlockByNumber(blockNum, fullTx)
|
||||
}
|
||||
|
||||
func formatBlock(
|
||||
@ -782,28 +728,7 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
|
||||
// PendingTransactions returns the transactions that are in the transaction pool
|
||||
// and have a from address that is one of the accounts this node manages.
|
||||
func (e *PublicEthAPI) PendingTransactions() ([]*Transaction, error) {
|
||||
pendingTxs, err := e.cliCtx.Client.UnconfirmedTxs(100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions := make([]*Transaction, 0, 100)
|
||||
for _, tx := range pendingTxs.Txs {
|
||||
ethTx, err := bytesToEthTx(e.cliCtx, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// * Should check signer and reference against accounts the node manages in future
|
||||
rpcTx, err := newRPCTransaction(*ethTx, common.Hash{}, nil, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions = append(transactions, rpcTx)
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
return e.backend.PendingTransactions()
|
||||
}
|
||||
|
||||
// GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil.
|
||||
@ -878,31 +803,6 @@ func (e *PublicEthAPI) GetProof(address common.Address, storageKeys []string, bl
|
||||
}, 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
|
||||
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))
|
||||
}
|
||||
e.gasLimit = &gasLimit
|
||||
return gasLimit, nil
|
||||
}
|
||||
|
||||
// generateFromArgs populates tx message with args (used in RPC API)
|
||||
func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (*types.MsgEthereumTx, error) {
|
||||
var (
|
||||
|
@ -3,12 +3,11 @@ package rpc
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
"math/big"
|
||||
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
@ -17,15 +16,61 @@ import (
|
||||
// PublicFilterAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
||||
type PublicFilterAPI struct {
|
||||
cliCtx context.CLIContext
|
||||
backend Backend
|
||||
filters map[rpc.ID]*Filter // ID to filter; TODO: change to sync.Map in case of concurrent writes
|
||||
}
|
||||
|
||||
// NewPublicEthAPI creates an instance of the public ETH Web3 API.
|
||||
func NewPublicFilterAPI(cliCtx context.CLIContext) *PublicFilterAPI {
|
||||
func NewPublicFilterAPI(cliCtx context.CLIContext, backend Backend) *PublicFilterAPI {
|
||||
return &PublicFilterAPI{
|
||||
cliCtx: cliCtx,
|
||||
backend: backend,
|
||||
filters: make(map[rpc.ID]*Filter),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFilter instantiates a new filter.
|
||||
func (e *PublicFilterAPI) NewFilter(criteria filters.FilterCriteria) rpc.ID {
|
||||
id := rpc.NewID()
|
||||
e.filters[id] = NewFilter(e.backend, &criteria)
|
||||
return id
|
||||
}
|
||||
|
||||
// NewBlockFilter instantiates a new block filter.
|
||||
func (e *PublicFilterAPI) NewBlockFilter() rpc.ID {
|
||||
id := rpc.NewID()
|
||||
e.filters[id] = NewBlockFilter(e.backend)
|
||||
return id
|
||||
}
|
||||
|
||||
// NewPendingTransactionFilter instantiates a new pending transaction filter.
|
||||
func (e *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID {
|
||||
id := rpc.NewID()
|
||||
e.filters[id] = NewPendingTransactionFilter(e.backend)
|
||||
return id
|
||||
}
|
||||
|
||||
// UninstallFilter uninstalls a filter with the given ID.
|
||||
func (e *PublicFilterAPI) UninstallFilter(id rpc.ID) bool {
|
||||
// TODO
|
||||
e.filters[id].uninstallFilter()
|
||||
delete(e.filters, id)
|
||||
return true
|
||||
}
|
||||
|
||||
// GetFilterChanges returns an array of changes since the last poll.
|
||||
// If the filter is a log filter, it returns an array of Logs.
|
||||
// If the filter is a block filter, it returns an array of block hashes.
|
||||
// If the filter is a pending transaction filter, it returns an array of transaction hashes.
|
||||
func (e *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} {
|
||||
return e.filters[id].getFilterChanges()
|
||||
}
|
||||
|
||||
// GetFilterLogs returns an array of all logs matching filter with given id.
|
||||
func (e *PublicFilterAPI) GetFilterLogs(id rpc.ID) []*ethtypes.Log {
|
||||
return e.filters[id].getFilterLogs()
|
||||
}
|
||||
|
||||
// GetLogs returns logs matching the given argument that are stored within the state.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
|
||||
@ -35,7 +80,7 @@ func (e *PublicFilterAPI) GetLogs(criteria filters.FilterCriteria) ([]*ethtypes.
|
||||
/*
|
||||
Still need to add blockhash in prepare function for log entry
|
||||
*/
|
||||
filter = NewBlockFilter(*criteria.BlockHash, criteria.Addresses, criteria.Topics)
|
||||
filter = NewFilterWithBlockHash(e.backend, &criteria)
|
||||
results := e.getLogs()
|
||||
logs := filterLogs(results, nil, nil, filter.addresses, filter.topics)
|
||||
return logs, nil
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -14,28 +15,65 @@ import (
|
||||
|
||||
// Filter can be used to retrieve and filter logs.
|
||||
type Filter struct {
|
||||
addresses []common.Address
|
||||
topics [][]common.Hash
|
||||
|
||||
block common.Hash // Block hash if filtering a single block
|
||||
backend Backend
|
||||
fromBlock, toBlock *big.Int // start and end block numbers
|
||||
addresses []common.Address // contract addresses to watch
|
||||
topics [][]common.Hash // log topics to watch for
|
||||
blockHash *common.Hash // Block hash if filtering a single block
|
||||
}
|
||||
|
||||
// NewBlockFilter creates a new filter which directly inspects the contents of
|
||||
// a block to figure out whether it is interesting or not.
|
||||
func NewBlockFilter(block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter {
|
||||
// Create a generic filter and convert it into a block filter
|
||||
filter := newFilter(addresses, topics)
|
||||
filter.block = block
|
||||
// NewFilter returns a new Filter
|
||||
func NewFilter(backend Backend, criteria *filters.FilterCriteria) *Filter {
|
||||
return &Filter{
|
||||
backend: backend,
|
||||
fromBlock: criteria.FromBlock,
|
||||
toBlock: criteria.ToBlock,
|
||||
addresses: criteria.Addresses,
|
||||
topics: criteria.Topics,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFilterWithBlockHash returns a new Filter with a blockHash.
|
||||
func NewFilterWithBlockHash(backend Backend, criteria *filters.FilterCriteria) *Filter {
|
||||
return &Filter{
|
||||
backend: backend,
|
||||
fromBlock: criteria.FromBlock,
|
||||
toBlock: criteria.ToBlock,
|
||||
addresses: criteria.Addresses,
|
||||
topics: criteria.Topics,
|
||||
blockHash: criteria.BlockHash,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBlockFilter creates a new filter that notifies when a block arrives.
|
||||
func NewBlockFilter(backend Backend) *Filter {
|
||||
// TODO: finish
|
||||
filter := NewFilter(backend, nil)
|
||||
return filter
|
||||
}
|
||||
|
||||
// newFilter creates a generic filter that can either filter based on a block hash,
|
||||
// or based on range queries. The search criteria needs to be explicitly set.
|
||||
func newFilter(addresses []common.Address, topics [][]common.Hash) *Filter {
|
||||
return &Filter{
|
||||
addresses: addresses,
|
||||
topics: topics,
|
||||
}
|
||||
// NewPendingTransactionFilter creates a new filter that notifies when a pending transaction arrives.
|
||||
func NewPendingTransactionFilter(backend Backend) *Filter {
|
||||
// TODO: finish
|
||||
filter := NewFilter(backend, nil)
|
||||
return filter
|
||||
}
|
||||
|
||||
func (f *Filter) uninstallFilter() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (f *Filter) getFilterChanges() interface{} {
|
||||
// TODO
|
||||
// we might want to use an interface for Filters themselves because of this function, it may return an array of logs
|
||||
// or an array of hashes, depending of whether Filter is a log filter or a block/transaction filter.
|
||||
// or, we can add a type field to Filter.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filter) getFilterLogs() []*ethtypes.Log {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func includes(addresses []common.Address, a common.Address) bool {
|
||||
|
@ -1,12 +1,14 @@
|
||||
package evm_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -14,6 +16,7 @@ import (
|
||||
|
||||
"github.com/cosmos/ethermint/app"
|
||||
"github.com/cosmos/ethermint/x/evm"
|
||||
"github.com/cosmos/ethermint/x/evm/keeper"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@ -24,7 +27,9 @@ type EvmTestSuite struct {
|
||||
|
||||
ctx sdk.Context
|
||||
handler sdk.Handler
|
||||
querier sdk.Querier
|
||||
app *app.EthermintApp
|
||||
codec *codec.Codec
|
||||
}
|
||||
|
||||
func (suite *EvmTestSuite) SetupTest() {
|
||||
@ -33,6 +38,8 @@ func (suite *EvmTestSuite) SetupTest() {
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
|
||||
suite.handler = evm.NewHandler(suite.app.EvmKeeper)
|
||||
suite.querier = keeper.NewQuerier(suite.app.EvmKeeper)
|
||||
suite.codec = codec.New()
|
||||
}
|
||||
|
||||
func TestEvmTestSuite(t *testing.T) {
|
||||
@ -87,3 +94,48 @@ func (suite *EvmTestSuite) TestHandler_Logs() {
|
||||
|
||||
suite.Require().Equal(logs, resultData.Logs)
|
||||
}
|
||||
|
||||
func (suite *EvmTestSuite) TestQueryTxLogs() {
|
||||
gasLimit := uint64(100000)
|
||||
gasPrice := big.NewInt(1000000)
|
||||
|
||||
priv, err := crypto.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
|
||||
// send contract deployment transaction with an event in the constructor
|
||||
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
||||
tx := types.NewMsgEthereumTx(1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode)
|
||||
tx.Sign(big.NewInt(3), priv)
|
||||
|
||||
// result, err := evm.HandleEthTxMsg(suite.ctx, suite.app.EvmKeeper, tx)
|
||||
// suite.Require().NoError(err, "failed to handle eth tx msg")
|
||||
|
||||
result := suite.handler(suite.ctx, tx)
|
||||
suite.Require().True(result.IsOK())
|
||||
|
||||
resultData, err := types.DecodeResultData(result.Data)
|
||||
suite.Require().NoError(err, "failed to decode result data")
|
||||
|
||||
suite.Require().Equal(len(resultData.Logs), 1)
|
||||
suite.Require().Equal(len(resultData.Logs[0].Topics), 2)
|
||||
|
||||
// get logs by tx hash
|
||||
hash := resultData.TxHash.Bytes()
|
||||
|
||||
logs, err := suite.app.EvmKeeper.GetTransactionLogs(suite.ctx, hash)
|
||||
suite.Require().NoError(err, "failed to get logs")
|
||||
|
||||
suite.Require().Equal(logs, resultData.Logs)
|
||||
|
||||
// query tx logs
|
||||
path := []string{"txLogs", fmt.Sprintf("0x%x", hash)}
|
||||
res, err := suite.querier(suite.ctx, path, abci.RequestQuery{})
|
||||
suite.Require().NoError(err, "failed to query txLogs")
|
||||
|
||||
var txLogs types.QueryETHLogs
|
||||
suite.codec.MustUnmarshalJSON(res, &txLogs)
|
||||
|
||||
// amino decodes an empty byte array as nil, whereas JSON decodes it as []byte{} causing a discrepancy
|
||||
resultData.Logs[0].Data = []byte{}
|
||||
suite.Require().Equal(txLogs.Logs[0], resultData.Logs[0])
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) {
|
||||
Bloom: bloomFilter,
|
||||
Logs: logs,
|
||||
Ret: ret,
|
||||
TxHash: *st.THash,
|
||||
}
|
||||
|
||||
resultData, err := EncodeResultData(res)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"sync"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
emint "github.com/cosmos/ethermint/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
@ -302,7 +303,7 @@ func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
||||
|
||||
logs, err := DecodeLogs(encLogs)
|
||||
if err != nil {
|
||||
return []*ethtypes.Log{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logs, nil
|
||||
|
@ -53,6 +53,7 @@ type ResultData struct {
|
||||
Bloom ethtypes.Bloom
|
||||
Logs []*ethtypes.Log
|
||||
Ret []byte
|
||||
TxHash ethcmn.Hash
|
||||
}
|
||||
|
||||
// EncodeReturnData takes all of the necessary data from the EVM execution
|
||||
|
Loading…
Reference in New Issue
Block a user