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 }