Skip to content

Commit

Permalink
Merge pull request #556 from CosmWasm/tkulik/conditional_migrate
Browse files Browse the repository at this point in the history
Conditional migrate calling
  • Loading branch information
chipshort authored Oct 10, 2024
2 parents 7d28c41 + c9d63a4 commit 34db228
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 2 deletions.
13 changes: 13 additions & 0 deletions internal/api/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,19 @@ struct UnmanagedVector migrate(struct cache_t *cache,
struct GasReport *gas_report,
struct UnmanagedVector *error_msg);

struct UnmanagedVector migrate_with_info(struct cache_t *cache,
struct ByteSliceView checksum,
struct ByteSliceView env,
struct ByteSliceView msg,
struct ByteSliceView migrate_info,
struct Db db,
struct GoApi api,
struct GoQuerier querier,
uint64_t gas_limit,
bool print_debug,
struct GasReport *gas_report,
struct UnmanagedVector *error_msg);

struct UnmanagedVector sudo(struct cache_t *cache,
struct ByteSliceView checksum,
struct ByteSliceView env,
Expand Down
45 changes: 45 additions & 0 deletions internal/api/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,51 @@ func Migrate(
return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil
}

func MigrateWithInfo(
cache Cache,
checksum []byte,
env []byte,
msg []byte,
migrateInfo []byte,
gasMeter *types.GasMeter,
store types.KVStore,
api *types.GoAPI,
querier *Querier,
gasLimit uint64,
printDebug bool,
) ([]byte, types.GasReport, error) {
cs := makeView(checksum)
defer runtime.KeepAlive(checksum)
e := makeView(env)
defer runtime.KeepAlive(env)
m := makeView(msg)
defer runtime.KeepAlive(msg)
i := makeView(migrateInfo)
defer runtime.KeepAlive(i)
var pinner runtime.Pinner
pinner.Pin(gasMeter)
checkAndPinAPI(api, pinner)
checkAndPinQuerier(querier, pinner)
defer pinner.Unpin()

callID := startCall()
defer endCall(callID)

dbState := buildDBState(store, callID)
db := buildDB(&dbState, gasMeter)
a := buildAPI(api)
q := buildQuerier(querier)
var gasReport C.GasReport
errmsg := uninitializedUnmanagedVector()

res, err := C.migrate_with_info(cache.ptr, cs, e, m, i, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg)
if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success {
// Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0.
return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg)
}
return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil
}

func Sudo(
cache Cache,
checksum []byte,
Expand Down
44 changes: 44 additions & 0 deletions lib_libwasmvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,50 @@ func (vm *VM) Migrate(
return &result, gasReport.UsedInternally, nil
}

// MigrateWithInfo will migrate an existing contract to a new code binary.
// This takes storage of the data from the original contract and the Checksum 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.
//
// MigrateWithInfo takes one more argument - `migateInfo`. It consist of an additional data
// related to the on-chain current contract's state version.
func (vm *VM) MigrateWithInfo(
checksum Checksum,
env types.Env,
migrateMsg []byte,
migrateInfo types.MigrateInfo,
store KVStore,
goapi GoAPI,
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.ContractResult, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
return nil, 0, err
}

migrateBin, err := json.Marshal(migrateInfo)
if err != nil {
return nil, 0, err
}

data, gasReport, err := api.MigrateWithInfo(vm.cache, checksum, envBin, migrateMsg, migrateBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug)
if err != nil {
return nil, gasReport.UsedInternally, err
}

var result types.ContractResult
err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result)
if err != nil {
return nil, gasReport.UsedInternally, err
}
return &result, gasReport.UsedInternally, nil
}

// Sudo allows native Go modules to make priviledged (sudo) calls on the contract.
// The contract can expose entry points that cannot be triggered by any transaction, but only via
// native Go modules, and delegate the access control to the system.
Expand Down
13 changes: 13 additions & 0 deletions libwasmvm/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,19 @@ struct UnmanagedVector migrate(struct cache_t *cache,
struct GasReport *gas_report,
struct UnmanagedVector *error_msg);

struct UnmanagedVector migrate_with_info(struct cache_t *cache,
struct ByteSliceView checksum,
struct ByteSliceView env,
struct ByteSliceView msg,
struct ByteSliceView migrate_info,
struct Db db,
struct GoApi api,
struct GoQuerier querier,
uint64_t gas_limit,
bool print_debug,
struct GasReport *gas_report,
struct UnmanagedVector *error_msg);

struct UnmanagedVector sudo(struct cache_t *cache,
struct ByteSliceView checksum,
struct ByteSliceView env,
Expand Down
36 changes: 34 additions & 2 deletions libwasmvm/src/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use cosmwasm_vm::{
call_execute_raw, call_ibc_channel_close_raw, call_ibc_channel_connect_raw,
call_ibc_channel_open_raw, call_ibc_destination_callback_raw, call_ibc_packet_ack_raw,
call_ibc_packet_receive_raw, call_ibc_packet_timeout_raw, call_ibc_source_callback_raw,
call_instantiate_raw, call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend,
Cache, Instance, InstanceOptions, VmResult,
call_instantiate_raw, call_migrate_raw, call_migrate_with_info_raw, call_query_raw,
call_reply_raw, call_sudo_raw, Backend, Cache, Instance, InstanceOptions, VmResult,
};

use crate::api::GoApi;
Expand Down Expand Up @@ -126,6 +126,38 @@ pub extern "C" fn migrate(
)
}

#[no_mangle]
pub extern "C" fn migrate_with_info(
cache: *mut cache_t,
checksum: ByteSliceView,
env: ByteSliceView,
msg: ByteSliceView,
migrate_info: ByteSliceView,
db: Db,
api: GoApi,
querier: GoQuerier,
gas_limit: u64,
print_debug: bool,
gas_report: Option<&mut GasReport>,
error_msg: Option<&mut UnmanagedVector>,
) -> UnmanagedVector {
call_3_args(
call_migrate_with_info_raw,
cache,
checksum,
env,
msg,
migrate_info,
db,
api,
querier,
gas_limit,
print_debug,
gas_report,
error_msg,
)
}

#[no_mangle]
pub extern "C" fn sudo(
cache: *mut cache_t,
Expand Down
15 changes: 15 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,21 @@ func (pm *PinnedMetrics) UnmarshalMessagePack(data []byte) error {
// never result in a "None" value on the Rust side, making the "Option" pointless.
type Array[C any] []C

// The structure contains additional information related to the
// contract's migration procedure - the sender address and
// the contract's migrate version currently stored on the blockchain.
// The `OldMigrateVersion` is optional, since there is no guarantee
// that the currently stored contract's binary contains that information.
type MigrateInfo struct {
// Address of the sender.
//
// This is the `sender` field from [`MsgMigrateContract`](https://github.com/CosmWasm/wasmd/blob/v0.53.0/proto/cosmwasm/wasm/v1/tx.proto#L217-L233).
Sender HumanAddress `json:"sender"`
// Migrate version of the previous contract. It's optional, since
// adding the version number to the binary is not a mandatory feature.
OldMigrateVersion *uint64 `json:"old_migrate_version"`
}

// MarshalJSON ensures that we get "[]" for nil arrays
func (a Array[C]) MarshalJSON() ([]byte, error) {
if len(a) == 0 {
Expand Down

0 comments on commit 34db228

Please sign in to comment.