Skip to content

Commit

Permalink
feat: Add conditional migrate calling
Browse files Browse the repository at this point in the history
  • Loading branch information
kulikthebird committed Aug 22, 2024
1 parent d63c1bd commit 15b4814
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 3 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ replace (

// pin version! 126854af5e6d has issues with the store so that queries fail
github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7

// TODO tkulik: UNDO
github.com/CosmWasm/wasmvm/v2 => /home/tkulik/Workspace/wasmvm
)

retract (
Expand Down
18 changes: 16 additions & 2 deletions x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ func (k Keeper) migrate(
var response *wasmvmtypes.Response

// check for migrate version
// TODO tkulik: Here we call AnalyzeCode:
oldCodeInfo := k.GetCodeInfo(ctx, contractInfo.CodeID)
oldReport, err := k.wasmVM.AnalyzeCode(oldCodeInfo.CodeHash)
if err != nil {
Expand All @@ -495,7 +496,7 @@ func (k Keeper) migrate(
if report.ContractMigrateVersion == nil ||
oldReport.ContractMigrateVersion == nil ||
*report.ContractMigrateVersion != *oldReport.ContractMigrateVersion {
response, err = k.callMigrateEntrypoint(sdkCtx, contractAddress, wasmvmtypes.Checksum(newCodeInfo.CodeHash), msg, newCodeID)
response, err = k.callMigrateEntrypoint(sdkCtx, contractAddress, wasmvmtypes.Checksum(newCodeInfo.CodeHash), msg, newCodeID, caller, oldReport.ContractMigrateVersion)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -553,6 +554,8 @@ func (k Keeper) callMigrateEntrypoint(
newChecksum wasmvmtypes.Checksum,
msg []byte,
newCodeID uint64,
senderAddress sdk.AccAddress,
OldStateVersion *uint64,
) (*wasmvmtypes.Response, error) {
setupCost := k.gasRegister.SetupContractCost(k.IsPinnedCode(sdkCtx, newCodeID), len(msg))
sdkCtx.GasMeter().ConsumeGas(setupCost, "Loading CosmWasm module: migrate")
Expand All @@ -565,7 +568,18 @@ func (k Keeper) callMigrateEntrypoint(
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(newChecksum, env, msg, vmStore, cosmwasmAPI, &querier, k.gasMeter(sdkCtx), gasLeft, costJSONDeserialization)

migrateInfo := wasmvmtypes.MigrateInfo{
Sender: senderAddress.String(),
OldStateVersion: OldStateVersion,
}
res, gasUsed, err := k.wasmVM.Migrate2(newChecksum, env, msg, migrateInfo, vmStore, cosmwasmAPI, &querier, k.gasMeter(sdkCtx), gasLeft, costJSONDeserialization)
if err != nil {
if strings.Contains(err.Error(), "The called function args arity mismatch, expected 2 args") {
res, gasUsed, err = k.wasmVM.Migrate(newChecksum, 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())
Expand Down
7 changes: 6 additions & 1 deletion x/wasm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1615,18 +1615,23 @@ func TestIterateContractsByCode(t *testing.T) {
}
}

// TODO tkulik: Update this testcase's mock
func TestIterateContractsByCodeWithMigration(t *testing.T) {
// mock migration so that it does not fail when migrate example1 to example2.codeID
mockWasmVM := wasmtesting.MockWasmEngine{MigrateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{}}, 1, nil
}}
},
Migrate2Fn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, migrateInfo wasmvmtypes.MigrateInfo, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{}}, 1, nil
}}
wasmtesting.MakeInstantiable(&mockWasmVM)
ctx, keepers := CreateTestInput(t, false, AvailableCapabilities, WithWasmEngine(&mockWasmVM))
k, c := keepers.WasmKeeper, keepers.ContractKeeper
example1 := InstantiateHackatomExampleContract(t, ctx, keepers)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
example2 := InstantiateIBCReflectContract(t, ctx, keepers)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// return
_, err := c.Migrate(ctx, example1.Contract, example1.CreatorAddr, example2.CodeID, []byte("{}"))
require.NoError(t, err)

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

// TODO tkulik: Update this testcase's mock
func TestMigrateGovSubMsgAuthzPropagated(t *testing.T) {
mockWasmVM := &wasmtesting.MockWasmEngine{}
wasmtesting.MakeInstantiable(mockWasmVM)
Expand Down Expand Up @@ -675,6 +676,29 @@ func TestMigrateGovSubMsgAuthzPropagated(t *testing.T) {
},
}, 0, nil
}
mockWasmVM.Migrate2Fn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, migrateInfo wasmvmtypes.MigrateInfo, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
if instanceLevel == 1 {
return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{}}, 0, nil
}
instanceLevel++
submsgPayload := fmt.Sprintf(`{"sub":%d}`, instanceLevel)
return &wasmvmtypes.ContractResult{
Ok: &wasmvmtypes.Response{
Messages: []wasmvmtypes.SubMsg{
{
ReplyOn: wasmvmtypes.ReplyNever,
Msg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{Migrate: &wasmvmtypes.MigrateMsg{
ContractAddr: example1.Contract.String(),
NewCodeID: example2.CodeID,
Msg: []byte(submsgPayload),
}},
},
},
},
},
}, 0, nil
}

specs := map[string]struct {
policy types.AuthorizationPolicy
Expand Down
8 changes: 8 additions & 0 deletions x/wasm/keeper/wasmtesting/mock_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type MockWasmEngine struct {
ExecuteFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error)
QueryFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.QueryResult, uint64, error)
MigrateFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error)
Migrate2Fn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, migrateInfo wasmvmtypes.MigrateInfo, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error)
SudoFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error)
ReplyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error)
GetCodeFn func(codeID wasmvm.Checksum) (wasmvm.WasmCode, error)
Expand Down Expand Up @@ -152,6 +153,13 @@ func (m *MockWasmEngine) Migrate(codeID wasmvm.Checksum, env wasmvmtypes.Env, mi
return m.MigrateFn(codeID, env, migrateMsg, store, goapi, querier, gasMeter, gasLimit, deserCost)
}

func (m *MockWasmEngine) Migrate2(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, migrateInfo wasmvmtypes.MigrateInfo, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
if m.MigrateFn == nil {
panic("not supposed to be called!")
}
return m.Migrate2Fn(codeID, env, migrateMsg, migrateInfo, store, goapi, querier, gasMeter, gasLimit, deserCost)
}

func (m *MockWasmEngine) Sudo(codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
if m.SudoFn == nil {
panic("not supposed to be called!")
Expand Down
20 changes: 20 additions & 0 deletions x/wasm/types/wasmer_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,26 @@ type WasmEngine interface {
deserCost wasmvmtypes.UFraction,
) (*wasmvmtypes.ContractResult, uint64, error)

// TODO tkulik: Update the description
// Migrate will migrate an existing contract to a new code binary.
// This takes storage of the data from the original contract and the CodeID of the new contract that should
// replace it. This allows it to run a migration step if needed, or return an error if unable to migrate
// the given data.
//
// MigrateMsg has some data on how to perform the migration.
Migrate2(
checksum wasmvm.Checksum,
env wasmvmtypes.Env,
migrateMsg []byte,
migrateInfo wasmvmtypes.MigrateInfo,
store wasmvm.KVStore,
goapi wasmvm.GoAPI,
querier wasmvm.Querier,
gasMeter wasmvm.GasMeter,
gasLimit uint64,
deserCost wasmvmtypes.UFraction,
) (*wasmvmtypes.ContractResult, uint64, error)

// Sudo runs an existing contract in read/write mode (like Execute), but is never exposed to external callers
// (either transactions or government proposals), but can only be called by other native Go modules directly.
//
Expand Down

0 comments on commit 15b4814

Please sign in to comment.