Skip to content

Commit 87dce54

Browse files
authored
Add StoreAndMigrateContract proposal (#1539)
* Add StoreAndMigrateContract msg * Add tests * Add proposal cli * Fix comments
1 parent cb887ee commit 87dce54

File tree

10 files changed

+1206
-111
lines changed

10 files changed

+1206
-111
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,12 @@ format-tools:
165165

166166
lint: format-tools
167167
golangci-lint run --tests=false
168-
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "*_test.go" | xargs gofumpt -d
168+
find . -name '*.go' -type f -not -path "./vendor*" -not -path "./tests/system/vendor*" -not -path "*.git*" -not -path "*_test.go" | xargs gofumpt -d
169169

170170
format: format-tools
171-
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofumpt -w
172-
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w
173-
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs goimports -w -local github.com/CosmWasm/wasmd
171+
find . -name '*.go' -type f -not -path "./vendor*" -not -path "./tests/system/vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofumpt -w
172+
find . -name '*.go' -type f -not -path "./vendor*" -not -path "./tests/system/vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w
173+
find . -name '*.go' -type f -not -path "./vendor*" -not -path "./tests/system/vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs goimports -w -local github.com/CosmWasm/wasmd
174174

175175

176176
###############################################################################

docs/proto/proto-docs.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@
100100
- [MsgRemoveCodeUploadParamsAddressesResponse](#cosmwasm.wasm.v1.MsgRemoveCodeUploadParamsAddressesResponse)
101101
- [MsgStoreAndInstantiateContract](#cosmwasm.wasm.v1.MsgStoreAndInstantiateContract)
102102
- [MsgStoreAndInstantiateContractResponse](#cosmwasm.wasm.v1.MsgStoreAndInstantiateContractResponse)
103+
- [MsgStoreAndMigrateContract](#cosmwasm.wasm.v1.MsgStoreAndMigrateContract)
104+
- [MsgStoreAndMigrateContractResponse](#cosmwasm.wasm.v1.MsgStoreAndMigrateContractResponse)
103105
- [MsgStoreCode](#cosmwasm.wasm.v1.MsgStoreCode)
104106
- [MsgStoreCodeResponse](#cosmwasm.wasm.v1.MsgStoreCodeResponse)
105107
- [MsgSudoContract](#cosmwasm.wasm.v1.MsgSudoContract)
@@ -1639,6 +1641,48 @@ Since: 0.40
16391641

16401642

16411643

1644+
<a name="cosmwasm.wasm.v1.MsgStoreAndMigrateContract"></a>
1645+
1646+
### MsgStoreAndMigrateContract
1647+
MsgStoreAndMigrateContract is the MsgStoreAndMigrateContract
1648+
request type.
1649+
1650+
Since: 0.42
1651+
1652+
1653+
| Field | Type | Label | Description |
1654+
| ----- | ---- | ----- | ----------- |
1655+
| `authority` | [string](#string) | | Authority is the address of the governance account. |
1656+
| `wasm_byte_code` | [bytes](#bytes) | | WASMByteCode can be raw or gzip compressed |
1657+
| `instantiate_permission` | [AccessConfig](#cosmwasm.wasm.v1.AccessConfig) | | InstantiatePermission to apply on contract creation, optional |
1658+
| `contract` | [string](#string) | | Contract is the address of the smart contract |
1659+
| `msg` | [bytes](#bytes) | | Msg json encoded message to be passed to the contract on migration |
1660+
1661+
1662+
1663+
1664+
1665+
1666+
<a name="cosmwasm.wasm.v1.MsgStoreAndMigrateContractResponse"></a>
1667+
1668+
### MsgStoreAndMigrateContractResponse
1669+
MsgStoreAndMigrateContractResponse defines the response structure
1670+
for executing a MsgStoreAndMigrateContract message.
1671+
1672+
Since: 0.42
1673+
1674+
1675+
| Field | Type | Label | Description |
1676+
| ----- | ---- | ----- | ----------- |
1677+
| `code_id` | [uint64](#uint64) | | CodeID is the reference to the stored WASM code |
1678+
| `checksum` | [bytes](#bytes) | | Checksum is the sha256 hash of the stored code |
1679+
| `data` | [bytes](#bytes) | | Data contains bytes to returned from the contract |
1680+
1681+
1682+
1683+
1684+
1685+
16421686
<a name="cosmwasm.wasm.v1.MsgStoreCode"></a>
16431687

16441688
### MsgStoreCode
@@ -1865,6 +1909,9 @@ Since: 0.40 | |
18651909
Since: 0.40 | |
18661910
| `RemoveCodeUploadParamsAddresses` | [MsgRemoveCodeUploadParamsAddresses](#cosmwasm.wasm.v1.MsgRemoveCodeUploadParamsAddresses) | [MsgRemoveCodeUploadParamsAddressesResponse](#cosmwasm.wasm.v1.MsgRemoveCodeUploadParamsAddressesResponse) | RemoveCodeUploadParamsAddresses defines a governance operation for removing addresses from code upload params. The authority is defined in the keeper. | |
18671911
| `AddCodeUploadParamsAddresses` | [MsgAddCodeUploadParamsAddresses](#cosmwasm.wasm.v1.MsgAddCodeUploadParamsAddresses) | [MsgAddCodeUploadParamsAddressesResponse](#cosmwasm.wasm.v1.MsgAddCodeUploadParamsAddressesResponse) | AddCodeUploadParamsAddresses defines a governance operation for adding addresses to code upload params. The authority is defined in the keeper. | |
1912+
| `StoreAndMigrateContract` | [MsgStoreAndMigrateContract](#cosmwasm.wasm.v1.MsgStoreAndMigrateContract) | [MsgStoreAndMigrateContractResponse](#cosmwasm.wasm.v1.MsgStoreAndMigrateContractResponse) | StoreAndMigrateContract defines a governance operation for storing and migrating the contract. The authority is defined in the keeper.
1913+
1914+
Since: 0.42 | |
18681915

18691916
<!-- end services -->
18701917

proto/cosmwasm/wasm/v1/tx.proto

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ service Msg {
7070
// The authority is defined in the keeper.
7171
rpc AddCodeUploadParamsAddresses(MsgAddCodeUploadParamsAddresses)
7272
returns (MsgAddCodeUploadParamsAddressesResponse);
73+
// StoreAndMigrateContract defines a governance operation for storing
74+
// and migrating the contract. The authority is defined in the keeper.
75+
//
76+
// Since: 0.42
77+
rpc StoreAndMigrateContract(MsgStoreAndMigrateContract)
78+
returns (MsgStoreAndMigrateContractResponse);
7379
}
7480

7581
// MsgStoreCode submit Wasm code to the system
@@ -430,4 +436,37 @@ message MsgRemoveCodeUploadParamsAddresses {
430436

431437
// MsgRemoveCodeUploadParamsAddressesResponse defines the response
432438
// structure for executing a MsgRemoveCodeUploadParamsAddresses message.
433-
message MsgRemoveCodeUploadParamsAddressesResponse {}
439+
message MsgRemoveCodeUploadParamsAddressesResponse {}
440+
441+
// MsgStoreAndMigrateContract is the MsgStoreAndMigrateContract
442+
// request type.
443+
//
444+
// Since: 0.42
445+
message MsgStoreAndMigrateContract {
446+
option (amino.name) = "wasm/MsgStoreAndMigrateContract";
447+
option (cosmos.msg.v1.signer) = "authority";
448+
449+
// Authority is the address of the governance account.
450+
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
451+
// WASMByteCode can be raw or gzip compressed
452+
bytes wasm_byte_code = 2 [ (gogoproto.customname) = "WASMByteCode" ];
453+
// InstantiatePermission to apply on contract creation, optional
454+
AccessConfig instantiate_permission = 3;
455+
// Contract is the address of the smart contract
456+
string contract = 4;
457+
// Msg json encoded message to be passed to the contract on migration
458+
bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ];
459+
}
460+
461+
// MsgStoreAndMigrateContractResponse defines the response structure
462+
// for executing a MsgStoreAndMigrateContract message.
463+
//
464+
// Since: 0.42
465+
message MsgStoreAndMigrateContractResponse {
466+
// CodeID is the reference to the stored WASM code
467+
uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ];
468+
// Checksum is the sha256 hash of the stored code
469+
bytes checksum = 2;
470+
// Data contains bytes to returned from the contract
471+
bytes data = 3;
472+
}

x/wasm/client/cli/gov_tx.go

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func SubmitProposalCmd() *cobra.Command {
5151
ProposalUpdateInstantiateConfigCmd(),
5252
ProposalAddCodeUploadParamsAddresses(),
5353
ProposalRemoveCodeUploadParamsAddresses(),
54+
ProposalStoreAndMigrateContractCmd(),
5455
)
5556
return cmd
5657
}
@@ -246,7 +247,7 @@ func ProposalStoreAndInstantiateContractCmd() *cobra.Command {
246247
cmd := &cobra.Command{
247248
Use: "store-instantiate [wasm file] [json_encoded_init_args] --authority [address] --label [text] --title [text] --summary [text]" +
248249
"--unpin-code [unpin_code,optional] --source [source,optional] --builder [builder,optional] --code-hash [code_hash,optional] --admin [address,optional] --amount [coins,optional]",
249-
Short: "Submit and instantiate a wasm contract proposal",
250+
Short: "Submit a store and instantiate wasm contract proposal",
250251
Args: cobra.ExactArgs(2),
251252
RunE: func(cmd *cobra.Command, args []string) error {
252253
clientCtx, proposalTitle, summary, deposit, err := getProposalInfo(cmd)
@@ -412,7 +413,7 @@ func ProposalMigrateContractCmd() *cobra.Command {
412413

413414
func ProposalExecuteContractCmd() *cobra.Command {
414415
cmd := &cobra.Command{
415-
Use: "execute-contract [contract_addr_bech32] [json_encoded_migration_args] --title [text] --summary [text] --authority [address]",
416+
Use: "execute-contract [contract_addr_bech32] [json_encoded_execution_args] --title [text] --summary [text] --authority [address]",
416417
Short: "Submit a execute wasm contract proposal (run by any address)",
417418
Args: cobra.ExactArgs(2),
418419
RunE: func(cmd *cobra.Command, args []string) error {
@@ -928,3 +929,55 @@ func getProposalInfo(cmd *cobra.Command) (client.Context, string, string, sdk.Co
928929

929930
return clientCtx, proposalTitle, summary, deposit, nil
930931
}
932+
933+
func ProposalStoreAndMigrateContractCmd() *cobra.Command {
934+
cmd := &cobra.Command{
935+
Use: "store-migrate [wasm file] [contract_addr_bech32] [json_encoded_migration_args] --title [text] --summary [text] --authority [address]",
936+
Short: "Submit a store and migrate wasm contract proposal",
937+
Args: cobra.ExactArgs(3),
938+
RunE: func(cmd *cobra.Command, args []string) error {
939+
clientCtx, proposalTitle, summary, deposit, err := getProposalInfo(cmd)
940+
if err != nil {
941+
return err
942+
}
943+
944+
authority, err := cmd.Flags().GetString(flagAuthority)
945+
if err != nil {
946+
return fmt.Errorf("authority: %s", err)
947+
}
948+
949+
if len(authority) == 0 {
950+
return errors.New("authority address is required")
951+
}
952+
953+
src, err := parseStoreCodeArgs(args[0], authority, cmd.Flags())
954+
if err != nil {
955+
return err
956+
}
957+
958+
msg := types.MsgStoreAndMigrateContract{
959+
Authority: authority,
960+
WASMByteCode: src.WASMByteCode,
961+
InstantiatePermission: src.InstantiatePermission,
962+
Msg: []byte(args[2]),
963+
Contract: args[1],
964+
}
965+
966+
proposalMsg, err := v1.NewMsgSubmitProposal([]sdk.Msg{&msg}, deposit, clientCtx.GetFromAddress().String(), "", proposalTitle, summary)
967+
if err != nil {
968+
return err
969+
}
970+
if err = proposalMsg.ValidateBasic(); err != nil {
971+
return err
972+
}
973+
974+
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), proposalMsg)
975+
},
976+
SilenceUsage: true,
977+
}
978+
979+
addInstantiatePermissionFlags(cmd)
980+
// proposal flags
981+
addCommonProposalFlags(cmd)
982+
return cmd
983+
}

x/wasm/keeper/msg_server.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,10 +328,6 @@ func (m msgServer) StoreAndInstantiateContract(goCtx context.Context, req *types
328328
return nil, errorsmod.Wrap(err, "authority")
329329
}
330330

331-
if err = req.ValidateBasic(); err != nil {
332-
return nil, err
333-
}
334-
335331
var adminAddr sdk.AccAddress
336332
if req.Admin != "" {
337333
if adminAddr, err = sdk.AccAddressFromBech32(req.Admin); err != nil {
@@ -442,3 +438,39 @@ func (m msgServer) selectAuthorizationPolicy(ctx sdk.Context, actor string) type
442438
}
443439
return DefaultAuthorizationPolicy{}
444440
}
441+
442+
// StoreAndMigrateContract stores and migrates the contract.
443+
func (m msgServer) StoreAndMigrateContract(goCtx context.Context, req *types.MsgStoreAndMigrateContract) (*types.MsgStoreAndMigrateContractResponse, error) {
444+
authorityAddr, err := sdk.AccAddressFromBech32(req.Authority)
445+
if err != nil {
446+
return nil, errorsmod.Wrap(err, "authority")
447+
}
448+
449+
if err = req.ValidateBasic(); err != nil {
450+
return nil, err
451+
}
452+
453+
ctx := sdk.UnwrapSDKContext(goCtx)
454+
policy := m.selectAuthorizationPolicy(ctx, req.Authority)
455+
456+
codeID, checksum, err := m.keeper.create(ctx, authorityAddr, req.WASMByteCode, req.InstantiatePermission, policy)
457+
if err != nil {
458+
return nil, err
459+
}
460+
461+
contractAddr, err := sdk.AccAddressFromBech32(req.Contract)
462+
if err != nil {
463+
return nil, errorsmod.Wrap(err, "contract")
464+
}
465+
466+
data, err := m.keeper.migrate(ctx, contractAddr, authorityAddr, codeID, req.Msg, policy)
467+
if err != nil {
468+
return nil, err
469+
}
470+
471+
return &types.MsgStoreAndMigrateContractResponse{
472+
CodeID: codeID,
473+
Checksum: checksum,
474+
Data: data,
475+
}, nil
476+
}

x/wasm/keeper/msg_server_integration_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,3 +1048,99 @@ func TestUpdateInstantiateConfig(t *testing.T) {
10481048
})
10491049
}
10501050
}
1051+
1052+
func TestStoreAndMigrateContract(t *testing.T) {
1053+
wasmApp := app.Setup(t)
1054+
ctx := wasmApp.BaseApp.NewContext(false, tmproto.Header{Time: time.Now()})
1055+
1056+
checksum, err := wasmvm.CreateChecksum(hackatomContract)
1057+
require.NoError(t, err)
1058+
1059+
var (
1060+
myAddress sdk.AccAddress = make([]byte, types.ContractAddrLen)
1061+
authority = wasmApp.WasmKeeper.GetAuthority()
1062+
)
1063+
1064+
specs := map[string]struct {
1065+
addr string
1066+
permission *types.AccessConfig
1067+
expChecksum []byte
1068+
expErr bool
1069+
}{
1070+
"authority can store and migrate a contract when permission is nobody": {
1071+
addr: authority,
1072+
permission: &types.AllowNobody,
1073+
expChecksum: checksum,
1074+
},
1075+
"authority can store and migrate a contract when permission is everybody": {
1076+
addr: authority,
1077+
permission: &types.AllowEverybody,
1078+
expChecksum: checksum,
1079+
},
1080+
"other address can store and migrate a contract when permission is everybody": {
1081+
addr: myAddress.String(),
1082+
permission: &types.AllowEverybody,
1083+
expChecksum: checksum,
1084+
},
1085+
"other address cannot store and migrate a contract when permission is nobody": {
1086+
addr: myAddress.String(),
1087+
permission: &types.AllowNobody,
1088+
expErr: true,
1089+
},
1090+
}
1091+
for name, spec := range specs {
1092+
t.Run(name, func(t *testing.T) {
1093+
// setup
1094+
initMsg := keeper.HackatomExampleInitMsg{
1095+
Verifier: myAddress,
1096+
Beneficiary: myAddress,
1097+
}
1098+
initMsgBz, err := json.Marshal(initMsg)
1099+
require.NoError(t, err)
1100+
storeAndInstantiateMsg := &types.MsgStoreAndInstantiateContract{
1101+
Authority: spec.addr,
1102+
WASMByteCode: hackatomContract,
1103+
InstantiatePermission: &types.AllowEverybody,
1104+
Admin: myAddress.String(),
1105+
UnpinCode: false,
1106+
Label: "test",
1107+
Msg: initMsgBz,
1108+
Funds: sdk.Coins{},
1109+
}
1110+
rsp, err := wasmApp.MsgServiceRouter().Handler(storeAndInstantiateMsg)(ctx, storeAndInstantiateMsg)
1111+
require.NoError(t, err)
1112+
var storeAndInstantiateResponse types.MsgStoreAndInstantiateContractResponse
1113+
require.NoError(t, wasmApp.AppCodec().Unmarshal(rsp.Data, &storeAndInstantiateResponse))
1114+
1115+
contractAddr := storeAndInstantiateResponse.Address
1116+
1117+
// when
1118+
migMsg := struct {
1119+
Verifier sdk.AccAddress `json:"verifier"`
1120+
}{Verifier: myAddress}
1121+
migMsgBz, err := json.Marshal(migMsg)
1122+
require.NoError(t, err)
1123+
msg := &types.MsgStoreAndMigrateContract{
1124+
Authority: spec.addr,
1125+
WASMByteCode: hackatomContract,
1126+
InstantiatePermission: spec.permission,
1127+
Msg: migMsgBz,
1128+
Contract: contractAddr,
1129+
}
1130+
rsp, err = wasmApp.MsgServiceRouter().Handler(msg)(ctx, msg)
1131+
1132+
// then
1133+
if spec.expErr {
1134+
require.Error(t, err)
1135+
require.Nil(t, rsp)
1136+
return
1137+
}
1138+
1139+
require.NoError(t, err)
1140+
var result types.MsgStoreAndMigrateContractResponse
1141+
require.NoError(t, wasmApp.AppCodec().Unmarshal(rsp.Data, &result))
1142+
assert.Equal(t, spec.expChecksum, result.Checksum)
1143+
require.NotZero(t, result.CodeID)
1144+
})
1145+
}
1146+
}

x/wasm/types/codec.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
3030
cdc.RegisterConcrete(&MsgStoreAndInstantiateContract{}, "wasm/MsgStoreAndInstantiateContract", nil)
3131
cdc.RegisterConcrete(&MsgAddCodeUploadParamsAddresses{}, "wasm/MsgAddCodeUploadParamsAddresses", nil)
3232
cdc.RegisterConcrete(&MsgRemoveCodeUploadParamsAddresses{}, "wasm/MsgRemoveCodeUploadParamsAddresses", nil)
33+
cdc.RegisterConcrete(&MsgStoreAndMigrateContract{}, "wasm/MsgStoreAndMigrateContract", nil)
3334

3435
cdc.RegisterConcrete(&PinCodesProposal{}, "wasm/PinCodesProposal", nil)
3536
cdc.RegisterConcrete(&UnpinCodesProposal{}, "wasm/UnpinCodesProposal", nil)
@@ -80,6 +81,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
8081
&MsgStoreAndInstantiateContract{},
8182
&MsgAddCodeUploadParamsAddresses{},
8283
&MsgRemoveCodeUploadParamsAddresses{},
84+
&MsgStoreAndMigrateContract{},
8385
)
8486
registry.RegisterImplementations(
8587
(*v1beta1.Content)(nil),

0 commit comments

Comments
 (0)