github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/keeper/reflect_test.go (about)

     1  package keeper
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"strings"
     7  	"testing"
     8  
     9  	ibcadapter "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/ibc-adapter"
    10  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank"
    11  	"github.com/fibonacci-chain/fbc/x/wasm/keeper/testdata"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  
    15  	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
    16  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    17  	codectypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec/types"
    18  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    19  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    20  	authkeeper "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/keeper"
    21  
    22  	//bankkeeper "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank/keeper"
    23  	//banktypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank/types"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    28  )
    29  
    30  func buildReflectQuery(t *testing.T, query *testdata.ReflectQueryMsg) []byte {
    31  	bz, err := json.Marshal(query)
    32  	require.NoError(t, err)
    33  	return bz
    34  }
    35  
    36  func mustParse(t *testing.T, data []byte, res interface{}) {
    37  	err := json.Unmarshal(data, res)
    38  	require.NoError(t, err)
    39  }
    40  
    41  const ReflectFeatures = "staking,mask,stargate"
    42  
    43  func TestReflectContractSend(t *testing.T) {
    44  	cdc := MakeEncodingConfig(t).Marshaler
    45  	ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)))
    46  	accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper
    47  
    48  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
    49  	creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
    50  	_, _, bob := keyPubAddr()
    51  
    52  	// upload reflect code
    53  	reflectID, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
    54  	require.NoError(t, err)
    55  	require.Equal(t, uint64(1), reflectID)
    56  
    57  	// upload hackatom escrow code
    58  	escrowCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
    59  	require.NoError(t, err)
    60  	escrowID, err := keeper.Create(ctx, creator, escrowCode, nil)
    61  	require.NoError(t, err)
    62  	require.Equal(t, uint64(2), escrowID)
    63  
    64  	// creator instantiates a contract and gives it tokens
    65  	reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
    66  	reflectAddr, _, err := keeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
    67  	require.NoError(t, err)
    68  	require.NotEmpty(t, reflectAddr)
    69  
    70  	// now we set contract as verifier of an escrow
    71  	initMsg := HackatomExampleInitMsg{
    72  		Verifier:    reflectAddr,
    73  		Beneficiary: bob,
    74  	}
    75  	initMsgBz, err := json.Marshal(initMsg)
    76  	require.NoError(t, err)
    77  	escrowStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 25000))
    78  	escrowAddr, _, err := keeper.Instantiate(ctx, escrowID, creator, nil, initMsgBz, "escrow contract 2", escrowStart)
    79  	require.NoError(t, err)
    80  	require.NotEmpty(t, escrowAddr)
    81  
    82  	// let's make sure all balances make sense
    83  	checkAccount(t, ctx, accKeeper, bankKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // 100k - 40k - 25k
    84  	checkAccount(t, ctx, accKeeper, bankKeeper, reflectAddr, reflectStart)
    85  	checkAccount(t, ctx, accKeeper, bankKeeper, escrowAddr, escrowStart)
    86  	checkAccount(t, ctx, accKeeper, bankKeeper, bob, nil)
    87  
    88  	// now for the trick.... we reflect a message through the reflect to call the escrow
    89  	// we also send an additional 14k tokens there.
    90  	// this should reduce the reflect balance by 14k (to 26k)
    91  	// this 14k is added to the escrow, then the entire balance is sent to bob (total: 39k)
    92  	approveMsg := []byte(`{"release":{}}`)
    93  	msgs := []wasmvmtypes.CosmosMsg{{
    94  		Wasm: &wasmvmtypes.WasmMsg{
    95  			Execute: &wasmvmtypes.ExecuteMsg{
    96  				ContractAddr: escrowAddr.String(),
    97  				Msg:          approveMsg,
    98  				Funds: []wasmvmtypes.Coin{{
    99  					Denom:  "denom",
   100  					Amount: "14000000000000000000000",
   101  				}},
   102  			},
   103  		},
   104  	}}
   105  	reflectSend := testdata.ReflectHandleMsg{
   106  		Reflect: &testdata.ReflectPayload{
   107  			Msgs: msgs,
   108  		},
   109  	}
   110  	reflectSendBz, err := json.Marshal(reflectSend)
   111  	require.NoError(t, err)
   112  	_, err = keeper.Execute(ctx, reflectAddr, creator, reflectSendBz, nil)
   113  	require.NoError(t, err)
   114  
   115  	// did this work???
   116  	checkAccount(t, ctx, accKeeper, bankKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000)))     // same as before
   117  	checkAccount(t, ctx, accKeeper, bankKeeper, reflectAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 26000))) // 40k - 14k (from send)
   118  	checkAccount(t, ctx, accKeeper, bankKeeper, escrowAddr, sdk.Coins{})                                     // emptied reserved
   119  	checkAccount(t, ctx, accKeeper, bankKeeper, bob, sdk.NewCoins(sdk.NewInt64Coin("denom", 39000)))         // all escrow of 25k + 14k
   120  }
   121  
   122  func TestReflectCustomMsg(t *testing.T) {
   123  	cdc := MakeEncodingConfig(t).Marshaler
   124  	ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)), WithQueryPlugins(reflectPlugins()))
   125  	accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper
   126  
   127  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
   128  	creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
   129  	bob := keepers.Faucet.NewFundedAccount(ctx, deposit...)
   130  	_, _, fred := keyPubAddr()
   131  
   132  	// upload code
   133  	codeID, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
   134  	require.NoError(t, err)
   135  	require.Equal(t, uint64(1), codeID)
   136  
   137  	// creator instantiates a contract and gives it tokens
   138  	contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
   139  	contractAddr, _, err := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
   140  	require.NoError(t, err)
   141  	require.NotEmpty(t, contractAddr)
   142  
   143  	// set owner to bob
   144  	transfer := testdata.ReflectHandleMsg{
   145  		ChangeOwner: &testdata.OwnerPayload{
   146  			Owner: bob,
   147  		},
   148  	}
   149  	transferBz, err := json.Marshal(transfer)
   150  	require.NoError(t, err)
   151  	_, err = keeper.Execute(ctx, contractAddr, creator, transferBz, nil)
   152  	require.NoError(t, err)
   153  
   154  	// check some account values
   155  	checkAccount(t, ctx, accKeeper, bankKeeper, contractAddr, contractStart)
   156  	checkAccount(t, ctx, accKeeper, bankKeeper, bob, deposit)
   157  	checkAccount(t, ctx, accKeeper, bankKeeper, fred, nil)
   158  
   159  	// bob can send contract's tokens to fred (using SendMsg)
   160  	msgs := []wasmvmtypes.CosmosMsg{{
   161  		Bank: &wasmvmtypes.BankMsg{
   162  			Send: &wasmvmtypes.SendMsg{
   163  				ToAddress: fred.String(),
   164  				Amount: []wasmvmtypes.Coin{{
   165  					Denom:  "denom",
   166  					Amount: "15000000000000000000000",
   167  				}},
   168  			},
   169  		},
   170  	}}
   171  	reflectSend := testdata.ReflectHandleMsg{
   172  		Reflect: &testdata.ReflectPayload{
   173  			Msgs: msgs,
   174  		},
   175  	}
   176  	reflectSendBz, err := json.Marshal(reflectSend)
   177  	require.NoError(t, err)
   178  	_, err = keeper.Execute(ctx, contractAddr, bob, reflectSendBz, nil)
   179  	require.NoError(t, err)
   180  
   181  	// fred got coins
   182  	checkAccount(t, ctx, accKeeper, bankKeeper, fred, sdk.NewCoins(sdk.NewInt64Coin("denom", 15000)))
   183  	// contract lost them
   184  	checkAccount(t, ctx, accKeeper, bankKeeper, contractAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 25000)))
   185  	checkAccount(t, ctx, accKeeper, bankKeeper, bob, deposit)
   186  
   187  	// construct an opaque message
   188  	var sdkSendMsg ibcadapter.Msg = &bank.MsgSendAdapter{
   189  		FromAddress: contractAddr.String(),
   190  		ToAddress:   fred.String(),
   191  		Amount:      sdk.CoinsToCoinAdapters(sdk.NewCoins(sdk.NewInt64Coin("denom", 23000))),
   192  	}
   193  	opaque, err := toReflectRawMsg(cdc, sdkSendMsg)
   194  	require.NoError(t, err)
   195  	reflectOpaque := testdata.ReflectHandleMsg{
   196  		Reflect: &testdata.ReflectPayload{
   197  			Msgs: []wasmvmtypes.CosmosMsg{opaque},
   198  		},
   199  	}
   200  	reflectOpaqueBz, err := json.Marshal(reflectOpaque)
   201  	require.NoError(t, err)
   202  
   203  	_, err = keeper.Execute(ctx, contractAddr, bob, reflectOpaqueBz, nil)
   204  	require.NoError(t, err)
   205  
   206  	// fred got more coins
   207  	checkAccount(t, ctx, accKeeper, bankKeeper, fred, sdk.NewCoins(sdk.NewInt64Coin("denom", 38000)))
   208  	// contract lost them
   209  	checkAccount(t, ctx, accKeeper, bankKeeper, contractAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 2000)))
   210  	checkAccount(t, ctx, accKeeper, bankKeeper, bob, deposit)
   211  }
   212  
   213  func TestMaskReflectCustomQuery(t *testing.T) {
   214  	cdc := MakeEncodingConfig(t).Marshaler
   215  	ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)), WithQueryPlugins(reflectPlugins()))
   216  	keeper := keepers.WasmKeeper
   217  
   218  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
   219  	creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
   220  
   221  	// upload code
   222  	codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
   223  	require.NoError(t, err)
   224  	require.Equal(t, uint64(1), codeID)
   225  
   226  	// creator instantiates a contract and gives it tokens
   227  	contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
   228  	contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
   229  	require.NoError(t, err)
   230  	require.NotEmpty(t, contractAddr)
   231  
   232  	// let's perform a normal query of state
   233  	ownerQuery := testdata.ReflectQueryMsg{
   234  		Owner: &struct{}{},
   235  	}
   236  	ownerQueryBz, err := json.Marshal(ownerQuery)
   237  	require.NoError(t, err)
   238  	ownerRes, err := keeper.QuerySmart(ctx, contractAddr, ownerQueryBz)
   239  	require.NoError(t, err)
   240  	var res testdata.OwnerResponse
   241  	err = json.Unmarshal(ownerRes, &res)
   242  	require.NoError(t, err)
   243  	assert.Equal(t, res.Owner, creator.String())
   244  
   245  	// and now making use of the custom querier callbacks
   246  	customQuery := testdata.ReflectQueryMsg{
   247  		Capitalized: &testdata.Text{
   248  			Text: "all Caps noW",
   249  		},
   250  	}
   251  	customQueryBz, err := json.Marshal(customQuery)
   252  	require.NoError(t, err)
   253  	custom, err := keeper.QuerySmart(ctx, contractAddr, customQueryBz)
   254  	require.NoError(t, err)
   255  	var resp capitalizedResponse
   256  	err = json.Unmarshal(custom, &resp)
   257  	require.NoError(t, err)
   258  	assert.Equal(t, resp.Text, "ALL CAPS NOW")
   259  }
   260  
   261  func TestReflectStargateQuery(t *testing.T) {
   262  	cdc := MakeEncodingConfig(t).Marshaler
   263  	ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)), WithQueryPlugins(reflectPlugins()))
   264  	keeper := keepers.WasmKeeper
   265  
   266  	funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000))
   267  	contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
   268  	expectedBalance := funds.Sub(contractStart)
   269  	creator := keepers.Faucet.NewFundedAccount(ctx, funds...)
   270  
   271  	// upload code
   272  	codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
   273  	require.NoError(t, err)
   274  	require.Equal(t, uint64(1), codeID)
   275  
   276  	// creator instantiates a contract and gives it tokens
   277  	contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
   278  	require.NoError(t, err)
   279  	require.NotEmpty(t, contractAddr)
   280  
   281  	// first, normal query for the bank balance (to make sure our query is proper)
   282  	bankQuery := wasmvmtypes.QueryRequest{
   283  		Bank: &wasmvmtypes.BankQuery{
   284  			AllBalances: &wasmvmtypes.AllBalancesQuery{
   285  				Address: creator.String(),
   286  			},
   287  		},
   288  	}
   289  	simpleQueryBz, err := json.Marshal(testdata.ReflectQueryMsg{
   290  		Chain: &testdata.ChainQuery{Request: &bankQuery},
   291  	})
   292  	require.NoError(t, err)
   293  	simpleRes, err := keeper.QuerySmart(ctx, contractAddr, simpleQueryBz)
   294  	require.NoError(t, err)
   295  	var simpleChain testdata.ChainResponse
   296  	mustParse(t, simpleRes, &simpleChain)
   297  	var simpleBalance wasmvmtypes.AllBalancesResponse
   298  	mustParse(t, simpleChain.Data, &simpleBalance)
   299  	expectedBalanceAdapter := sdk.CoinsToCoinAdapters(expectedBalance)
   300  	require.Equal(t, len(expectedBalanceAdapter), len(simpleBalance.Amount))
   301  	assert.Equal(t, simpleBalance.Amount[0].Amount, expectedBalanceAdapter[0].Amount.String())
   302  	assert.Equal(t, simpleBalance.Amount[0].Denom, expectedBalanceAdapter[0].Denom)
   303  }
   304  
   305  func TestReflectInvalidStargateQuery(t *testing.T) {
   306  	cdc := MakeEncodingConfig(t).Marshaler
   307  	ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)), WithQueryPlugins(reflectPlugins()))
   308  	keeper := keepers.WasmKeeper
   309  
   310  	funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000))
   311  	contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
   312  	creator := keepers.Faucet.NewFundedAccount(ctx, funds...)
   313  
   314  	// upload code
   315  	codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
   316  	require.NoError(t, err)
   317  	require.Equal(t, uint64(1), codeID)
   318  
   319  	// creator instantiates a contract and gives it tokens
   320  	contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
   321  	require.NoError(t, err)
   322  	require.NotEmpty(t, contractAddr)
   323  
   324  	// now, try to build a protobuf query
   325  	protoQuery := bank.QueryAllBalancesRequestAdapter{
   326  		Address: creator.String(),
   327  	}
   328  	protoQueryBin, err := proto.Marshal(&protoQuery)
   329  	protoRequest := wasmvmtypes.QueryRequest{
   330  		Stargate: &wasmvmtypes.StargateQuery{
   331  			Path: "/cosmos.bank.v1beta1.Query/AllBalances",
   332  			Data: protoQueryBin,
   333  		},
   334  	}
   335  	protoQueryBz, err := json.Marshal(testdata.ReflectQueryMsg{
   336  		Chain: &testdata.ChainQuery{Request: &protoRequest},
   337  	})
   338  	require.NoError(t, err)
   339  
   340  	// make a query on the chain, should be blacklisted
   341  	_, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz)
   342  	require.Error(t, err)
   343  	require.Contains(t, err.Error(), "Stargate queries are disabled")
   344  
   345  	// now, try to build a protobuf query
   346  	protoRequest = wasmvmtypes.QueryRequest{
   347  		Stargate: &wasmvmtypes.StargateQuery{
   348  			Path: "/cosmos.tx.v1beta1.Service/GetTx",
   349  			Data: []byte{},
   350  		},
   351  	}
   352  	protoQueryBz, err = json.Marshal(testdata.ReflectQueryMsg{
   353  		Chain: &testdata.ChainQuery{Request: &protoRequest},
   354  	})
   355  	require.NoError(t, err)
   356  
   357  	// make a query on the chain, should be blacklisted
   358  	_, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz)
   359  	require.Error(t, err)
   360  	require.Contains(t, err.Error(), "Stargate queries are disabled")
   361  
   362  	// and another one
   363  	protoRequest = wasmvmtypes.QueryRequest{
   364  		Stargate: &wasmvmtypes.StargateQuery{
   365  			Path: "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo",
   366  			Data: []byte{},
   367  		},
   368  	}
   369  	protoQueryBz, err = json.Marshal(testdata.ReflectQueryMsg{
   370  		Chain: &testdata.ChainQuery{Request: &protoRequest},
   371  	})
   372  	require.NoError(t, err)
   373  
   374  	// make a query on the chain, should be blacklisted
   375  	_, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz)
   376  	require.Error(t, err)
   377  	require.Contains(t, err.Error(), "Stargate queries are disabled")
   378  }
   379  
   380  type reflectState struct {
   381  	Owner string `json:"owner"`
   382  }
   383  
   384  func TestMaskReflectWasmQueries(t *testing.T) {
   385  	cdc := MakeEncodingConfig(t).Marshaler
   386  	ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)), WithQueryPlugins(reflectPlugins()))
   387  	keeper := keepers.WasmKeeper
   388  
   389  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
   390  	creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
   391  
   392  	// upload reflect code
   393  	reflectID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
   394  	require.NoError(t, err)
   395  	require.Equal(t, uint64(1), reflectID)
   396  
   397  	// creator instantiates a contract and gives it tokens
   398  	reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
   399  	reflectAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
   400  	require.NoError(t, err)
   401  	require.NotEmpty(t, reflectAddr)
   402  
   403  	// for control, let's make some queries directly on the reflect
   404  	ownerQuery := buildReflectQuery(t, &testdata.ReflectQueryMsg{Owner: &struct{}{}})
   405  	res, err := keeper.QuerySmart(ctx, reflectAddr, ownerQuery)
   406  	require.NoError(t, err)
   407  	var ownerRes testdata.OwnerResponse
   408  	mustParse(t, res, &ownerRes)
   409  	require.Equal(t, ownerRes.Owner, creator.String())
   410  
   411  	// and a raw query: cosmwasm_storage::Singleton uses 2 byte big-endian length-prefixed to store data
   412  	configKey := append([]byte{0, 6}, []byte("config")...)
   413  	raw := keeper.QueryRaw(ctx, reflectAddr, configKey)
   414  	var stateRes reflectState
   415  	mustParse(t, raw, &stateRes)
   416  	require.Equal(t, stateRes.Owner, creator.String())
   417  
   418  	// now, let's reflect a smart query into the x/wasm handlers and see if we get the same result
   419  	reflectOwnerQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
   420  		Smart: &wasmvmtypes.SmartQuery{
   421  			ContractAddr: reflectAddr.String(),
   422  			Msg:          ownerQuery,
   423  		},
   424  	}}}}
   425  	reflectOwnerBin := buildReflectQuery(t, &reflectOwnerQuery)
   426  	res, err = keeper.QuerySmart(ctx, reflectAddr, reflectOwnerBin)
   427  	require.NoError(t, err)
   428  	// first we pull out the data from chain response, before parsing the original response
   429  	var reflectRes testdata.ChainResponse
   430  	mustParse(t, res, &reflectRes)
   431  	var reflectOwnerRes testdata.OwnerResponse
   432  	mustParse(t, reflectRes.Data, &reflectOwnerRes)
   433  	require.Equal(t, reflectOwnerRes.Owner, creator.String())
   434  
   435  	// and with queryRaw
   436  	reflectStateQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
   437  		Raw: &wasmvmtypes.RawQuery{
   438  			ContractAddr: reflectAddr.String(),
   439  			Key:          configKey,
   440  		},
   441  	}}}}
   442  	reflectStateBin := buildReflectQuery(t, &reflectStateQuery)
   443  	res, err = keeper.QuerySmart(ctx, reflectAddr, reflectStateBin)
   444  	require.NoError(t, err)
   445  	// first we pull out the data from chain response, before parsing the original response
   446  	var reflectRawRes testdata.ChainResponse
   447  	mustParse(t, res, &reflectRawRes)
   448  	// now, with the raw data, we can parse it into state
   449  	var reflectStateRes reflectState
   450  	mustParse(t, reflectRawRes.Data, &reflectStateRes)
   451  	require.Equal(t, reflectStateRes.Owner, creator.String())
   452  }
   453  
   454  func TestWasmRawQueryWithNil(t *testing.T) {
   455  	cdc := MakeEncodingConfig(t).Marshaler
   456  	ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)), WithQueryPlugins(reflectPlugins()))
   457  	keeper := keepers.WasmKeeper
   458  
   459  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
   460  	creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
   461  
   462  	// upload reflect code
   463  	reflectID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
   464  	require.NoError(t, err)
   465  	require.Equal(t, uint64(1), reflectID)
   466  
   467  	// creator instantiates a contract and gives it tokens
   468  	reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
   469  	reflectAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
   470  	require.NoError(t, err)
   471  	require.NotEmpty(t, reflectAddr)
   472  
   473  	// control: query directly
   474  	missingKey := []byte{0, 1, 2, 3, 4}
   475  	raw := keeper.QueryRaw(ctx, reflectAddr, missingKey)
   476  	require.Nil(t, raw)
   477  
   478  	// and with queryRaw
   479  	reflectQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
   480  		Raw: &wasmvmtypes.RawQuery{
   481  			ContractAddr: reflectAddr.String(),
   482  			Key:          missingKey,
   483  		},
   484  	}}}}
   485  	reflectStateBin := buildReflectQuery(t, &reflectQuery)
   486  	res, err := keeper.QuerySmart(ctx, reflectAddr, reflectStateBin)
   487  	require.NoError(t, err)
   488  
   489  	// first we pull out the data from chain response, before parsing the original response
   490  	var reflectRawRes testdata.ChainResponse
   491  	mustParse(t, res, &reflectRawRes)
   492  	// and make sure there is no data
   493  	require.Empty(t, reflectRawRes.Data)
   494  	// we get an empty byte slice not nil (if anyone care in go-land)
   495  	require.Equal(t, []byte{}, reflectRawRes.Data)
   496  }
   497  
   498  func checkAccount(t *testing.T, ctx sdk.Context, accKeeper authkeeper.AccountKeeper, bankKeeper bank.Keeper, addr sdk.AccAddress, expected sdk.Coins) {
   499  	acct := accKeeper.GetAccount(ctx, addr)
   500  	if expected == nil {
   501  		assert.Nil(t, acct)
   502  	} else {
   503  		assert.NotNil(t, acct)
   504  		if expected.Empty() {
   505  			// there is confusion between nil and empty slice... let's just treat them the same
   506  			assert.True(t, bankKeeper.GetCoins(ctx, acct.GetAddress()).Empty())
   507  		} else {
   508  			assert.Equal(t, bankKeeper.GetCoins(ctx, acct.GetAddress()), expected)
   509  		}
   510  	}
   511  }
   512  
   513  /**** Code to support custom messages *****/
   514  
   515  type reflectCustomMsg struct {
   516  	Debug string `json:"debug,omitempty"`
   517  	Raw   []byte `json:"raw,omitempty"`
   518  }
   519  
   520  // toReflectRawMsg encodes an sdk msg using any type with json encoding.
   521  // Then wraps it as an opaque message
   522  func toReflectRawMsg(cdc codec.CodecProxy, msg ibcadapter.Msg) (wasmvmtypes.CosmosMsg, error) {
   523  	any, err := codectypes.NewAnyWithValue(msg)
   524  	if err != nil {
   525  		return wasmvmtypes.CosmosMsg{}, err
   526  	}
   527  	rawBz, err := cdc.GetProtocMarshal().MarshalJSON(any)
   528  	if err != nil {
   529  		return wasmvmtypes.CosmosMsg{}, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   530  	}
   531  	customMsg, err := json.Marshal(reflectCustomMsg{
   532  		Raw: rawBz,
   533  	})
   534  	res := wasmvmtypes.CosmosMsg{
   535  		Custom: customMsg,
   536  	}
   537  	return res, nil
   538  }
   539  
   540  // reflectEncoders needs to be registered in test setup to handle custom message callbacks
   541  func reflectEncoders(cdc codec.CodecProxy) *MessageEncoders {
   542  	return &MessageEncoders{
   543  		Custom: fromReflectRawMsg(cdc),
   544  	}
   545  }
   546  
   547  // fromReflectRawMsg decodes msg.Data to an sdk.Msg using proto Any and json encoding.
   548  // this needs to be registered on the Encoders
   549  func fromReflectRawMsg(cdc codec.CodecProxy) CustomEncoder {
   550  	return func(_sender sdk.AccAddress, msg json.RawMessage) ([]ibcadapter.Msg, error) {
   551  		var custom reflectCustomMsg
   552  		err := json.Unmarshal(msg, &custom)
   553  		if err != nil {
   554  			return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
   555  		}
   556  		if custom.Raw != nil {
   557  			var any codectypes.Any
   558  			if err := cdc.GetProtocMarshal().UnmarshalJSON(custom.Raw, &any); err != nil {
   559  				return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
   560  			}
   561  			var msg ibcadapter.Msg
   562  			if err := cdc.GetProtocMarshal().UnpackAny(&any, &msg); err != nil {
   563  				return nil, err
   564  			}
   565  			return []ibcadapter.Msg{msg}, nil
   566  		}
   567  		if custom.Debug != "" {
   568  			return nil, sdkerrors.Wrapf(types.ErrInvalidMsg, "Custom Debug: %s", custom.Debug)
   569  		}
   570  		return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown Custom message variant")
   571  	}
   572  }
   573  
   574  type reflectCustomQuery struct {
   575  	Ping        *struct{}      `json:"ping,omitempty"`
   576  	Capitalized *testdata.Text `json:"capitalized,omitempty"`
   577  }
   578  
   579  // this is from the go code back to the contract (capitalized or ping)
   580  type customQueryResponse struct {
   581  	Msg string `json:"msg"`
   582  }
   583  
   584  // these are the return values from contract -> go depending on type of query
   585  type ownerResponse struct {
   586  	Owner string `json:"owner"`
   587  }
   588  
   589  type capitalizedResponse struct {
   590  	Text string `json:"text"`
   591  }
   592  
   593  type chainResponse struct {
   594  	Data []byte `json:"data"`
   595  }
   596  
   597  // reflectPlugins needs to be registered in test setup to handle custom query callbacks
   598  func reflectPlugins() *QueryPlugins {
   599  	return &QueryPlugins{
   600  		Custom: performCustomQuery,
   601  	}
   602  }
   603  
   604  func performCustomQuery(_ sdk.Context, request json.RawMessage) ([]byte, error) {
   605  	var custom reflectCustomQuery
   606  	err := json.Unmarshal(request, &custom)
   607  	if err != nil {
   608  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
   609  	}
   610  	if custom.Capitalized != nil {
   611  		msg := strings.ToUpper(custom.Capitalized.Text)
   612  		return json.Marshal(customQueryResponse{Msg: msg})
   613  	}
   614  	if custom.Ping != nil {
   615  		return json.Marshal(customQueryResponse{Msg: "pong"})
   616  	}
   617  	return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown Custom query variant")
   618  }