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 f8d401a commit e06a7fc
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 8 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 migrate2(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 Migrate2(
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.migrate2(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
Binary file modified internal/api/libwasmvm.x86_64.so
Binary file not shown.
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
}

// Migrate 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.
//
// Migrate2 takes one more argument - `migateInfo`. It consist of an additional data
// related to the on-chain current contract's state version.
func (vm *VM) Migrate2(
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.Migrate2(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
6 changes: 1 addition & 5 deletions libwasmvm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions libwasmvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ default = []
backtraces = []

[dependencies]
cosmwasm-std = { git = "https://github.com/CosmWasm/cosmwasm.git", rev = "v2.1.3", features = [
# TODO tkulik: UNDO
cosmwasm-std = { path = "/home/tkulik/Workspace/cosmwasm/packages/std", features = [
"staking",
"stargate",
"iterator",
] }
cosmwasm-vm = { git = "https://github.com/CosmWasm/cosmwasm.git", rev = "v2.1.3", features = [
# TODO tkulik: UNDO
cosmwasm-vm = { path = "/home/tkulik/Workspace/cosmwasm/packages/vm", features = [
"staking",
"stargate",
"iterator",
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 migrate2(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
34 changes: 33 additions & 1 deletion libwasmvm/src/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ 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,
call_instantiate_raw, call_migrate_raw, call_migrate2_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend,
Cache, Instance, InstanceOptions, VmResult,
};

Expand Down Expand Up @@ -126,6 +126,38 @@ pub extern "C" fn migrate(
)
}

#[no_mangle]
pub extern "C" fn migrate2(
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_migrate2_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
14 changes: 14 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,20 @@ 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 state migration procedure - the sender address and
// the contract's state version currently stored on the blockchain.
// The `old_state_version` is optional, since there is no guarantee
// that the currently stored contract's binary contains that information.
type MigrateInfo struct {
// Bech32 encoded sdk.AccAddress of the contract, to be used when sending messages
Sender HumanAddress `json:"sender"`
// Version of the previous contract's state. It's optional, since
// adding the state's version number to the binary is not a
// mandatory feature.
OldStateVersion *uint64 `json:"old_state_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 e06a7fc

Please sign in to comment.