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

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"testing"
    10  
    11  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    12  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    17  )
    18  
    19  func TestLegacyQueryContractState(t *testing.T) {
    20  	ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
    21  	keeper := keepers.WasmKeeper
    22  
    23  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
    24  	creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...)
    25  	anyAddr := keepers.Faucet.NewFundedAccount(ctx, sdk.NewInt64Coin("denom", 5000))
    26  
    27  	wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
    28  	require.NoError(t, err)
    29  
    30  	contractID, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil)
    31  	require.NoError(t, err)
    32  
    33  	_, _, bob := keyPubAddr()
    34  	initMsg := HackatomExampleInitMsg{
    35  		Verifier:    anyAddr,
    36  		Beneficiary: bob,
    37  	}
    38  	initMsgBz, err := json.Marshal(initMsg)
    39  	require.NoError(t, err)
    40  
    41  	addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract to query", deposit)
    42  	require.NoError(t, err)
    43  
    44  	contractModel := []types.Model{
    45  		{Key: []byte("foo"), Value: []byte(`"bar"`)},
    46  		{Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)},
    47  	}
    48  	keeper.importContractState(ctx, addr, contractModel)
    49  
    50  	// this gets us full error, not redacted sdk.Error
    51  	var defaultQueryGasLimit sdk.Gas = 3000000
    52  	q := NewLegacyQuerier(keeper, defaultQueryGasLimit)
    53  
    54  	specs := map[string]struct {
    55  		srcPath []string
    56  		srcReq  abci.RequestQuery
    57  		// smart and raw queries (not all queries) return raw bytes from contract not []types.Model
    58  		// if this is set, then we just compare - (should be json encoded string)
    59  		expRes []byte
    60  		// if success and expSmartRes is not set, we parse into []types.Model and compare (all state)
    61  		expModelLen      int
    62  		expModelContains []types.Model
    63  		expErr           error
    64  	}{
    65  		"query all": {
    66  			srcPath:     []string{QueryGetContractState, addr.String(), QueryMethodContractStateAll},
    67  			expModelLen: 3,
    68  			expModelContains: []types.Model{
    69  				{Key: []byte("foo"), Value: []byte(`"bar"`)},
    70  				{Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)},
    71  			},
    72  		},
    73  		"query raw key": {
    74  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
    75  			srcReq:  abci.RequestQuery{Data: []byte("foo")},
    76  			expRes:  []byte(`"bar"`),
    77  		},
    78  		"query raw binary key": {
    79  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
    80  			srcReq:  abci.RequestQuery{Data: []byte{0x0, 0x1}},
    81  			expRes:  []byte(`{"count":8}`),
    82  		},
    83  		"query smart": {
    84  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
    85  			srcReq:  abci.RequestQuery{Data: []byte(`{"verifier":{}}`)},
    86  			expRes:  []byte(fmt.Sprintf(`{"verifier":"%s"}`, anyAddr.String())),
    87  		},
    88  		"query smart invalid request": {
    89  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
    90  			srcReq:  abci.RequestQuery{Data: []byte(`{"raw":{"key":"config"}}`)},
    91  			expErr:  types.ErrQueryFailed,
    92  		},
    93  		"query smart with invalid json": {
    94  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
    95  			srcReq:  abci.RequestQuery{Data: []byte(`not a json string`)},
    96  			expErr:  types.ErrInvalid,
    97  		},
    98  		"query non-existent raw key": {
    99  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
   100  			srcReq:  abci.RequestQuery{Data: []byte("i do not exist")},
   101  			expRes:  nil,
   102  		},
   103  		"query empty raw key": {
   104  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
   105  			srcReq:  abci.RequestQuery{Data: []byte("")},
   106  			expRes:  nil,
   107  		},
   108  		"query nil raw key": {
   109  			srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
   110  			srcReq:  abci.RequestQuery{Data: nil},
   111  			expRes:  nil,
   112  		},
   113  		"query raw with unknown address": {
   114  			srcPath: []string{QueryGetContractState, anyAddr.String(), QueryMethodContractStateRaw},
   115  			expRes:  nil,
   116  		},
   117  		"query all with unknown address": {
   118  			srcPath:     []string{QueryGetContractState, anyAddr.String(), QueryMethodContractStateAll},
   119  			expModelLen: 0,
   120  		},
   121  		"query smart with unknown address": {
   122  			srcPath:     []string{QueryGetContractState, anyAddr.String(), QueryMethodContractStateSmart},
   123  			srcReq:      abci.RequestQuery{Data: []byte(`{}`)},
   124  			expModelLen: 0,
   125  			expErr:      types.ErrNotFound,
   126  		},
   127  	}
   128  
   129  	for msg, spec := range specs {
   130  		t.Run(msg, func(t *testing.T) {
   131  			binResult, err := q(ctx, spec.srcPath, spec.srcReq)
   132  			// require.True(t, spec.expErr.Is(err), "unexpected error")
   133  			require.True(t, errors.Is(err, spec.expErr), err)
   134  
   135  			// if smart query, check custom response
   136  			if spec.srcPath[2] != QueryMethodContractStateAll {
   137  				require.Equal(t, spec.expRes, binResult)
   138  				return
   139  			}
   140  
   141  			// otherwise, check returned models
   142  			var r []types.Model
   143  			if spec.expErr == nil {
   144  				require.NoError(t, json.Unmarshal(binResult, &r))
   145  				require.NotNil(t, r)
   146  			}
   147  			require.Len(t, r, spec.expModelLen)
   148  			// and in result set
   149  			for _, v := range spec.expModelContains {
   150  				assert.Contains(t, r, v)
   151  			}
   152  		})
   153  	}
   154  }
   155  
   156  func TestLegacyQueryContractListByCodeOrdering(t *testing.T) {
   157  	ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
   158  	keeper := keepers.WasmKeeper
   159  
   160  	deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000000))
   161  	topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 500))
   162  	creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...)
   163  	anyAddr := keepers.Faucet.NewFundedAccount(ctx, topUp...)
   164  
   165  	wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
   166  	require.NoError(t, err)
   167  
   168  	codeID, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil)
   169  	require.NoError(t, err)
   170  
   171  	_, _, bob := keyPubAddr()
   172  	initMsg := HackatomExampleInitMsg{
   173  		Verifier:    anyAddr,
   174  		Beneficiary: bob,
   175  	}
   176  	initMsgBz, err := json.Marshal(initMsg)
   177  	require.NoError(t, err)
   178  
   179  	// manage some realistic block settings
   180  	var h int64 = 10
   181  	setBlock := func(ctx sdk.Context, height int64) sdk.Context {
   182  		ctx = ctx.WithBlockHeight(height)
   183  		meter := sdk.NewGasMeter(1000000)
   184  		ctx.SetGasMeter(meter)
   185  		ctx.SetBlockGasMeter(meter)
   186  		return ctx
   187  	}
   188  
   189  	// create 10 contracts with real block/gas setup
   190  	for i := range [10]int{} {
   191  		// 3 tx per block, so we ensure both comparisons work
   192  		if i%3 == 0 {
   193  			ctx = setBlock(ctx, h)
   194  			h++
   195  		}
   196  		_, _, err = keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, initMsgBz, fmt.Sprintf("contract %d", i), topUp)
   197  		require.NoError(t, err)
   198  	}
   199  
   200  	// query and check the results are properly sorted
   201  	var defaultQueryGasLimit sdk.Gas = 3000000
   202  	q := NewLegacyQuerier(keeper, defaultQueryGasLimit)
   203  
   204  	query := []string{QueryListContractByCode, fmt.Sprintf("%d", codeID)}
   205  	data := abci.RequestQuery{}
   206  	res, err := q(ctx, query, data)
   207  	require.NoError(t, err)
   208  
   209  	var contracts []string
   210  	err = json.Unmarshal(res, &contracts)
   211  	require.NoError(t, err)
   212  
   213  	require.Equal(t, 10, len(contracts))
   214  
   215  	for _, contract := range contracts {
   216  		assert.NotEmpty(t, contract)
   217  	}
   218  }
   219  
   220  func TestLegacyQueryContractHistory(t *testing.T) {
   221  	ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
   222  	keeper := keepers.WasmKeeper
   223  
   224  	var otherAddr sdk.AccAddress = bytes.Repeat([]byte{0x2}, types.ContractAddrLen)
   225  
   226  	specs := map[string]struct {
   227  		srcQueryAddr sdk.AccAddress
   228  		srcHistory   []types.ContractCodeHistoryEntry
   229  		expContent   []types.ContractCodeHistoryEntry
   230  	}{
   231  		"response with internal fields cleared": {
   232  			srcHistory: []types.ContractCodeHistoryEntry{{
   233  				Operation: types.ContractCodeHistoryOperationTypeGenesis,
   234  				CodeID:    firstCodeID,
   235  				Updated:   types.NewAbsoluteTxPosition(ctx),
   236  				Msg:       []byte(`"init message"`),
   237  			}},
   238  			expContent: []types.ContractCodeHistoryEntry{{
   239  				Operation: types.ContractCodeHistoryOperationTypeGenesis,
   240  				CodeID:    firstCodeID,
   241  				Msg:       []byte(`"init message"`),
   242  			}},
   243  		},
   244  		"response with multiple entries": {
   245  			srcHistory: []types.ContractCodeHistoryEntry{{
   246  				Operation: types.ContractCodeHistoryOperationTypeInit,
   247  				CodeID:    firstCodeID,
   248  				Updated:   types.NewAbsoluteTxPosition(ctx),
   249  				Msg:       []byte(`"init message"`),
   250  			}, {
   251  				Operation: types.ContractCodeHistoryOperationTypeMigrate,
   252  				CodeID:    2,
   253  				Updated:   types.NewAbsoluteTxPosition(ctx),
   254  				Msg:       []byte(`"migrate message 1"`),
   255  			}, {
   256  				Operation: types.ContractCodeHistoryOperationTypeMigrate,
   257  				CodeID:    3,
   258  				Updated:   types.NewAbsoluteTxPosition(ctx),
   259  				Msg:       []byte(`"migrate message 2"`),
   260  			}},
   261  			expContent: []types.ContractCodeHistoryEntry{{
   262  				Operation: types.ContractCodeHistoryOperationTypeInit,
   263  				CodeID:    firstCodeID,
   264  				Msg:       []byte(`"init message"`),
   265  			}, {
   266  				Operation: types.ContractCodeHistoryOperationTypeMigrate,
   267  				CodeID:    2,
   268  				Msg:       []byte(`"migrate message 1"`),
   269  			}, {
   270  				Operation: types.ContractCodeHistoryOperationTypeMigrate,
   271  				CodeID:    3,
   272  				Msg:       []byte(`"migrate message 2"`),
   273  			}},
   274  		},
   275  		"unknown contract address": {
   276  			srcQueryAddr: otherAddr,
   277  			srcHistory: []types.ContractCodeHistoryEntry{{
   278  				Operation: types.ContractCodeHistoryOperationTypeGenesis,
   279  				CodeID:    firstCodeID,
   280  				Updated:   types.NewAbsoluteTxPosition(ctx),
   281  				Msg:       []byte(`"init message"`),
   282  			}},
   283  			expContent: []types.ContractCodeHistoryEntry{},
   284  		},
   285  	}
   286  	for msg, spec := range specs {
   287  		t.Run(msg, func(t *testing.T) {
   288  			_, _, myContractAddr := keyPubAddr()
   289  			keeper.appendToContractHistory(ctx, myContractAddr, spec.srcHistory...)
   290  
   291  			var defaultQueryGasLimit sdk.Gas = 3000000
   292  			q := NewLegacyQuerier(keeper, defaultQueryGasLimit)
   293  			queryContractAddr := spec.srcQueryAddr
   294  			if queryContractAddr == nil {
   295  				queryContractAddr = myContractAddr
   296  			}
   297  
   298  			// when
   299  			query := []string{QueryContractHistory, queryContractAddr.String()}
   300  			data := abci.RequestQuery{}
   301  			resData, err := q(ctx, query, data)
   302  
   303  			// then
   304  			require.NoError(t, err)
   305  			var got []types.ContractCodeHistoryEntry
   306  			err = json.Unmarshal(resData, &got)
   307  			require.NoError(t, err)
   308  
   309  			assert.Equal(t, spec.expContent, got)
   310  		})
   311  	}
   312  }
   313  
   314  func TestLegacyQueryCodeList(t *testing.T) {
   315  	wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
   316  	require.NoError(t, err)
   317  
   318  	specs := map[string]struct {
   319  		codeIDs []uint64
   320  	}{
   321  		"none": {},
   322  		"no gaps": {
   323  			codeIDs: []uint64{1, 2, 3},
   324  		},
   325  		"with gaps": {
   326  			codeIDs: []uint64{2, 4, 6},
   327  		},
   328  	}
   329  
   330  	for msg, spec := range specs {
   331  		t.Run(msg, func(t *testing.T) {
   332  			ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
   333  			keeper := keepers.WasmKeeper
   334  
   335  			for _, codeID := range spec.codeIDs {
   336  				require.NoError(t, keeper.importCode(ctx, codeID,
   337  					types.CodeInfoFixture(types.WithSHA256CodeHash(wasmCode)),
   338  					wasmCode),
   339  				)
   340  			}
   341  			var defaultQueryGasLimit sdk.Gas = 3000000
   342  			q := NewLegacyQuerier(keeper, defaultQueryGasLimit)
   343  			// when
   344  			query := []string{QueryListCode}
   345  			data := abci.RequestQuery{}
   346  			resData, err := q(ctx, query, data)
   347  
   348  			// then
   349  			require.NoError(t, err)
   350  			if len(spec.codeIDs) == 0 {
   351  				require.Nil(t, resData)
   352  				return
   353  			}
   354  
   355  			var got []map[string]interface{}
   356  			err = json.Unmarshal(resData, &got)
   357  			require.NoError(t, err)
   358  			require.Len(t, got, len(spec.codeIDs))
   359  			for i, exp := range spec.codeIDs {
   360  				assert.EqualValues(t, exp, got[i]["id"])
   361  			}
   362  		})
   363  	}
   364  }