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

[DO NOT MERGE] Allow more gas efficient migrations #1846

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
63 changes: 37 additions & 26 deletions x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,10 @@
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "to use new code")
}

report, err := k.wasmVM.AnalyzeCode(newCodeInfo.CodeHash)

// check for IBC flag
switch report, err := k.wasmVM.AnalyzeCode(newCodeInfo.CodeHash); {
switch {
case err != nil:
return nil, errorsmod.Wrap(types.ErrVMError, err.Error())
case !report.HasIBCEntryPoints && contractInfo.IBCPortID != "":
Expand All @@ -482,26 +484,12 @@
contractInfo.IBCPortID = ibcPort
}

env := types.NewEnv(sdkCtx, contractAddress)

// prepare querier
querier := k.newQueryHandler(sdkCtx, contractAddress)

prefixStoreKey := types.GetContractStorePrefix(contractAddress)
vmStore := types.NewStoreAdapter(prefix.NewStore(runtime.KVStoreAdapter(k.storeService.OpenKVStore(sdkCtx)), prefixStoreKey))
gasLeft := k.runtimeGasForContract(sdkCtx)
res, gasUsed, err := k.wasmVM.Migrate(newCodeInfo.CodeHash, env, msg, vmStore, cosmwasmAPI, &querier, k.gasMeter(sdkCtx), gasLeft, costJSONDeserialization)
k.consumeRuntimeGas(sdkCtx, gasUsed)
if err != nil {
return nil, errorsmod.Wrap(types.ErrVMError, err.Error())
}
if res == nil {
// If this gets executed, that's a bug in wasmvm
return nil, errorsmod.Wrap(types.ErrVMError, "internal wasmvm error")
}
if res.Err != "" {
return nil, types.MarkErrorDeterministic(errorsmod.Wrap(types.ErrMigrationFailed, res.Err))
// check for migrate endpoint
hasMigrateEndpoint := contains(report.Entrypoints, "migrate")
if hasMigrateEndpoint != (len(msg) != 0) {
return nil, errorsmod.Wrap(types.ErrMigrationFailed, "invalid migration parameters")
}

// delete old secondary index entry
err = k.removeFromContractCodeSecondaryIndex(ctx, contractAddress, k.mustGetLastContractHistoryEntry(sdkCtx, contractAddress))
if err != nil {
Expand All @@ -525,13 +513,36 @@
sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()),
))

sdkCtx = types.WithSubMsgAuthzPolicy(sdkCtx, authZ.SubMessageAuthorizationPolicy(types.AuthZActionMigrateContract))
data, err := k.handleContractResponse(sdkCtx, contractAddress, contractInfo.IBCPortID, res.Ok.Messages, res.Ok.Attributes, res.Ok.Data, res.Ok.Events)
if err != nil {
return nil, errorsmod.Wrap(err, "dispatch")
}
if hasMigrateEndpoint {
env := types.NewEnv(sdkCtx, contractAddress)

return data, nil
// prepare querier
querier := k.newQueryHandler(sdkCtx, contractAddress)

prefixStoreKey := types.GetContractStorePrefix(contractAddress)
vmStore := types.NewStoreAdapter(prefix.NewStore(runtime.KVStoreAdapter(k.storeService.OpenKVStore(sdkCtx)), prefixStoreKey))
gasLeft := k.runtimeGasForContract(sdkCtx)
res, gasUsed, err := k.wasmVM.Migrate(newCodeInfo.CodeHash, env, msg, vmStore, cosmwasmAPI, &querier, k.gasMeter(sdkCtx), gasLeft, costJSONDeserialization)
k.consumeRuntimeGas(sdkCtx, gasUsed)
if err != nil {
return nil, errorsmod.Wrap(types.ErrVMError, err.Error())

Check warning on line 528 in x/wasm/keeper/keeper.go

View check run for this annotation

Codecov / codecov/patch

x/wasm/keeper/keeper.go#L528

Added line #L528 was not covered by tests
}
if res == nil {
// If this gets executed, that's a bug in wasmvm
return nil, errorsmod.Wrap(types.ErrVMError, "internal wasmvm error")

Check warning on line 532 in x/wasm/keeper/keeper.go

View check run for this annotation

Codecov / codecov/patch

x/wasm/keeper/keeper.go#L532

Added line #L532 was not covered by tests
}
if res.Err != "" {
return nil, types.MarkErrorDeterministic(errorsmod.Wrap(types.ErrMigrationFailed, res.Err))
}

sdkCtx = types.WithSubMsgAuthzPolicy(sdkCtx, authZ.SubMessageAuthorizationPolicy(types.AuthZActionMigrateContract))
data, err := k.handleContractResponse(sdkCtx, contractAddress, contractInfo.IBCPortID, res.Ok.Messages, res.Ok.Attributes, res.Ok.Data, res.Ok.Events)
if err != nil {
return nil, errorsmod.Wrap(err, "dispatch")
}
return data, nil
}
return nil, nil
}

// Sudo allows privileged access to a contract. This can never be called by an external tx, but only by
Expand Down
36 changes: 30 additions & 6 deletions x/wasm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,8 @@ func TestInstantiateWithContractFactoryChildQueriesParent(t *testing.T) {
},
},
},
}},
},
},
}, 0, nil
}

Expand Down Expand Up @@ -1221,6 +1222,10 @@ func TestMigrate(t *testing.T) {
require.NoError(t, keeper.SetAccessConfig(parentCtx, restrictedCodeExample.CodeID, restrictedCodeExample.CreatorAddr, types.AllowNobody))
require.NotEqual(t, originalCodeID, restrictedCodeExample.CodeID)

// reflect contract does not have migrate endpoint
codeWithoutMigrateEndpoint := StoreReflectContract(t, parentCtx, keepers)
require.NotEqual(t, originalCodeID, codeWithoutMigrateEndpoint.CodeID)

anyAddr := RandomAccountAddress(t)
newVerifierAddr := RandomAccountAddress(t)
initMsgBz := HackatomExampleInitMsg{
Expand All @@ -1246,7 +1251,7 @@ func TestMigrate(t *testing.T) {
expIBCPort bool
initMsg []byte
}{
"all good with same code id": {
"all good with same code id: msg != nil": {
admin: creator,
caller: creator,
initMsg: initMsgBz,
Expand All @@ -1255,6 +1260,13 @@ func TestMigrate(t *testing.T) {
migrateMsg: migMsgBz,
expVerifier: newVerifierAddr,
},
"all good with valid migration setup: msg == nil": {
admin: creator,
caller: creator,
initMsg: initMsgBz,
fromCodeID: originalCodeID,
toCodeID: codeWithoutMigrateEndpoint.CodeID,
},
"all good with different code id": {
admin: creator,
caller: creator,
Expand Down Expand Up @@ -1333,13 +1345,22 @@ func TestMigrate(t *testing.T) {
migrateMsg: bytes.Repeat([]byte{0x1}, 7),
expErr: types.ErrMigrationFailed,
},
"fail in contract without migrate msg": {
"prevent invalid migration setup: msg == nil": {
admin: creator,
caller: creator,
initMsg: initMsgBz,
fromCodeID: originalCodeID,
toCodeID: originalCodeID,
expErr: types.ErrVMError,
expErr: types.ErrMigrationFailed,
},
"prevent invalid migration setup: msg != nil": {
admin: creator,
caller: creator,
initMsg: initMsgBz,
fromCodeID: originalCodeID,
toCodeID: codeWithoutMigrateEndpoint.CodeID,
migrateMsg: migMsgBz,
expErr: types.ErrMigrationFailed,
},
"fail when no IBC callbacks": {
admin: fred,
Expand Down Expand Up @@ -1395,7 +1416,10 @@ func TestMigrate(t *testing.T) {
require.NoError(t, json.Unmarshal(raw, &stored))
require.Contains(t, stored, "verifier")
require.NoError(t, err)
assert.Equal(t, spec.expVerifier.String(), stored["verifier"])

if spec.expVerifier != nil {
assert.Equal(t, spec.expVerifier.String(), stored["verifier"])
}
})
}
}
Expand Down Expand Up @@ -1576,7 +1600,7 @@ func TestIterateContractsByCodeWithMigration(t *testing.T) {
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
example2 := InstantiateIBCReflectContract(t, ctx, keepers)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
_, err := c.Migrate(ctx, example1.Contract, example1.CreatorAddr, example2.CodeID, []byte("{}"))
_, err := c.Migrate(ctx, example1.Contract, example1.CreatorAddr, example2.CodeID, nil)
require.NoError(t, err)

// when
Expand Down
6 changes: 6 additions & 0 deletions x/wasm/keeper/submsg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,12 @@ func TestMigrateGovSubMsgAuthzPropagated(t *testing.T) {
}, 0, nil
}

mockWasmVM.AnalyzeCodeFn = func(codeID wasmvm.Checksum) (*wasmvmtypes.AnalysisReport, error) {
return &wasmvmtypes.AnalysisReport{
Entrypoints: []string{"migrate"},
}, nil
}

specs := map[string]struct {
policy types.AuthorizationPolicy
expErr *errorsmod.Error
Expand Down
2 changes: 1 addition & 1 deletion x/wasm/migrations/v1/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

func TestMigrate1To2(t *testing.T) {
var AvailableCapabilities = []string{"iterator", "staking", "stargate", "cosmwasm_1_1"}
AvailableCapabilities := []string{"iterator", "staking", "stargate", "cosmwasm_1_1"}
ctx, keepers := keeper.CreateTestInput(t, false, AvailableCapabilities)
wasmKeeper := keepers.WasmKeeper

Expand Down
2 changes: 1 addition & 1 deletion x/wasm/migrations/v3/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

func TestMigrate3To4(t *testing.T) {
var AvailableCapabilities = []string{"iterator", "staking", "stargate", "cosmwasm_1_1"}
AvailableCapabilities := []string{"iterator", "staking", "stargate", "cosmwasm_1_1"}
ctx, keepers := keeper.CreateTestInput(t, false, AvailableCapabilities)
store := ctx.KVStore(keepers.WasmStoreKey)
cdc := moduletestutil.MakeTestEncodingConfig(wasm.AppModuleBasic{}).Codec
Expand Down