Skip to content

Commit 029ed3b

Browse files
feat: Add Preinstall Message Handler (#251)
1 parent 9b96d1f commit 029ed3b

File tree

16 files changed

+2502
-134
lines changed

16 files changed

+2502
-134
lines changed

api/cosmos/evm/vm/v1/tx.pulsar.go

Lines changed: 1096 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/cosmos/evm/vm/v1/tx_grpc.pb.go

Lines changed: 45 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

evmd/app.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,8 @@ func init() {
140140

141141
const appName = "evmd"
142142

143-
var (
144-
// defaultNodeHome default home directories for the application daemon
145-
defaultNodeHome string
146-
)
143+
// defaultNodeHome default home directories for the application daemon
144+
var defaultNodeHome string
147145

148146
var (
149147
_ runtime.AppI = (*EVMD)(nil)

proto/cosmos/evm/vm/v1/tx.proto

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ service Msg {
2424
// parameters. The authority is hard-coded to the Cosmos SDK x/gov module
2525
// account
2626
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
27+
28+
// RegisterPreinstalls defines a governance operation for directly registering
29+
// preinstalled contracts in the EVM. The authority is the same as is used for
30+
// Params updates.
31+
rpc RegisterPreinstalls(MsgRegisterPreinstalls)
32+
returns (MsgRegisterPreinstallsResponse);
2733
}
2834

2935
// MsgEthereumTx encapsulates an Ethereum transaction as an SDK message.
@@ -208,3 +214,20 @@ message MsgUpdateParams {
208214
// MsgUpdateParamsResponse defines the response structure for executing a
209215
// MsgUpdateParams message.
210216
message MsgUpdateParamsResponse {}
217+
218+
// MsgRegisterPreinstalls defines a Msg for creating preinstalls in evm state.
219+
message MsgRegisterPreinstalls {
220+
option (amino.name) = "cosmos/evm/x/vm/MsgRegisterPreinstalls";
221+
option (cosmos.msg.v1.signer) = "authority";
222+
223+
// authority is the address of the governance account.
224+
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
225+
226+
// preinstalls defines the preinstalls to create.
227+
repeated Preinstall preinstalls = 2
228+
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
229+
}
230+
231+
// MsgRegisterPreinstallsResponse defines the response structure for executing a
232+
// MsgRegisterPreinstalls message.
233+
message MsgRegisterPreinstallsResponse {}

tests/integration/x/vm/test_msg_server.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,60 @@ func (s *KeeperTestSuite) TestUpdateParams() {
121121
s.Require().NoError(err)
122122
}
123123
}
124+
125+
func (s *KeeperTestSuite) TestRegisterPreinstalls() {
126+
s.SetupTest()
127+
testCases := []struct {
128+
name string
129+
getMsg func() *types.MsgRegisterPreinstalls
130+
expectedErr error
131+
}{
132+
{
133+
name: "fail - invalid authority",
134+
getMsg: func() *types.MsgRegisterPreinstalls {
135+
return &types.MsgRegisterPreinstalls{Authority: "foobar"}
136+
},
137+
expectedErr: govtypes.ErrInvalidSigner,
138+
},
139+
{
140+
name: "pass - valid Update msg",
141+
getMsg: func() *types.MsgRegisterPreinstalls {
142+
return &types.MsgRegisterPreinstalls{
143+
Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(),
144+
Preinstalls: []types.Preinstall{{
145+
Name: "Test1",
146+
Address: "0xb364E75b1189DcbBF7f0C856456c1ba8e4d6481b",
147+
Code: "0x000000000",
148+
}},
149+
}
150+
},
151+
expectedErr: nil,
152+
},
153+
{
154+
name: "fail - double registration",
155+
getMsg: func() *types.MsgRegisterPreinstalls {
156+
return &types.MsgRegisterPreinstalls{
157+
Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(),
158+
Preinstalls: types.DefaultPreinstalls,
159+
}
160+
},
161+
expectedErr: types.ErrInvalidPreinstall,
162+
},
163+
}
164+
165+
for _, tc := range testCases {
166+
s.Run("MsgRegisterPreinstalls_"+tc.name, func() {
167+
msg := tc.getMsg()
168+
_, err := s.Network.App.GetEVMKeeper().RegisterPreinstalls(s.Network.GetContext(), msg)
169+
if tc.expectedErr != nil {
170+
s.Require().Error(err)
171+
s.Contains(err.Error(), tc.expectedErr.Error())
172+
} else {
173+
s.Require().NoError(err)
174+
}
175+
})
176+
177+
err := s.Network.NextBlock()
178+
s.Require().NoError(err)
179+
}
180+
}

x/erc20/types/mocks/EVMKeeper.go

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x/vm/keeper/keeper_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package keeper_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/mock"
7+
"github.com/stretchr/testify/suite"
8+
9+
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
10+
cmttime "github.com/cometbft/cometbft/types/time"
11+
12+
vmkeeper "github.com/cosmos/evm/x/vm/keeper"
13+
vmtypes "github.com/cosmos/evm/x/vm/types"
14+
"github.com/cosmos/evm/x/vm/types/mocks"
15+
16+
storetypes "cosmossdk.io/store/types"
17+
18+
"github.com/cosmos/cosmos-sdk/testutil"
19+
sdk "github.com/cosmos/cosmos-sdk/types"
20+
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
21+
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
22+
)
23+
24+
type KeeperTestSuite struct {
25+
suite.Suite
26+
27+
ctx sdk.Context
28+
bankKeeper *mocks.BankKeeper
29+
accKeeper *mocks.AccountKeeper
30+
stakingKeeper *mocks.StakingKeeper
31+
fmKeeper *mocks.FeeMarketKeeper
32+
erc20Keeper *mocks.Erc20Keeper
33+
vmKeeper *vmkeeper.Keeper
34+
}
35+
36+
func TestKeeperTestSuite(t *testing.T) {
37+
suite.Run(t, new(KeeperTestSuite))
38+
}
39+
40+
func (suite *KeeperTestSuite) SetupTest() {
41+
key := storetypes.NewKVStoreKey(vmtypes.StoreKey)
42+
transientKey := storetypes.NewTransientStoreKey(vmtypes.TransientKey)
43+
testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test"))
44+
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
45+
encCfg := moduletestutil.MakeTestEncodingConfig()
46+
47+
// storeService := runtime.NewKVStoreService(key)
48+
authority := sdk.AccAddress("foobar")
49+
50+
suite.bankKeeper = mocks.NewBankKeeper(suite.T())
51+
suite.accKeeper = mocks.NewAccountKeeper(suite.T())
52+
suite.stakingKeeper = mocks.NewStakingKeeper(suite.T())
53+
suite.fmKeeper = mocks.NewFeeMarketKeeper(suite.T())
54+
suite.erc20Keeper = mocks.NewErc20Keeper(suite.T())
55+
suite.ctx = ctx
56+
57+
suite.accKeeper.On("GetModuleAddress", vmtypes.ModuleName).Return(sdk.AccAddress("evm"))
58+
suite.vmKeeper = vmkeeper.NewKeeper(
59+
encCfg.Codec,
60+
key,
61+
transientKey,
62+
authority,
63+
suite.accKeeper,
64+
suite.bankKeeper,
65+
suite.stakingKeeper,
66+
suite.fmKeeper,
67+
suite.erc20Keeper,
68+
"",
69+
)
70+
}
71+
72+
func (suite *KeeperTestSuite) TestAddPreinstalls() {
73+
testCases := []struct {
74+
name string
75+
malleate func()
76+
preinstalls []vmtypes.Preinstall
77+
err error
78+
}{
79+
{
80+
"Default pass",
81+
func() {
82+
suite.accKeeper.On("GetAccount", mock.Anything, mock.Anything).Return(nil)
83+
suite.accKeeper.On("NewAccountWithAddress", mock.Anything,
84+
mock.Anything).Return(authtypes.NewBaseAccountWithAddress(sdk.AccAddress("evm")), nil)
85+
suite.accKeeper.On("SetAccount", mock.Anything, mock.Anything).Return()
86+
},
87+
vmtypes.DefaultPreinstalls,
88+
nil,
89+
},
90+
{
91+
"Acc already exists -- expect error",
92+
func() {
93+
suite.accKeeper.ExpectedCalls = suite.accKeeper.ExpectedCalls[:0]
94+
suite.accKeeper.On("GetAccount", mock.Anything, mock.Anything).Return(authtypes.NewBaseAccountWithAddress(sdk.AccAddress("evm")))
95+
},
96+
vmtypes.DefaultPreinstalls,
97+
vmtypes.ErrInvalidPreinstall,
98+
},
99+
}
100+
for _, tc := range testCases {
101+
suite.Run(tc.name, func() {
102+
tc.malleate()
103+
err := suite.vmKeeper.AddPreinstalls(suite.ctx, vmtypes.DefaultPreinstalls)
104+
if tc.err != nil {
105+
suite.Require().ErrorContains(err, tc.err.Error())
106+
} else {
107+
suite.Require().NoError(err)
108+
}
109+
})
110+
}
111+
}

x/vm/keeper/msg_server.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,22 @@ func (k *Keeper) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams)
146146

147147
return &types.MsgUpdateParamsResponse{}, nil
148148
}
149+
150+
// RegisterPreinstalls implements the gRPC MsgServer interface. When a RegisterPreinstalls
151+
// proposal passes, it creates the preinstalls. The registration can only be
152+
// performed if the requested authority is the Cosmos SDK governance module
153+
// account.
154+
func (k *Keeper) RegisterPreinstalls(goCtx context.Context, req *types.MsgRegisterPreinstalls) (*types.
155+
MsgRegisterPreinstallsResponse, error,
156+
) {
157+
if k.authority.String() != req.Authority {
158+
return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority, expected %s, got %s", k.authority.String(), req.Authority)
159+
}
160+
161+
ctx := sdk.UnwrapSDKContext(goCtx)
162+
if err := k.AddPreinstalls(ctx, req.Preinstalls); err != nil {
163+
return nil, err
164+
}
165+
166+
return &types.MsgRegisterPreinstallsResponse{}, nil
167+
}

x/vm/keeper/preinstalls.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ func (k *Keeper) AddPreinstalls(ctx sdk.Context, preinstalls []types.Preinstall)
3333
}
3434

3535
// check that the account is not already set
36-
if exists := k.accountKeeper.HasAccount(ctx, accAddress); !exists {
37-
// return errorsmod.Wrapf(types.ErrInvalidPreinstall, "preinstall %s already has an account in account keeper", preinstall.Address.String())
38-
// create account with the account keeper
39-
account := k.accountKeeper.NewAccountWithAddress(ctx, accAddress)
40-
k.accountKeeper.SetAccount(ctx, account)
36+
if acc := k.accountKeeper.GetAccount(ctx, accAddress); acc != nil {
37+
return errorsmod.Wrapf(types.ErrInvalidPreinstall, "preinstall %s already has an account in account keeper", preinstall.Address)
4138
}
39+
// create account with the account keeper
40+
account := k.accountKeeper.NewAccountWithAddress(ctx, accAddress)
41+
k.accountKeeper.SetAccount(ctx, account)
4242

4343
k.SetCodeHash(ctx, address.Bytes(), codeHash)
4444

x/vm/module.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727
)
2828

2929
// consensusVersion defines the current x/evm module consensus version.
30-
const consensusVersion = 10
30+
const consensusVersion = 9
3131

3232
var (
3333
_ module.AppModuleBasic = AppModuleBasic{}

0 commit comments

Comments
 (0)