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

Conditional migrate calling #556

Merged
merged 1 commit into from
Oct 10, 2024
Merged
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
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
Loading