Skip to content

Commit a58899e

Browse files
committed
Merge branch 'main' into releases/v2.0.0
2 parents 63b2be4 + 0e3ab43 commit a58899e

File tree

19 files changed

+465
-33
lines changed

19 files changed

+465
-33
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,18 @@ JSON encoding for the `EIP55Addr` struct was not following the Go conventions an
7575
needed to include double quotes around the hexadecimal string.
7676
- [#2156](https://github.com/NibiruChain/nibiru/pull/2156) - test(evm-e2e): add E2E test using the Nibiru Oracle's ChainLink impl
7777
- [#2157](https://github.com/NibiruChain/nibiru/pull/2157) - fix(evm): Fix unit inconsistency related to AuthInfo.Fee and txData.Fee using effective fee
78+
- [#2159](https://github.com/NibiruChain/nibiru/pull/2159) - chore(evm): Augment the Wasm msg handler so that wasm contracts cannot send MsgEthereumTx
7879
- [#2160](https://github.com/NibiruChain/nibiru/pull/2160) - fix(evm-precompile): use bank.MsgServer Send in precompile IFunToken.bankMsgSend
7980
- [#2161](https://github.com/NibiruChain/nibiru/pull/2161) - fix(evm): added tx logs events to the funtoken related txs
8081
- [#2162](https://github.com/NibiruChain/nibiru/pull/2162) - test(testutil): try retrying for 'panic: pebbledb: closed'
8182
- [#2167](https://github.com/NibiruChain/nibiru/pull/2167) - refactor(evm): removed blockGasUsed transient variable
8283
- [#2168](https://github.com/NibiruChain/nibiru/pull/2168) - chore(evm-solidity): Move unrelated docs, gen-embeds, and add Solidity docs
8384
- [#2165](https://github.com/NibiruChain/nibiru/pull/2165) - fix(evm): use Singleton StateDB pattern for EVM txs
8485
- [#2169](https://github.com/NibiruChain/nibiru/pull/2169) - fix(evm): Better handling erc20 metadata
86+
- [#2170](https://github.com/NibiruChain/nibiru/pull/2170) - chore: Remove redundant allowUnprotectedTxs
87+
- [#2172](https://github.com/NibiruChain/nibiru/pull/2172) - chore: close iterator in IterateEpochInfo
88+
- [#2173](https://github.com/NibiruChain/nibiru/pull/2173) - fix(evm): clear `StateDB` between calls
89+
8590
#### Nibiru EVM | Before Audit 2 - 2024-12-06
8691

8792
The codebase went through a third-party [Code4rena

app/app.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ func init() {
113113
}
114114

115115
// GetWasmOpts build wasm options
116-
func GetWasmOpts(nibiru NibiruApp, appOpts servertypes.AppOptions) []wasmkeeper.Option {
116+
func GetWasmOpts(
117+
nibiru NibiruApp,
118+
appOpts servertypes.AppOptions,
119+
wasmMsgHandlerArgs wasmext.MsgHandlerArgs,
120+
) []wasmkeeper.Option {
117121
var wasmOpts []wasmkeeper.Option
118122
if cast.ToBool(appOpts.Get("telemetry.enabled")) {
119123
wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer))
@@ -122,6 +126,7 @@ func GetWasmOpts(nibiru NibiruApp, appOpts servertypes.AppOptions) []wasmkeeper.
122126
return append(wasmOpts, wasmext.NibiruWasmOptions(
123127
nibiru.GRPCQueryRouter(),
124128
nibiru.appCodec,
129+
wasmMsgHandlerArgs,
125130
)...)
126131
}
127132

app/evmante/evmante_sigverify.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ func (esvd EthSigVerificationDecorator) AnteHandle(
4747
)
4848
}
4949

50-
allowUnprotectedTxs := false
5150
ethTx := msgEthTx.AsTransaction()
52-
if !allowUnprotectedTxs && !ethTx.Protected() {
51+
if !ethTx.Protected() {
5352
return ctx, errors.Wrapf(
5453
sdkerrors.ErrNotSupported,
5554
"rejected unprotected Ethereum transaction. "+

app/keepers.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ import (
104104
// Nibiru Custom Modules
105105

106106
"github.com/NibiruChain/nibiru/v2/app/keepers"
107+
"github.com/NibiruChain/nibiru/v2/app/wasmext"
107108
"github.com/NibiruChain/nibiru/v2/eth"
108109
"github.com/NibiruChain/nibiru/v2/x/common"
109110
"github.com/NibiruChain/nibiru/v2/x/devgas/v1"
@@ -451,25 +452,36 @@ func (app *NibiruApp) InitKeepers(
451452
// For example, if there are bindings for the x/inflation module, then the app
452453
// passed to GetWasmOpts must already have a non-nil InflationKeeper.
453454
supportedFeatures := strings.Join(wasmdapp.AllCapabilities(), ",")
455+
456+
wmha := wasmext.MsgHandlerArgs{
457+
Router: app.MsgServiceRouter(),
458+
Ics4Wrapper: app.ibcFeeKeeper,
459+
ChannelKeeper: app.ibcKeeper.ChannelKeeper,
460+
CapabilityKeeper: app.ScopedWasmKeeper,
461+
BankKeeper: app.BankKeeper,
462+
Unpacker: appCodec,
463+
PortSource: app.ibcTransferKeeper,
464+
}
465+
app.WasmMsgHandlerArgs = wmha
454466
app.WasmKeeper = wasmkeeper.NewKeeper(
455467
appCodec,
456468
keys[wasmtypes.StoreKey],
457469
app.AccountKeeper,
458470
app.BankKeeper,
459471
app.StakingKeeper,
460472
distrkeeper.NewQuerier(app.DistrKeeper),
461-
app.ibcFeeKeeper, // ISC4 Wrapper: fee IBC middleware
462-
app.ibcKeeper.ChannelKeeper,
473+
wmha.Ics4Wrapper, // ISC4 Wrapper: fee IBC middleware
474+
wmha.ChannelKeeper,
463475
&app.ibcKeeper.PortKeeper,
464-
app.ScopedWasmKeeper,
465-
app.ibcTransferKeeper,
466-
app.MsgServiceRouter(),
476+
wmha.CapabilityKeeper,
477+
wmha.PortSource,
478+
wmha.Router,
467479
app.GRPCQueryRouter(),
468480
wasmDir,
469481
wasmConfig,
470482
supportedFeatures,
471483
govModuleAddr,
472-
GetWasmOpts(*app, appOpts)...,
484+
GetWasmOpts(*app, appOpts, wmha)...,
473485
)
474486

475487
// DevGas uses WasmKeeper

app/keepers/all_keepers.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
// ---------------------------------------------------------------
2222
// Nibiru Custom Modules
2323

24+
"github.com/NibiruChain/nibiru/v2/app/wasmext"
2425
devgaskeeper "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper"
2526
epochskeeper "github.com/NibiruChain/nibiru/v2/x/epochs/keeper"
2627
evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper"
@@ -66,6 +67,8 @@ type PublicKeepers struct {
6667
EvmKeeper *evmkeeper.Keeper
6768

6869
// WASM keepers
69-
WasmKeeper wasmkeeper.Keeper
70+
WasmKeeper wasmkeeper.Keeper
71+
WasmMsgHandlerArgs wasmext.MsgHandlerArgs
72+
7073
ScopedWasmKeeper capabilitykeeper.ScopedKeeper
7174
}

app/wasmext/stargate_query_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package wasmext_test
33
import (
44
"fmt"
55
"strings"
6-
"testing"
76

87
"github.com/cosmos/gogoproto/proto"
98
"github.com/stretchr/testify/assert"
@@ -36,7 +35,8 @@ Given only the `PB_MSG.PACKAGE` and the `PB_MSG.NAME` of either the query
3635
request or response, we should know the `QueryRequest::Stargate.path`
3736
deterministically.
3837
*/
39-
func TestWasmAcceptedStargateQueries(t *testing.T) {
38+
func (s *Suite) TestWasmAcceptedStargateQueries() {
39+
t := s.T()
4040
t.Log("stargateQueryPaths: Add nibiru query paths from GRPC service descriptions")
4141
queryServiceDescriptions := []grpc.ServiceDesc{
4242
epochs.GrpcQueryServiceDesc(),

app/wasmext/wasm.go

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
package wasmext
22

33
import (
4+
"github.com/NibiruChain/nibiru/v2/x/evm"
5+
6+
"cosmossdk.io/errors"
47
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
8+
wasm "github.com/CosmWasm/wasmd/x/wasm/types"
9+
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
510
"github.com/cosmos/cosmos-sdk/baseapp"
611
"github.com/cosmos/cosmos-sdk/codec"
12+
sdkcodec "github.com/cosmos/cosmos-sdk/codec/types"
13+
sdk "github.com/cosmos/cosmos-sdk/types"
14+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
715
)
816

917
// NibiruWasmOptions: Wasm Options are extension points to instantiate the Wasm
1018
// keeper with non-default values
1119
func NibiruWasmOptions(
1220
grpcQueryRouter *baseapp.GRPCQueryRouter,
1321
appCodec codec.Codec,
22+
msgHandlerArgs MsgHandlerArgs,
1423
) []wasmkeeper.Option {
1524
wasmQueryOption := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{
1625
Stargate: wasmkeeper.AcceptListStargateQuerier(
@@ -20,5 +29,110 @@ func NibiruWasmOptions(
2029
),
2130
})
2231

23-
return []wasmkeeper.Option{wasmQueryOption}
32+
wasmMsgHandlerOption := wasmkeeper.WithMessageHandler(WasmMessageHandler(msgHandlerArgs))
33+
34+
return []wasmkeeper.Option{
35+
wasmQueryOption,
36+
wasmMsgHandlerOption,
37+
}
38+
}
39+
40+
func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (*sdk.Result, error) {
41+
if err := msg.ValidateBasic(); err != nil {
42+
return nil, err
43+
}
44+
45+
// make sure this account can send it
46+
for _, acct := range msg.GetSigners() {
47+
if !acct.Equals(contractAddr) {
48+
return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
49+
}
50+
}
51+
52+
msgTypeUrl := sdk.MsgTypeURL(msg)
53+
if msgTypeUrl == sdk.MsgTypeURL(new(evm.MsgEthereumTx)) {
54+
return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "Wasm VM to EVM call pattern is not yet supported")
55+
}
56+
57+
// find the handler and execute it
58+
if handler := h.router.Handler(msg); handler != nil {
59+
// ADR 031 request type routing
60+
msgResult, err := handler(ctx, msg)
61+
return msgResult, err
62+
}
63+
// legacy sdk.Msg routing
64+
// Assuming that the app developer has migrated all their Msgs to
65+
// proto messages and has registered all `Msg services`, then this
66+
// path should never be called, because all those Msgs should be
67+
// registered within the `msgServiceRouter` already.
68+
return nil, errors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg)
69+
}
70+
71+
type MsgHandlerArgs struct {
72+
Router MessageRouter
73+
Ics4Wrapper wasm.ICS4Wrapper
74+
ChannelKeeper wasm.ChannelKeeper
75+
CapabilityKeeper wasm.CapabilityKeeper
76+
BankKeeper wasm.Burner
77+
Unpacker sdkcodec.AnyUnpacker
78+
PortSource wasm.ICS20TransferPortSource
79+
}
80+
81+
// SDKMessageHandler can handles messages that can be encoded into sdk.Message types and routed.
82+
type SDKMessageHandler struct {
83+
router MessageRouter
84+
encoders msgEncoder
85+
}
86+
87+
// MessageRouter ADR 031 request type routing
88+
type MessageRouter interface {
89+
Handler(msg sdk.Msg) baseapp.MsgServiceHandler
90+
}
91+
92+
// msgEncoder is an extension point to customize encodings
93+
type msgEncoder interface {
94+
// Encode converts wasmvm message to n cosmos message types
95+
Encode(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Msg, error)
96+
}
97+
98+
// WasmMessageHandler is a replacement constructor for
99+
// [wasmkeeper.NewDefaultMessageHandler] inside of [wasmkeeper.NewKeeper].
100+
func WasmMessageHandler(
101+
args MsgHandlerArgs,
102+
) wasmkeeper.Messenger {
103+
encoders := wasmkeeper.DefaultEncoders(args.Unpacker, args.PortSource)
104+
return wasmkeeper.NewMessageHandlerChain(
105+
NewSDKMessageHandler(args.Router, encoders),
106+
wasmkeeper.NewIBCRawPacketHandler(args.Ics4Wrapper, args.ChannelKeeper, args.CapabilityKeeper),
107+
wasmkeeper.NewBurnCoinMessageHandler(args.BankKeeper),
108+
)
109+
}
110+
111+
func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder) SDKMessageHandler {
112+
return SDKMessageHandler{
113+
router: router,
114+
encoders: encoders,
115+
}
116+
}
117+
118+
func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
119+
sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg)
120+
if err != nil {
121+
return nil, nil, err
122+
}
123+
for _, sdkMsg := range sdkMsgs {
124+
res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg)
125+
if err != nil {
126+
return nil, nil, err
127+
}
128+
// append data
129+
data = append(data, res.Data)
130+
// append events
131+
sdkEvents := make([]sdk.Event, len(res.Events))
132+
for i := range res.Events {
133+
sdkEvents[i] = sdk.Event(res.Events[i])
134+
}
135+
events = append(events, sdkEvents...)
136+
}
137+
return
24138
}

app/wasmext/wasmext_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package wasmext_test
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
wasmvm "github.com/CosmWasm/wasmvm/types"
8+
sdkcodec "github.com/cosmos/cosmos-sdk/codec/types"
9+
sdk "github.com/cosmos/cosmos-sdk/types"
10+
bank "github.com/cosmos/cosmos-sdk/x/bank/types"
11+
"github.com/stretchr/testify/suite"
12+
13+
"github.com/NibiruChain/nibiru/v2/app/wasmext"
14+
"github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp"
15+
"github.com/NibiruChain/nibiru/v2/x/evm"
16+
"github.com/NibiruChain/nibiru/v2/x/evm/evmtest"
17+
)
18+
19+
type Suite struct {
20+
suite.Suite
21+
}
22+
23+
func TestWasmExtSuite(t *testing.T) {
24+
suite.Run(t, new(Suite))
25+
}
26+
27+
// WasmVM to EVM call pattern is not yet supported. This test verifies the
28+
// Nibiru's [wasmkeeper.Option] function as expected.
29+
func (s *Suite) TestEvmFilter() {
30+
deps := evmtest.NewTestDeps()
31+
// wk := wasmkeeper.NewDefaultPermissionKeeper(deps.App.WasmKeeper)
32+
wasmMsgHandler := wasmext.WasmMessageHandler(deps.App.WasmMsgHandlerArgs)
33+
34+
s.T().Log("Create a valid Ethereum tx msg")
35+
36+
to := evmtest.NewEthPrivAcc()
37+
ethTxMsg, err := evmtest.TxTransferWei{
38+
Deps: &deps,
39+
To: to.EthAddr,
40+
AmountWei: evm.NativeToWei(big.NewInt(420)),
41+
}.Build()
42+
s.NoError(err)
43+
44+
s.T().Log("Validate Eth tx msg proto encoding as wasmvm.StargateMsg")
45+
wasmContractAddr := deps.Sender.NibiruAddr
46+
protoValueBz, err := deps.EncCfg.Codec.Marshal(ethTxMsg)
47+
s.Require().NoError(err, "expect ethTxMsg to proto marshal", protoValueBz)
48+
49+
_, ok := deps.EncCfg.Codec.(sdkcodec.AnyUnpacker)
50+
s.Require().True(ok, "codec must be an AnyUnpacker")
51+
52+
pbAny, err := sdkcodec.NewAnyWithValue(ethTxMsg)
53+
s.NoError(err)
54+
pbAnyBz, err := pbAny.Marshal()
55+
s.NoError(err, pbAnyBz)
56+
57+
var sdkMsg sdk.Msg
58+
err = deps.EncCfg.Codec.UnpackAny(pbAny, &sdkMsg)
59+
s.Require().NoError(err)
60+
s.Equal("/eth.evm.v1.MsgEthereumTx", sdk.MsgTypeURL(sdkMsg))
61+
62+
s.T().Log("Dispatch the Eth tx msg from Wasm (unsuccessfully)")
63+
_, _, err = wasmMsgHandler.DispatchMsg(
64+
deps.Ctx,
65+
wasmContractAddr,
66+
"ibcport-unused",
67+
wasmvm.CosmosMsg{
68+
Stargate: &wasmvm.StargateMsg{
69+
TypeURL: sdk.MsgTypeURL(ethTxMsg),
70+
Value: protoValueBz,
71+
},
72+
},
73+
)
74+
s.Require().ErrorContains(err, "Wasm VM to EVM call pattern is not yet supported")
75+
76+
coins := sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 420)) // arbitrary constant
77+
err = testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, coins)
78+
s.NoError(err)
79+
txMsg := &bank.MsgSend{
80+
FromAddress: deps.Sender.NibiruAddr.String(),
81+
ToAddress: evmtest.NewEthPrivAcc().NibiruAddr.String(),
82+
Amount: []sdk.Coin{sdk.NewInt64Coin(evm.EVMBankDenom, 20)},
83+
}
84+
protoValueBz, err = deps.EncCfg.Codec.Marshal(txMsg)
85+
s.NoError(err)
86+
_, _, err = wasmMsgHandler.DispatchMsg(
87+
deps.Ctx,
88+
wasmContractAddr,
89+
"ibcport-unused",
90+
wasmvm.CosmosMsg{
91+
Stargate: &wasmvm.StargateMsg{
92+
TypeURL: sdk.MsgTypeURL(txMsg),
93+
Value: protoValueBz,
94+
},
95+
},
96+
)
97+
s.Require().NoError(err)
98+
}

x/epochs/keeper/epoch.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func (k Keeper) IterateEpochInfo(
6666
iterate := k.Epochs.Iterate(ctx, &collections.Range[string]{})
6767
i := int64(0)
6868

69+
defer iterate.Close()
6970
for ; iterate.Valid(); iterate.Next() {
7071
epoch := iterate.Value()
7172
stop := fn(i, epoch)

0 commit comments

Comments
 (0)