diff --git a/go.mod b/go.mod index 97d125c2d..355a7495a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/CosmWasm/wasmd go 1.13 require ( - github.com/CosmWasm/go-cosmwasm v0.8.1-0.20200604114456-1b2359bb7eb9 + github.com/CosmWasm/go-cosmwasm v0.8.2-0.20200608192602-082a8c18f964 github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a // indirect github.com/cosmos/cosmos-sdk v0.38.3 github.com/golang/mock v1.4.3 // indirect diff --git a/go.sum b/go.sum index a368520d2..c7e6f7926 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f h1:4O1om+U github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/CosmWasm/go-cosmwasm v0.8.1-0.20200604114456-1b2359bb7eb9 h1:UJOWFHfh2SG47GJcQsjbcEnfw7JM4HPM2cFO1fjjqEs= github.com/CosmWasm/go-cosmwasm v0.8.1-0.20200604114456-1b2359bb7eb9/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc= +github.com/CosmWasm/go-cosmwasm v0.8.2-0.20200608192602-082a8c18f964 h1:qPuQfJFDXLcm8jhoWZs6x8jKr3c3yAlv40e93DUBHRY= +github.com/CosmWasm/go-cosmwasm v0.8.2-0.20200608192602-082a8c18f964/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= diff --git a/x/wasm/internal/keeper/keeper_test.go b/x/wasm/internal/keeper/keeper_test.go index 3c9b84f30..f668a5dad 100644 --- a/x/wasm/internal/keeper/keeper_test.go +++ b/x/wasm/internal/keeper/keeper_test.go @@ -2,6 +2,7 @@ package keeper import ( "bytes" + "encoding/base64" "encoding/binary" "encoding/json" "io/ioutil" @@ -297,7 +298,7 @@ func TestExecute(t *testing.T) { // make sure gas is properly deducted from ctx gasAfter := ctx.GasMeter().GasConsumed() - require.Equal(t, uint64(0x7fa1), gasAfter-gasBefore) + require.Equal(t, uint64(0x7f9e), gasAfter-gasBefore) // ensure bob now exists and got both payments released bobAcct = accKeeper.GetAccount(ctx, bob) @@ -310,7 +311,7 @@ func TestExecute(t *testing.T) { require.NotNil(t, contractAcct) assert.Equal(t, sdk.Coins(nil), contractAcct.GetCoins()) - t.Logf("Duration: %v (31728 gas)\n", diff) + t.Logf("Duration: %v (%d gas)\n", diff, gasAfter-gasBefore) } func TestExecuteWithNonExistingAddress(t *testing.T) { @@ -461,9 +462,8 @@ func TestMigrate(t *testing.T) { accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit...)) - fred := createFakeFundedAccount(ctx, accKeeper, topUp) + fred := createFakeFundedAccount(ctx, accKeeper, sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))) wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm") require.NoError(t, err) @@ -475,12 +475,20 @@ func TestMigrate(t *testing.T) { require.NotEqual(t, originalContractID, newContractID) _, _, anyAddr := keyPubAddr() + _, _, newVerifierAddr := keyPubAddr() initMsg := InitMsg{ Verifier: fred, Beneficiary: anyAddr, } initMsgBz, err := json.Marshal(initMsg) require.NoError(t, err) + + migMsg := struct { + Verifier sdk.AccAddress `json:"verifier"` + }{Verifier: newVerifierAddr} + migMsgBz, err := json.Marshal(migMsg) + require.NoError(t, err) + specs := map[string]struct { admin sdk.AccAddress overrideContractAddr sdk.AccAddress @@ -488,28 +496,35 @@ func TestMigrate(t *testing.T) { codeID uint64 migrateMsg []byte expErr *sdkerrors.Error + expVerifier sdk.AccAddress }{ "all good with same code id": { - admin: creator, - caller: creator, - codeID: originalContractID, + admin: creator, + caller: creator, + codeID: originalContractID, + migrateMsg: migMsgBz, + expVerifier: newVerifierAddr, }, - "all good with new code id": { - admin: creator, - caller: creator, - codeID: newContractID, + "all good with different code id": { + admin: creator, + caller: creator, + codeID: newContractID, + migrateMsg: migMsgBz, + expVerifier: newVerifierAddr, }, "all good with admin set": { - admin: fred, - caller: fred, - codeID: newContractID, + admin: fred, + caller: fred, + codeID: newContractID, + migrateMsg: migMsgBz, + expVerifier: newVerifierAddr, }, "prevent migration when admin was not set on instantiate": { caller: creator, codeID: originalContractID, expErr: sdkerrors.ErrUnauthorized, }, - "prevent migration when not admin": { + "prevent migration when not sent by admin": { caller: creator, admin: fred, codeID: originalContractID, @@ -528,18 +543,21 @@ func TestMigrate(t *testing.T) { codeID: originalContractID, expErr: sdkerrors.ErrInvalidRequest, }, - "fail when migration caused error": { + "fail in contract with invalid migrate msg": { admin: creator, caller: creator, codeID: originalContractID, - migrateMsg: bytes.Repeat([]byte{0x1}, 7), // condition hard coded in stub: >6 = error + migrateMsg: bytes.Repeat([]byte{0x1}, 7), expErr: types.ErrMigrationFailed, }, + "fail in contract without migrate msg": { + admin: creator, + caller: creator, + codeID: originalContractID, + expErr: types.ErrMigrationFailed, + }, } - var ( - builtIntoGoCosmWasmStubGas = sdk.Gas(10000) - builtIntoGoCosmWasmStubData = []byte(("my-migration-response-data")) - ) + for msg, spec := range specs { t.Run(msg, func(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) @@ -548,26 +566,141 @@ func TestMigrate(t *testing.T) { if spec.overrideContractAddr != nil { addr = spec.overrideContractAddr } - gasBefore := ctx.GasMeter().GasConsumed() - res, err := keeper.Migrate(ctx, addr, spec.caller, spec.codeID, spec.migrateMsg) + _, err = keeper.Migrate(ctx, addr, spec.caller, spec.codeID, spec.migrateMsg) require.True(t, spec.expErr.Is(err), "expected %v but got %+v", spec.expErr, err) if spec.expErr != nil { return } - gasAfter := ctx.GasMeter().GasConsumed() - assert.Greater(t, gasAfter-gasBefore, builtIntoGoCosmWasmStubGas/GasMultiplier) - assert.Equal(t, builtIntoGoCosmWasmStubData, res.Data) cInfo := keeper.GetContractInfo(ctx, addr) assert.Equal(t, spec.codeID, cInfo.CodeID) assert.Equal(t, originalContractID, cInfo.PreviousCodeID) assert.Equal(t, types.NewCreatedAt(ctx), cInfo.LastUpdated) - // TODO: check contract store was updated by migration code (impl also in contract) - // TODO: check any messages dispatched proper - // TODO: check events? + + m := keeper.QueryRaw(ctx, addr, []byte("config")) + require.Len(t, m, 1) + var stored map[string][]byte + require.NoError(t, json.Unmarshal(m[0].Value, &stored)) + require.Contains(t, stored, "verifier") + require.NoError(t, err) + assert.Equal(t, spec.expVerifier, sdk.AccAddress(stored["verifier"])) }) } } +func TestMigrateWithDispatchedMessage(t *testing.T) { + tempDir, err := ioutil.TempDir("", "wasm") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + ctx, keepers := CreateTestInput(t, false, tempDir, SupportedFeatures, nil, nil) + accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper + + deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) + creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit...)) + fred := createFakeFundedAccount(ctx, accKeeper, sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))) + + wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm") + require.NoError(t, err) + burnerCode, err := ioutil.ReadFile("./testdata/burner.wasm") + require.NoError(t, err) + + originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "") + require.NoError(t, err) + burnerContractID, err := keeper.Create(ctx, creator, burnerCode, "", "") + require.NoError(t, err) + require.NotEqual(t, originalContractID, burnerContractID) + + _, _, myPayoutAddr := keyPubAddr() + initMsg := InitMsg{ + Verifier: fred, + Beneficiary: fred, + } + initMsgBz, err := json.Marshal(initMsg) + require.NoError(t, err) + + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + contractAddr, err := keeper.Instantiate(ctx, originalContractID, creator, fred, initMsgBz, "demo contract", deposit) + require.NoError(t, err) + + migMsg := struct { + Payout sdk.AccAddress `json:"payout"` + }{Payout: myPayoutAddr} + migMsgBz, err := json.Marshal(migMsg) + require.NoError(t, err) + ctx = ctx.WithEventManager(sdk.NewEventManager()).WithBlockHeight(ctx.BlockHeight() + 1) + res, err := keeper.Migrate(ctx, contractAddr, fred, burnerContractID, migMsgBz) + require.NoError(t, err) + dataBz, err := base64.StdEncoding.DecodeString(string(res.Data)) + require.NoError(t, err) + assert.Equal(t, "burnt", string(dataBz)) + assert.Equal(t, "", res.Log) + type dict map[string]interface{} + expEvents := []dict{ + { + "Type": "wasm", + "Attr": []dict{ + {"contract_address": contractAddr}, + {"action": "burn"}, + {"payout": myPayoutAddr}, + }, + }, + { + "Type": "transfer", + "Attr": []dict{ + {"recipient": myPayoutAddr}, + {"sender": contractAddr}, + {"amount": "100000denom"}, + }, + }, + { + "Type": "message", + "Attr": []dict{ + {"sender": contractAddr}, + }, + }, + { + "Type": "message", + "Attr": []dict{ + {"module": "bank"}, + }, + }, + } + expJsonEvts := string(mustMarshal(t, expEvents)) + assert.JSONEq(t, expJsonEvts, prettyEvents(t, ctx.EventManager().Events())) + + // all persistent data cleared + m := keeper.QueryRaw(ctx, contractAddr, []byte("config")) + require.Len(t, m, 0) + + // and all deposit tokens sent to myPayoutAddr + balance := accKeeper.GetAccount(ctx, myPayoutAddr).GetCoins() + assert.Equal(t, deposit, balance) +} + +func prettyEvents(t *testing.T, events sdk.Events) string { + t.Helper() + type prettyEvent struct { + Type string + Attr []map[string]string + } + + r := make([]prettyEvent, len(events)) + for i, e := range events { + attr := make([]map[string]string, len(e.Attributes)) + for j, a := range e.Attributes { + attr[j] = map[string]string{string(a.Key): string(a.Value)} + } + r[i] = prettyEvent{Type: e.Type, Attr: attr} + } + return string(mustMarshal(t, r)) +} + +func mustMarshal(t *testing.T, r interface{}) []byte { + t.Helper() + bz, err := json.Marshal(r) + require.NoError(t, err) + return bz +} + func TestUpdateContractAdmin(t *testing.T) { tempDir, err := ioutil.TempDir("", "wasm") require.NoError(t, err) diff --git a/x/wasm/internal/keeper/testdata/burner.wasm b/x/wasm/internal/keeper/testdata/burner.wasm new file mode 100644 index 000000000..a226f4769 Binary files /dev/null and b/x/wasm/internal/keeper/testdata/burner.wasm differ diff --git a/x/wasm/internal/keeper/testdata/contract.wasm b/x/wasm/internal/keeper/testdata/contract.wasm index 204385cce..a6bd46f9d 100644 Binary files a/x/wasm/internal/keeper/testdata/contract.wasm and b/x/wasm/internal/keeper/testdata/contract.wasm differ diff --git a/x/wasm/internal/keeper/testdata/contract.wasm.gzip b/x/wasm/internal/keeper/testdata/contract.wasm.gzip index 6d5c5113d..ab118a042 100644 Binary files a/x/wasm/internal/keeper/testdata/contract.wasm.gzip and b/x/wasm/internal/keeper/testdata/contract.wasm.gzip differ diff --git a/x/wasm/internal/keeper/testdata/reflect.wasm b/x/wasm/internal/keeper/testdata/reflect.wasm index 80ba3e413..1d15281b2 100644 Binary files a/x/wasm/internal/keeper/testdata/reflect.wasm and b/x/wasm/internal/keeper/testdata/reflect.wasm differ diff --git a/x/wasm/internal/keeper/testdata/staking.wasm b/x/wasm/internal/keeper/testdata/staking.wasm index b63f86d3a..869666f2f 100644 Binary files a/x/wasm/internal/keeper/testdata/staking.wasm and b/x/wasm/internal/keeper/testdata/staking.wasm differ