Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(evm): Augment the Wasm msg handler so that wasm contracts cannot send MsgEthereumTx #2159

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5ce2608
chore(evm): Augment the Wasm msg handler so that wasm contracts canno…
Unique-Divine Jan 9, 2025
3d41225
Merge branch 'main' into ud/ban-wasm-to-evm
Unique-Divine Jan 10, 2025
be62c0a
Merge branch 'main' into ud/ban-wasm-to-evm
Unique-Divine Jan 11, 2025
3b0387a
cleanup
k-yang Jan 13, 2025
d64647e
cleanup
k-yang Jan 13, 2025
8507e06
partial refactor
k-yang Jan 13, 2025
bd6e0d5
fix: compilation errors
k-yang Jan 14, 2025
f44ce1e
refactor: remove CallContract completely
k-yang Jan 14, 2025
837a6a0
refactor: mintOrUnescrowERC20
k-yang Jan 14, 2025
ae354d6
refactor: use simpler evmObj creation pattern
k-yang Jan 14, 2025
e9ab63d
fix: empty gas price in gethcore Message
k-yang Jan 14, 2025
84e4bf6
fix: contract address collision error
k-yang Jan 15, 2025
4cb566c
fix: TestPrecompileSelfCallRevert
k-yang Jan 15, 2025
3b420a3
fix: unnecessary stateDB creation
k-yang Jan 15, 2025
aec26d0
fix: ensure stateDB singleton
k-yang Jan 16, 2025
1962805
fix: balance check
k-yang Jan 16, 2025
4f9d897
fix: balance transfer check had wrong direction
k-yang Jan 16, 2025
6d082cc
fix: deploy contract args
k-yang Jan 16, 2025
4d92d5b
fix: use a new StateDB before CallContractWithInput
k-yang Jan 16, 2025
edf959e
Merge branch 'main' into refactor/evm/call-contract
k-yang Jan 16, 2025
e94abc7
Update CHANGELOG.md
k-yang Jan 16, 2025
e95f809
fix: clear StateDB between txs
k-yang Jan 16, 2025
396afb4
Update account_info_test.go
k-yang Jan 16, 2025
39fdac9
Update account_info_test.go
k-yang Jan 16, 2025
8445c92
Update app/evmante/evmante_can_transfer.go
k-yang Jan 16, 2025
b4f3ac8
fix base fee wei
k-yang Jan 16, 2025
ff27306
refactor: funtoken tests
k-yang Jan 16, 2025
0d26abc
refactor: simplify NewEVM() object creation in tests
k-yang Jan 17, 2025
ab913ba
fix: stateDB should not commit on ERC20 calls
k-yang Jan 17, 2025
ecfd2fe
fix: commit after minting funtokens
k-yang Jan 17, 2025
4117272
fix: erc20 burn was committing when it shouldn't be
k-yang Jan 17, 2025
997a946
fix: linter
k-yang Jan 17, 2025
cfc3bbe
Merge branch 'main' into refactor/evm/call-contract
k-yang Jan 17, 2025
b775d16
Merge branch 'main' into refactor/evm/call-contract
Unique-Divine Jan 17, 2025
a8dd8a6
Merge branch 'refactor/evm/call-contract' of https://github.com/Nibir…
Unique-Divine Jan 17, 2025
deb378f
Merge branch 'main' into ud/ban-wasm-to-evm
Unique-Divine Jan 17, 2025
eb47f0a
refactor: TestCreateFunTokenFromCoin
k-yang Jan 17, 2025
d871c0d
test(app-wasmext): add happy path DispatchMsg example to test
Unique-Divine Jan 17, 2025
755a030
Merge branch 'refactor/evm/call-contract' into ud/ban-wasm-to-evm
Unique-Divine Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ JSON encoding for the `EIP55Addr` struct was not following the Go conventions an
needed to include double quotes around the hexadecimal string.
- [#2156](https://github.com/NibiruChain/nibiru/pull/2156) - test(evm-e2e): add E2E test using the Nibiru Oracle's ChainLink impl
- [#2157](https://github.com/NibiruChain/nibiru/pull/2157) - fix(evm): Fix unit inconsistency related to AuthInfo.Fee and txData.Fee using effective fee
- [#2159](https://github.com/NibiruChain/nibiru/pull/2159) - chore(evm): Augment the Wasm msg handler so that wasm contracts cannot send MsgEthereumTx

#### Nibiru EVM | Before Audit 2 - 2024-12-06

Expand Down
7 changes: 6 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ func init() {
}

// GetWasmOpts build wasm options
func GetWasmOpts(nibiru NibiruApp, appOpts servertypes.AppOptions) []wasmkeeper.Option {
func GetWasmOpts(
nibiru NibiruApp,
appOpts servertypes.AppOptions,
wasmMsgHandlerArgs wasmext.MsgHandlerArgs,
) []wasmkeeper.Option {
var wasmOpts []wasmkeeper.Option
if cast.ToBool(appOpts.Get("telemetry.enabled")) {
wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer))
Expand All @@ -129,6 +133,7 @@ func GetWasmOpts(nibiru NibiruApp, appOpts servertypes.AppOptions) []wasmkeeper.
return append(wasmOpts, wasmext.NibiruWasmOptions(
nibiru.GRPCQueryRouter(),
nibiru.appCodec,
wasmMsgHandlerArgs,
)...)
}

Expand Down
23 changes: 17 additions & 6 deletions app/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import (
// Nibiru Custom Modules

"github.com/NibiruChain/nibiru/v2/app/keepers"
"github.com/NibiruChain/nibiru/v2/app/wasmext"
"github.com/NibiruChain/nibiru/v2/eth"
"github.com/NibiruChain/nibiru/v2/x/common"
"github.com/NibiruChain/nibiru/v2/x/devgas/v1"
Expand Down Expand Up @@ -465,25 +466,35 @@ func (app *NibiruApp) InitKeepers(
panic(err)
}

wmha := wasmext.MsgHandlerArgs{
Router: app.MsgServiceRouter(),
Ics4Wrapper: app.ibcFeeKeeper,
ChannelKeeper: app.ibcKeeper.ChannelKeeper,
CapabilityKeeper: app.ScopedWasmKeeper,
BankKeeper: app.BankKeeper,
Unpacker: appCodec,
PortSource: app.ibcTransferKeeper,
}
app.WasmMsgHandlerArgs = wmha
app.WasmKeeper = wasmkeeper.NewKeeper(
appCodec,
keys[wasmtypes.StoreKey],
app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
distrkeeper.NewQuerier(app.DistrKeeper),
app.ibcFeeKeeper, // ISC4 Wrapper: fee IBC middleware
app.ibcKeeper.ChannelKeeper,
wmha.Ics4Wrapper, // ISC4 Wrapper: fee IBC middleware
wmha.ChannelKeeper,
&app.ibcKeeper.PortKeeper,
app.ScopedWasmKeeper,
app.ibcTransferKeeper,
app.MsgServiceRouter(),
wmha.CapabilityKeeper,
wmha.PortSource,
wmha.Router,
app.GRPCQueryRouter(),
wasmDir,
wasmConfig,
supportedFeatures,
govModuleAddr,
append(GetWasmOpts(*app, appOpts), wasmkeeper.WithWasmEngine(wasmVM))...,
append(GetWasmOpts(*app, appOpts, wmha), wasmkeeper.WithWasmEngine(wasmVM))...,
)

app.WasmClientKeeper = ibcwasmkeeper.NewKeeperWithVM(
Expand Down
5 changes: 4 additions & 1 deletion app/keepers/all_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
// ---------------------------------------------------------------
// Nibiru Custom Modules

"github.com/NibiruChain/nibiru/v2/app/wasmext"
devgaskeeper "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper"
epochskeeper "github.com/NibiruChain/nibiru/v2/x/epochs/keeper"
evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper"
Expand Down Expand Up @@ -67,7 +68,9 @@ type PublicKeepers struct {
EvmKeeper *evmkeeper.Keeper

// WASM keepers
WasmKeeper wasmkeeper.Keeper
WasmKeeper wasmkeeper.Keeper
WasmMsgHandlerArgs wasmext.MsgHandlerArgs

ScopedWasmKeeper capabilitykeeper.ScopedKeeper
WasmClientKeeper ibcwasmkeeper.Keeper
}
4 changes: 2 additions & 2 deletions app/wasmext/stargate_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package wasmext_test
import (
"fmt"
"strings"
"testing"

"github.com/cosmos/gogoproto/proto"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -36,7 +35,8 @@ Given only the `PB_MSG.PACKAGE` and the `PB_MSG.NAME` of either the query
request or response, we should know the `QueryRequest::Stargate.path`
deterministically.
*/
func TestWasmAcceptedStargateQueries(t *testing.T) {
func (s *Suite) TestWasmAcceptedStargateQueries() {
t := s.T()
t.Log("stargateQueryPaths: Add nibiru query paths from GRPC service descriptions")
queryServiceDescriptions := []grpc.ServiceDesc{
epochs.GrpcQueryServiceDesc(),
Expand Down
116 changes: 115 additions & 1 deletion app/wasmext/wasm.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
package wasmext

import (
"github.com/NibiruChain/nibiru/v2/x/evm"

"cosmossdk.io/errors"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasm "github.com/CosmWasm/wasmd/x/wasm/types"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdkcodec "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// NibiruWasmOptions: Wasm Options are extension points to instantiate the Wasm
// keeper with non-default values
func NibiruWasmOptions(
grpcQueryRouter *baseapp.GRPCQueryRouter,
appCodec codec.Codec,
msgHandlerArgs MsgHandlerArgs,
) []wasmkeeper.Option {
wasmQueryOption := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{
Stargate: wasmkeeper.AcceptListStargateQuerier(
Expand All @@ -20,5 +29,110 @@
),
})

return []wasmkeeper.Option{wasmQueryOption}
wasmMsgHandlerOption := wasmkeeper.WithMessageHandler(WasmMessageHandler(msgHandlerArgs))

return []wasmkeeper.Option{
wasmQueryOption,
wasmMsgHandlerOption,
}
}

func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (*sdk.Result, error) {
if err := msg.ValidateBasic(); err != nil {
return nil, err
}

Check warning on line 43 in app/wasmext/wasm.go

View check run for this annotation

Codecov / codecov/patch

app/wasmext/wasm.go#L42-L43

Added lines #L42 - L43 were not covered by tests
Comment on lines +41 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add test coverage for validation error handling

The error handling when msg.ValidateBasic() fails in the handleSdkMessage function is not covered by tests. Consider adding a test case to ensure that messages failing basic validation are properly rejected.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 42-43: app/wasmext/wasm.go#L42-L43
Added lines #L42 - L43 were not covered by tests


// make sure this account can send it
for _, acct := range msg.GetSigners() {
if !acct.Equals(contractAddr) {
return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
}

Check warning on line 49 in app/wasmext/wasm.go

View check run for this annotation

Codecov / codecov/patch

app/wasmext/wasm.go#L48-L49

Added lines #L48 - L49 were not covered by tests
}

msgTypeUrl := sdk.MsgTypeURL(msg)
if msgTypeUrl == sdk.MsgTypeURL(new(evm.MsgEthereumTx)) {
return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "Wasm VM to EVM call pattern is not yet supported")
}

// find the handler and execute it
if handler := h.router.Handler(msg); handler != nil {
// ADR 031 request type routing
msgResult, err := handler(ctx, msg)
return msgResult, err
}

Check warning on line 62 in app/wasmext/wasm.go

View check run for this annotation

Codecov / codecov/patch

app/wasmext/wasm.go#L58-L62

Added lines #L58 - L62 were not covered by tests
Comment on lines +58 to +62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add test coverage for message routing logic

The message handling within handleSdkMessage, especially the routing of messages using the handler, lacks test coverage. Adding tests to verify that messages are correctly routed and processed will improve reliability.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 58-62: app/wasmext/wasm.go#L58-L62
Added lines #L58 - L62 were not covered by tests

// legacy sdk.Msg routing
// Assuming that the app developer has migrated all their Msgs to
// proto messages and has registered all `Msg services`, then this
// path should never be called, because all those Msgs should be
// registered within the `msgServiceRouter` already.
return nil, errors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg)

Check warning on line 68 in app/wasmext/wasm.go

View check run for this annotation

Codecov / codecov/patch

app/wasmext/wasm.go#L68

Added line #L68 was not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add test for unknown message types

The error path when a message cannot be routed (unknown request) is not covered by tests. Include a test case to ensure that appropriate errors are returned for unrecognized messages.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 68-68: app/wasmext/wasm.go#L68
Added line #L68 was not covered by tests

}

type MsgHandlerArgs struct {
Router MessageRouter
Ics4Wrapper wasm.ICS4Wrapper
ChannelKeeper wasm.ChannelKeeper
CapabilityKeeper wasm.CapabilityKeeper
BankKeeper wasm.Burner
Unpacker sdkcodec.AnyUnpacker
PortSource wasm.ICS20TransferPortSource
}

// SDKMessageHandler can handles messages that can be encoded into sdk.Message types and routed.
type SDKMessageHandler struct {
router MessageRouter
encoders msgEncoder
}

// MessageRouter ADR 031 request type routing
type MessageRouter interface {
Handler(msg sdk.Msg) baseapp.MsgServiceHandler
}

// msgEncoder is an extension point to customize encodings
type msgEncoder interface {
// Encode converts wasmvm message to n cosmos message types
Encode(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Msg, error)
}

// WasmMessageHandler is a replacement constructor for
// [wasmkeeper.NewDefaultMessageHandler] inside of [wasmkeeper.NewKeeper].
func WasmMessageHandler(
args MsgHandlerArgs,
) wasmkeeper.Messenger {
encoders := wasmkeeper.DefaultEncoders(args.Unpacker, args.PortSource)
return wasmkeeper.NewMessageHandlerChain(
NewSDKMessageHandler(args.Router, encoders),
wasmkeeper.NewIBCRawPacketHandler(args.Ics4Wrapper, args.ChannelKeeper, args.CapabilityKeeper),
wasmkeeper.NewBurnCoinMessageHandler(args.BankKeeper),
)
}

func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder) SDKMessageHandler {
return SDKMessageHandler{
router: router,
encoders: encoders,
}
}

func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg)
if err != nil {
return nil, nil, err
}

Check warning on line 122 in app/wasmext/wasm.go

View check run for this annotation

Codecov / codecov/patch

app/wasmext/wasm.go#L121-L122

Added lines #L121 - L122 were not covered by tests
for _, sdkMsg := range sdkMsgs {
res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg)
if err != nil {
return nil, nil, err
}
// append data
data = append(data, res.Data)
// append events
sdkEvents := make([]sdk.Event, len(res.Events))
for i := range res.Events {
sdkEvents[i] = sdk.Event(res.Events[i])
}
events = append(events, sdkEvents...)

Check warning on line 135 in app/wasmext/wasm.go

View check run for this annotation

Codecov / codecov/patch

app/wasmext/wasm.go#L129-L135

Added lines #L129 - L135 were not covered by tests
Comment on lines +129 to +135
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add test coverage for event and data aggregation

The code segment that appends res.Data and processes events is not covered by tests. Ensure that tests cover the aggregation of data and events from dispatched messages.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 129-135: app/wasmext/wasm.go#L129-L135
Added lines #L129 - L135 were not covered by tests

}
return

Check warning on line 137 in app/wasmext/wasm.go

View check run for this annotation

Codecov / codecov/patch

app/wasmext/wasm.go#L137

Added line #L137 was not covered by tests
}
73 changes: 73 additions & 0 deletions app/wasmext/wasmext_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package wasmext_test

import (
"math/big"
"testing"

wasmvm "github.com/CosmWasm/wasmvm/types"
sdkcodec "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"

"github.com/NibiruChain/nibiru/v2/app/wasmext"
"github.com/NibiruChain/nibiru/v2/x/evm"
"github.com/NibiruChain/nibiru/v2/x/evm/evmtest"
)

type Suite struct {
suite.Suite
}

func TestWasmExtSuite(t *testing.T) {
suite.Run(t, new(Suite))
}

// WasmVM to EVM call pattern is not yet supported. This test verifies the
// Nibiru's [wasmkeeper.Option] function as expected.
func (s *Suite) TestEvmFilter() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add here some happy paths too to ensure the functionality not regressed?

deps := evmtest.NewTestDeps()
// wk := wasmkeeper.NewDefaultPermissionKeeper(deps.App.WasmKeeper)
wasmMsgHandler := wasmext.WasmMessageHandler(deps.App.WasmMsgHandlerArgs)

s.T().Log("Create a valid Ethereum tx msg")

to := evmtest.NewEthPrivAcc()
ethTxMsg, err := evmtest.TxTransferWei{
Deps: &deps,
To: to.EthAddr,
AmountWei: evm.NativeToWei(big.NewInt(420)),
}.Build()
s.NoError(err)

s.T().Log("Validate Eth tx msg proto encoding as wasmvm.StargateMsg")
wasmContractAddr := deps.Sender.NibiruAddr
protoValueBz, err := deps.EncCfg.Codec.Marshal(ethTxMsg)
s.Require().NoError(err, "expect ethTxMsg to proto marshal", protoValueBz)

_, ok := deps.EncCfg.Codec.(sdkcodec.AnyUnpacker)
s.Require().True(ok, "codec must be an AnyUnpacker")

pbAny, err := sdkcodec.NewAnyWithValue(ethTxMsg)
s.NoError(err)
pbAnyBz, err := pbAny.Marshal()
s.NoError(err, pbAnyBz)

var sdkMsg sdk.Msg
err = deps.EncCfg.Codec.UnpackAny(pbAny, &sdkMsg)
s.Require().NoError(err)
s.Equal("/eth.evm.v1.MsgEthereumTx", sdk.MsgTypeURL(sdkMsg))

s.T().Log("Dispatch the Eth tx msg from Wasm (unsuccessfully)")
_, _, err = wasmMsgHandler.DispatchMsg(
deps.Ctx,
wasmContractAddr,
"ibcport-unused",
wasmvm.CosmosMsg{
Stargate: &wasmvm.StargateMsg{
TypeURL: sdk.MsgTypeURL(ethTxMsg),
Value: protoValueBz,
},
},
)
s.Require().ErrorContains(err, "Wasm VM to EVM call pattern is not yet supported")
}
Loading