Skip to content

Commit c9d63a4

Browse files
author
Tomasz Kulik
committed
feat: Add conditional migrate calling
1 parent 7d28c41 commit c9d63a4

File tree

6 files changed

+164
-2
lines changed

6 files changed

+164
-2
lines changed

internal/api/bindings.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,19 @@ struct UnmanagedVector migrate(struct cache_t *cache,
491491
struct GasReport *gas_report,
492492
struct UnmanagedVector *error_msg);
493493

494+
struct UnmanagedVector migrate_with_info(struct cache_t *cache,
495+
struct ByteSliceView checksum,
496+
struct ByteSliceView env,
497+
struct ByteSliceView msg,
498+
struct ByteSliceView migrate_info,
499+
struct Db db,
500+
struct GoApi api,
501+
struct GoQuerier querier,
502+
uint64_t gas_limit,
503+
bool print_debug,
504+
struct GasReport *gas_report,
505+
struct UnmanagedVector *error_msg);
506+
494507
struct UnmanagedVector sudo(struct cache_t *cache,
495508
struct ByteSliceView checksum,
496509
struct ByteSliceView env,

internal/api/lib.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,51 @@ func Migrate(
337337
return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil
338338
}
339339

340+
func MigrateWithInfo(
341+
cache Cache,
342+
checksum []byte,
343+
env []byte,
344+
msg []byte,
345+
migrateInfo []byte,
346+
gasMeter *types.GasMeter,
347+
store types.KVStore,
348+
api *types.GoAPI,
349+
querier *Querier,
350+
gasLimit uint64,
351+
printDebug bool,
352+
) ([]byte, types.GasReport, error) {
353+
cs := makeView(checksum)
354+
defer runtime.KeepAlive(checksum)
355+
e := makeView(env)
356+
defer runtime.KeepAlive(env)
357+
m := makeView(msg)
358+
defer runtime.KeepAlive(msg)
359+
i := makeView(migrateInfo)
360+
defer runtime.KeepAlive(i)
361+
var pinner runtime.Pinner
362+
pinner.Pin(gasMeter)
363+
checkAndPinAPI(api, pinner)
364+
checkAndPinQuerier(querier, pinner)
365+
defer pinner.Unpin()
366+
367+
callID := startCall()
368+
defer endCall(callID)
369+
370+
dbState := buildDBState(store, callID)
371+
db := buildDB(&dbState, gasMeter)
372+
a := buildAPI(api)
373+
q := buildQuerier(querier)
374+
var gasReport C.GasReport
375+
errmsg := uninitializedUnmanagedVector()
376+
377+
res, err := C.migrate_with_info(cache.ptr, cs, e, m, i, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg)
378+
if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success {
379+
// Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0.
380+
return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg)
381+
}
382+
return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil
383+
}
384+
340385
func Sudo(
341386
cache Cache,
342387
checksum []byte,

lib_libwasmvm.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,50 @@ func (vm *VM) Migrate(
262262
return &result, gasReport.UsedInternally, nil
263263
}
264264

265+
// MigrateWithInfo will migrate an existing contract to a new code binary.
266+
// This takes storage of the data from the original contract and the Checksum of the new contract that should
267+
// replace it. This allows it to run a migration step if needed, or return an error if unable to migrate
268+
// the given data.
269+
//
270+
// MigrateMsg has some data on how to perform the migration.
271+
//
272+
// MigrateWithInfo takes one more argument - `migateInfo`. It consist of an additional data
273+
// related to the on-chain current contract's state version.
274+
func (vm *VM) MigrateWithInfo(
275+
checksum Checksum,
276+
env types.Env,
277+
migrateMsg []byte,
278+
migrateInfo types.MigrateInfo,
279+
store KVStore,
280+
goapi GoAPI,
281+
querier Querier,
282+
gasMeter GasMeter,
283+
gasLimit uint64,
284+
deserCost types.UFraction,
285+
) (*types.ContractResult, uint64, error) {
286+
envBin, err := json.Marshal(env)
287+
if err != nil {
288+
return nil, 0, err
289+
}
290+
291+
migrateBin, err := json.Marshal(migrateInfo)
292+
if err != nil {
293+
return nil, 0, err
294+
}
295+
296+
data, gasReport, err := api.MigrateWithInfo(vm.cache, checksum, envBin, migrateMsg, migrateBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug)
297+
if err != nil {
298+
return nil, gasReport.UsedInternally, err
299+
}
300+
301+
var result types.ContractResult
302+
err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result)
303+
if err != nil {
304+
return nil, gasReport.UsedInternally, err
305+
}
306+
return &result, gasReport.UsedInternally, nil
307+
}
308+
265309
// Sudo allows native Go modules to make priviledged (sudo) calls on the contract.
266310
// The contract can expose entry points that cannot be triggered by any transaction, but only via
267311
// native Go modules, and delegate the access control to the system.

libwasmvm/bindings.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,19 @@ struct UnmanagedVector migrate(struct cache_t *cache,
491491
struct GasReport *gas_report,
492492
struct UnmanagedVector *error_msg);
493493

494+
struct UnmanagedVector migrate_with_info(struct cache_t *cache,
495+
struct ByteSliceView checksum,
496+
struct ByteSliceView env,
497+
struct ByteSliceView msg,
498+
struct ByteSliceView migrate_info,
499+
struct Db db,
500+
struct GoApi api,
501+
struct GoQuerier querier,
502+
uint64_t gas_limit,
503+
bool print_debug,
504+
struct GasReport *gas_report,
505+
struct UnmanagedVector *error_msg);
506+
494507
struct UnmanagedVector sudo(struct cache_t *cache,
495508
struct ByteSliceView checksum,
496509
struct ByteSliceView env,

libwasmvm/src/calls.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use cosmwasm_vm::{
1010
call_execute_raw, call_ibc_channel_close_raw, call_ibc_channel_connect_raw,
1111
call_ibc_channel_open_raw, call_ibc_destination_callback_raw, call_ibc_packet_ack_raw,
1212
call_ibc_packet_receive_raw, call_ibc_packet_timeout_raw, call_ibc_source_callback_raw,
13-
call_instantiate_raw, call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend,
14-
Cache, Instance, InstanceOptions, VmResult,
13+
call_instantiate_raw, call_migrate_raw, call_migrate_with_info_raw, call_query_raw,
14+
call_reply_raw, call_sudo_raw, Backend, Cache, Instance, InstanceOptions, VmResult,
1515
};
1616

1717
use crate::api::GoApi;
@@ -126,6 +126,38 @@ pub extern "C" fn migrate(
126126
)
127127
}
128128

129+
#[no_mangle]
130+
pub extern "C" fn migrate_with_info(
131+
cache: *mut cache_t,
132+
checksum: ByteSliceView,
133+
env: ByteSliceView,
134+
msg: ByteSliceView,
135+
migrate_info: ByteSliceView,
136+
db: Db,
137+
api: GoApi,
138+
querier: GoQuerier,
139+
gas_limit: u64,
140+
print_debug: bool,
141+
gas_report: Option<&mut GasReport>,
142+
error_msg: Option<&mut UnmanagedVector>,
143+
) -> UnmanagedVector {
144+
call_3_args(
145+
call_migrate_with_info_raw,
146+
cache,
147+
checksum,
148+
env,
149+
msg,
150+
migrate_info,
151+
db,
152+
api,
153+
querier,
154+
gas_limit,
155+
print_debug,
156+
gas_report,
157+
error_msg,
158+
)
159+
}
160+
129161
#[no_mangle]
130162
pub extern "C" fn sudo(
131163
cache: *mut cache_t,

types/types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,21 @@ func (pm *PinnedMetrics) UnmarshalMessagePack(data []byte) error {
218218
// never result in a "None" value on the Rust side, making the "Option" pointless.
219219
type Array[C any] []C
220220

221+
// The structure contains additional information related to the
222+
// contract's migration procedure - the sender address and
223+
// the contract's migrate version currently stored on the blockchain.
224+
// The `OldMigrateVersion` is optional, since there is no guarantee
225+
// that the currently stored contract's binary contains that information.
226+
type MigrateInfo struct {
227+
// Address of the sender.
228+
//
229+
// This is the `sender` field from [`MsgMigrateContract`](https://github.com/CosmWasm/wasmd/blob/v0.53.0/proto/cosmwasm/wasm/v1/tx.proto#L217-L233).
230+
Sender HumanAddress `json:"sender"`
231+
// Migrate version of the previous contract. It's optional, since
232+
// adding the version number to the binary is not a mandatory feature.
233+
OldMigrateVersion *uint64 `json:"old_migrate_version"`
234+
}
235+
221236
// MarshalJSON ensures that we get "[]" for nil arrays
222237
func (a Array[C]) MarshalJSON() ([]byte, error) {
223238
if len(a) == 0 {

0 commit comments

Comments
 (0)