Skip to content

Commit

Permalink
Merge branch 'main' into feat/evm/oracle-precompile
Browse files Browse the repository at this point in the history
  • Loading branch information
Unique-Divine authored Oct 3, 2024
2 parents 2584155 + 1588744 commit 51f49fe
Show file tree
Hide file tree
Showing 27 changed files with 765 additions and 1,041 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#2039](https://github.com/NibiruChain/nibiru/pull/2039) - refactor(rpc-backend): remove unnecessary interface code
- [#2044](https://github.com/NibiruChain/nibiru/pull/2044) - feat(evm): evm tx indexer service implemented
- [#2045](https://github.com/NibiruChain/nibiru/pull/2045) - test(evm): backend tests with test network and real txs
- [#2053](https://github.com/NibiruChain/nibiru/pull/2053) - refactor(evm): converted untyped event to typed and cleaned up
- [#2054](https://github.com/NibiruChain/nibiru/pull/2054) - feat(evm-precompile): Precompile for one-way EVM calls to invoke/execute Wasm contracts.
- [#2060](https://github.com/NibiruChain/nibiru/pull/2060) - fix(evm-precompiles): add assertNumArgs validation
- [#2056](https://github.com/NibiruChain/nibiru/pull/2056) - feat(evm): add oracle precompile

#### Dapp modules: perp, spot, oracle, etc
Expand Down
25 changes: 9 additions & 16 deletions app/evmante/evmante_emit_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,17 @@ func (eeed EthEmitEventDecorator) AnteHandle(
msg, (*evm.MsgEthereumTx)(nil),
)
}

// emit ethereum tx hash as an event so that it can be indexed by
// Tendermint for query purposes it's emitted in ante handler, so we can
// query failed transaction (out of block gas limit).
// Untyped event "pending_ethereum_tx" is emitted for then indexing purposes.
// Tendermint tx_search can only search the untyped events.
// TxHash and TxIndex values are exposed in the ante handler (before the actual tx execution)
// to allow searching for txs which are failed due to "out of block gas limit" error.
ctx.EventManager().EmitEvent(
sdk.NewEvent(
evm.EventTypeEthereumTx,
sdk.NewAttribute(evm.AttributeKeyEthereumTxHash, msgEthTx.Hash),
sdk.NewAttribute(
evm.AttributeKeyTxIndex, strconv.FormatUint(txIndex+uint64(i),
10,
),
), // #nosec G701
// TODO: fix: It's odd that each event is emitted twice. Migrate to typed
// events and change EVM indexer to align.
// sdk.NewAttribute("emitted_from", "EthEmitEventDecorator"),
))
evm.PendingEthereumTxEvent,
sdk.NewAttribute(evm.PendingEthereumTxEventAttrEthHash, msgEthTx.Hash),
sdk.NewAttribute(evm.PendingEthereumTxEventAttrIndex, strconv.FormatUint(txIndex+uint64(i), 10)),
),
)
}

return next(ctx, tx, simulate)
}
6 changes: 3 additions & 3 deletions app/evmante/evmante_emit_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,19 @@ func (s *TestSuite) TestEthEmitEventDecorator() {

s.Require().Greater(len(events), 0)
event := events[len(events)-1]
s.Require().Equal(evm.EventTypeEthereumTx, event.Type)
s.Require().Equal(evm.PendingEthereumTxEvent, event.Type)

// Convert tx to msg to get hash
txMsg, ok := tx.GetMsgs()[0].(*evm.MsgEthereumTx)
s.Require().True(ok)

// TX hash attr must present
attr, ok := event.GetAttribute(evm.AttributeKeyEthereumTxHash)
attr, ok := event.GetAttribute(evm.PendingEthereumTxEventAttrEthHash)
s.Require().True(ok, "tx hash attribute not found")
s.Require().Equal(txMsg.Hash, attr.Value)

// TX index attr must present
attr, ok = event.GetAttribute(evm.AttributeKeyTxIndex)
attr, ok = event.GetAttribute(evm.PendingEthereumTxEventAttrIndex)
s.Require().True(ok, "tx index attribute not found")
s.Require().Equal("0", attr.Value)
})
Expand Down
2 changes: 1 addition & 1 deletion eth/indexer/evm_tx_indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func NewEVMTxIndexer(db dbm.DB, logger log.Logger, clientCtx client.Context) *EV
// - Iterates over all the Txs in Block
// - Parses eth Tx infos from cosmos-sdk events for every TxResult
// - Iterates over all the messages of the Tx
// - Builds and stores a indexer.TxResult based on parsed events for every message
// - Builds and stores indexer.TxResult based on parsed events for every message
func (indexer *EVMTxIndexer) IndexBlock(block *tmtypes.Block, txResults []*abci.ResponseDeliverTx) error {
height := block.Header.Height

Expand Down
83 changes: 42 additions & 41 deletions eth/indexer/evm_tx_indexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"math/big"
"testing"

"cosmossdk.io/simapp/params"
dbm "github.com/cometbft/cometbft-db"
abci "github.com/cometbft/cometbft/abci/types"
tmlog "github.com/cometbft/cometbft/libs/log"
Expand All @@ -17,7 +16,6 @@ import (
"github.com/NibiruChain/nibiru/v2/app"
"github.com/NibiruChain/nibiru/v2/eth"
"github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1"
evmenc "github.com/NibiruChain/nibiru/v2/eth/encoding"
"github.com/NibiruChain/nibiru/v2/eth/indexer"
"github.com/NibiruChain/nibiru/v2/x/evm"
evmtest "github.com/NibiruChain/nibiru/v2/x/evm/evmtest"
Expand Down Expand Up @@ -50,16 +48,16 @@ func TestEVMTxIndexer(t *testing.T) {
WithCodec(encCfg.Codec)

// build cosmos-sdk wrapper tx
tmTx, err := tx.BuildTx(clientCtx.TxConfig.NewTxBuilder(), eth.EthBaseDenom)
validEVMTx, err := tx.BuildTx(clientCtx.TxConfig.NewTxBuilder(), eth.EthBaseDenom)
require.NoError(t, err)
txBz, err := clientCtx.TxConfig.TxEncoder()(tmTx)
validEVMTxBz, err := clientCtx.TxConfig.TxEncoder()(validEVMTx)
require.NoError(t, err)

// build an invalid wrapper tx
builder := clientCtx.TxConfig.NewTxBuilder()
require.NoError(t, builder.SetMsgs(tx))
tmTx2 := builder.GetTx()
txBz2, err := clientCtx.TxConfig.TxEncoder()(tmTx2)
invalidTx := builder.GetTx()
invalidTxBz, err := clientCtx.TxConfig.TxEncoder()(invalidTx)
require.NoError(t, err)

testCases := []struct {
Expand All @@ -69,50 +67,58 @@ func TestEVMTxIndexer(t *testing.T) {
expSuccess bool
}{
{
"success, format 1",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}},
"happy, only pending_ethereum_tx presents",
&tmtypes.Block{
Header: tmtypes.Header{Height: 1},
Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}},
},
[]*abci.ResponseDeliverTx{
{
Code: 0,
Events: []abci.Event{
{Type: evm.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: "ethereumTxHash", Value: txHash.Hex()},
{Key: "txIndex", Value: "0"},
{Key: "amount", Value: "1000"},
{Key: "txGasUsed", Value: "21000"},
{Key: "txHash", Value: ""},
{Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"},
}},
{
Type: evm.PendingEthereumTxEvent,
Attributes: []abci.EventAttribute{
{Key: evm.PendingEthereumTxEventAttrEthHash, Value: txHash.Hex()},
{Key: evm.PendingEthereumTxEventAttrIndex, Value: "0"},
},
},
},
},
},
true,
},
{
"success, format 2",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}},
"happy: code 0, pending_ethereum_tx and typed EventEthereumTx present",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}},
[]*abci.ResponseDeliverTx{
{
Code: 0,
Events: []abci.Event{
{Type: evm.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: "ethereumTxHash", Value: txHash.Hex()},
{Key: "txIndex", Value: "0"},
}},
{Type: evm.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: "amount", Value: "1000"},
{Key: "txGasUsed", Value: "21000"},
{Key: "txHash", Value: "14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57"},
{Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"},
}},
{
Type: evm.PendingEthereumTxEvent,
Attributes: []abci.EventAttribute{
{Key: evm.PendingEthereumTxEventAttrEthHash, Value: txHash.Hex()},
{Key: evm.PendingEthereumTxEventAttrIndex, Value: "0"},
},
},
{
Type: evm.TypeUrlEventEthereumTx,
Attributes: []abci.EventAttribute{
{Key: "amount", Value: `"1000"`},
{Key: "gas_used", Value: `"21000"`},
{Key: "index", Value: `"0"`},
{Key: "hash", Value: `"14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57"`},
},
},
},
},
},
true,
},
{
"success, exceed block gas limit",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}},
"happy: code 11, exceed block gas limit",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}},
[]*abci.ResponseDeliverTx{
{
Code: 11,
Expand All @@ -123,8 +129,8 @@ func TestEVMTxIndexer(t *testing.T) {
true,
},
{
"fail, failed eth tx",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}},
"sad: failed eth tx",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}},
[]*abci.ResponseDeliverTx{
{
Code: 15,
Expand All @@ -135,8 +141,8 @@ func TestEVMTxIndexer(t *testing.T) {
false,
},
{
"fail, invalid events",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}},
"sad: invalid events",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}},
[]*abci.ResponseDeliverTx{
{
Code: 0,
Expand All @@ -146,8 +152,8 @@ func TestEVMTxIndexer(t *testing.T) {
false,
},
{
"fail, not eth tx",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz2}}},
"sad: not eth tx",
&tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{invalidTxBz}}},
[]*abci.ResponseDeliverTx{
{
Code: 0,
Expand Down Expand Up @@ -192,8 +198,3 @@ func TestEVMTxIndexer(t *testing.T) {
})
}
}

// MakeEncodingConfig creates the EncodingConfig
func MakeEncodingConfig() params.EncodingConfig {
return evmenc.MakeConfig(app.ModuleBasics)
}
2 changes: 1 addition & 1 deletion eth/rpc/backend/account_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func generateStorageKey(key gethcommon.Address, slot uint64) string {
// Concatenate key and slot
data := append(keyBytes, slotBytes...)

// Hash the data using Keccak256
// Compute the data hash using Keccak256
hash := sha3.NewLegacyKeccak256()
hash.Write(data)
return gethcommon.BytesToHash(hash.Sum(nil)).Hex()
Expand Down
5 changes: 4 additions & 1 deletion eth/rpc/backend/backend_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ func WaitForReceipt(s *BackendSuite, txHash gethcommon.Hash) (*big.Int, *gethcom

for {
receipt, err := s.backend.GetTransactionReceipt(txHash)
if err == nil {
if err != nil {
return nil, nil
}
if receipt != nil {
return receipt.BlockNumber, &receipt.BlockHash
}
select {
Expand Down
16 changes: 5 additions & 11 deletions eth/rpc/backend/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"math"
"math/big"
"strconv"
"strings"

tmrpcclient "github.com/cometbft/cometbft/rpc/client"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
Expand Down Expand Up @@ -334,18 +333,13 @@ func (b *Backend) BlockBloom(blockRes *tmrpctypes.ResultBlockResults) (gethcore.
if event.Type != msgType {
continue
}

for _, attr := range event.Attributes {
if attr.Key == evm.AttributeKeyEthereumBloom {
return gethcore.BytesToBloom(
hexutils.HexToBytes( // Bloom stores hex bytes
strings.ReplaceAll(attr.Value, `"`, ""), // Unquote typed event
),
), nil
}
blockBloomEvent, err := evm.EventBlockBloomFromABCIEvent(event)
if err != nil {
continue
}
return gethcore.BytesToBloom(hexutils.HexToBytes(blockBloomEvent.Bloom)), nil
}
return gethcore.Bloom{}, errors.New("block bloom event is not found")
return gethcore.Bloom{}, errors.New(msgType + " not found in end block results")
}

// RPCBlockFromTendermintBlock returns a JSON-RPC compatible Ethereum block from a
Expand Down
10 changes: 6 additions & 4 deletions eth/rpc/backend/tx_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"math/big"

errorsmod "cosmossdk.io/errors"

tmrpcclient "github.com/cometbft/cometbft/rpc/client"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -307,7 +306,8 @@ func (b *Backend) GetTxByEthHash(hash gethcommon.Hash) (*eth.TxResult, error) {
}

// fallback to tendermint tx evmTxIndexer
query := fmt.Sprintf("%s.%s='%s'", evm.TypeMsgEthereumTx, evm.AttributeKeyEthereumTxHash, hash.Hex())
query := fmt.Sprintf("%s.%s='%s'", evm.PendingEthereumTxEvent, evm.PendingEthereumTxEventAttrEthHash, hash.Hex())

txResult, err := b.queryTendermintTxIndexer(query, func(txs *rpc.ParsedTxs) *rpc.ParsedTx {
return txs.GetTxByHash(hash)
})
Expand All @@ -326,8 +326,10 @@ func (b *Backend) GetTxByTxIndex(height int64, index uint) (*eth.TxResult, error

// fallback to tendermint tx evmTxIndexer
query := fmt.Sprintf("tx.height=%d AND %s.%s=%d",
height, evm.TypeMsgEthereumTx,
evm.AttributeKeyTxIndex, index,
height,
evm.PendingEthereumTxEvent,
evm.PendingEthereumTxEventAttrIndex,
index,
)
txResult, err := b.queryTendermintTxIndexer(query, func(txs *rpc.ParsedTxs) *rpc.ParsedTx {
return txs.GetTxByTxIndex(int(index)) // #nosec G701 -- checked for int overflow already
Expand Down
29 changes: 15 additions & 14 deletions eth/rpc/backend/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"sort"
"strings"

"cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

Expand Down Expand Up @@ -212,7 +214,7 @@ func (b *Backend) retrieveEVMTxFeesFromBlock(
func AllTxLogsFromEvents(events []abci.Event) ([][]*gethcore.Log, error) {
allLogs := make([][]*gethcore.Log, 0, 4)
for _, event := range events {
if event.Type != evm.EventTypeTxLog {
if event.Type != proto.MessageName(new(evm.EventTxLog)) {
continue
}

Expand All @@ -229,7 +231,7 @@ func AllTxLogsFromEvents(events []abci.Event) ([][]*gethcore.Log, error) {
// TxLogsFromEvents parses ethereum logs from cosmos events for specific msg index
func TxLogsFromEvents(events []abci.Event, msgIndex int) ([]*gethcore.Log, error) {
for _, event := range events {
if event.Type != evm.EventTypeTxLog {
if event.Type != proto.MessageName(new(evm.EventTxLog)) {
continue
}

Expand All @@ -246,20 +248,19 @@ func TxLogsFromEvents(events []abci.Event, msgIndex int) ([]*gethcore.Log, error

// ParseTxLogsFromEvent parse tx logs from one event
func ParseTxLogsFromEvent(event abci.Event) ([]*gethcore.Log, error) {
logs := make([]*evm.Log, 0, len(event.Attributes))
for _, attr := range event.Attributes {
if attr.Key != evm.AttributeKeyTxLog {
continue
}

var log evm.Log
if err := json.Unmarshal([]byte(attr.Value), &log); err != nil {
return nil, err
eventTxLog, err := evm.EventTxLogFromABCIEvent(event)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse event tx log")
}
var evmLogs []*evm.Log
for _, logString := range eventTxLog.TxLogs {
var evmLog evm.Log
if err = json.Unmarshal([]byte(logString), &evmLog); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal event tx log")
}

logs = append(logs, &log)
evmLogs = append(evmLogs, &evmLog)
}
return evm.LogsToEthereum(logs), nil
return evm.LogsToEthereum(evmLogs), nil
}

// ShouldIgnoreGasUsed returns true if the gasUsed in result should be ignored
Expand Down
Loading

0 comments on commit 51f49fe

Please sign in to comment.