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

     1  package wasm
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"testing"
     8  
     9  	"github.com/fibonacci-chain/fbc/x/wasm/keeper/testdata"
    10  
    11  	"github.com/dvsekhvalnov/jose2go/base64url"
    12  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/module"
    14  	authkeeper "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/keeper"
    15  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank"
    16  	bankkeeper "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank"
    17  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    18  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto"
    19  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto/ed25519"
    20  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/kv"
    21  	types2 "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    22  	stakingkeeper "github.com/fibonacci-chain/fbc/x/staking/keeper"
    23  	"github.com/fibonacci-chain/fbc/x/wasm/keeper"
    24  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    25  	"github.com/spf13/viper"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  var zeroCoins sdk.Coins
    31  
    32  type testData struct {
    33  	module        module.AppModule
    34  	ctx           sdk.Context
    35  	acctKeeper    authkeeper.AccountKeeper
    36  	keeper        Keeper
    37  	bankKeeper    bankkeeper.Keeper
    38  	stakingKeeper stakingkeeper.Keeper
    39  	faucet        *keeper.TestFaucet
    40  }
    41  
    42  func setupTest(t *testing.T) testData {
    43  	ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
    44  	cdc := keeper.MakeTestCodec(t)
    45  	data := testData{
    46  		module:        NewAppModule(cdc, keepers.WasmKeeper),
    47  		ctx:           ctx,
    48  		acctKeeper:    keepers.AccountKeeper,
    49  		keeper:        *keepers.WasmKeeper,
    50  		bankKeeper:    keepers.BankKeeper,
    51  		stakingKeeper: keepers.StakingKeeper,
    52  		faucet:        keepers.Faucet,
    53  	}
    54  	return data
    55  }
    56  
    57  func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) {
    58  	key := ed25519.GenPrivKey()
    59  	pub := key.PubKey()
    60  	addr := sdk.AccAddress(pub.Address())
    61  	return key, pub, addr
    62  }
    63  
    64  func mustLoad(path string) []byte {
    65  	bz, err := ioutil.ReadFile(path)
    66  	if err != nil {
    67  		panic(err)
    68  	}
    69  	return bz
    70  }
    71  
    72  var (
    73  	_, _, addrAcc1 = keyPubAddr()
    74  	addr1          = addrAcc1.String()
    75  	testContract   = mustLoad("./keeper/testdata/hackatom.wasm")
    76  	maskContract   = testdata.ReflectContractWasm()
    77  	oldContract    = mustLoad("./testdata/escrow_0.7.wasm")
    78  )
    79  
    80  func TestHandleCreate(t *testing.T) {
    81  	types2.UnittestOnlySetMilestoneEarthHeight(1)
    82  	cases := map[string]struct {
    83  		msg     sdk.Msg
    84  		isValid bool
    85  	}{
    86  		"empty": {
    87  			msg:     &MsgStoreCode{},
    88  			isValid: false,
    89  		},
    90  		"invalid wasm": {
    91  			msg: &MsgStoreCode{
    92  				Sender:       addr1,
    93  				WASMByteCode: []byte("foobar"),
    94  			},
    95  			isValid: false,
    96  		},
    97  		"valid wasm": {
    98  			msg: &MsgStoreCode{
    99  				Sender:       addr1,
   100  				WASMByteCode: testContract,
   101  			},
   102  			isValid: true,
   103  		},
   104  		"other valid wasm": {
   105  			msg: &MsgStoreCode{
   106  				Sender:       addr1,
   107  				WASMByteCode: maskContract,
   108  			},
   109  			isValid: true,
   110  		},
   111  		"old wasm (0.7)": {
   112  			msg: &MsgStoreCode{
   113  				Sender:       addr1,
   114  				WASMByteCode: oldContract,
   115  			},
   116  			isValid: false,
   117  		},
   118  	}
   119  
   120  	for name, tc := range cases {
   121  		tc := tc
   122  		t.Run(name, func(t *testing.T) {
   123  			data := setupTest(t)
   124  
   125  			h := data.module.NewHandler()
   126  			q := data.module.NewQuerierHandler()
   127  
   128  			res, err := h(data.ctx, tc.msg)
   129  			if !tc.isValid {
   130  				require.Error(t, err, "%#v", res)
   131  				assertCodeList(t, q, data.ctx, 0)
   132  				assertCodeBytes(t, q, data.ctx, 1, nil)
   133  				return
   134  			}
   135  			require.NoError(t, err)
   136  			assertCodeList(t, q, data.ctx, 1)
   137  		})
   138  	}
   139  }
   140  
   141  type initMsg struct {
   142  	Verifier    sdk.AccAddress `json:"verifier"`
   143  	Beneficiary sdk.AccAddress `json:"beneficiary"`
   144  }
   145  
   146  type state struct {
   147  	Verifier    string `json:"verifier"`
   148  	Beneficiary string `json:"beneficiary"`
   149  	Funder      string `json:"funder"`
   150  }
   151  
   152  func TestHandleInstantiate(t *testing.T) {
   153  	types2.UnittestOnlySetMilestoneEarthHeight(1)
   154  	data := setupTest(t)
   155  	creator := data.faucet.NewFundedAccount(data.ctx, sdk.NewInt64Coin("denom", 100000))
   156  
   157  	h := data.module.NewHandler()
   158  	q := data.module.NewQuerierHandler()
   159  
   160  	msg := &MsgStoreCode{
   161  		Sender:       creator.String(),
   162  		WASMByteCode: testContract,
   163  	}
   164  	res, err := h(data.ctx, msg)
   165  	require.NoError(t, err)
   166  	assertStoreCodeResponse(t, res.Data, 1)
   167  
   168  	_, _, bob := keyPubAddr()
   169  	_, _, fred := keyPubAddr()
   170  
   171  	initMsg := initMsg{
   172  		Verifier:    fred,
   173  		Beneficiary: bob,
   174  	}
   175  	initMsgBz, err := json.Marshal(initMsg)
   176  	require.NoError(t, err)
   177  
   178  	// create with no balance is also legal
   179  	initCmd := MsgInstantiateContract{
   180  		Sender: creator.String(),
   181  		CodeID: firstCodeID,
   182  		Msg:    initMsgBz,
   183  		Funds:  nil,
   184  	}
   185  	res, err = h(data.ctx, &initCmd)
   186  	require.NoError(t, err)
   187  	contractBech32Addr := parseInitResponse(t, res.Data)
   188  
   189  	require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", contractBech32Addr)
   190  	// this should be standard x/wasm init event, nothing from contract
   191  	require.Equal(t, 3, len(res.Events), prettyEvents(res.Events))
   192  	require.Equal(t, "message", res.Events[0].Type)
   193  	assertAttribute(t, "module", "wasm", res.Events[0].Attributes[0])
   194  	require.Equal(t, "instantiate", res.Events[1].Type)
   195  	require.Equal(t, "wasm", res.Events[2].Type)
   196  	assertAttribute(t, "_contract_address", contractBech32Addr, res.Events[2].Attributes[0])
   197  
   198  	assertCodeList(t, q, data.ctx, 1)
   199  	assertCodeBytes(t, q, data.ctx, 1, testContract)
   200  
   201  	assertContractList(t, q, data.ctx, 1, []string{contractBech32Addr})
   202  	assertContractInfo(t, q, data.ctx, contractBech32Addr, 1, creator)
   203  	assertContractState(t, q, data.ctx, contractBech32Addr, state{
   204  		Verifier:    fred.String(),
   205  		Beneficiary: bob.String(),
   206  		Funder:      creator.String(),
   207  	})
   208  }
   209  
   210  func TestHandleExecute(t *testing.T) {
   211  	types2.UnittestOnlySetMilestoneEarthHeight(1)
   212  	data := setupTest(t)
   213  	types2.UnittestOnlySetMilestoneEarthHeight(1)
   214  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
   215  	topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
   216  
   217  	creator := data.faucet.NewFundedAccount(data.ctx, deposit.Add(deposit...)...)
   218  	fred := data.faucet.NewFundedAccount(data.ctx, topUp...)
   219  
   220  	h := data.module.NewHandler()
   221  	q := data.module.NewQuerierHandler()
   222  
   223  	msg := &MsgStoreCode{
   224  		Sender:       creator.String(),
   225  		WASMByteCode: testContract,
   226  	}
   227  	res, err := h(data.ctx, msg)
   228  	require.NoError(t, err)
   229  	assertStoreCodeResponse(t, res.Data, 1)
   230  
   231  	_, _, bob := keyPubAddr()
   232  	initMsg := initMsg{
   233  		Verifier:    fred,
   234  		Beneficiary: bob,
   235  	}
   236  	initMsgBz, err := json.Marshal(initMsg)
   237  	require.NoError(t, err)
   238  
   239  	initCmd := MsgInstantiateContract{
   240  		Sender: creator.String(),
   241  		CodeID: firstCodeID,
   242  		Msg:    initMsgBz,
   243  		Funds:  sdk.CoinsToCoinAdapters(deposit),
   244  	}
   245  	res, err = h(data.ctx, &initCmd)
   246  	require.NoError(t, err)
   247  	contractBech32Addr := parseInitResponse(t, res.Data)
   248  
   249  	require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", contractBech32Addr)
   250  	// this should be standard x/wasm message event,  init event, plus a bank send event (2), with no custom contract events
   251  	require.Equal(t, 3, len(res.Events), prettyEvents(res.Events))
   252  	require.Equal(t, "message", res.Events[0].Type)
   253  	assertAttribute(t, "module", "wasm", res.Events[0].Attributes[0])
   254  	require.Equal(t, "instantiate", res.Events[1].Type)
   255  	require.Equal(t, "wasm", res.Events[2].Type)
   256  	assertAttribute(t, "_contract_address", contractBech32Addr, res.Events[2].Attributes[0])
   257  
   258  	// ensure bob doesn't exist
   259  	bobAcct := data.acctKeeper.GetAccount(data.ctx, bob)
   260  	require.Nil(t, bobAcct)
   261  
   262  	// ensure funder has reduced balance
   263  	creatorAcct := data.acctKeeper.GetAccount(data.ctx, creator)
   264  	require.NotNil(t, creatorAcct)
   265  	// we started at 2*deposit, should have spent one above
   266  	assert.Equal(t, deposit, bank.NewBankKeeperAdapter(data.bankKeeper).GetAllBalances(data.ctx, creatorAcct.GetAddress()))
   267  
   268  	// ensure contract has updated balance
   269  	contractAddr, _ := sdk.AccAddressFromBech32(contractBech32Addr)
   270  	contractAcct := data.acctKeeper.GetAccount(data.ctx, contractAddr)
   271  	require.NotNil(t, contractAcct)
   272  	assert.Equal(t, deposit, bank.NewBankKeeperAdapter(data.bankKeeper).GetAllBalances(data.ctx, contractAcct.GetAddress()))
   273  
   274  	execCmd := MsgExecuteContract{
   275  		Sender:   fred.String(),
   276  		Contract: contractBech32Addr,
   277  		Msg:      []byte(`{"release":{}}`),
   278  		Funds:    sdk.CoinsToCoinAdapters(topUp),
   279  	}
   280  	res, err = h(data.ctx, &execCmd)
   281  	require.NoError(t, err)
   282  	// from https://github.com/CosmWasm/cosmwasm/blob/master/contracts/hackatom/src/contract.rs#L167
   283  	assertExecuteResponse(t, res.Data, []byte{0xf0, 0x0b, 0xaa})
   284  
   285  	// this should be standard message event, plus x/wasm init event, plus 2 bank send event, plus a special event from the contract
   286  	require.Equal(t, 5, len(res.Events), prettyEvents(res.Events))
   287  
   288  	assert.Equal(t, "message", res.Events[0].Type)
   289  	assertAttribute(t, "module", "wasm", res.Events[0].Attributes[0])
   290  
   291  	assert.Equal(t, "execute", res.Events[1].Type)
   292  
   293  	// custom contract event attribute
   294  	assert.Equal(t, "wasm", res.Events[2].Type)
   295  	assertAttribute(t, "_contract_address", contractBech32Addr, res.Events[2].Attributes[0])
   296  	assertAttribute(t, "action", "release", res.Events[2].Attributes[1])
   297  	// custom contract event
   298  	assert.Equal(t, "wasm-hackatom", res.Events[3].Type)
   299  	assertAttribute(t, "_contract_address", contractBech32Addr, res.Events[3].Attributes[0])
   300  	assertAttribute(t, "action", "release", res.Events[3].Attributes[1])
   301  	// second transfer (this without conflicting message)
   302  	assert.Equal(t, "transfer", res.Events[4].Type)
   303  	assertAttribute(t, "recipient", bob.String(), res.Events[4].Attributes[0])
   304  	assertAttribute(t, "sender", contractBech32Addr, res.Events[4].Attributes[1])
   305  	assertAttribute(t, "amount", "105000.000000000000000000denom", res.Events[4].Attributes[2])
   306  	// finally, standard x/wasm tag
   307  
   308  	// ensure bob now exists and got both payments released
   309  	bobAcct = data.acctKeeper.GetAccount(data.ctx, bob)
   310  	require.NotNil(t, bobAcct)
   311  	balance := bank.NewBankKeeperAdapter(data.bankKeeper).GetAllBalances(data.ctx, bobAcct.GetAddress())
   312  	assert.Equal(t, deposit.Add(topUp...), balance)
   313  
   314  	// ensure contract has updated balance
   315  
   316  	contractAcct = data.acctKeeper.GetAccount(data.ctx, contractAddr)
   317  	require.NotNil(t, contractAcct)
   318  	assert.Equal(t, zeroCoins, bank.NewBankKeeperAdapter(data.bankKeeper).GetAllBalances(data.ctx, contractAcct.GetAddress()))
   319  
   320  	// ensure all contract state is as after init
   321  	assertCodeList(t, q, data.ctx, 1)
   322  	assertCodeBytes(t, q, data.ctx, 1, testContract)
   323  
   324  	assertContractList(t, q, data.ctx, 1, []string{contractBech32Addr})
   325  	assertContractInfo(t, q, data.ctx, contractBech32Addr, 1, creator)
   326  	assertContractState(t, q, data.ctx, contractBech32Addr, state{
   327  		Verifier:    fred.String(),
   328  		Beneficiary: bob.String(),
   329  		Funder:      creator.String(),
   330  	})
   331  }
   332  
   333  func TestHandleExecuteEscrow(t *testing.T) {
   334  	types2.UnittestOnlySetMilestoneEarthHeight(1)
   335  	data := setupTest(t)
   336  	types2.UnittestOnlySetMilestoneEarthHeight(1)
   337  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
   338  	topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
   339  	creator := data.faucet.NewFundedAccount(data.ctx, deposit.Add(deposit...)...)
   340  	fred := data.faucet.NewFundedAccount(data.ctx, topUp...)
   341  
   342  	h := data.module.NewHandler()
   343  
   344  	msg := &MsgStoreCode{
   345  		Sender:       creator.String(),
   346  		WASMByteCode: testContract,
   347  	}
   348  	res, err := h(data.ctx, msg)
   349  	require.NoError(t, err)
   350  
   351  	_, _, bob := keyPubAddr()
   352  	initMsg := map[string]interface{}{
   353  		"verifier":    fred.String(),
   354  		"beneficiary": bob.String(),
   355  	}
   356  	initMsgBz, err := json.Marshal(initMsg)
   357  	require.NoError(t, err)
   358  
   359  	initCmd := MsgInstantiateContract{
   360  		Sender: creator.String(),
   361  		CodeID: firstCodeID,
   362  		Msg:    initMsgBz,
   363  		Funds:  sdk.CoinsToCoinAdapters(deposit),
   364  	}
   365  	res, err = h(data.ctx, &initCmd)
   366  	require.NoError(t, err)
   367  	contractBech32Addr := parseInitResponse(t, res.Data)
   368  	require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", contractBech32Addr)
   369  
   370  	handleMsg := map[string]interface{}{
   371  		"release": map[string]interface{}{},
   372  	}
   373  	handleMsgBz, err := json.Marshal(handleMsg)
   374  	require.NoError(t, err)
   375  
   376  	execCmd := MsgExecuteContract{
   377  		Sender:   fred.String(),
   378  		Contract: contractBech32Addr,
   379  		Msg:      handleMsgBz,
   380  		Funds:    sdk.CoinsToCoinAdapters(topUp),
   381  	}
   382  	res, err = h(data.ctx, &execCmd)
   383  	require.NoError(t, err)
   384  	// from https://github.com/CosmWasm/cosmwasm/blob/master/contracts/hackatom/src/contract.rs#L167
   385  	assertExecuteResponse(t, res.Data, []byte{0xf0, 0x0b, 0xaa})
   386  
   387  	// ensure bob now exists and got both payments released
   388  	bobAcct := data.acctKeeper.GetAccount(data.ctx, bob)
   389  	require.NotNil(t, bobAcct)
   390  	balance := bank.NewBankKeeperAdapter(data.bankKeeper).GetAllBalances(data.ctx, bobAcct.GetAddress())
   391  	assert.Equal(t, deposit.Add(topUp...), balance)
   392  
   393  	// ensure contract has updated balance
   394  	contractAddr, _ := sdk.AccAddressFromBech32(contractBech32Addr)
   395  	contractAcct := data.acctKeeper.GetAccount(data.ctx, contractAddr)
   396  	require.NotNil(t, contractAcct)
   397  	assert.Equal(t, zeroCoins, bank.NewBankKeeperAdapter(data.bankKeeper).GetAllBalances(data.ctx, contractAcct.GetAddress()))
   398  }
   399  
   400  func TestReadWasmConfig(t *testing.T) {
   401  	defaults := DefaultWasmConfig()
   402  	specs := map[string]struct {
   403  		src AppOptionsMock
   404  		exp types.WasmConfig
   405  	}{
   406  		"set query gas limit via opts": {
   407  			src: AppOptionsMock{
   408  				"wasm.query_gas_limit": 1,
   409  			},
   410  			exp: types.WasmConfig{
   411  				SimulationGasLimit: defaults.SimulationGasLimit,
   412  				SmartQueryGasLimit: 1,
   413  				MemoryCacheSize:    defaults.MemoryCacheSize,
   414  			},
   415  		},
   416  		"set cache via opts": {
   417  			src: AppOptionsMock{
   418  				"wasm.memory_cache_size": 2,
   419  			},
   420  			exp: types.WasmConfig{
   421  				SimulationGasLimit: defaults.SimulationGasLimit,
   422  				MemoryCacheSize:    2,
   423  				SmartQueryGasLimit: defaults.SmartQueryGasLimit,
   424  			},
   425  		},
   426  		"set debug via opts": {
   427  			src: AppOptionsMock{
   428  				"trace": true,
   429  			},
   430  			exp: types.WasmConfig{
   431  				SimulationGasLimit: defaults.SimulationGasLimit,
   432  				SmartQueryGasLimit: defaults.SmartQueryGasLimit,
   433  				MemoryCacheSize:    defaults.MemoryCacheSize,
   434  				ContractDebugMode:  true,
   435  			},
   436  		},
   437  		"all defaults when no options set": {
   438  			exp: defaults,
   439  		},
   440  	}
   441  	for msg, spec := range specs {
   442  		t.Run(msg, func(t *testing.T) {
   443  			viper.Reset()
   444  			for k, v := range spec.src {
   445  				viper.Set(k, v)
   446  			}
   447  			got, err := ReadWasmConfig()
   448  			require.NoError(t, err)
   449  			assert.Equal(t, spec.exp, got)
   450  			viper.Reset()
   451  		})
   452  	}
   453  }
   454  
   455  type AppOptionsMock map[string]interface{}
   456  
   457  func (a AppOptionsMock) Get(s string) interface{} {
   458  	return a[s]
   459  }
   460  
   461  type prettyEvent struct {
   462  	Type string
   463  	Attr []sdk.Attribute
   464  }
   465  
   466  func prettyEvents(evts []sdk.Event) string {
   467  	res := make([]prettyEvent, len(evts))
   468  	for i, e := range evts {
   469  		res[i] = prettyEvent{
   470  			Type: e.Type,
   471  			Attr: prettyAttrs(e.Attributes),
   472  		}
   473  	}
   474  	bz, err := json.MarshalIndent(res, "", "  ")
   475  	if err != nil {
   476  		panic(err)
   477  	}
   478  	return string(bz)
   479  }
   480  
   481  func prettyAttrs(attrs []kv.Pair) []sdk.Attribute {
   482  	pretty := make([]sdk.Attribute, len(attrs))
   483  	for i, a := range attrs {
   484  		pretty[i] = prettyAttr(a)
   485  	}
   486  	return pretty
   487  }
   488  
   489  func prettyAttr(attr kv.Pair) sdk.Attribute {
   490  	return sdk.NewAttribute(string(attr.Key), string(attr.Value))
   491  }
   492  
   493  func assertAttribute(t *testing.T, key string, value string, attr kv.Pair) {
   494  	t.Helper()
   495  	assert.Equal(t, key, string(attr.Key), prettyAttr(attr))
   496  	assert.Equal(t, value, string(attr.Value), prettyAttr(attr))
   497  }
   498  
   499  func assertCodeList(t *testing.T, q sdk.Querier, ctx sdk.Context, expectedNum int) {
   500  	bz, sdkerr := q(ctx, []string{QueryListCode}, abci.RequestQuery{})
   501  	require.NoError(t, sdkerr)
   502  
   503  	if len(bz) == 0 {
   504  		require.Equal(t, expectedNum, 0)
   505  		return
   506  	}
   507  
   508  	var res []CodeInfo
   509  	err := json.Unmarshal(bz, &res)
   510  	require.NoError(t, err)
   511  
   512  	assert.Equal(t, expectedNum, len(res))
   513  }
   514  
   515  func assertCodeBytes(t *testing.T, q sdk.Querier, ctx sdk.Context, codeID uint64, expectedBytes []byte) {
   516  	path := []string{QueryGetCode, fmt.Sprintf("%d", codeID)}
   517  	bz, sdkerr := q(ctx, path, abci.RequestQuery{})
   518  	require.NoError(t, sdkerr)
   519  
   520  	if len(expectedBytes) == 0 {
   521  		require.Equal(t, len(bz), 0, "%q", string(bz))
   522  		return
   523  	}
   524  	var res map[string]interface{}
   525  	err := json.Unmarshal(bz, &res)
   526  	require.NoError(t, err)
   527  
   528  	require.Contains(t, res, "data")
   529  	b, err := base64url.Decode(res["data"].(string))
   530  	require.NoError(t, err)
   531  	assert.Equal(t, expectedBytes, b)
   532  	assert.EqualValues(t, codeID, res["id"])
   533  }
   534  
   535  func assertContractList(t *testing.T, q sdk.Querier, ctx sdk.Context, codeID uint64, expContractAddrs []string) {
   536  	bz, sdkerr := q(ctx, []string{QueryListContractByCode, fmt.Sprintf("%d", codeID)}, abci.RequestQuery{})
   537  	require.NoError(t, sdkerr)
   538  
   539  	if len(bz) == 0 {
   540  		require.Equal(t, len(expContractAddrs), 0)
   541  		return
   542  	}
   543  
   544  	var res []string
   545  	err := json.Unmarshal(bz, &res)
   546  	require.NoError(t, err)
   547  
   548  	hasAddrs := make([]string, len(res))
   549  	for i, r := range res {
   550  		hasAddrs[i] = r
   551  	}
   552  
   553  	assert.Equal(t, expContractAddrs, hasAddrs)
   554  }
   555  
   556  func assertContractState(t *testing.T, q sdk.Querier, ctx sdk.Context, contractBech32Addr string, expected state) {
   557  	t.Helper()
   558  	path := []string{QueryGetContractState, contractBech32Addr, keeper.QueryMethodContractStateAll}
   559  	bz, sdkerr := q(ctx, path, abci.RequestQuery{})
   560  	require.NoError(t, sdkerr)
   561  
   562  	var res []Model
   563  	err := json.Unmarshal(bz, &res)
   564  	require.NoError(t, err)
   565  	require.Equal(t, 1, len(res), "#v", res)
   566  	require.Equal(t, []byte("config"), []byte(res[0].Key))
   567  
   568  	expectedBz, err := json.Marshal(expected)
   569  	require.NoError(t, err)
   570  	assert.Equal(t, expectedBz, res[0].Value)
   571  }
   572  
   573  func assertContractInfo(t *testing.T, q sdk.Querier, ctx sdk.Context, contractBech32Addr string, codeID uint64, creator sdk.AccAddress) {
   574  	t.Helper()
   575  	path := []string{QueryGetContract, contractBech32Addr}
   576  	bz, sdkerr := q(ctx, path, abci.RequestQuery{})
   577  	require.NoError(t, sdkerr)
   578  
   579  	var res ContractInfo
   580  	err := json.Unmarshal(bz, &res)
   581  	require.NoError(t, err)
   582  
   583  	assert.Equal(t, codeID, res.CodeID)
   584  	assert.Equal(t, creator.String(), res.Creator)
   585  }