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 }