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

     1  package keeper
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store"
     8  	dbm "github.com/fibonacci-chain/fbc/libs/tm-db"
     9  
    10  	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
    11  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    12  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    13  	channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/fibonacci-chain/fbc/x/wasm/keeper/wasmtesting"
    18  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    19  )
    20  
    21  func TestIBCQuerier(t *testing.T) {
    22  	myExampleChannels := []channeltypes.IdentifiedChannel{
    23  		// this is returned
    24  		{
    25  			State:    channeltypes.OPEN,
    26  			Ordering: channeltypes.ORDERED,
    27  			Counterparty: channeltypes.Counterparty{
    28  				PortId:    "counterPartyPortID",
    29  				ChannelId: "counterPartyChannelID",
    30  			},
    31  			ConnectionHops: []string{"one"},
    32  			Version:        "v1",
    33  			PortId:         "myPortID",
    34  			ChannelId:      "myChannelID",
    35  		},
    36  		// this is filtered out
    37  		{
    38  			State:    channeltypes.INIT,
    39  			Ordering: channeltypes.UNORDERED,
    40  			Counterparty: channeltypes.Counterparty{
    41  				PortId: "foobar",
    42  			},
    43  			ConnectionHops: []string{"one"},
    44  			Version:        "initversion",
    45  			PortId:         "initPortID",
    46  			ChannelId:      "initChannelID",
    47  		},
    48  		// this is returned
    49  		{
    50  			State:    channeltypes.OPEN,
    51  			Ordering: channeltypes.UNORDERED,
    52  			Counterparty: channeltypes.Counterparty{
    53  				PortId:    "otherCounterPartyPortID",
    54  				ChannelId: "otherCounterPartyChannelID",
    55  			},
    56  			ConnectionHops: []string{"other", "second"},
    57  			Version:        "otherVersion",
    58  			PortId:         "otherPortID",
    59  			ChannelId:      "otherChannelID",
    60  		},
    61  		// this is filtered out
    62  		{
    63  			State:    channeltypes.CLOSED,
    64  			Ordering: channeltypes.ORDERED,
    65  			Counterparty: channeltypes.Counterparty{
    66  				PortId:    "super",
    67  				ChannelId: "duper",
    68  			},
    69  			ConnectionHops: []string{"no-more"},
    70  			Version:        "closedVersion",
    71  			PortId:         "closedPortID",
    72  			ChannelId:      "closedChannelID",
    73  		},
    74  	}
    75  	specs := map[string]struct {
    76  		srcQuery      *wasmvmtypes.IBCQuery
    77  		wasmKeeper    *mockWasmQueryKeeper
    78  		channelKeeper *wasmtesting.MockChannelKeeper
    79  		expJsonResult string
    80  		expErr        *sdkerrors.Error
    81  	}{
    82  		"query port id": {
    83  			srcQuery: &wasmvmtypes.IBCQuery{
    84  				PortID: &wasmvmtypes.PortIDQuery{},
    85  			},
    86  			wasmKeeper: &mockWasmQueryKeeper{
    87  				GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
    88  					return &types.ContractInfo{IBCPortID: "myIBCPortID"}
    89  				},
    90  			},
    91  			channelKeeper: &wasmtesting.MockChannelKeeper{},
    92  			expJsonResult: `{"port_id":"myIBCPortID"}`,
    93  		},
    94  		"query list channels - all": {
    95  			srcQuery: &wasmvmtypes.IBCQuery{
    96  				ListChannels: &wasmvmtypes.ListChannelsQuery{},
    97  			},
    98  			channelKeeper: &wasmtesting.MockChannelKeeper{
    99  				IterateChannelsFn: wasmtesting.MockChannelKeeperIterator(myExampleChannels),
   100  			},
   101  			expJsonResult: `{
   102    "channels": [
   103      {
   104        "endpoint": {
   105          "port_id": "myPortID",
   106          "channel_id": "myChannelID"
   107        },
   108        "counterparty_endpoint": {
   109          "port_id": "counterPartyPortID",
   110          "channel_id": "counterPartyChannelID"
   111        },
   112        "order": "ORDER_ORDERED",
   113        "version": "v1",
   114        "connection_id": "one"
   115      },
   116      {
   117        "endpoint": {
   118          "port_id": "otherPortID",
   119          "channel_id": "otherChannelID"
   120        },
   121        "counterparty_endpoint": {
   122          "port_id": "otherCounterPartyPortID",
   123          "channel_id": "otherCounterPartyChannelID"
   124        },
   125        "order": "ORDER_UNORDERED",
   126        "version": "otherVersion",
   127        "connection_id": "other"
   128      }
   129    ]
   130  }`,
   131  		},
   132  		"query list channels - filtered": {
   133  			srcQuery: &wasmvmtypes.IBCQuery{
   134  				ListChannels: &wasmvmtypes.ListChannelsQuery{
   135  					PortID: "otherPortID",
   136  				},
   137  			},
   138  			channelKeeper: &wasmtesting.MockChannelKeeper{
   139  				IterateChannelsFn: wasmtesting.MockChannelKeeperIterator(myExampleChannels),
   140  			},
   141  			expJsonResult: `{
   142    "channels": [
   143      {
   144        "endpoint": {
   145          "port_id": "otherPortID",
   146          "channel_id": "otherChannelID"
   147        },
   148        "counterparty_endpoint": {
   149          "port_id": "otherCounterPartyPortID",
   150          "channel_id": "otherCounterPartyChannelID"
   151        },
   152        "order": "ORDER_UNORDERED",
   153        "version": "otherVersion",
   154        "connection_id": "other"
   155      }
   156    ]
   157  }`,
   158  		},
   159  		"query list channels - filtered empty": {
   160  			srcQuery: &wasmvmtypes.IBCQuery{
   161  				ListChannels: &wasmvmtypes.ListChannelsQuery{
   162  					PortID: "none-existing",
   163  				},
   164  			},
   165  			channelKeeper: &wasmtesting.MockChannelKeeper{
   166  				IterateChannelsFn: wasmtesting.MockChannelKeeperIterator(myExampleChannels),
   167  			},
   168  			expJsonResult: `{"channels": []}`,
   169  		},
   170  		"query channel": {
   171  			srcQuery: &wasmvmtypes.IBCQuery{
   172  				Channel: &wasmvmtypes.ChannelQuery{
   173  					PortID:    "myQueryPortID",
   174  					ChannelID: "myQueryChannelID",
   175  				},
   176  			},
   177  			channelKeeper: &wasmtesting.MockChannelKeeper{
   178  				GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) {
   179  					return channeltypes.Channel{
   180  						State:    channeltypes.OPEN,
   181  						Ordering: channeltypes.UNORDERED,
   182  						Counterparty: channeltypes.Counterparty{
   183  							PortId:    "counterPartyPortID",
   184  							ChannelId: "otherCounterPartyChannelID",
   185  						},
   186  						ConnectionHops: []string{"one"},
   187  						Version:        "version",
   188  					}, true
   189  				},
   190  			},
   191  			expJsonResult: `{
   192    "channel": {
   193      "endpoint": {
   194        "port_id": "myQueryPortID",
   195        "channel_id": "myQueryChannelID"
   196      },
   197      "counterparty_endpoint": {
   198        "port_id": "counterPartyPortID",
   199        "channel_id": "otherCounterPartyChannelID"
   200      },
   201      "order": "ORDER_UNORDERED",
   202      "version": "version",
   203      "connection_id": "one"
   204    }
   205  }`,
   206  		},
   207  		"query channel - without port set": {
   208  			srcQuery: &wasmvmtypes.IBCQuery{
   209  				Channel: &wasmvmtypes.ChannelQuery{
   210  					ChannelID: "myQueryChannelID",
   211  				},
   212  			},
   213  			wasmKeeper: &mockWasmQueryKeeper{
   214  				GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
   215  					return &types.ContractInfo{IBCPortID: "myLoadedPortID"}
   216  				},
   217  			},
   218  			channelKeeper: &wasmtesting.MockChannelKeeper{
   219  				GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) {
   220  					return channeltypes.Channel{
   221  						State:    channeltypes.OPEN,
   222  						Ordering: channeltypes.UNORDERED,
   223  						Counterparty: channeltypes.Counterparty{
   224  							PortId:    "counterPartyPortID",
   225  							ChannelId: "otherCounterPartyChannelID",
   226  						},
   227  						ConnectionHops: []string{"one"},
   228  						Version:        "version",
   229  					}, true
   230  				},
   231  			},
   232  			expJsonResult: `{
   233    "channel": {
   234      "endpoint": {
   235        "port_id": "myLoadedPortID",
   236        "channel_id": "myQueryChannelID"
   237      },
   238      "counterparty_endpoint": {
   239        "port_id": "counterPartyPortID",
   240        "channel_id": "otherCounterPartyChannelID"
   241      },
   242      "order": "ORDER_UNORDERED",
   243      "version": "version",
   244      "connection_id": "one"
   245    }
   246  }`,
   247  		},
   248  		"query channel in init state": {
   249  			srcQuery: &wasmvmtypes.IBCQuery{
   250  				Channel: &wasmvmtypes.ChannelQuery{
   251  					PortID:    "myQueryPortID",
   252  					ChannelID: "myQueryChannelID",
   253  				},
   254  			},
   255  			channelKeeper: &wasmtesting.MockChannelKeeper{
   256  				GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) {
   257  					return channeltypes.Channel{
   258  						State:    channeltypes.INIT,
   259  						Ordering: channeltypes.UNORDERED,
   260  						Counterparty: channeltypes.Counterparty{
   261  							PortId: "foobar",
   262  						},
   263  						ConnectionHops: []string{"one"},
   264  						Version:        "initversion",
   265  					}, true
   266  				},
   267  			},
   268  			expJsonResult: "{}",
   269  		},
   270  		"query channel in closed state": {
   271  			srcQuery: &wasmvmtypes.IBCQuery{
   272  				Channel: &wasmvmtypes.ChannelQuery{
   273  					PortID:    "myQueryPortID",
   274  					ChannelID: "myQueryChannelID",
   275  				},
   276  			},
   277  			channelKeeper: &wasmtesting.MockChannelKeeper{
   278  				GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) {
   279  					return channeltypes.Channel{
   280  						State:    channeltypes.CLOSED,
   281  						Ordering: channeltypes.ORDERED,
   282  						Counterparty: channeltypes.Counterparty{
   283  							PortId:    "super",
   284  							ChannelId: "duper",
   285  						},
   286  						ConnectionHops: []string{"no-more"},
   287  						Version:        "closedVersion",
   288  					}, true
   289  				},
   290  			},
   291  			expJsonResult: "{}",
   292  		},
   293  		"query channel - empty result": {
   294  			srcQuery: &wasmvmtypes.IBCQuery{
   295  				Channel: &wasmvmtypes.ChannelQuery{
   296  					PortID:    "myQueryPortID",
   297  					ChannelID: "myQueryChannelID",
   298  				},
   299  			},
   300  			channelKeeper: &wasmtesting.MockChannelKeeper{
   301  				GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) {
   302  					return channeltypes.Channel{}, false
   303  				},
   304  			},
   305  			expJsonResult: "{}",
   306  		},
   307  	}
   308  	for name, spec := range specs {
   309  		t.Run(name, func(t *testing.T) {
   310  			h := IBCQuerier(spec.wasmKeeper, spec.channelKeeper)
   311  			gotResult, gotErr := h(sdk.Context{}, RandomAccountAddress(t), spec.srcQuery)
   312  			require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr)
   313  			if spec.expErr != nil {
   314  				return
   315  			}
   316  			assert.JSONEq(t, spec.expJsonResult, string(gotResult), string(gotResult))
   317  		})
   318  	}
   319  }
   320  
   321  func TestBankQuerierBalance(t *testing.T) {
   322  	mock := bankKeeperMock{GetBalanceFn: func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
   323  		return sdk.NewCoin(denom, sdk.NewInt(1))
   324  	}}
   325  
   326  	ctx := sdk.Context{}
   327  	q := BankQuerier(mock)
   328  	gotBz, gotErr := q(ctx, &wasmvmtypes.BankQuery{
   329  		Balance: &wasmvmtypes.BalanceQuery{
   330  			Address: RandomBech32AccountAddress(t),
   331  			Denom:   "alx",
   332  		},
   333  	})
   334  	require.NoError(t, gotErr)
   335  	var got wasmvmtypes.BalanceResponse
   336  	require.NoError(t, json.Unmarshal(gotBz, &got))
   337  	exp := wasmvmtypes.BalanceResponse{
   338  		Amount: wasmvmtypes.Coin{
   339  			Denom:  "alx",
   340  			Amount: "1000000000000000000",
   341  		},
   342  	}
   343  	assert.Equal(t, exp, got)
   344  }
   345  
   346  func TestContractInfoWasmQuerier(t *testing.T) {
   347  	myValidContractAddr := RandomBech32AccountAddress(t)
   348  	myCreatorAddr := RandomBech32AccountAddress(t)
   349  	myAdminAddr := RandomBech32AccountAddress(t)
   350  	var ctx sdk.Context
   351  
   352  	specs := map[string]struct {
   353  		req    *wasmvmtypes.WasmQuery
   354  		mock   mockWasmQueryKeeper
   355  		expRes wasmvmtypes.ContractInfoResponse
   356  		expErr bool
   357  	}{
   358  		"all good": {
   359  			req: &wasmvmtypes.WasmQuery{
   360  				ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr},
   361  			},
   362  			mock: mockWasmQueryKeeper{
   363  				GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
   364  					val := types.ContractInfoFixture(func(i *types.ContractInfo) {
   365  						i.Admin, i.Creator, i.IBCPortID = myAdminAddr, myCreatorAddr, "myIBCPort"
   366  					})
   367  					return &val
   368  				},
   369  				IsPinnedCodeFn: func(ctx sdk.Context, codeID uint64) bool { return true },
   370  			},
   371  			expRes: wasmvmtypes.ContractInfoResponse{
   372  				CodeID:  1,
   373  				Creator: myCreatorAddr,
   374  				Admin:   myAdminAddr,
   375  				Pinned:  true,
   376  				IBCPort: "myIBCPort",
   377  			},
   378  		},
   379  		"invalid addr": {
   380  			req: &wasmvmtypes.WasmQuery{
   381  				ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: "not a valid addr"},
   382  			},
   383  			expErr: true,
   384  		},
   385  		"unknown addr": {
   386  			req: &wasmvmtypes.WasmQuery{
   387  				ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr},
   388  			},
   389  			mock: mockWasmQueryKeeper{GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
   390  				return nil
   391  			}},
   392  			expErr: true,
   393  		},
   394  		"not pinned": {
   395  			req: &wasmvmtypes.WasmQuery{
   396  				ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr},
   397  			},
   398  			mock: mockWasmQueryKeeper{
   399  				GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
   400  					val := types.ContractInfoFixture(func(i *types.ContractInfo) {
   401  						i.Admin, i.Creator = myAdminAddr, myCreatorAddr
   402  					})
   403  					return &val
   404  				},
   405  				IsPinnedCodeFn: func(ctx sdk.Context, codeID uint64) bool { return false },
   406  			},
   407  			expRes: wasmvmtypes.ContractInfoResponse{
   408  				CodeID:  1,
   409  				Creator: myCreatorAddr,
   410  				Admin:   myAdminAddr,
   411  				Pinned:  false,
   412  			},
   413  		},
   414  		"without admin": {
   415  			req: &wasmvmtypes.WasmQuery{
   416  				ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr},
   417  			},
   418  			mock: mockWasmQueryKeeper{
   419  				GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
   420  					val := types.ContractInfoFixture(func(i *types.ContractInfo) {
   421  						i.Creator = myCreatorAddr
   422  					})
   423  					return &val
   424  				},
   425  				IsPinnedCodeFn: func(ctx sdk.Context, codeID uint64) bool { return true },
   426  			},
   427  			expRes: wasmvmtypes.ContractInfoResponse{
   428  				CodeID:  1,
   429  				Creator: myCreatorAddr,
   430  				Pinned:  true,
   431  			},
   432  		},
   433  	}
   434  	for name, spec := range specs {
   435  		t.Run(name, func(t *testing.T) {
   436  			q := WasmQuerier(spec.mock)
   437  			gotBz, gotErr := q(ctx, spec.req)
   438  			if spec.expErr {
   439  				require.Error(t, gotErr)
   440  				return
   441  			}
   442  			require.NoError(t, gotErr)
   443  			var gotRes wasmvmtypes.ContractInfoResponse
   444  			require.NoError(t, json.Unmarshal(gotBz, &gotRes))
   445  			assert.Equal(t, spec.expRes, gotRes)
   446  		})
   447  	}
   448  }
   449  
   450  func TestQueryErrors(t *testing.T) {
   451  	specs := map[string]struct {
   452  		src    error
   453  		expErr error
   454  	}{
   455  		"no error": {},
   456  		"no such contract": {
   457  			src:    &types.ErrNoSuchContract{Addr: "contract-addr"},
   458  			expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"},
   459  		},
   460  		"no such contract - wrapped": {
   461  			src:    sdkerrors.Wrap(&types.ErrNoSuchContract{Addr: "contract-addr"}, "my additional data"),
   462  			expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"},
   463  		},
   464  	}
   465  	for name, spec := range specs {
   466  		t.Run(name, func(t *testing.T) {
   467  			mock := WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) {
   468  				return nil, spec.src
   469  			})
   470  			ctx := sdk.Context{}
   471  			ctx.SetGasMeter(sdk.NewInfiniteGasMeter())
   472  			ctx.SetMultiStore(store.NewCommitMultiStore(dbm.NewMemDB()))
   473  			q := NewQueryHandler(ctx, mock, sdk.AccAddress{}, NewDefaultWasmGasRegister())
   474  			_, gotErr := q.Query(wasmvmtypes.QueryRequest{}, 1)
   475  			assert.Equal(t, spec.expErr, gotErr)
   476  		})
   477  	}
   478  }
   479  
   480  type mockWasmQueryKeeper struct {
   481  	GetContractInfoFn func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo
   482  	QueryRawFn        func(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte
   483  	QuerySmartFn      func(ctx sdk.Context, contractAddr sdk.AccAddress, req types.RawContractMessage) ([]byte, error)
   484  	IsPinnedCodeFn    func(ctx sdk.Context, codeID uint64) bool
   485  }
   486  
   487  func (m mockWasmQueryKeeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
   488  	if m.GetContractInfoFn == nil {
   489  		panic("not expected to be called")
   490  	}
   491  	return m.GetContractInfoFn(ctx, contractAddress)
   492  }
   493  
   494  func (m mockWasmQueryKeeper) QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte {
   495  	if m.QueryRawFn == nil {
   496  		panic("not expected to be called")
   497  	}
   498  	return m.QueryRawFn(ctx, contractAddress, key)
   499  }
   500  
   501  func (m mockWasmQueryKeeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) {
   502  	if m.QuerySmartFn == nil {
   503  		panic("not expected to be called")
   504  	}
   505  	return m.QuerySmartFn(ctx, contractAddr, req)
   506  }
   507  
   508  func (m mockWasmQueryKeeper) IsPinnedCode(ctx sdk.Context, codeID uint64) bool {
   509  	if m.IsPinnedCodeFn == nil {
   510  		panic("not expected to be called")
   511  	}
   512  	return m.IsPinnedCodeFn(ctx, codeID)
   513  }
   514  
   515  type bankKeeperMock struct {
   516  	GetBalanceFn     func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
   517  	GetAllBalancesFn func(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
   518  }
   519  
   520  func (m bankKeeperMock) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
   521  	if m.GetBalanceFn == nil {
   522  		panic("not expected to be called")
   523  	}
   524  	return m.GetBalanceFn(ctx, addr, denom)
   525  }
   526  
   527  func (m bankKeeperMock) GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
   528  	if m.GetAllBalancesFn == nil {
   529  		panic("not expected to be called")
   530  	}
   531  	return m.GetAllBalancesFn(ctx, addr)
   532  }