Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

Commit

Permalink
block and tx logs bloom and tx receipt logs (#119)
Browse files Browse the repository at this point in the history
* Add bloom filter to tx receipt

* wip tx receipt logs to be tested

* Added Bloom - Height Mapping
* keeper.go sets <height:bloombits>
* keeper.go gets <height> --> bloombits

* updating and querying bloom by block (to be tested with real logs)

* fix bug with contract address return

* added error handling instead of logging
  • Loading branch information
austinabell authored Oct 4, 2019
1 parent 8bb8b40 commit eab81bc
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 22 deletions.
37 changes: 30 additions & 7 deletions rpc/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"math/big"
"strconv"

emintcrypto "github.com/cosmos/ethermint/crypto"
emintkeys "github.com/cosmos/ethermint/keys"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore"
"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/rlp"

"github.com/cosmos/cosmos-sdk/client/context"
Expand Down Expand Up @@ -393,20 +395,28 @@ func (e *PublicEthAPI) getEthBlockByNumber(value int64, fullTx bool) (map[string
}
}

return formatBlock(header, block.Block.Size(), gasLimit, gasUsed, transactions), nil
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
}

func formatBlock(
header tmtypes.Header, size int, gasLimit int64,
gasUsed *big.Int, transactions []interface{},
gasUsed *big.Int, transactions []interface{}, bloom ethtypes.Bloom,
) map[string]interface{} {
return map[string]interface{}{
"number": hexutil.Uint64(header.Height),
"hash": hexutil.Bytes(header.Hash()),
"parentHash": hexutil.Bytes(header.LastBlockID.Hash),
"nonce": nil, // PoW specific
"sha3Uncles": nil, // No uncles in Tendermint
"logsBloom": "", // TODO: Complete with #55
"logsBloom": bloom,
"transactionsRoot": hexutil.Bytes(header.DataHash),
"stateRoot": hexutil.Bytes(header.AppHash),
"miner": hexutil.Bytes(header.ValidatorsHash),
Expand Down Expand Up @@ -584,6 +594,17 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
status = hexutil.Uint(0)
}

res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryTxLogs, hash.Hex()))
if err != nil {
return nil, err
}

var logs types.QueryTxLogs
e.cliCtx.Codec.MustUnmarshalJSON(res, &logs)

// TODO: change hard coded indexing of bytes
bloomFilter := ethtypes.BytesToBloom(tx.TxResult.GetData()[20:])

fields := map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(tx.Height),
Expand All @@ -594,13 +615,15 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
"gasUsed": hexutil.Uint64(tx.TxResult.GasUsed),
"cumulativeGasUsed": nil, // ignore until needed
"contractAddress": nil,
"logs": nil, // TODO: Do with #55 (eth_getLogs output)
"logsBloom": nil,
"logs": logs.Logs,
"logsBloom": bloomFilter,
"status": status,
}

if common.BytesToAddress(tx.TxResult.GetData()) != (common.Address{}) {
fields["contractAddress"] = hexutil.Bytes(tx.TxResult.GetData())
contractAddress := common.BytesToAddress(tx.TxResult.GetData()[:20])
if contractAddress != (common.Address{}) {
// TODO: change hard coded indexing of first 20 bytes
fields["contractAddress"] = contractAddress
}

return fields, nil
Expand Down
28 changes: 16 additions & 12 deletions x/evm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ func handleETHTxMsg(ctx sdk.Context, keeper Keeper, msg types.EthereumTxMsg) sdk
return emint.ErrInvalidSender(err.Error()).Result()
}

// Encode transaction by default Tx encoder
txEncoder := authutils.GetTxEncoder(types.ModuleCdc)
txBytes, err := txEncoder(msg)
if err != nil {
return sdk.ErrInternal(err.Error()).Result()
}
txHash := tm.Tx(txBytes).Hash()
ethHash := common.BytesToHash(txHash)

st := types.StateTransition{
Sender: sender,
AccountNonce: msg.Data.AccountNonce,
Expand All @@ -57,21 +66,15 @@ func handleETHTxMsg(ctx sdk.Context, keeper Keeper, msg types.EthereumTxMsg) sdk
Payload: msg.Data.Payload,
Csdb: keeper.csdb,
ChainID: intChainID,
THash: &ethHash,
}

// Encode transaction by default Tx encoder
txEncoder := authutils.GetTxEncoder(types.ModuleCdc)
txBytes, err := txEncoder(msg)
if err != nil {
return sdk.ErrInternal(err.Error()).Result()
}
txHash := tm.Tx(txBytes).Hash()

// Prepare db for logs
keeper.csdb.Prepare(common.BytesToHash(txHash), common.Hash{}, keeper.txCount.get())
keeper.csdb.Prepare(ethHash, common.Hash{}, keeper.txCount.get())
keeper.txCount.increment()

return st.TransitionCSDB(ctx)
res, bloom := st.TransitionCSDB(ctx)
keeper.bloom.Or(keeper.bloom, bloom)
return res
}

func handleEmintMsg(ctx sdk.Context, keeper Keeper, msg types.EmintMsg) sdk.Result {
Expand Down Expand Up @@ -105,5 +108,6 @@ func handleEmintMsg(ctx sdk.Context, keeper Keeper, msg types.EmintMsg) sdk.Resu
keeper.csdb.Prepare(common.Hash{}, common.Hash{}, keeper.txCount.get()) // Cannot provide tx hash
keeper.txCount.increment()

return st.TransitionCSDB(ctx)
res, _ := st.TransitionCSDB(ctx)
return res
}
27 changes: 27 additions & 0 deletions x/evm/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Keeper struct {
cdc *codec.Codec
blockKey sdk.StoreKey
txCount *count
bloom *big.Int
}

type count int
Expand All @@ -48,6 +49,7 @@ func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey,
cdc: cdc,
blockKey: blockKey,
txCount: new(count),
bloom: big.NewInt(0),
}
}

Expand Down Expand Up @@ -75,6 +77,31 @@ func (k *Keeper) GetBlockHashMapping(ctx sdk.Context, hash []byte) (height int64
return
}

// ----------------------------------------------------------------------------
// Block bloom bits mapping functions
// May be removed when using only as module (only required by rpc api)
// ----------------------------------------------------------------------------

// SetBlockBloomMapping sets the mapping from block height to bloom bits
func (k *Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, height int64) {
store := ctx.KVStore(k.blockKey)
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
if !bytes.Equal(heightHash, []byte{}) {
store.Set(heightHash, bloom.Bytes())
}
}

// GetBlockBloomMapping gets bloombits from block height
func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) ethtypes.Bloom {
store := ctx.KVStore(k.blockKey)
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
bloom := store.Get(heightHash)
if bytes.Equal(heightHash, []byte{}) {
panic(fmt.Errorf("block with bloombits %s not found", bloom))
}
return ethtypes.BytesToBloom(bloom)
}

// ----------------------------------------------------------------------------
// Genesis
// ----------------------------------------------------------------------------
Expand Down
7 changes: 7 additions & 0 deletions x/evm/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
evmtypes "github.com/cosmos/ethermint/x/evm/types"

ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"

abci "github.com/tendermint/tendermint/abci/types"
Expand Down Expand Up @@ -84,6 +85,10 @@ func TestDBStorage(t *testing.T) {
ek.SetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68"), 7)
ek.SetBlockHashMapping(ctx, []byte{0x43, 0x32}, 8)

// Test block height mapping functionality
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
ek.SetBlockBloomMapping(ctx, testBloom, 4)

// Get those state transitions
require.Equal(t, ek.GetBalance(ctx, address).Cmp(big.NewInt(5)), 0)
require.Equal(t, ek.GetNonce(ctx, address), uint64(4))
Expand All @@ -93,6 +98,8 @@ func TestDBStorage(t *testing.T) {
require.Equal(t, ek.GetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68")), int64(7))
require.Equal(t, ek.GetBlockHashMapping(ctx, []byte{0x43, 0x32}), int64(8))

require.Equal(t, ek.GetBlockBloomMapping(ctx, 4), testBloom)

// commit stateDB
_, err = ek.Commit(ctx, false)
require.NoError(t, err, "failed to commit StateDB")
Expand Down
5 changes: 5 additions & 0 deletions x/evm/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package evm

import (
"encoding/json"
"math/big"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/ethermint/x/evm/client/cli"
"github.com/cosmos/ethermint/x/evm/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
Expand Down Expand Up @@ -106,7 +108,10 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
// BeginBlock function for module at start of each block
func (am AppModule) BeginBlock(ctx sdk.Context, bl abci.RequestBeginBlock) {
// Consider removing this when using evm as module without web3 API
bloom := ethtypes.BytesToBloom(am.keeper.bloom.Bytes())
am.keeper.SetBlockBloomMapping(ctx, bloom, bl.Header.GetHeight()-1)
am.keeper.SetBlockHashMapping(ctx, bl.Header.LastBlockId.GetHash(), bl.Header.GetHeight()-1)
am.keeper.bloom = big.NewInt(0)
am.keeper.txCount.reset()
}

Expand Down
38 changes: 38 additions & 0 deletions x/evm/querier.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package evm

import (
"strconv"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ethermint/utils"
Expand All @@ -20,6 +22,8 @@ const (
QueryCode = "code"
QueryNonce = "nonce"
QueryHashToHeight = "hashToHeight"
QueryTxLogs = "txLogs"
QueryLogsBloom = "logsBloom"
)

// NewQuerier is the module level router for state queries
Expand All @@ -40,6 +44,10 @@ func NewQuerier(keeper Keeper) sdk.Querier {
return queryNonce(ctx, path, keeper)
case QueryHashToHeight:
return queryHashToHeight(ctx, path, keeper)
case QueryTxLogs:
return queryTxLogs(ctx, path, keeper)
case QueryLogsBloom:
return queryBlockLogsBloom(ctx, path, keeper)
default:
return nil, sdk.ErrUnknownRequest("unknown query endpoint")
}
Expand Down Expand Up @@ -129,3 +137,33 @@ func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, s

return res, nil
}

func queryBlockLogsBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
num, err := strconv.ParseInt(path[1], 10, 64)
if err != nil {
panic("could not unmarshall block number: " + err.Error())
}

bloom := keeper.GetBlockBloomMapping(ctx, num)

bRes := types.QueryBloomFilter{Bloom: bloom}
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
}

return res, nil
}

func queryTxLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
txHash := ethcmn.HexToHash(path[1])
logs := keeper.GetLogs(ctx, txHash)

bRes := types.QueryTxLogs{Logs: logs}
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
}

return res, nil
}
38 changes: 38 additions & 0 deletions x/evm/types/msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import (
"math/big"
"testing"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/ethermint/crypto"
"github.com/cosmos/ethermint/utils"
"github.com/ethereum/go-ethereum/common"
ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -174,3 +177,38 @@ func TestMarshalAndUnmarshalData(t *testing.T) {
require.NoError(t, err)
require.Equal(t, e, *e2)
}

func TestMarshalAndUnmarshalLogs(t *testing.T) {
var cdc = codec.New()

logs := []*ethtypes.Log{
{
Address: common.BytesToAddress([]byte{0x11}),
TxHash: common.HexToHash("0x01"),
// May need to find workaround since Topics is required to unmarshal from JSON
Topics: []common.Hash{},
Removed: true,
},
{Address: common.BytesToAddress([]byte{0x01, 0x11}), Topics: []common.Hash{}},
}

raw, err := codec.MarshalJSONIndent(cdc, logs)
require.NoError(t, err)

var logs2 []*ethtypes.Log
err = cdc.UnmarshalJSON(raw, &logs2)
require.NoError(t, err)

require.Len(t, logs2, 2)
require.Equal(t, logs[0].Address, logs2[0].Address)
require.Equal(t, logs[0].TxHash, logs2[0].TxHash)
require.True(t, logs[0].Removed)

emptyLogs := []*ethtypes.Log{}

raw, err = codec.MarshalJSONIndent(cdc, emptyLogs)
require.NoError(t, err)

err = cdc.UnmarshalJSON(raw, &logs2)
require.NoError(t, err)
}
24 changes: 24 additions & 0 deletions x/evm/types/querier.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package types

import (
"fmt"

ethtypes "github.com/ethereum/go-ethereum/core/types"
)

// QueryResProtocolVersion is response type for protocol version query
type QueryResProtocolVersion struct {
Version string `json:"version"`
Expand Down Expand Up @@ -53,3 +59,21 @@ type QueryResNonce struct {
func (q QueryResNonce) String() string {
return string(q.Nonce)
}

// QueryTxLogs is response type for tx logs query
type QueryTxLogs struct {
Logs []*ethtypes.Log `json:"logs"`
}

func (q QueryTxLogs) String() string {
return string(fmt.Sprintf("%+v", q.Logs))
}

// QueryBloomFilter is response type for tx logs query
type QueryBloomFilter struct {
Bloom ethtypes.Bloom `json:"bloom"`
}

func (q QueryBloomFilter) String() string {
return string(q.Bloom.Bytes())
}
Loading

0 comments on commit eab81bc

Please sign in to comment.