diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79fc47f23..28c7ccd35 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,8 @@ jobs: "test", "integration tests coreum-modules", "integration tests coreum-ibc", - "integration tests coreum-upgrade-v3.0.0", + "integration tests coreum-upgrade v2.0.2 -> v3.0.0", + "integration tests coreum-upgrade v3.0.0 -> v3.0.x", "integration tests faucet", ] include: @@ -47,10 +48,19 @@ jobs: wasm-cache: true linter-cache: false docker-cache: true - - ci_step: "integration tests coreum-upgrade-v3.0.0" + - ci_step: "integration tests coreum-upgrade v2.0.2 -> v3.0.0" + # Note that we run upgrade tests only no module or IBC because some features in v3 are not compatible anymore. command: | crust build/integration-tests/coreum/upgrade build/integration-tests/coreum/modules build/integration-tests/coreum/ibc images - crust znet test --cored-version=v2.0.2 --test-groups=coreum-upgrade,coreum-modules,coreum-ibc --timeout-commit 1s + crust znet test --cored-version=v2.0.2 --test-groups=coreum-upgrade --timeout-commit 1s + go-cache: true + wasm-cache: true + linter-cache: false + docker-cache: true + - ci_step: "integration tests coreum-upgrade v3.0.0 -> v3.0.x" + command: | + crust build/integration-tests/coreum/upgrade build/integration-tests/coreum/modules build/integration-tests/coreum/ibc images + crust znet test --cored-version=v3.0.0 --test-groups=coreum-upgrade,coreum-modules,coreum-ibc --timeout-commit 1s go-cache: true wasm-cache: true linter-cache: false diff --git a/app/app.go b/app/app.go index a5810ef0f..71e09e56d 100644 --- a/app/app.go +++ b/app/app.go @@ -590,8 +590,6 @@ func New( } wasmOpts := []wasmkeeper.Option{ - wasmkeeper.WithAcceptedAccountTypesOnContractInstantiation(), - wasmkeeper.WithAccountPruner(cwasmtypes.AccountPruner{}), wasmkeeper.WithCoinTransferrer(cwasmtypes.NewBankCoinTransferrer(app.BankKeeper)), wasmkeeper.WithMessageHandler(wasmcustomhandler.NewMessengerWrapper(wasmkeeper.NewDefaultMessageHandler( app.MsgServiceRouter(), diff --git a/integration-tests/modules/wasm_test.go b/integration-tests/modules/wasm_test.go index 375aa89e5..7fc401154 100644 --- a/integration-tests/modules/wasm_test.go +++ b/integration-tests/modules/wasm_test.go @@ -2116,126 +2116,190 @@ func TestWASMBankSendContractWithMultipleFundsAttached(t *testing.T) { requireT.NoError(client.AwaitNextBlocks(waitCtx, chain.ClientContext, 2)) } -// TestWASMContractInstantiationIsRejectedIfThereAreTokensOnItsAccount verifies that smart contract instantiation -// is rejected if account exists. -func TestWASMContractInstantiationIsRejectedIfAccountExists(t *testing.T) { +// TestWASMContractInstantiationForExistingAccounts verifies that WASM contract instantiation behaves correctly when +// instantiating contract on top of existing addresses of different types. +func TestWASMContractInstantiationForExistingAccounts(t *testing.T) { t.Parallel() ctx, chain := integrationtests.NewCoreumTestingContext(t) + contractAdmin := chain.GenAccount() + vestingAccCreator := chain.GenAccount() - admin := chain.GenAccount() + bankClient := banktypes.NewQueryClient(chain.ClientContext) + + amount1 := chain.NewCoin(sdkmath.NewInt(500)) + amount2 := chain.NewCoin(sdkmath.NewInt(550)) + amount3 := chain.NewCoin(sdkmath.NewInt(555)) requireT := require.New(t) chain.Faucet.FundAccounts(ctx, t, - integration.NewFundedAccount(admin, chain.NewCoin(sdkmath.NewInt(5000000000))), + // Funds for instantiating contracts. + integration.NewFundedAccount(contractAdmin, chain.NewCoin(sdkmath.NewInt(1000000))), + // Funds for creating vesting accounts. + integration.NewFundedAccount(vestingAccCreator, chain.NewCoin(sdkmath.NewInt(5000000000))), ) clientCtx := chain.ClientContext txf := chain.TxFactory(). WithSimulateAndExecute(true) - // Deploy smart contract. - + // Deploy smart contract to be used inside test cases. codeID, err := chain.Wasm.DeployWASMContract( ctx, txf, - admin, + contractAdmin, moduleswasm.BankSendWASM, ) requireT.NoError(err) - // Predict the address of the smart contract. - - salt, err := chain.Wasm.GenerateSalt() - requireT.NoError(err) - - contract, err := chain.Wasm.PredictWASMContractAddress( - ctx, - admin, - salt, - codeID, - ) - requireT.NoError(err) - - // Send coins to the contract address before instantiation. - - msg := &banktypes.MsgSend{ - FromAddress: admin.String(), - ToAddress: contract.String(), - Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(500))), - } - - _, err = client.BroadcastTx(ctx, clientCtx.WithFromAddress(admin), txf, msg) - requireT.NoError(err) - - // Instantiate the smart contract. It should fail because its account holds some funds. - - _, err = chain.Wasm.InstantiateWASMContract( - ctx, - txf, - admin, - salt, - integration.InstantiateConfig{ - CodeID: codeID, - AccessType: wasmtypes.AccessTypeUnspecified, - Payload: moduleswasm.EmptyPayload, - Label: "bank_send", + testCases := []struct { + name string + beforeContractInstantiation func(t *testing.T, predictedContractAddr sdk.AccAddress) + expectedBalanceAfterInstantiation sdk.Coin + }{ + { + name: "banktypes.MsgSend", + beforeContractInstantiation: func(t *testing.T, predictedContractAddr sdk.AccAddress) { + msg := &banktypes.MsgSend{ + FromAddress: vestingAccCreator.String(), + ToAddress: predictedContractAddr.String(), + Amount: sdk.NewCoins(amount1), + } + + _, err := client.BroadcastTx( + ctx, + clientCtx.WithFromAddress(vestingAccCreator), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(msg)), + msg, + ) + requireT.NoError(err) + }, + expectedBalanceAfterInstantiation: amount1, }, - ) - requireT.ErrorContains(err, "contract account already exists") - - // Predict the address of another smart contract. - - salt, err = chain.Wasm.GenerateSalt() - requireT.NoError(err) + { + name: "vestingtypes.MsgCreateVestingAccount (delayed, vested)", + beforeContractInstantiation: func(t *testing.T, predictedContractAddr sdk.AccAddress) { + msg := &vestingtypes.MsgCreateVestingAccount{ + FromAddress: vestingAccCreator.String(), + ToAddress: predictedContractAddr.String(), + Amount: sdk.NewCoins(amount2), + EndTime: time.Now().Unix(), + Delayed: true, + } + + _, err := client.BroadcastTx( + ctx, + clientCtx.WithFromAddress(vestingAccCreator), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(msg)), + msg, + ) + requireT.NoError(err) + + // Await next block to ensure that funds are vested. + requireT.NoError(client.AwaitNextBlocks(ctx, clientCtx, 1)) + }, - contract, err = chain.Wasm.PredictWASMContractAddress( - ctx, - admin, - salt, - codeID, - ) - requireT.NoError(err) + expectedBalanceAfterInstantiation: chain.NewCoin(sdk.ZeroInt()), + }, + { + name: "vestingtypes.MsgCreateVestingAccount (continuous, vested)", + beforeContractInstantiation: func(t *testing.T, predictedContractAddr sdk.AccAddress) { + msg := &vestingtypes.MsgCreateVestingAccount{ + FromAddress: vestingAccCreator.String(), + ToAddress: predictedContractAddr.String(), + Amount: sdk.NewCoins(amount3), + EndTime: time.Now().Unix(), + } + + _, err := client.BroadcastTx( + ctx, + clientCtx.WithFromAddress(vestingAccCreator), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(msg)), + msg, + ) + requireT.NoError(err) + + // Await next block to ensure that funds are vested. + requireT.NoError(client.AwaitNextBlocks(ctx, clientCtx, 1)) + }, - // Create vesting account using address of the smart cotntract. + expectedBalanceAfterInstantiation: chain.NewCoin(sdk.ZeroInt()), + }, + { + name: "vestingtypes.MsgCreateVestingAccount (delayed, non-vested)", + beforeContractInstantiation: func(t *testing.T, predictedContractAddr sdk.AccAddress) { + msg := &vestingtypes.MsgCreateVestingAccount{ + FromAddress: vestingAccCreator.String(), + ToAddress: predictedContractAddr.String(), + Amount: sdk.NewCoins(amount2), + EndTime: time.Now().Add(time.Hour).Unix(), + Delayed: true, + } + + _, err := client.BroadcastTx( + ctx, + clientCtx.WithFromAddress(vestingAccCreator), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(msg)), + msg, + ) + requireT.NoError(err) + }, - createVestingAccMsg := &vestingtypes.MsgCreateVestingAccount{ - FromAddress: admin.String(), - ToAddress: contract.String(), - Amount: sdk.NewCoins( - chain.NewCoin(sdkmath.NewInt(10000)), - ), - EndTime: time.Now().Unix(), - Delayed: true, + expectedBalanceAfterInstantiation: chain.NewCoin(sdk.ZeroInt()), + }, } - _, err = client.BroadcastTx( - ctx, - chain.ClientContext.WithFromAddress(admin), - chain.TxFactory().WithGas(chain.GasLimitByMsgs(createVestingAccMsg)), - createVestingAccMsg, - ) - requireT.NoError(err) - - // Await next block to ensure that funds are vested. - - requireT.NoError(client.AwaitNextBlocks(ctx, clientCtx, 1)) - - // Instantiate the smart contract. It should fail because its account holds some funds. - - _, err = chain.Wasm.InstantiateWASMContract( - ctx, - txf, - admin, - salt, - integration.InstantiateConfig{ - CodeID: codeID, - AccessType: wasmtypes.AccessTypeUnspecified, - Payload: moduleswasm.EmptyPayload, - Label: "bank_send", - }, - ) - requireT.ErrorContains(err, "contract account already exists") + for _, tc := range testCases { + t.Run(tc.name, func(tt *testing.T) { + salt, err := chain.Wasm.GenerateSalt() + requireT.NoError(err) + + contractAddrPredicted, err := chain.Wasm.PredictWASMContractAddress( + ctx, + contractAdmin, + salt, + codeID, + ) + requireT.NoError(err) + + tc.beforeContractInstantiation(tt, contractAddrPredicted) + + contractAddr, err := chain.Wasm.InstantiateWASMContract( + ctx, + txf, + contractAdmin, + salt, + integration.InstantiateConfig{ + CodeID: codeID, + AccessType: wasmtypes.AccessTypeUnspecified, + Payload: moduleswasm.EmptyPayload, + Label: "bank_send", + }, + ) + requireT.NoError(err) + requireT.Equal(contractAddrPredicted.String(), contractAddr) + + authClient := authtypes.NewQueryClient(chain.ClientContext) + accountRes, err := authClient.Account(ctx, &authtypes.QueryAccountRequest{ + Address: contractAddr, + }) + requireT.NoError(err) + + // When instantiating WASM converts any account to base account. + // If account is not defined in acceptedAccountTypes then extra manipulation will be done with it before + // contract instantiation. By default, coins from vesting accounts are fully burnt and once account balance + // is 0 then keeper sets account to auth.BaseAccount. + // For more details see: github.com/CosmWasm/wasmd@v0.44.0/x/wasm/keeper/keeper.go:280 + requireT.Equal("/cosmos.auth.v1beta1.BaseAccount", accountRes.Account.TypeUrl) + + res, err := bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: contractAddr, + Denom: tc.expectedBalanceAfterInstantiation.Denom, + }) + requireT.NoError(err) + requireT.Equal(tc.expectedBalanceAfterInstantiation.String(), res.Balance.String()) + }) + } } func randStringWithLength(n int) string { diff --git a/x/wasm/types/prunner.go b/x/wasm/types/prunner.go deleted file mode 100644 index a28652bca..000000000 --- a/x/wasm/types/prunner.go +++ /dev/null @@ -1,15 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// AccountPruner implements wasm's account pruner in a way causing smart contract instantiation to be rejected if -// account exists. -type AccountPruner struct{} - -// CleanupExistingAccount informs wasm module to reject smart contract instantiation if account exists. -func (ap AccountPruner) CleanupExistingAccount(_ sdk.Context, _ authtypes.AccountI) (bool, error) { - return false, nil -}