Skip to content

Commit 03bebe0

Browse files
authored
Merge pull request #136 from CosmWasm/real-contract-migrations
Integrate contract migrations
2 parents f1977a0 + 5d482fc commit 03bebe0

File tree

8 files changed

+165
-30
lines changed

8 files changed

+165
-30
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/CosmWasm/wasmd
33
go 1.13
44

55
require (
6-
github.com/CosmWasm/go-cosmwasm v0.8.1-0.20200604114456-1b2359bb7eb9
6+
github.com/CosmWasm/go-cosmwasm v0.8.2-0.20200608192602-082a8c18f964
77
github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a // indirect
88
github.com/cosmos/cosmos-sdk v0.38.3
99
github.com/golang/mock v1.4.3 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f h1:4O1om+U
1111
github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
1212
github.com/CosmWasm/go-cosmwasm v0.8.1-0.20200604114456-1b2359bb7eb9 h1:UJOWFHfh2SG47GJcQsjbcEnfw7JM4HPM2cFO1fjjqEs=
1313
github.com/CosmWasm/go-cosmwasm v0.8.1-0.20200604114456-1b2359bb7eb9/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc=
14+
github.com/CosmWasm/go-cosmwasm v0.8.2-0.20200608192602-082a8c18f964 h1:qPuQfJFDXLcm8jhoWZs6x8jKr3c3yAlv40e93DUBHRY=
15+
github.com/CosmWasm/go-cosmwasm v0.8.2-0.20200608192602-082a8c18f964/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc=
1416
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
1517
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
1618
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=

x/wasm/internal/keeper/keeper_test.go

+162-29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package keeper
22

33
import (
44
"bytes"
5+
"encoding/base64"
56
"encoding/binary"
67
"encoding/json"
78
"io/ioutil"
@@ -297,7 +298,7 @@ func TestExecute(t *testing.T) {
297298

298299
// make sure gas is properly deducted from ctx
299300
gasAfter := ctx.GasMeter().GasConsumed()
300-
require.Equal(t, uint64(0x7fa1), gasAfter-gasBefore)
301+
require.Equal(t, uint64(0x7f9e), gasAfter-gasBefore)
301302

302303
// ensure bob now exists and got both payments released
303304
bobAcct = accKeeper.GetAccount(ctx, bob)
@@ -310,7 +311,7 @@ func TestExecute(t *testing.T) {
310311
require.NotNil(t, contractAcct)
311312
assert.Equal(t, sdk.Coins(nil), contractAcct.GetCoins())
312313

313-
t.Logf("Duration: %v (31728 gas)\n", diff)
314+
t.Logf("Duration: %v (%d gas)\n", diff, gasAfter-gasBefore)
314315
}
315316

316317
func TestExecuteWithNonExistingAddress(t *testing.T) {
@@ -461,9 +462,8 @@ func TestMigrate(t *testing.T) {
461462
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper
462463

463464
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
464-
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
465465
creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit...))
466-
fred := createFakeFundedAccount(ctx, accKeeper, topUp)
466+
fred := createFakeFundedAccount(ctx, accKeeper, sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)))
467467

468468
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
469469
require.NoError(t, err)
@@ -475,41 +475,56 @@ func TestMigrate(t *testing.T) {
475475
require.NotEqual(t, originalContractID, newContractID)
476476

477477
_, _, anyAddr := keyPubAddr()
478+
_, _, newVerifierAddr := keyPubAddr()
478479
initMsg := InitMsg{
479480
Verifier: fred,
480481
Beneficiary: anyAddr,
481482
}
482483
initMsgBz, err := json.Marshal(initMsg)
483484
require.NoError(t, err)
485+
486+
migMsg := struct {
487+
Verifier sdk.AccAddress `json:"verifier"`
488+
}{Verifier: newVerifierAddr}
489+
migMsgBz, err := json.Marshal(migMsg)
490+
require.NoError(t, err)
491+
484492
specs := map[string]struct {
485493
admin sdk.AccAddress
486494
overrideContractAddr sdk.AccAddress
487495
caller sdk.AccAddress
488496
codeID uint64
489497
migrateMsg []byte
490498
expErr *sdkerrors.Error
499+
expVerifier sdk.AccAddress
491500
}{
492501
"all good with same code id": {
493-
admin: creator,
494-
caller: creator,
495-
codeID: originalContractID,
502+
admin: creator,
503+
caller: creator,
504+
codeID: originalContractID,
505+
migrateMsg: migMsgBz,
506+
expVerifier: newVerifierAddr,
496507
},
497-
"all good with new code id": {
498-
admin: creator,
499-
caller: creator,
500-
codeID: newContractID,
508+
"all good with different code id": {
509+
admin: creator,
510+
caller: creator,
511+
codeID: newContractID,
512+
migrateMsg: migMsgBz,
513+
expVerifier: newVerifierAddr,
501514
},
502515
"all good with admin set": {
503-
admin: fred,
504-
caller: fred,
505-
codeID: newContractID,
516+
admin: fred,
517+
caller: fred,
518+
codeID: newContractID,
519+
migrateMsg: migMsgBz,
520+
expVerifier: newVerifierAddr,
506521
},
507522
"prevent migration when admin was not set on instantiate": {
508523
caller: creator,
509524
codeID: originalContractID,
510525
expErr: sdkerrors.ErrUnauthorized,
511526
},
512-
"prevent migration when not admin": {
527+
"prevent migration when not sent by admin": {
513528
caller: creator,
514529
admin: fred,
515530
codeID: originalContractID,
@@ -528,18 +543,21 @@ func TestMigrate(t *testing.T) {
528543
codeID: originalContractID,
529544
expErr: sdkerrors.ErrInvalidRequest,
530545
},
531-
"fail when migration caused error": {
546+
"fail in contract with invalid migrate msg": {
532547
admin: creator,
533548
caller: creator,
534549
codeID: originalContractID,
535-
migrateMsg: bytes.Repeat([]byte{0x1}, 7), // condition hard coded in stub: >6 = error
550+
migrateMsg: bytes.Repeat([]byte{0x1}, 7),
536551
expErr: types.ErrMigrationFailed,
537552
},
553+
"fail in contract without migrate msg": {
554+
admin: creator,
555+
caller: creator,
556+
codeID: originalContractID,
557+
expErr: types.ErrMigrationFailed,
558+
},
538559
}
539-
var (
540-
builtIntoGoCosmWasmStubGas = sdk.Gas(10000)
541-
builtIntoGoCosmWasmStubData = []byte(("my-migration-response-data"))
542-
)
560+
543561
for msg, spec := range specs {
544562
t.Run(msg, func(t *testing.T) {
545563
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
@@ -548,26 +566,141 @@ func TestMigrate(t *testing.T) {
548566
if spec.overrideContractAddr != nil {
549567
addr = spec.overrideContractAddr
550568
}
551-
gasBefore := ctx.GasMeter().GasConsumed()
552-
res, err := keeper.Migrate(ctx, addr, spec.caller, spec.codeID, spec.migrateMsg)
569+
_, err = keeper.Migrate(ctx, addr, spec.caller, spec.codeID, spec.migrateMsg)
553570
require.True(t, spec.expErr.Is(err), "expected %v but got %+v", spec.expErr, err)
554571
if spec.expErr != nil {
555572
return
556573
}
557-
gasAfter := ctx.GasMeter().GasConsumed()
558-
assert.Greater(t, gasAfter-gasBefore, builtIntoGoCosmWasmStubGas/GasMultiplier)
559-
assert.Equal(t, builtIntoGoCosmWasmStubData, res.Data)
560574
cInfo := keeper.GetContractInfo(ctx, addr)
561575
assert.Equal(t, spec.codeID, cInfo.CodeID)
562576
assert.Equal(t, originalContractID, cInfo.PreviousCodeID)
563577
assert.Equal(t, types.NewCreatedAt(ctx), cInfo.LastUpdated)
564-
// TODO: check contract store was updated by migration code (impl also in contract)
565-
// TODO: check any messages dispatched proper
566-
// TODO: check events?
578+
579+
m := keeper.QueryRaw(ctx, addr, []byte("config"))
580+
require.Len(t, m, 1)
581+
var stored map[string][]byte
582+
require.NoError(t, json.Unmarshal(m[0].Value, &stored))
583+
require.Contains(t, stored, "verifier")
584+
require.NoError(t, err)
585+
assert.Equal(t, spec.expVerifier, sdk.AccAddress(stored["verifier"]))
567586
})
568587
}
569588
}
570589

590+
func TestMigrateWithDispatchedMessage(t *testing.T) {
591+
tempDir, err := ioutil.TempDir("", "wasm")
592+
require.NoError(t, err)
593+
defer os.RemoveAll(tempDir)
594+
ctx, keepers := CreateTestInput(t, false, tempDir, SupportedFeatures, nil, nil)
595+
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper
596+
597+
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
598+
creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit...))
599+
fred := createFakeFundedAccount(ctx, accKeeper, sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)))
600+
601+
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
602+
require.NoError(t, err)
603+
burnerCode, err := ioutil.ReadFile("./testdata/burner.wasm")
604+
require.NoError(t, err)
605+
606+
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
607+
require.NoError(t, err)
608+
burnerContractID, err := keeper.Create(ctx, creator, burnerCode, "", "")
609+
require.NoError(t, err)
610+
require.NotEqual(t, originalContractID, burnerContractID)
611+
612+
_, _, myPayoutAddr := keyPubAddr()
613+
initMsg := InitMsg{
614+
Verifier: fred,
615+
Beneficiary: fred,
616+
}
617+
initMsgBz, err := json.Marshal(initMsg)
618+
require.NoError(t, err)
619+
620+
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
621+
contractAddr, err := keeper.Instantiate(ctx, originalContractID, creator, fred, initMsgBz, "demo contract", deposit)
622+
require.NoError(t, err)
623+
624+
migMsg := struct {
625+
Payout sdk.AccAddress `json:"payout"`
626+
}{Payout: myPayoutAddr}
627+
migMsgBz, err := json.Marshal(migMsg)
628+
require.NoError(t, err)
629+
ctx = ctx.WithEventManager(sdk.NewEventManager()).WithBlockHeight(ctx.BlockHeight() + 1)
630+
res, err := keeper.Migrate(ctx, contractAddr, fred, burnerContractID, migMsgBz)
631+
require.NoError(t, err)
632+
dataBz, err := base64.StdEncoding.DecodeString(string(res.Data))
633+
require.NoError(t, err)
634+
assert.Equal(t, "burnt", string(dataBz))
635+
assert.Equal(t, "", res.Log)
636+
type dict map[string]interface{}
637+
expEvents := []dict{
638+
{
639+
"Type": "wasm",
640+
"Attr": []dict{
641+
{"contract_address": contractAddr},
642+
{"action": "burn"},
643+
{"payout": myPayoutAddr},
644+
},
645+
},
646+
{
647+
"Type": "transfer",
648+
"Attr": []dict{
649+
{"recipient": myPayoutAddr},
650+
{"sender": contractAddr},
651+
{"amount": "100000denom"},
652+
},
653+
},
654+
{
655+
"Type": "message",
656+
"Attr": []dict{
657+
{"sender": contractAddr},
658+
},
659+
},
660+
{
661+
"Type": "message",
662+
"Attr": []dict{
663+
{"module": "bank"},
664+
},
665+
},
666+
}
667+
expJsonEvts := string(mustMarshal(t, expEvents))
668+
assert.JSONEq(t, expJsonEvts, prettyEvents(t, ctx.EventManager().Events()))
669+
670+
// all persistent data cleared
671+
m := keeper.QueryRaw(ctx, contractAddr, []byte("config"))
672+
require.Len(t, m, 0)
673+
674+
// and all deposit tokens sent to myPayoutAddr
675+
balance := accKeeper.GetAccount(ctx, myPayoutAddr).GetCoins()
676+
assert.Equal(t, deposit, balance)
677+
}
678+
679+
func prettyEvents(t *testing.T, events sdk.Events) string {
680+
t.Helper()
681+
type prettyEvent struct {
682+
Type string
683+
Attr []map[string]string
684+
}
685+
686+
r := make([]prettyEvent, len(events))
687+
for i, e := range events {
688+
attr := make([]map[string]string, len(e.Attributes))
689+
for j, a := range e.Attributes {
690+
attr[j] = map[string]string{string(a.Key): string(a.Value)}
691+
}
692+
r[i] = prettyEvent{Type: e.Type, Attr: attr}
693+
}
694+
return string(mustMarshal(t, r))
695+
}
696+
697+
func mustMarshal(t *testing.T, r interface{}) []byte {
698+
t.Helper()
699+
bz, err := json.Marshal(r)
700+
require.NoError(t, err)
701+
return bz
702+
}
703+
571704
func TestUpdateContractAdmin(t *testing.T) {
572705
tempDir, err := ioutil.TempDir("", "wasm")
573706
require.NoError(t, err)
135 KB
Binary file not shown.
3.77 KB
Binary file not shown.
Binary file not shown.
-620 Bytes
Binary file not shown.
-616 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)