github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/keeper/keeper_test.go (about) 1 package keeper 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "math" 10 "testing" 11 "time" 12 13 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth" 14 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 15 16 wasmvm "github.com/CosmWasm/wasmvm" 17 wasmvmtypes "github.com/CosmWasm/wasmvm/types" 18 19 stypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/types" 20 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 21 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 22 authtypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 "github.com/fibonacci-chain/fbc/x/wasm/keeper/wasmtesting" 27 "github.com/fibonacci-chain/fbc/x/wasm/types" 28 ) 29 30 // When migrated to go 1.16, embed package should be used instead. 31 func init() { 32 b, err := ioutil.ReadFile("./testdata/hackatom.wasm") 33 if err != nil { 34 panic(err) 35 } 36 hackatomWasm = b 37 } 38 39 var hackatomWasm []byte 40 41 func TestNewKeeper(t *testing.T) { 42 _, keepers := CreateTestInput(t, false, SupportedFeatures) 43 require.NotNil(t, keepers.ContractKeeper) 44 } 45 46 func TestCreateSuccess(t *testing.T) { 47 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 48 keeper := keepers.ContractKeeper 49 50 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 51 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 52 53 em := sdk.NewEventManager() 54 newCtx := ctx 55 ctx.SetEventManager(em) 56 contractID, err := keeper.Create(newCtx, creator, hackatomWasm, nil) 57 require.NoError(t, err) 58 require.Equal(t, uint64(1), contractID) 59 // and verify content 60 storedCode, err := keepers.WasmKeeper.GetByteCode(ctx, contractID) 61 require.NoError(t, err) 62 require.Equal(t, hackatomWasm, storedCode) 63 // and events emitted 64 exp := sdk.Events{sdk.NewEvent("store_code", sdk.NewAttribute("code_id", "1"))} 65 assert.Equal(t, exp, newCtx.EventManager().Events()) 66 } 67 68 func TestCreateNilCreatorAddress(t *testing.T) { 69 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 70 71 _, err := keepers.ContractKeeper.Create(ctx, nil, hackatomWasm, nil) 72 require.Error(t, err, "nil creator is not allowed") 73 } 74 75 func TestCreateNilWasmCode(t *testing.T) { 76 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 77 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 78 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 79 80 _, err := keepers.ContractKeeper.Create(ctx, creator, nil, nil) 81 require.Error(t, err, "nil WASM code is not allowed") 82 } 83 84 func TestCreateInvalidWasmCode(t *testing.T) { 85 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 86 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 87 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 88 89 _, err := keepers.ContractKeeper.Create(ctx, creator, []byte("potatoes"), nil) 90 require.Error(t, err, "potatoes are not valid WASM code") 91 } 92 93 func TestCreateStoresInstantiatePermission(t *testing.T) { 94 var ( 95 deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 96 myAddr sdk.AccAddress = bytes.Repeat([]byte{1}, types.SDKAddrLen) 97 ) 98 99 specs := map[string]struct { 100 srcPermission types.AccessType 101 expInstConf types.AccessConfig 102 }{ 103 "default": { 104 srcPermission: types.DefaultParams().InstantiateDefaultPermission, 105 expInstConf: types.AllowEverybody, 106 }, 107 "everybody": { 108 srcPermission: types.AccessTypeEverybody, 109 expInstConf: types.AllowEverybody, 110 }, 111 "nobody": { 112 srcPermission: types.AccessTypeNobody, 113 expInstConf: types.AllowNobody, 114 }, 115 "onlyAddress with matching address": { 116 srcPermission: types.AccessTypeOnlyAddress, 117 expInstConf: types.AccessConfig{Permission: types.AccessTypeOnlyAddress, Address: myAddr.String()}, 118 }, 119 } 120 for msg, spec := range specs { 121 t.Run(msg, func(t *testing.T) { 122 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 123 accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper 124 keepers.WasmKeeper.SetParams(ctx, types.Params{ 125 CodeUploadAccess: types.AllowEverybody, 126 InstantiateDefaultPermission: spec.srcPermission, 127 }) 128 fundAccounts(t, ctx, accKeeper, bankKeeper, keepers.supplyKeepr, myAddr, deposit) 129 130 codeID, err := keeper.Create(ctx, myAddr, hackatomWasm, nil) 131 require.NoError(t, err) 132 133 codeInfo := keepers.WasmKeeper.GetCodeInfo(ctx, codeID) 134 require.NotNil(t, codeInfo) 135 assert.True(t, spec.expInstConf.Equals(codeInfo.InstantiateConfig), "got %#v", codeInfo.InstantiateConfig) 136 }) 137 } 138 } 139 140 func TestCreateWithParamPermissions(t *testing.T) { 141 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 142 keeper := keepers.ContractKeeper 143 144 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 145 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 146 otherAddr := keepers.Faucet.NewFundedAccount(ctx, deposit...) 147 148 specs := map[string]struct { 149 srcPermission types.AccessConfig 150 expError *sdkerrors.Error 151 }{ 152 "default": { 153 srcPermission: types.DefaultUploadAccess, 154 expError: sdkerrors.ErrUnauthorized, 155 }, 156 "everybody": { 157 srcPermission: types.AllowEverybody, 158 }, 159 "nobody": { 160 srcPermission: types.AllowNobody, 161 expError: sdkerrors.ErrUnauthorized, 162 }, 163 "onlyAddress with matching address": { 164 srcPermission: types.AccessTypeOnlyAddress.With(creator), 165 }, 166 "onlyAddress with non matching address": { 167 srcPermission: types.AccessTypeOnlyAddress.With(otherAddr), 168 expError: sdkerrors.ErrUnauthorized, 169 }, 170 } 171 for msg, spec := range specs { 172 t.Run(msg, func(t *testing.T) { 173 params := types.TestParams() 174 params.CodeUploadAccess = spec.srcPermission 175 keepers.WasmKeeper.SetParams(ctx, params) 176 _, err := keeper.Create(ctx, creator, hackatomWasm, nil) 177 require.True(t, spec.expError.Is(err), err) 178 if spec.expError != nil { 179 return 180 } 181 }) 182 } 183 } 184 185 // ensure that the user cannot set the code instantiate permission to something more permissive 186 // than the default 187 func TestEnforceValidPermissionsOnCreate(t *testing.T) { 188 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 189 keeper := keepers.WasmKeeper 190 contractKeeper := keepers.ContractKeeper 191 192 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 193 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 194 other := keepers.Faucet.NewFundedAccount(ctx, deposit...) 195 196 onlyCreator := types.AccessTypeOnlyAddress.With(creator) 197 onlyOther := types.AccessTypeOnlyAddress.With(other) 198 199 specs := map[string]struct { 200 defaultPermssion types.AccessType 201 requestedPermission *types.AccessConfig 202 // grantedPermission is set iff no error 203 grantedPermission types.AccessConfig 204 // expError is nil iff the request is allowed 205 expError *sdkerrors.Error 206 }{ 207 "override everybody": { 208 defaultPermssion: types.AccessTypeEverybody, 209 requestedPermission: &onlyCreator, 210 grantedPermission: onlyCreator, 211 }, 212 "default to everybody": { 213 defaultPermssion: types.AccessTypeEverybody, 214 requestedPermission: nil, 215 grantedPermission: types.AccessConfig{Permission: types.AccessTypeEverybody}, 216 }, 217 "explicitly set everybody": { 218 defaultPermssion: types.AccessTypeEverybody, 219 requestedPermission: &types.AccessConfig{Permission: types.AccessTypeEverybody}, 220 grantedPermission: types.AccessConfig{Permission: types.AccessTypeEverybody}, 221 }, 222 "cannot override nobody": { 223 defaultPermssion: types.AccessTypeNobody, 224 requestedPermission: &onlyCreator, 225 expError: sdkerrors.ErrUnauthorized, 226 }, 227 "default to nobody": { 228 defaultPermssion: types.AccessTypeNobody, 229 requestedPermission: nil, 230 grantedPermission: types.AccessConfig{Permission: types.AccessTypeNobody}, 231 }, 232 "only defaults to code creator": { 233 defaultPermssion: types.AccessTypeOnlyAddress, 234 requestedPermission: nil, 235 grantedPermission: onlyCreator, 236 }, 237 "can explicitly set to code creator": { 238 defaultPermssion: types.AccessTypeOnlyAddress, 239 requestedPermission: &onlyCreator, 240 grantedPermission: onlyCreator, 241 }, 242 "cannot override which address in only": { 243 defaultPermssion: types.AccessTypeOnlyAddress, 244 requestedPermission: &onlyOther, 245 expError: sdkerrors.ErrUnauthorized, 246 }, 247 } 248 for msg, spec := range specs { 249 t.Run(msg, func(t *testing.T) { 250 params := types.TestParams() 251 params.InstantiateDefaultPermission = spec.defaultPermssion 252 keeper.SetParams(ctx, params) 253 codeID, err := contractKeeper.Create(ctx, creator, hackatomWasm, spec.requestedPermission) 254 require.True(t, spec.expError.Is(err), err) 255 if spec.expError == nil { 256 codeInfo := keeper.GetCodeInfo(ctx, codeID) 257 require.Equal(t, codeInfo.InstantiateConfig, spec.grantedPermission) 258 } 259 }) 260 } 261 } 262 263 func TestCreateDuplicate(t *testing.T) { 264 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 265 keeper := keepers.ContractKeeper 266 267 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 268 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 269 270 // create one copy 271 contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 272 require.NoError(t, err) 273 require.Equal(t, uint64(1), contractID) 274 275 // create second copy 276 duplicateID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 277 require.NoError(t, err) 278 require.Equal(t, uint64(2), duplicateID) 279 280 // and verify both content is proper 281 storedCode, err := keepers.WasmKeeper.GetByteCode(ctx, contractID) 282 require.NoError(t, err) 283 require.Equal(t, hackatomWasm, storedCode) 284 storedCode, err = keepers.WasmKeeper.GetByteCode(ctx, duplicateID) 285 require.NoError(t, err) 286 require.Equal(t, hackatomWasm, storedCode) 287 } 288 289 func TestCreateWithSimulation(t *testing.T) { 290 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 291 ctx.SetBlockHeader(abci.Header{Height: 1}) 292 ctx.SetGasMeter(stypes.NewInfiniteGasMeter()) 293 294 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 295 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 296 297 // create this once in simulation mode 298 contractID, err := keepers.ContractKeeper.Create(ctx, creator, hackatomWasm, nil) 299 require.NoError(t, err) 300 require.Equal(t, uint64(1), contractID) 301 302 // then try to create it in non-simulation mode (should not fail) 303 ctx, keepers = CreateTestInput(t, false, SupportedFeatures) 304 ctx.SetGasMeter(sdk.NewGasMeter(10_000_000)) 305 creator = keepers.Faucet.NewFundedAccount(ctx, deposit...) 306 contractID, err = keepers.ContractKeeper.Create(ctx, creator, hackatomWasm, nil) 307 308 require.NoError(t, err) 309 require.Equal(t, uint64(1), contractID) 310 311 // and verify content 312 code, err := keepers.WasmKeeper.GetByteCode(ctx, contractID) 313 require.NoError(t, err) 314 require.Equal(t, code, hackatomWasm) 315 } 316 317 func TestIsSimulationMode(t *testing.T) { 318 genesisCtx := sdk.Context{} 319 genesisCtx.SetBlockHeader(abci.Header{}) 320 genesisCtx.SetGasMeter(stypes.NewInfiniteGasMeter()) 321 322 regularBlockCtx := sdk.Context{} 323 regularBlockCtx.SetBlockHeader(abci.Header{Height: 1}) 324 regularBlockCtx.SetGasMeter(stypes.NewGasMeter(10000000)) 325 326 simulationCtx := sdk.Context{} 327 simulationCtx.SetBlockHeader(abci.Header{Height: 1}) 328 simulationCtx.SetGasMeter(stypes.NewInfiniteGasMeter()) 329 specs := map[string]struct { 330 ctx sdk.Context 331 exp bool 332 }{ 333 "genesis block": { 334 ctx: genesisCtx, 335 exp: false, 336 }, 337 "any regular block": { 338 ctx: regularBlockCtx, 339 exp: false, 340 }, 341 "simulation": { 342 ctx: simulationCtx, 343 exp: true, 344 }, 345 } 346 for msg := range specs { 347 t.Run(msg, func(t *testing.T) { 348 // assert.Equal(t, spec.exp, isSimulationMode(spec.ctx)) 349 }) 350 } 351 } 352 353 func TestCreateWithGzippedPayload(t *testing.T) { 354 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 355 keeper := keepers.ContractKeeper 356 357 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 358 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 359 360 wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm.gzip") 361 require.NoError(t, err, "reading gzipped WASM code") 362 363 contractID, err := keeper.Create(ctx, creator, wasmCode, nil) 364 require.NoError(t, err) 365 require.Equal(t, uint64(1), contractID) 366 // and verify content 367 storedCode, err := keepers.WasmKeeper.GetByteCode(ctx, contractID) 368 require.NoError(t, err) 369 require.Equal(t, hackatomWasm, storedCode) 370 } 371 372 func TestInstantiate(t *testing.T) { 373 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 374 keeper := keepers.ContractKeeper 375 376 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 377 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 378 379 codeID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 380 require.NoError(t, err) 381 382 _, _, bob := keyPubAddr() 383 _, _, fred := keyPubAddr() 384 385 initMsg := HackatomExampleInitMsg{ 386 Verifier: fred, 387 Beneficiary: bob, 388 } 389 initMsgBz, err := json.Marshal(initMsg) 390 require.NoError(t, err) 391 392 gasBefore := ctx.GasMeter().GasConsumed() 393 394 em := sdk.NewEventManager() 395 // create with no balance is also legal 396 ctx.SetEventManager(em) 397 gotContractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, initMsgBz, "demo contract 1", nil) 398 require.NoError(t, err) 399 require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", gotContractAddr.String()) 400 401 gasAfter := ctx.GasMeter().GasConsumed() 402 if types.EnableGasVerification { 403 require.Equal(t, uint64(0x16d87), gasAfter-gasBefore) 404 } 405 406 // ensure it is stored properly 407 info := keepers.WasmKeeper.GetContractInfo(ctx, gotContractAddr) 408 require.NotNil(t, info) 409 assert.Equal(t, creator.String(), info.Creator) 410 assert.Equal(t, codeID, info.CodeID) 411 assert.Equal(t, "demo contract 1", info.Label) 412 413 exp := []types.ContractCodeHistoryEntry{{ 414 Operation: types.ContractCodeHistoryOperationTypeInit, 415 CodeID: codeID, 416 Updated: types.NewAbsoluteTxPosition(ctx), 417 Msg: initMsgBz, 418 }} 419 assert.Equal(t, exp, keepers.WasmKeeper.GetContractHistory(ctx, gotContractAddr)) 420 421 // and events emitted 422 expEvt := sdk.Events{ 423 sdk.NewEvent("instantiate", 424 sdk.NewAttribute("_contract_address", gotContractAddr.String()), sdk.NewAttribute("code_id", "1")), 425 sdk.NewEvent("wasm", 426 sdk.NewAttribute("_contract_address", gotContractAddr.String()), sdk.NewAttribute("Let the", "hacking begin")), 427 } 428 assert.Equal(t, expEvt, em.Events()) 429 } 430 431 func TestInstantiateWithDeposit(t *testing.T) { 432 var ( 433 bob = bytes.Repeat([]byte{1}, types.SDKAddrLen) 434 fred = bytes.Repeat([]byte{2}, types.SDKAddrLen) 435 436 deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100)) 437 initMsg = HackatomExampleInitMsg{Verifier: fred, Beneficiary: bob} 438 ) 439 440 initMsgBz, err := json.Marshal(initMsg) 441 require.NoError(t, err) 442 443 specs := map[string]struct { 444 srcActor sdk.AccAddress 445 expError bool 446 fundAddr bool 447 }{ 448 "address with funds": { 449 srcActor: bob, 450 fundAddr: true, 451 }, 452 "address without funds": { 453 srcActor: bob, 454 expError: true, 455 }, 456 "blocked address": { 457 srcActor: authtypes.NewModuleAddress(authtypes.FeeCollectorName), 458 fundAddr: true, 459 expError: false, 460 }, 461 } 462 for msg, spec := range specs { 463 t.Run(msg, func(t *testing.T) { 464 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 465 accKeeper, bankKeeper, keeper := keepers.AccountKeeper, keepers.BankKeeper, keepers.ContractKeeper 466 467 if spec.fundAddr { 468 fundAccounts(t, ctx, accKeeper, bankKeeper, keepers.supplyKeepr, spec.srcActor, sdk.NewCoins(sdk.NewInt64Coin("denom", 200))) 469 } 470 contractID, err := keeper.Create(ctx, spec.srcActor, hackatomWasm, nil) 471 require.NoError(t, err) 472 473 // when 474 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, spec.srcActor, nil, initMsgBz, "my label", deposit) 475 // then 476 if spec.expError { 477 require.Error(t, err) 478 return 479 } 480 require.NoError(t, err) 481 balances := bankKeeper.GetCoins(ctx, addr) 482 assert.Equal(t, deposit, balances) 483 }) 484 } 485 } 486 487 func TestInstantiateWithPermissions(t *testing.T) { 488 var ( 489 deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 490 myAddr = bytes.Repeat([]byte{1}, types.SDKAddrLen) 491 otherAddr = bytes.Repeat([]byte{2}, types.SDKAddrLen) 492 anyAddr = bytes.Repeat([]byte{3}, types.SDKAddrLen) 493 ) 494 495 initMsg := HackatomExampleInitMsg{ 496 Verifier: anyAddr, 497 Beneficiary: anyAddr, 498 } 499 initMsgBz, err := json.Marshal(initMsg) 500 require.NoError(t, err) 501 502 specs := map[string]struct { 503 srcPermission types.AccessConfig 504 srcActor sdk.AccAddress 505 expError *sdkerrors.Error 506 }{ 507 "default": { 508 srcPermission: types.DefaultUploadAccess, 509 srcActor: anyAddr, 510 expError: sdkerrors.ErrUnauthorized, 511 }, 512 "everybody": { 513 srcPermission: types.AllowEverybody, 514 srcActor: anyAddr, 515 }, 516 "nobody": { 517 srcPermission: types.AllowNobody, 518 srcActor: myAddr, 519 expError: sdkerrors.ErrUnauthorized, 520 }, 521 "onlyAddress with matching address": { 522 srcPermission: types.AccessTypeOnlyAddress.With(myAddr), 523 srcActor: myAddr, 524 }, 525 "onlyAddress with non matching address": { 526 srcPermission: types.AccessTypeOnlyAddress.With(otherAddr), 527 expError: sdkerrors.ErrUnauthorized, 528 }, 529 } 530 for msg, spec := range specs { 531 t.Run(msg, func(t *testing.T) { 532 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 533 accKeeper, bankKeeper, keeper := keepers.AccountKeeper, keepers.BankKeeper, keepers.ContractKeeper 534 fundAccounts(t, ctx, accKeeper, bankKeeper, keepers.supplyKeepr, spec.srcActor, deposit) 535 536 contractID, err := keeper.Create(ctx, myAddr, hackatomWasm, &spec.srcPermission) 537 require.NoError(t, err) 538 539 _, _, err = keepers.ContractKeeper.Instantiate(ctx, contractID, spec.srcActor, nil, initMsgBz, "demo contract 1", nil) 540 fmt.Printf("case: %s result: %v \n", msg, err) 541 assert.True(t, spec.expError.Is(err), "got %+v", err) 542 }) 543 } 544 } 545 546 func TestInstantiateWithNonExistingCodeID(t *testing.T) { 547 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 548 549 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 550 creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) 551 552 initMsg := HackatomExampleInitMsg{} 553 initMsgBz, err := json.Marshal(initMsg) 554 require.NoError(t, err) 555 556 const nonExistingCodeID = 9999 557 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, nonExistingCodeID, creator, nil, initMsgBz, "demo contract 2", nil) 558 require.True(t, types.ErrNotFound.Is(err), err) 559 require.Nil(t, addr) 560 } 561 562 func TestInstantiateWithContractDataResponse(t *testing.T) { 563 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 564 565 wasmerMock := &wasmtesting.MockWasmer{ 566 InstantiateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 567 return &wasmvmtypes.Response{Data: []byte("my-response-data")}, 0, nil 568 }, 569 AnalyzeCodeFn: wasmtesting.WithoutIBCAnalyzeFn, 570 CreateFn: wasmtesting.NoOpCreateFn, 571 } 572 573 example := StoreRandomContract(t, ctx, keepers, wasmerMock) 574 _, data, err := keepers.ContractKeeper.Instantiate(ctx, example.CodeID, example.CreatorAddr, nil, nil, "test", nil) 575 require.NoError(t, err) 576 assert.Equal(t, []byte("my-response-data"), data) 577 } 578 579 func TestExecute(t *testing.T) { 580 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 581 accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper 582 583 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 584 topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) 585 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 586 fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) 587 588 contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 589 require.NoError(t, err) 590 591 _, _, bob := keyPubAddr() 592 initMsg := HackatomExampleInitMsg{ 593 Verifier: fred, 594 Beneficiary: bob, 595 } 596 initMsgBz, err := json.Marshal(initMsg) 597 require.NoError(t, err) 598 599 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract 3", deposit) 600 require.NoError(t, err) 601 require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", addr.String()) 602 603 // ensure bob doesn't exist 604 bobAcct := accKeeper.GetAccount(ctx, bob) 605 require.Nil(t, bobAcct) 606 607 // ensure funder has reduced balance 608 creatorAcct := accKeeper.GetAccount(ctx, creator) 609 require.NotNil(t, creatorAcct) 610 // we started at 2*deposit, should have spent one above 611 assert.Equal(t, deposit, bankKeeper.GetCoins(ctx, creatorAcct.GetAddress())) 612 613 // ensure contract has updated balance 614 contractAcct := accKeeper.GetAccount(ctx, addr) 615 require.NotNil(t, contractAcct) 616 assert.Equal(t, deposit, bankKeeper.GetCoins(ctx, contractAcct.GetAddress())) 617 618 // unauthorized - trialCtx so we don't change state 619 trialCtx := ctx 620 trialCtx.SetMultiStore(ctx.MultiStore().CacheWrap().(sdk.MultiStore)) 621 res, err := keepers.ContractKeeper.Execute(trialCtx, addr, creator, []byte(`{"release":{}}`), nil) 622 require.Error(t, err) 623 require.True(t, errors.Is(err, types.ErrExecuteFailed)) 624 require.Equal(t, "execute wasm contract failed: Unauthorized", err.Error()) 625 626 // verifier can execute, and get proper gas amount 627 start := time.Now() 628 gasBefore := ctx.GasMeter().GasConsumed() 629 em := sdk.NewEventManager() 630 // when 631 ctx.SetEventManager(em) 632 res, err = keepers.ContractKeeper.Execute(ctx, addr, fred, []byte(`{"release":{}}`), topUp) 633 diff := time.Now().Sub(start) 634 require.NoError(t, err) 635 require.NotNil(t, res) 636 637 // make sure gas is properly deducted from ctx 638 gasAfter := ctx.GasMeter().GasConsumed() 639 if types.EnableGasVerification { 640 require.Equal(t, uint64(0x19c71), gasAfter-gasBefore) 641 } 642 // ensure bob now exists and got both payments released 643 bobAcct = accKeeper.GetAccount(ctx, bob) 644 require.NotNil(t, bobAcct) 645 balance := bankKeeper.GetCoins(ctx, bobAcct.GetAddress()) 646 assert.Equal(t, deposit.Add(topUp...), balance) 647 648 // ensure contract has updated balance 649 contractAcct = accKeeper.GetAccount(ctx, addr) 650 require.NotNil(t, contractAcct) 651 assert.Equal(t, sdk.Coins{}.String(), bankKeeper.GetCoins(ctx, contractAcct.GetAddress()).String()) 652 653 // and events emitted 654 require.Len(t, em.Events(), 4) 655 expEvt := sdk.NewEvent("execute", 656 sdk.NewAttribute("_contract_address", addr.String())) 657 assert.Equal(t, expEvt, em.Events()[0], prettyEvents(t, em.Events())) 658 659 t.Logf("Duration: %v (%d gas)\n", diff, gasAfter-gasBefore) 660 } 661 662 func TestExecuteWithDeposit(t *testing.T) { 663 var ( 664 bob = bytes.Repeat([]byte{1}, types.SDKAddrLen) 665 fred = bytes.Repeat([]byte{2}, types.SDKAddrLen) 666 blockedAddr = authtypes.NewModuleAddress(auth.FeeCollectorName) 667 deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100)) 668 ) 669 670 type bankParams struct { 671 DefaultSendEnabled bool 672 } 673 674 specs := map[string]struct { 675 srcActor sdk.AccAddress 676 beneficiary sdk.AccAddress 677 newBankParams *bankParams 678 expError bool 679 fundAddr bool 680 }{ 681 "actor with funds": { 682 srcActor: bob, 683 fundAddr: true, 684 beneficiary: fred, 685 }, 686 "actor without funds": { 687 srcActor: bob, 688 beneficiary: fred, 689 expError: true, 690 }, 691 "blocked address as actor": { 692 srcActor: blockedAddr, 693 fundAddr: true, 694 beneficiary: fred, 695 expError: false, 696 }, 697 "coin transfer with all transfers disabled": { 698 srcActor: bob, 699 fundAddr: true, 700 beneficiary: fred, 701 newBankParams: &bankParams{DefaultSendEnabled: false}, 702 expError: true, 703 }, 704 "blocked address as beneficiary": { 705 srcActor: bob, 706 fundAddr: true, 707 beneficiary: blockedAddr, 708 expError: true, 709 }, 710 } 711 for msg, spec := range specs { 712 t.Run(msg, func(t *testing.T) { 713 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 714 accKeeper, bankKeeper, keeper := keepers.AccountKeeper, keepers.BankKeeper, keepers.ContractKeeper 715 if spec.newBankParams != nil { 716 bankKeeper.SetSendEnabled(ctx, spec.newBankParams.DefaultSendEnabled) 717 } 718 if spec.fundAddr { 719 fundAccounts(t, ctx, accKeeper, bankKeeper, keepers.supplyKeepr, spec.srcActor, sdk.NewCoins(sdk.NewInt64Coin("denom", 200))) 720 } 721 codeID, err := keeper.Create(ctx, spec.srcActor, hackatomWasm, nil) 722 require.NoError(t, err) 723 724 initMsg := HackatomExampleInitMsg{Verifier: spec.srcActor, Beneficiary: spec.beneficiary} 725 initMsgBz, err := json.Marshal(initMsg) 726 require.NoError(t, err) 727 728 contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, spec.srcActor, nil, initMsgBz, "my label", nil) 729 require.NoError(t, err) 730 731 // when 732 _, err = keepers.ContractKeeper.Execute(ctx, contractAddr, spec.srcActor, []byte(`{"release":{}}`), deposit) 733 734 // then 735 if spec.expError { 736 require.Error(t, err) 737 return 738 } 739 require.NoError(t, err) 740 balances := bankKeeper.GetCoins(ctx, spec.beneficiary) 741 assert.Equal(t, deposit, balances) 742 }) 743 } 744 } 745 746 func TestExecuteWithNonExistingAddress(t *testing.T) { 747 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 748 keeper := keepers.ContractKeeper 749 750 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 751 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 752 753 // unauthorized - trialCtx so we don't change state 754 nonExistingAddress := RandomAccountAddress(t) 755 _, err := keeper.Execute(ctx, nonExistingAddress, creator, []byte(`{}`), nil) 756 require.True(t, types.ErrNotFound.Is(err), err) 757 } 758 759 func TestExecuteWithPanic(t *testing.T) { 760 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 761 keeper := keepers.ContractKeeper 762 763 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 764 topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) 765 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 766 fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) 767 768 contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 769 require.NoError(t, err) 770 771 _, _, bob := keyPubAddr() 772 initMsg := HackatomExampleInitMsg{ 773 Verifier: fred, 774 Beneficiary: bob, 775 } 776 initMsgBz, err := json.Marshal(initMsg) 777 require.NoError(t, err) 778 779 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract 4", deposit) 780 require.NoError(t, err) 781 782 // let's make sure we get a reasonable error, no panic/crash 783 _, err = keepers.ContractKeeper.Execute(ctx, addr, fred, []byte(`{"panic":{}}`), topUp) 784 require.Error(t, err) 785 require.True(t, errors.Is(err, types.ErrExecuteFailed)) 786 // test with contains as "Display" implementation of the Wasmer "RuntimeError" is different for Mac and Linux 787 assert.Contains(t, err.Error(), "Error calling the VM: Error executing Wasm: Wasmer runtime error: RuntimeError: unreachable") 788 } 789 790 func TestExecuteWithCpuLoop(t *testing.T) { 791 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 792 keeper := keepers.ContractKeeper 793 794 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 795 topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) 796 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 797 fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) 798 799 contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 800 require.NoError(t, err) 801 802 _, _, bob := keyPubAddr() 803 initMsg := HackatomExampleInitMsg{ 804 Verifier: fred, 805 Beneficiary: bob, 806 } 807 initMsgBz, err := json.Marshal(initMsg) 808 require.NoError(t, err) 809 810 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract 5", deposit) 811 require.NoError(t, err) 812 813 // make sure we set a limit before calling 814 var gasLimit uint64 = 400_000 815 ctx.SetGasMeter(sdk.NewGasMeter(gasLimit)) 816 require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) 817 818 // ensure we get an out of gas panic 819 defer func() { 820 r := recover() 821 require.NotNil(t, r) 822 _, ok := r.(sdk.ErrorOutOfGas) 823 require.True(t, ok, "%v", r) 824 }() 825 826 // this should throw out of gas exception (panic) 827 _, err = keepers.ContractKeeper.Execute(ctx, addr, fred, []byte(`{"cpu_loop":{}}`), nil) 828 require.True(t, false, "We must panic before this line") 829 } 830 831 func TestExecuteWithStorageLoop(t *testing.T) { 832 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 833 keeper := keepers.ContractKeeper 834 835 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 836 topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) 837 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 838 fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) 839 840 contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 841 require.NoError(t, err) 842 843 _, _, bob := keyPubAddr() 844 initMsg := HackatomExampleInitMsg{ 845 Verifier: fred, 846 Beneficiary: bob, 847 } 848 initMsgBz, err := json.Marshal(initMsg) 849 require.NoError(t, err) 850 851 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract 6", deposit) 852 require.NoError(t, err) 853 854 // make sure we set a limit before calling 855 var gasLimit uint64 = 400_002 856 ctx.SetGasMeter(sdk.NewGasMeter(gasLimit)) 857 require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) 858 859 // ensure we get an out of gas panic 860 defer func() { 861 r := recover() 862 require.NotNil(t, r) 863 _, ok := r.(sdk.ErrorOutOfGas) 864 require.True(t, ok, "%v", r) 865 }() 866 867 // this should throw out of gas exception (panic) 868 _, err = keepers.ContractKeeper.Execute(ctx, addr, fred, []byte(`{"storage_loop":{}}`), nil) 869 require.True(t, false, "We must panic before this line") 870 } 871 872 func TestMigrate(t *testing.T) { 873 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 874 keeper := keepers.ContractKeeper 875 876 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 877 topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) 878 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 879 fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) 880 881 originalCodeID := StoreHackatomExampleContract(t, ctx, keepers).CodeID 882 newCodeID := StoreHackatomExampleContract(t, ctx, keepers).CodeID 883 ibcCodeID := StoreIBCReflectContract(t, ctx, keepers).CodeID 884 require.NotEqual(t, originalCodeID, newCodeID) 885 886 anyAddr := RandomAccountAddress(t) 887 newVerifierAddr := RandomAccountAddress(t) 888 initMsgBz := HackatomExampleInitMsg{ 889 Verifier: fred, 890 Beneficiary: anyAddr, 891 }.GetBytes(t) 892 893 migMsg := struct { 894 Verifier sdk.AccAddress `json:"verifier"` 895 }{Verifier: newVerifierAddr} 896 migMsgBz, err := json.Marshal(migMsg) 897 require.NoError(t, err) 898 899 specs := map[string]struct { 900 admin sdk.AccAddress 901 overrideContractAddr sdk.AccAddress 902 caller sdk.AccAddress 903 fromCodeID uint64 904 toCodeID uint64 905 migrateMsg []byte 906 expErr *sdkerrors.Error 907 expVerifier sdk.AccAddress 908 expIBCPort bool 909 initMsg []byte 910 }{ 911 "all good with same code id": { 912 admin: creator, 913 caller: creator, 914 initMsg: initMsgBz, 915 fromCodeID: originalCodeID, 916 toCodeID: originalCodeID, 917 migrateMsg: migMsgBz, 918 expVerifier: newVerifierAddr, 919 }, 920 "all good with different code id": { 921 admin: creator, 922 caller: creator, 923 initMsg: initMsgBz, 924 fromCodeID: originalCodeID, 925 toCodeID: newCodeID, 926 migrateMsg: migMsgBz, 927 expVerifier: newVerifierAddr, 928 }, 929 "all good with admin set": { 930 admin: fred, 931 caller: fred, 932 initMsg: initMsgBz, 933 fromCodeID: originalCodeID, 934 toCodeID: newCodeID, 935 migrateMsg: migMsgBz, 936 expVerifier: newVerifierAddr, 937 }, 938 "adds IBC port for IBC enabled contracts": { 939 admin: fred, 940 caller: fred, 941 initMsg: initMsgBz, 942 fromCodeID: originalCodeID, 943 toCodeID: ibcCodeID, 944 migrateMsg: []byte(`{}`), 945 expIBCPort: true, 946 expVerifier: fred, // not updated 947 }, 948 "prevent migration when admin was not set on instantiate": { 949 caller: creator, 950 initMsg: initMsgBz, 951 fromCodeID: originalCodeID, 952 toCodeID: originalCodeID, 953 expErr: sdkerrors.ErrUnauthorized, 954 }, 955 "prevent migration when not sent by admin": { 956 caller: creator, 957 admin: fred, 958 initMsg: initMsgBz, 959 fromCodeID: originalCodeID, 960 toCodeID: originalCodeID, 961 expErr: sdkerrors.ErrUnauthorized, 962 }, 963 "fail with non existing code id": { 964 admin: creator, 965 caller: creator, 966 initMsg: initMsgBz, 967 fromCodeID: originalCodeID, 968 toCodeID: 99999, 969 expErr: sdkerrors.ErrInvalidRequest, 970 }, 971 "fail with non existing contract addr": { 972 admin: creator, 973 caller: creator, 974 initMsg: initMsgBz, 975 overrideContractAddr: anyAddr, 976 fromCodeID: originalCodeID, 977 toCodeID: originalCodeID, 978 expErr: sdkerrors.ErrInvalidRequest, 979 }, 980 "fail in contract with invalid migrate msg": { 981 admin: creator, 982 caller: creator, 983 initMsg: initMsgBz, 984 fromCodeID: originalCodeID, 985 toCodeID: originalCodeID, 986 migrateMsg: bytes.Repeat([]byte{0x1}, 7), 987 expErr: types.ErrMigrationFailed, 988 }, 989 "fail in contract without migrate msg": { 990 admin: creator, 991 caller: creator, 992 initMsg: initMsgBz, 993 fromCodeID: originalCodeID, 994 toCodeID: originalCodeID, 995 expErr: types.ErrMigrationFailed, 996 }, 997 "fail when no IBC callbacks": { 998 admin: fred, 999 caller: fred, 1000 initMsg: IBCReflectInitMsg{ReflectCodeID: StoreReflectContract(t, ctx, keepers)}.GetBytes(t), 1001 fromCodeID: ibcCodeID, 1002 toCodeID: newCodeID, 1003 migrateMsg: migMsgBz, 1004 expErr: types.ErrMigrationFailed, 1005 }, 1006 } 1007 1008 for msg, spec := range specs { 1009 t.Run(msg, func(t *testing.T) { 1010 // given a contract instance 1011 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 1012 contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, spec.fromCodeID, creator, spec.admin, spec.initMsg, "demo contract", nil) 1013 require.NoError(t, err) 1014 if spec.overrideContractAddr != nil { 1015 contractAddr = spec.overrideContractAddr 1016 } 1017 // when 1018 _, err = keeper.Migrate(ctx, contractAddr, spec.caller, spec.toCodeID, spec.migrateMsg) 1019 1020 // then 1021 require.True(t, spec.expErr.Is(err), "expected %v but got %+v", spec.expErr, err) 1022 if spec.expErr != nil { 1023 return 1024 } 1025 cInfo := keepers.WasmKeeper.GetContractInfo(ctx, contractAddr) 1026 assert.Equal(t, spec.toCodeID, cInfo.CodeID) 1027 assert.Equal(t, spec.expIBCPort, cInfo.IBCPortID != "", cInfo.IBCPortID) 1028 1029 expHistory := []types.ContractCodeHistoryEntry{{ 1030 Operation: types.ContractCodeHistoryOperationTypeInit, 1031 CodeID: spec.fromCodeID, 1032 Updated: types.NewAbsoluteTxPosition(ctx), 1033 Msg: initMsgBz, 1034 }, { 1035 Operation: types.ContractCodeHistoryOperationTypeMigrate, 1036 CodeID: spec.toCodeID, 1037 Updated: types.NewAbsoluteTxPosition(ctx), 1038 Msg: spec.migrateMsg, 1039 }} 1040 assert.Equal(t, expHistory, keepers.WasmKeeper.GetContractHistory(ctx, contractAddr)) 1041 1042 // and verify contract state 1043 raw := keepers.WasmKeeper.QueryRaw(ctx, contractAddr, []byte("config")) 1044 var stored map[string]string 1045 require.NoError(t, json.Unmarshal(raw, &stored)) 1046 require.Contains(t, stored, "verifier") 1047 require.NoError(t, err) 1048 assert.Equal(t, spec.expVerifier.String(), stored["verifier"]) 1049 }) 1050 } 1051 } 1052 1053 func TestMigrateReplacesTheSecondIndex(t *testing.T) { 1054 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1055 example := InstantiateHackatomExampleContract(t, ctx, keepers) 1056 1057 // then assert a second index exists 1058 store := ctx.KVStore(keepers.WasmKeeper.storeKey) 1059 oldContractInfo := keepers.WasmKeeper.GetContractInfo(ctx, example.Contract) 1060 require.NotNil(t, oldContractInfo) 1061 createHistoryEntry := types.ContractCodeHistoryEntry{ 1062 CodeID: example.CodeID, 1063 Updated: types.NewAbsoluteTxPosition(ctx), 1064 } 1065 exists := store.Has(types.GetContractByCreatedSecondaryIndexKey(example.Contract, createHistoryEntry)) 1066 require.True(t, exists) 1067 1068 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // increment for different block 1069 // when do migrate 1070 newCodeExample := StoreBurnerExampleContract(t, ctx, keepers) 1071 migMsgBz := BurnerExampleInitMsg{Payout: example.CreatorAddr}.GetBytes(t) 1072 _, err := keepers.ContractKeeper.Migrate(ctx, example.Contract, example.CreatorAddr, newCodeExample.CodeID, migMsgBz) 1073 require.NoError(t, err) 1074 1075 // then the new index exists 1076 migrateHistoryEntry := types.ContractCodeHistoryEntry{ 1077 CodeID: newCodeExample.CodeID, 1078 Updated: types.NewAbsoluteTxPosition(ctx), 1079 } 1080 exists = store.Has(types.GetContractByCreatedSecondaryIndexKey(example.Contract, migrateHistoryEntry)) 1081 require.True(t, exists) 1082 // and the old index was removed 1083 exists = store.Has(types.GetContractByCreatedSecondaryIndexKey(example.Contract, createHistoryEntry)) 1084 require.False(t, exists) 1085 } 1086 1087 func TestMigrateWithDispatchedMessage(t *testing.T) { 1088 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1089 keeper := keepers.ContractKeeper 1090 1091 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 1092 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 1093 fred := keepers.Faucet.NewFundedAccount(ctx, sdk.NewInt64Coin("denom", 5000)) 1094 1095 burnerCode, err := ioutil.ReadFile("./testdata/burner.wasm") 1096 require.NoError(t, err) 1097 1098 originalContractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 1099 require.NoError(t, err) 1100 burnerContractID, err := keeper.Create(ctx, creator, burnerCode, nil) 1101 require.NoError(t, err) 1102 require.NotEqual(t, originalContractID, burnerContractID) 1103 1104 _, _, myPayoutAddr := keyPubAddr() 1105 initMsg := HackatomExampleInitMsg{ 1106 Verifier: fred, 1107 Beneficiary: fred, 1108 } 1109 initMsgBz := initMsg.GetBytes(t) 1110 1111 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 1112 contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, originalContractID, creator, fred, initMsgBz, "demo contract", deposit) 1113 require.NoError(t, err) 1114 1115 migMsgBz := BurnerExampleInitMsg{Payout: myPayoutAddr}.GetBytes(t) 1116 ctx.SetEventManager(sdk.NewEventManager()) 1117 ctx.SetBlockHeight((ctx.BlockHeight() + 1)) 1118 data, err := keeper.Migrate(ctx, contractAddr, fred, burnerContractID, migMsgBz) 1119 require.NoError(t, err) 1120 assert.Equal(t, "burnt 1 keys", string(data)) 1121 type dict map[string]interface{} 1122 expEvents := []dict{ 1123 { 1124 "Type": "migrate", 1125 "Attr": []dict{ 1126 {"code_id": "2"}, 1127 {"_contract_address": contractAddr}, 1128 }, 1129 }, 1130 { 1131 "Type": "wasm", 1132 "Attr": []dict{ 1133 {"_contract_address": contractAddr}, 1134 {"action": "burn"}, 1135 {"payout": myPayoutAddr}, 1136 }, 1137 }, 1138 { 1139 "Type": "transfer", 1140 "Attr": []dict{ 1141 {"recipient": myPayoutAddr}, 1142 {"sender": contractAddr}, 1143 {"amount": "100000.000000000000000000denom"}, 1144 }, 1145 }, 1146 } 1147 expJSONEvts := string(mustMarshal(t, expEvents)) 1148 assert.JSONEq(t, expJSONEvts, prettyEvents(t, ctx.EventManager().Events()), prettyEvents(t, ctx.EventManager().Events())) 1149 1150 // all persistent data cleared 1151 m := keepers.WasmKeeper.QueryRaw(ctx, contractAddr, []byte("config")) 1152 require.Len(t, m, 0) 1153 1154 // and all deposit tokens sent to myPayoutAddr 1155 balance := keepers.BankKeeper.GetCoins(ctx, myPayoutAddr) 1156 assert.Equal(t, deposit, balance) 1157 } 1158 1159 func TestIterateContractsByCode(t *testing.T) { 1160 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1161 k, c := keepers.WasmKeeper, keepers.ContractKeeper 1162 example1 := InstantiateHackatomExampleContract(t, ctx, keepers) 1163 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 1164 example2 := InstantiateIBCReflectContract(t, ctx, keepers) 1165 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 1166 initMsg := HackatomExampleInitMsg{ 1167 Verifier: RandomAccountAddress(t), 1168 Beneficiary: RandomAccountAddress(t), 1169 }.GetBytes(t) 1170 contractAddr3, _, err := c.Instantiate(ctx, example1.CodeID, example1.CreatorAddr, nil, initMsg, "foo", nil) 1171 require.NoError(t, err) 1172 specs := map[string]struct { 1173 codeID uint64 1174 exp []sdk.AccAddress 1175 }{ 1176 "multiple results": { 1177 codeID: example1.CodeID, 1178 exp: []sdk.AccAddress{example1.Contract, contractAddr3}, 1179 }, 1180 "single results": { 1181 codeID: example2.CodeID, 1182 exp: []sdk.AccAddress{example2.Contract}, 1183 }, 1184 "empty results": { 1185 codeID: 99999, 1186 }, 1187 } 1188 for name, spec := range specs { 1189 t.Run(name, func(t *testing.T) { 1190 var gotAddr []sdk.AccAddress 1191 k.IterateContractsByCode(ctx, spec.codeID, func(address sdk.AccAddress) bool { 1192 gotAddr = append(gotAddr, address) 1193 return false 1194 }) 1195 assert.Equal(t, spec.exp, gotAddr) 1196 }) 1197 } 1198 } 1199 1200 func TestIterateContractsByCodeWithMigration(t *testing.T) { 1201 // mock migration so that it does not fail when migrate example1 to example2.codeID 1202 mockWasmVM := wasmtesting.MockWasmer{MigrateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 1203 return &wasmvmtypes.Response{}, 1, nil 1204 }} 1205 wasmtesting.MakeInstantiable(&mockWasmVM) 1206 ctx, keepers := CreateTestInput(t, false, SupportedFeatures, WithWasmEngine(&mockWasmVM)) 1207 k, c := keepers.WasmKeeper, keepers.ContractKeeper 1208 example1 := InstantiateHackatomExampleContract(t, ctx, keepers) 1209 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 1210 example2 := InstantiateIBCReflectContract(t, ctx, keepers) 1211 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 1212 _, err := c.Migrate(ctx, example1.Contract, example1.CreatorAddr, example2.CodeID, []byte("{}")) 1213 require.NoError(t, err) 1214 1215 // when 1216 var gotAddr []sdk.AccAddress 1217 k.IterateContractsByCode(ctx, example2.CodeID, func(address sdk.AccAddress) bool { 1218 gotAddr = append(gotAddr, address) 1219 return false 1220 }) 1221 1222 // then 1223 exp := []sdk.AccAddress{example2.Contract, example1.Contract} 1224 assert.Equal(t, exp, gotAddr) 1225 } 1226 1227 type sudoMsg struct { 1228 // This is a tongue-in-check demo command. This is not the intended purpose of Sudo. 1229 // Here we show that some priviledged Go module can make a call that should never be exposed 1230 // to end users (via Tx/Execute). 1231 // 1232 // The contract developer can choose to expose anything to sudo. This functionality is not a true 1233 // backdoor (it can never be called by end users), but allows the developers of the native blockchain 1234 // code to make special calls. This can also be used as an authentication mechanism, if you want to expose 1235 // some callback that only can be triggered by some system module and not faked by external users. 1236 StealFunds stealFundsMsg `json:"steal_funds"` 1237 } 1238 1239 type stealFundsMsg struct { 1240 Recipient string `json:"recipient"` 1241 Amount wasmvmtypes.Coins `json:"amount"` 1242 } 1243 1244 func TestSudo(t *testing.T) { 1245 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1246 accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper 1247 1248 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 1249 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 1250 1251 contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 1252 require.NoError(t, err) 1253 1254 _, _, bob := keyPubAddr() 1255 _, _, fred := keyPubAddr() 1256 initMsg := HackatomExampleInitMsg{ 1257 Verifier: fred, 1258 Beneficiary: bob, 1259 } 1260 initMsgBz, err := json.Marshal(initMsg) 1261 require.NoError(t, err) 1262 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract 3", deposit) 1263 require.NoError(t, err) 1264 require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", addr.String()) 1265 1266 // the community is broke 1267 _, _, community := keyPubAddr() 1268 comAcct := accKeeper.GetAccount(ctx, community) 1269 require.Nil(t, comAcct) 1270 1271 // now the community wants to get paid via sudo 1272 msg := sudoMsg{ 1273 // This is a tongue-in-check demo command. This is not the intended purpose of Sudo. 1274 // Here we show that some priviledged Go module can make a call that should never be exposed 1275 // to end users (via Tx/Execute). 1276 StealFunds: stealFundsMsg{ 1277 Recipient: community.String(), 1278 Amount: wasmvmtypes.Coins{wasmvmtypes.Coin{"denom", "76543000000000000000000"}}, 1279 }, 1280 } 1281 sudoMsg, err := json.Marshal(msg) 1282 require.NoError(t, err) 1283 1284 em := sdk.NewEventManager() 1285 1286 // when 1287 newCtx := ctx 1288 newCtx.SetEventManager(em) 1289 _, err = keepers.WasmKeeper.Sudo(newCtx, addr, sudoMsg) 1290 require.NoError(t, err) 1291 1292 // ensure community now exists and got paid 1293 comAcct = accKeeper.GetAccount(ctx, community) 1294 require.NotNil(t, comAcct) 1295 balance := bankKeeper.GetCoins(ctx, comAcct.GetAddress()) 1296 assert.Equal(t, sdk.Coins{sdk.NewInt64Coin("denom", 76543)}, balance) 1297 // and events emitted 1298 require.Len(t, em.Events(), 2, prettyEvents(t, em.Events())) 1299 expEvt := sdk.NewEvent("sudo", 1300 sdk.NewAttribute("_contract_address", addr.String())) 1301 assert.Equal(t, expEvt, em.Events()[0]) 1302 } 1303 1304 func prettyEvents(t *testing.T, events sdk.Events) string { 1305 t.Helper() 1306 type prettyEvent struct { 1307 Type string 1308 Attr []map[string]string 1309 } 1310 1311 r := make([]prettyEvent, len(events)) 1312 for i, e := range events { 1313 attr := make([]map[string]string, len(e.Attributes)) 1314 for j, a := range e.Attributes { 1315 attr[j] = map[string]string{string(a.Key): string(a.Value)} 1316 } 1317 r[i] = prettyEvent{Type: e.Type, Attr: attr} 1318 } 1319 return string(mustMarshal(t, r)) 1320 } 1321 1322 func mustMarshal(t *testing.T, r interface{}) []byte { 1323 t.Helper() 1324 bz, err := json.Marshal(r) 1325 require.NoError(t, err) 1326 return bz 1327 } 1328 1329 func TestUpdateContractAdmin(t *testing.T) { 1330 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1331 keeper := keepers.ContractKeeper 1332 1333 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 1334 topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) 1335 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 1336 fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) 1337 1338 originalContractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 1339 require.NoError(t, err) 1340 1341 _, _, anyAddr := keyPubAddr() 1342 initMsg := HackatomExampleInitMsg{ 1343 Verifier: fred, 1344 Beneficiary: anyAddr, 1345 } 1346 initMsgBz, err := json.Marshal(initMsg) 1347 require.NoError(t, err) 1348 specs := map[string]struct { 1349 instAdmin sdk.AccAddress 1350 newAdmin sdk.AccAddress 1351 overrideContractAddr sdk.AccAddress 1352 caller sdk.AccAddress 1353 expErr *sdkerrors.Error 1354 }{ 1355 "all good with admin set": { 1356 instAdmin: fred, 1357 newAdmin: anyAddr, 1358 caller: fred, 1359 }, 1360 "prevent update when admin was not set on instantiate": { 1361 caller: creator, 1362 newAdmin: fred, 1363 expErr: sdkerrors.ErrUnauthorized, 1364 }, 1365 "prevent updates from non admin address": { 1366 instAdmin: creator, 1367 newAdmin: fred, 1368 caller: fred, 1369 expErr: sdkerrors.ErrUnauthorized, 1370 }, 1371 "fail with non existing contract addr": { 1372 instAdmin: creator, 1373 newAdmin: anyAddr, 1374 caller: creator, 1375 overrideContractAddr: anyAddr, 1376 expErr: sdkerrors.ErrInvalidRequest, 1377 }, 1378 } 1379 for msg, spec := range specs { 1380 t.Run(msg, func(t *testing.T) { 1381 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, originalContractID, creator, spec.instAdmin, initMsgBz, "demo contract", nil) 1382 require.NoError(t, err) 1383 if spec.overrideContractAddr != nil { 1384 addr = spec.overrideContractAddr 1385 } 1386 err = keeper.UpdateContractAdmin(ctx, addr, spec.caller, spec.newAdmin) 1387 require.True(t, spec.expErr.Is(err), "expected %v but got %+v", spec.expErr, err) 1388 if spec.expErr != nil { 1389 return 1390 } 1391 cInfo := keepers.WasmKeeper.GetContractInfo(ctx, addr) 1392 assert.Equal(t, spec.newAdmin.String(), cInfo.Admin) 1393 }) 1394 } 1395 } 1396 1397 func TestClearContractAdmin(t *testing.T) { 1398 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1399 keeper := keepers.ContractKeeper 1400 1401 deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) 1402 topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) 1403 creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) 1404 fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) 1405 1406 originalContractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) 1407 require.NoError(t, err) 1408 1409 _, _, anyAddr := keyPubAddr() 1410 initMsg := HackatomExampleInitMsg{ 1411 Verifier: fred, 1412 Beneficiary: anyAddr, 1413 } 1414 initMsgBz, err := json.Marshal(initMsg) 1415 require.NoError(t, err) 1416 specs := map[string]struct { 1417 instAdmin sdk.AccAddress 1418 overrideContractAddr sdk.AccAddress 1419 caller sdk.AccAddress 1420 expErr *sdkerrors.Error 1421 }{ 1422 "all good when called by proper admin": { 1423 instAdmin: fred, 1424 caller: fred, 1425 }, 1426 "prevent update when admin was not set on instantiate": { 1427 caller: creator, 1428 expErr: sdkerrors.ErrUnauthorized, 1429 }, 1430 "prevent updates from non admin address": { 1431 instAdmin: creator, 1432 caller: fred, 1433 expErr: sdkerrors.ErrUnauthorized, 1434 }, 1435 "fail with non existing contract addr": { 1436 instAdmin: creator, 1437 caller: creator, 1438 overrideContractAddr: anyAddr, 1439 expErr: sdkerrors.ErrInvalidRequest, 1440 }, 1441 } 1442 for msg, spec := range specs { 1443 t.Run(msg, func(t *testing.T) { 1444 addr, _, err := keepers.ContractKeeper.Instantiate(ctx, originalContractID, creator, spec.instAdmin, initMsgBz, "demo contract", nil) 1445 require.NoError(t, err) 1446 if spec.overrideContractAddr != nil { 1447 addr = spec.overrideContractAddr 1448 } 1449 err = keeper.ClearContractAdmin(ctx, addr, spec.caller) 1450 require.True(t, spec.expErr.Is(err), "expected %v but got %+v", spec.expErr, err) 1451 if spec.expErr != nil { 1452 return 1453 } 1454 cInfo := keepers.WasmKeeper.GetContractInfo(ctx, addr) 1455 assert.Empty(t, cInfo.Admin) 1456 }) 1457 } 1458 } 1459 1460 func TestPinCode(t *testing.T) { 1461 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1462 k := keepers.WasmKeeper 1463 1464 var capturedChecksums []wasmvm.Checksum 1465 mock := wasmtesting.MockWasmer{PinFn: func(checksum wasmvm.Checksum) error { 1466 capturedChecksums = append(capturedChecksums, checksum) 1467 return nil 1468 }} 1469 wasmtesting.MakeInstantiable(&mock) 1470 myCodeID := StoreRandomContract(t, ctx, keepers, &mock).CodeID 1471 require.Equal(t, uint64(1), myCodeID) 1472 em := sdk.NewEventManager() 1473 1474 // when 1475 newCtx := ctx 1476 newCtx.SetEventManager(em) 1477 gotErr := k.pinCode(newCtx, myCodeID) 1478 1479 // then 1480 require.NoError(t, gotErr) 1481 assert.NotEmpty(t, capturedChecksums) 1482 assert.True(t, k.IsPinnedCode(ctx, myCodeID)) 1483 1484 // and events 1485 exp := sdk.Events{sdk.NewEvent("pin_code", sdk.NewAttribute("code_id", "1"))} 1486 assert.Equal(t, exp, em.Events()) 1487 } 1488 1489 func TestUnpinCode(t *testing.T) { 1490 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1491 k := keepers.WasmKeeper 1492 1493 var capturedChecksums []wasmvm.Checksum 1494 mock := wasmtesting.MockWasmer{ 1495 PinFn: func(checksum wasmvm.Checksum) error { 1496 return nil 1497 }, 1498 UnpinFn: func(checksum wasmvm.Checksum) error { 1499 capturedChecksums = append(capturedChecksums, checksum) 1500 return nil 1501 }, 1502 } 1503 wasmtesting.MakeInstantiable(&mock) 1504 myCodeID := StoreRandomContract(t, ctx, keepers, &mock).CodeID 1505 require.Equal(t, uint64(1), myCodeID) 1506 err := k.pinCode(ctx, myCodeID) 1507 require.NoError(t, err) 1508 em := sdk.NewEventManager() 1509 1510 // when 1511 newCtx := ctx 1512 newCtx.SetEventManager(em) 1513 gotErr := k.unpinCode(newCtx, myCodeID) 1514 1515 // then 1516 require.NoError(t, gotErr) 1517 assert.NotEmpty(t, capturedChecksums) 1518 assert.False(t, k.IsPinnedCode(ctx, myCodeID)) 1519 1520 // and events 1521 exp := sdk.Events{sdk.NewEvent("unpin_code", sdk.NewAttribute("code_id", "1"))} 1522 assert.Equal(t, exp, em.Events()) 1523 } 1524 1525 func TestInitializePinnedCodes(t *testing.T) { 1526 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1527 k := keepers.WasmKeeper 1528 1529 var capturedChecksums []wasmvm.Checksum 1530 mock := wasmtesting.MockWasmer{PinFn: func(checksum wasmvm.Checksum) error { 1531 capturedChecksums = append(capturedChecksums, checksum) 1532 return nil 1533 }} 1534 wasmtesting.MakeInstantiable(&mock) 1535 1536 const testItems = 3 1537 myCodeIDs := make([]uint64, testItems) 1538 for i := 0; i < testItems; i++ { 1539 myCodeIDs[i] = StoreRandomContract(t, ctx, keepers, &mock).CodeID 1540 require.NoError(t, k.pinCode(ctx, myCodeIDs[i])) 1541 } 1542 capturedChecksums = nil 1543 1544 // when 1545 gotErr := k.InitializePinnedCodes(ctx) 1546 1547 // then 1548 require.NoError(t, gotErr) 1549 require.Len(t, capturedChecksums, testItems) 1550 for i, c := range myCodeIDs { 1551 var exp wasmvm.Checksum = k.GetCodeInfo(ctx, c).CodeHash 1552 assert.Equal(t, exp, capturedChecksums[i]) 1553 } 1554 } 1555 1556 func TestPinnedContractLoops(t *testing.T) { 1557 var capturedChecksums []wasmvm.Checksum 1558 mock := wasmtesting.MockWasmer{PinFn: func(checksum wasmvm.Checksum) error { 1559 capturedChecksums = append(capturedChecksums, checksum) 1560 return nil 1561 }} 1562 wasmtesting.MakeInstantiable(&mock) 1563 1564 // a pinned contract that calls itself via submessages should terminate with an 1565 // error at some point 1566 ctx, keepers := CreateTestInput(t, false, SupportedFeatures, WithWasmEngine(&mock)) 1567 k := keepers.WasmKeeper 1568 1569 example := SeedNewContractInstance(t, ctx, keepers, &mock) 1570 require.NoError(t, k.pinCode(ctx, example.CodeID)) 1571 var loops int 1572 anyMsg := []byte(`{}`) 1573 mock.ExecuteFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 1574 loops++ 1575 return &wasmvmtypes.Response{ 1576 Messages: []wasmvmtypes.SubMsg{ 1577 { 1578 ID: 1, 1579 ReplyOn: wasmvmtypes.ReplyNever, 1580 Msg: wasmvmtypes.CosmosMsg{ 1581 Wasm: &wasmvmtypes.WasmMsg{ 1582 Execute: &wasmvmtypes.ExecuteMsg{ 1583 ContractAddr: example.Contract.String(), 1584 Msg: anyMsg, 1585 }, 1586 }, 1587 }, 1588 }, 1589 }, 1590 }, 0, nil 1591 } 1592 1593 ctx.SetGasMeter(sdk.NewGasMeter(24000)) 1594 require.PanicsWithValue(t, sdk.ErrorOutOfGas{Descriptor: "ReadFlat"}, func() { 1595 _, err := k.execute(ctx, example.Contract, RandomAccountAddress(t), anyMsg, nil) 1596 require.NoError(t, err) 1597 }) 1598 assert.True(t, ctx.GasMeter().IsOutOfGas()) 1599 assert.Greater(t, loops, 2) 1600 } 1601 1602 func TestNewDefaultWasmVMContractResponseHandler(t *testing.T) { 1603 specs := map[string]struct { 1604 srcData []byte 1605 setup func(m *wasmtesting.MockMsgDispatcher) 1606 expErr bool 1607 expData []byte 1608 expEvts sdk.Events 1609 }{ 1610 "submessage overwrites result when set": { 1611 srcData: []byte("otherData"), 1612 setup: func(m *wasmtesting.MockMsgDispatcher) { 1613 m.DispatchSubmessagesFn = func(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) ([]byte, error) { 1614 return []byte("mySubMsgData"), nil 1615 } 1616 }, 1617 expErr: false, 1618 expData: []byte("mySubMsgData"), 1619 expEvts: sdk.Events{}, 1620 }, 1621 "submessage overwrites result when empty": { 1622 srcData: []byte("otherData"), 1623 setup: func(m *wasmtesting.MockMsgDispatcher) { 1624 m.DispatchSubmessagesFn = func(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) ([]byte, error) { 1625 return []byte(""), nil 1626 } 1627 }, 1628 expErr: false, 1629 expData: []byte(""), 1630 expEvts: sdk.Events{}, 1631 }, 1632 "submessage do not overwrite result when nil": { 1633 srcData: []byte("otherData"), 1634 setup: func(m *wasmtesting.MockMsgDispatcher) { 1635 m.DispatchSubmessagesFn = func(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) ([]byte, error) { 1636 return nil, nil 1637 } 1638 }, 1639 expErr: false, 1640 expData: []byte("otherData"), 1641 expEvts: sdk.Events{}, 1642 }, 1643 "submessage error aborts process": { 1644 setup: func(m *wasmtesting.MockMsgDispatcher) { 1645 m.DispatchSubmessagesFn = func(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) ([]byte, error) { 1646 return nil, errors.New("test - ignore") 1647 } 1648 }, 1649 expErr: true, 1650 }, 1651 "message emit non message events": { 1652 setup: func(m *wasmtesting.MockMsgDispatcher) { 1653 m.DispatchSubmessagesFn = func(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) ([]byte, error) { 1654 ctx.EventManager().EmitEvent(sdk.NewEvent("myEvent")) 1655 return nil, nil 1656 } 1657 }, 1658 expEvts: sdk.Events{sdk.NewEvent("myEvent")}, 1659 }, 1660 } 1661 for name, spec := range specs { 1662 t.Run(name, func(t *testing.T) { 1663 var msgs []wasmvmtypes.SubMsg 1664 var mock wasmtesting.MockMsgDispatcher 1665 spec.setup(&mock) 1666 d := NewDefaultWasmVMContractResponseHandler(&mock) 1667 em := sdk.NewEventManager() 1668 1669 // when 1670 newCtx := sdk.Context{} 1671 newCtx.SetEventManager(em) 1672 gotData, gotErr := d.Handle(newCtx, RandomAccountAddress(t), "ibc-port", msgs, spec.srcData) 1673 if spec.expErr { 1674 require.Error(t, gotErr) 1675 return 1676 } 1677 require.NoError(t, gotErr) 1678 assert.Equal(t, spec.expData, gotData) 1679 assert.Equal(t, spec.expEvts, em.Events()) 1680 }) 1681 } 1682 } 1683 1684 func TestReply(t *testing.T) { 1685 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1686 k := keepers.WasmKeeper 1687 var mock wasmtesting.MockWasmer 1688 wasmtesting.MakeInstantiable(&mock) 1689 example := SeedNewContractInstance(t, ctx, keepers, &mock) 1690 1691 specs := map[string]struct { 1692 replyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) 1693 expData []byte 1694 expErr bool 1695 expEvt sdk.Events 1696 }{ 1697 "all good": { 1698 replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 1699 return &wasmvmtypes.Response{Data: []byte("foo")}, 1, nil 1700 }, 1701 expData: []byte("foo"), 1702 expEvt: sdk.Events{sdk.NewEvent("reply", sdk.NewAttribute("_contract_address", example.Contract.String()))}, 1703 }, 1704 "with query": { 1705 replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 1706 bzRsp, err := querier.Query(wasmvmtypes.QueryRequest{ 1707 Bank: &wasmvmtypes.BankQuery{ 1708 Balance: &wasmvmtypes.BalanceQuery{Address: env.Contract.Address, Denom: "stake"}, 1709 }, 1710 }, 10_000*DefaultGasMultiplier) 1711 require.NoError(t, err) 1712 var gotBankRsp wasmvmtypes.BalanceResponse 1713 require.NoError(t, json.Unmarshal(bzRsp, &gotBankRsp)) 1714 assert.Equal(t, wasmvmtypes.BalanceResponse{Amount: wasmvmtypes.NewCoin(0, "stake")}, gotBankRsp) 1715 return &wasmvmtypes.Response{Data: []byte("foo")}, 1, nil 1716 }, 1717 expData: []byte("foo"), 1718 expEvt: sdk.Events{sdk.NewEvent("reply", sdk.NewAttribute("_contract_address", example.Contract.String()))}, 1719 }, 1720 "with query error handled": { 1721 replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 1722 bzRsp, err := querier.Query(wasmvmtypes.QueryRequest{}, 0) 1723 require.Error(t, err) 1724 assert.Nil(t, bzRsp) 1725 return &wasmvmtypes.Response{Data: []byte("foo")}, 1, nil 1726 }, 1727 expData: []byte("foo"), 1728 expEvt: sdk.Events{sdk.NewEvent("reply", sdk.NewAttribute("_contract_address", example.Contract.String()))}, 1729 }, 1730 "error": { 1731 replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 1732 return nil, 1, errors.New("testing") 1733 }, 1734 expErr: true, 1735 }, 1736 } 1737 for name, spec := range specs { 1738 t.Run(name, func(t *testing.T) { 1739 mock.ReplyFn = spec.replyFn 1740 em := sdk.NewEventManager() 1741 1742 ctx.SetEventManager(em) 1743 gotData, gotErr := k.reply(ctx, example.Contract, wasmvmtypes.Reply{}) 1744 if spec.expErr { 1745 require.Error(t, gotErr) 1746 return 1747 } 1748 require.NoError(t, gotErr) 1749 assert.Equal(t, spec.expData, gotData) 1750 assert.Equal(t, spec.expEvt, ctx.EventManager().Events()) 1751 }) 1752 } 1753 } 1754 1755 func TestQueryIsolation(t *testing.T) { 1756 ctx, keepers := CreateTestInput(t, false, SupportedFeatures) 1757 k := keepers.WasmKeeper 1758 var mock wasmtesting.MockWasmer 1759 wasmtesting.MakeInstantiable(&mock) 1760 example := SeedNewContractInstance(t, ctx, keepers, &mock) 1761 WithQueryHandlerDecorator(func(other WasmVMQueryHandler) WasmVMQueryHandler { 1762 return WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { 1763 if request.Custom == nil { 1764 return other.HandleQuery(ctx, caller, request) 1765 } 1766 // here we write to DB which should not be persisted 1767 ctx.KVStore(k.storeKey).Set([]byte(`set_in_query`), []byte(`this_is_allowed`)) 1768 return nil, nil 1769 }) 1770 }).apply(k) 1771 1772 // when 1773 mock.ReplyFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { 1774 _, err := querier.Query(wasmvmtypes.QueryRequest{ 1775 Custom: []byte(`{}`), 1776 }, 10000*DefaultGasMultiplier) 1777 require.NoError(t, err) 1778 return &wasmvmtypes.Response{}, 0, nil 1779 } 1780 em := sdk.NewEventManager() 1781 //newCtx := ctx 1782 ctx.SetEventManager(em) 1783 assert.Nil(t, ctx.KVStore(k.storeKey).Get([]byte(`set_in_query`))) 1784 _, gotErr := k.reply(ctx, example.Contract, wasmvmtypes.Reply{}) 1785 require.NoError(t, gotErr) 1786 assert.Nil(t, ctx.KVStore(k.storeKey).Get([]byte(`set_in_query`))) 1787 } 1788 1789 func TestBuildContractAddress(t *testing.T) { 1790 specs := map[string]struct { 1791 srcCodeID uint64 1792 srcInstanceID uint64 1793 expectedAddr string 1794 }{ 1795 "initial contract": { 1796 srcCodeID: 1, 1797 srcInstanceID: 1, 1798 expectedAddr: "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", 1799 }, 1800 "demo value": { 1801 srcCodeID: 1, 1802 srcInstanceID: 100, 1803 expectedAddr: "cosmos1mujpjkwhut9yjw4xueyugc02evfv46y0dtmnz4lh8xxkkdapym9stu5qm8", 1804 }, 1805 "both below max": { 1806 srcCodeID: math.MaxUint32 - 1, 1807 srcInstanceID: math.MaxUint32 - 1, 1808 }, 1809 "both at max": { 1810 srcCodeID: math.MaxUint32, 1811 srcInstanceID: math.MaxUint32, 1812 }, 1813 "codeID > max u32": { 1814 srcCodeID: math.MaxUint32 + 1, 1815 srcInstanceID: 17, 1816 expectedAddr: "cosmos1673hrexz4h6s0ft04l96ygq667djzh2nsr335kstjp49x5dk6rpsf5t0le", 1817 }, 1818 "instanceID > max u32": { 1819 srcCodeID: 22, 1820 srcInstanceID: math.MaxUint32 + 1, 1821 expectedAddr: "cosmos10q3pgfvmeyy0veekgtqhxujxkhz0vm9zmalqgc7evrhj68q3l62qrdfg4m", 1822 }, 1823 } 1824 for name, spec := range specs { 1825 t.Run(name, func(t *testing.T) { 1826 gotAddr := BuildContractAddress(spec.srcCodeID, spec.srcInstanceID) 1827 require.NotNil(t, gotAddr) 1828 assert.Nil(t, sdk.VerifyAddressFormat(gotAddr)) 1829 if len(spec.expectedAddr) > 0 { 1830 require.Equal(t, spec.expectedAddr, gotAddr.String()) 1831 } 1832 }) 1833 } 1834 }