From 7caa786d716e965fabeaef07eb9e6ae145f00169 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 May 2020 10:40:54 +0200 Subject: [PATCH] Register and demo custom message handlers with mask --- x/wasm/internal/keeper/mask_test.go | 242 ++++++++++++++------------ x/wasm/internal/keeper/test_common.go | 1 + 2 files changed, 130 insertions(+), 113 deletions(-) diff --git a/x/wasm/internal/keeper/mask_test.go b/x/wasm/internal/keeper/mask_test.go index 95e5b3c..129e730 100644 --- a/x/wasm/internal/keeper/mask_test.go +++ b/x/wasm/internal/keeper/mask_test.go @@ -33,13 +33,94 @@ type reflectPayload struct { Msgs []wasmTypes.CosmosMsg `json:"msgs"` } -func TestMaskReflectCustom(t *testing.T) { - // TODO - t.Skip("this causes a panic in the rust code!") +func TestMaskReflectContractSend(t *testing.T) { tempDir, err := ioutil.TempDir("", "wasm") require.NoError(t, err) defer os.RemoveAll(tempDir) - ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil) + ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, maskEncoders(MakeTestCodec())) + + deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) + creator := createFakeFundedAccount(ctx, accKeeper, deposit) + _, _, bob := keyPubAddr() + + // upload mask code + maskCode, err := ioutil.ReadFile("./testdata/mask.wasm") + require.NoError(t, err) + maskID, err := keeper.Create(ctx, creator, maskCode, "", "") + require.NoError(t, err) + require.Equal(t, uint64(1), maskID) + + // upload hackatom escrow code + escrowCode, err := ioutil.ReadFile("./testdata/contract.wasm") + require.NoError(t, err) + escrowID, err := keeper.Create(ctx, creator, escrowCode, "", "") + require.NoError(t, err) + require.Equal(t, uint64(2), escrowID) + + // creator instantiates a contract and gives it tokens + maskStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) + maskAddr, err := keeper.Instantiate(ctx, maskID, creator, []byte("{}"), "mask contract 2", maskStart) + require.NoError(t, err) + require.NotEmpty(t, maskAddr) + + // now we set contract as verifier of an escrow + initMsg := InitMsg{ + Verifier: maskAddr, + Beneficiary: bob, + } + initMsgBz, err := json.Marshal(initMsg) + require.NoError(t, err) + escrowStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 25000)) + escrowAddr, err := keeper.Instantiate(ctx, escrowID, creator, initMsgBz, "escrow contract 2", escrowStart) + require.NoError(t, err) + require.NotEmpty(t, escrowAddr) + + // let's make sure all balances make sense + checkAccount(t, ctx, accKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // 100k - 40k - 25k + checkAccount(t, ctx, accKeeper, maskAddr, maskStart) + checkAccount(t, ctx, accKeeper, escrowAddr, escrowStart) + checkAccount(t, ctx, accKeeper, bob, nil) + + // now for the trick.... we reflect a message through the mask to call the escrow + // we also send an additional 14k tokens there. + // this should reduce the mask balance by 14k (to 26k) + // this 14k is added to the escrow, then the entire balance is sent to bob (total: 39k) + approveMsg := []byte(`{"release":{}}`) + msgs := []wasmTypes.CosmosMsg{{ + Wasm: &wasmTypes.WasmMsg{ + Execute: &wasmTypes.ExecuteMsg{ + ContractAddr: escrowAddr.String(), + Msg: approveMsg, + Send: []wasmTypes.Coin{{ + Denom: "denom", + Amount: "14000", + }}, + }, + }, + }} + reflectSend := MaskHandleMsg{ + Reflect: &reflectPayload{ + Msgs: msgs, + }, + } + reflectSendBz, err := json.Marshal(reflectSend) + require.NoError(t, err) + _, err = keeper.Execute(ctx, maskAddr, creator, reflectSendBz, nil) + require.NoError(t, err) + + // did this work??? + checkAccount(t, ctx, accKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // same as before + checkAccount(t, ctx, accKeeper, maskAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 26000))) // 40k - 14k (from send) + checkAccount(t, ctx, accKeeper, escrowAddr, sdk.Coins{}) // emptied reserved + checkAccount(t, ctx, accKeeper, bob, sdk.NewCoins(sdk.NewInt64Coin("denom", 39000))) // all escrow of 25k + 14k + +} + +func TestMaskReflectCustom(t *testing.T) { + tempDir, err := ioutil.TempDir("", "wasm") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, maskEncoders(MakeTestCodec())) deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) creator := createFakeFundedAccount(ctx, accKeeper, deposit) @@ -130,9 +211,26 @@ func TestMaskReflectCustom(t *testing.T) { checkAccount(t, ctx, accKeeper, bob, deposit) } +func checkAccount(t *testing.T, ctx sdk.Context, accKeeper auth.AccountKeeper, addr sdk.AccAddress, expected sdk.Coins) { + acct := accKeeper.GetAccount(ctx, addr) + if expected == nil { + assert.Nil(t, acct) + } else { + assert.NotNil(t, acct) + if expected.Empty() { + // there is confusion between nil and empty slice... let's just treat them the same + assert.True(t, acct.GetCoins().Empty()) + } else { + assert.Equal(t, acct.GetCoins(), expected) + } + } +} + +/**** Code to support custom messages *****/ + type maskCustomMsg struct { - Debug string `json:"debug"` - Raw []byte `json:"raw"` + Debug string `json:"debug,omitempty"` + Raw []byte `json:"raw,omitempty"` } // toMaskRawMsg encodes an sdk msg using amino json encoding. @@ -151,115 +249,33 @@ func toMaskRawMsg(cdc *codec.Codec, msg sdk.Msg) (wasmTypes.CosmosMsg, error) { return res, nil } +// maskEncoders needs to be registered in test setup to handle custom callbacks +func maskEncoders(cdc *codec.Codec) *MessageEncoders { + return &MessageEncoders{ + Custom: fromMaskRawMsg(cdc), + } +} + // fromMaskRawMsg decodes msg.Data to an sdk.Msg using amino json encoding. // this needs to be registered on the Encoders -func fromMaskRawMsg(cdc *codec.Codec, msg json.RawMessage) (sdk.Msg, error) { - // TODO - return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Reflect Custom message unimplemented") - - //// until more is changes, format is amino json encoding, wrapped base64 - //var sdkmsg sdk.Msg - //err := cdc.UnmarshalJSON(msg.Data, &sdkmsg) - //if err != nil { - // return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - //} - //return sdkmsg, nil -} - -func TestMaskReflectContractSend(t *testing.T) { - tempDir, err := ioutil.TempDir("", "wasm") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil) - - deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := createFakeFundedAccount(ctx, accKeeper, deposit) - _, _, bob := keyPubAddr() - - // upload mask code - maskCode, err := ioutil.ReadFile("./testdata/mask.wasm") - require.NoError(t, err) - maskID, err := keeper.Create(ctx, creator, maskCode, "", "") - require.NoError(t, err) - require.Equal(t, uint64(1), maskID) - - // upload hackatom escrow code - escrowCode, err := ioutil.ReadFile("./testdata/contract.wasm") - require.NoError(t, err) - escrowID, err := keeper.Create(ctx, creator, escrowCode, "", "") - require.NoError(t, err) - require.Equal(t, uint64(2), escrowID) - - // creator instantiates a contract and gives it tokens - maskStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) - maskAddr, err := keeper.Instantiate(ctx, maskID, creator, []byte("{}"), "mask contract 2", maskStart) - require.NoError(t, err) - require.NotEmpty(t, maskAddr) - - // now we set contract as verifier of an escrow - initMsg := InitMsg{ - Verifier: maskAddr, - Beneficiary: bob, - } - initMsgBz, err := json.Marshal(initMsg) - require.NoError(t, err) - escrowStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 25000)) - escrowAddr, err := keeper.Instantiate(ctx, escrowID, creator, initMsgBz, "escrow contract 2", escrowStart) - require.NoError(t, err) - require.NotEmpty(t, escrowAddr) - - // let's make sure all balances make sense - checkAccount(t, ctx, accKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // 100k - 40k - 25k - checkAccount(t, ctx, accKeeper, maskAddr, maskStart) - checkAccount(t, ctx, accKeeper, escrowAddr, escrowStart) - checkAccount(t, ctx, accKeeper, bob, nil) - - // now for the trick.... we reflect a message through the mask to call the escrow - // we also send an additional 14k tokens there. - // this should reduce the mask balance by 14k (to 26k) - // this 14k is added to the escrow, then the entire balance is sent to bob (total: 39k) - approveMsg := []byte(`{"release":{}}`) - msgs := []wasmTypes.CosmosMsg{{ - Wasm: &wasmTypes.WasmMsg{ - Execute: &wasmTypes.ExecuteMsg{ - ContractAddr: escrowAddr.String(), - Msg: approveMsg, - Send: []wasmTypes.Coin{{ - Denom: "denom", - Amount: "14000", - }}, - }, - }, - }} - reflectSend := MaskHandleMsg{ - Reflect: &reflectPayload{ - Msgs: msgs, - }, - } - reflectSendBz, err := json.Marshal(reflectSend) - require.NoError(t, err) - _, err = keeper.Execute(ctx, maskAddr, creator, reflectSendBz, nil) - require.NoError(t, err) - - // did this work??? - checkAccount(t, ctx, accKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // same as before - checkAccount(t, ctx, accKeeper, maskAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 26000))) // 40k - 14k (from send) - checkAccount(t, ctx, accKeeper, escrowAddr, sdk.Coins{}) // emptied reserved - checkAccount(t, ctx, accKeeper, bob, sdk.NewCoins(sdk.NewInt64Coin("denom", 39000))) // all escrow of 25k + 14k - -} - -func checkAccount(t *testing.T, ctx sdk.Context, accKeeper auth.AccountKeeper, addr sdk.AccAddress, expected sdk.Coins) { - acct := accKeeper.GetAccount(ctx, addr) - if expected == nil { - assert.Nil(t, acct) - } else { - assert.NotNil(t, acct) - if expected.Empty() { - // there is confusion between nil and empty slice... let's just treat them the same - assert.True(t, acct.GetCoins().Empty()) - } else { - assert.Equal(t, acct.GetCoins(), expected) +func fromMaskRawMsg(cdc *codec.Codec) func(_sender sdk.AccAddress, msg json.RawMessage) (sdk.Msg, error) { + return func(_sender sdk.AccAddress, msg json.RawMessage) (sdk.Msg, error) { + var custom maskCustomMsg + err := json.Unmarshal(msg, &custom) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } + if custom.Raw != nil { + var sdkMsg sdk.Msg + err := cdc.UnmarshalJSON(custom.Raw, &sdkMsg) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + return sdkMsg, nil + } + if custom.Debug != "" { + return nil, sdkerrors.Wrapf(types.ErrInvalidMsg, "Custom Debug: %s", custom.Debug) + } + return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown Custom message variant") } } diff --git a/x/wasm/internal/keeper/test_common.go b/x/wasm/internal/keeper/test_common.go index b5c85b5..8da5f2f 100644 --- a/x/wasm/internal/keeper/test_common.go +++ b/x/wasm/internal/keeper/test_common.go @@ -33,6 +33,7 @@ func MakeTestCodec() *codec.Codec { // cdc.RegisterConcrete(&auth.BaseAccount{}, "test/wasm/BaseAccount", nil) auth.AppModuleBasic{}.RegisterCodec(cdc) bank.AppModuleBasic{}.RegisterCodec(cdc) + wasmTypes.RegisterCodec(cdc) sdk.RegisterCodec(cdc) codec.RegisterCrypto(cdc)