Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change WASM behaviour to default: burn vesting balance when instantiating contract #740

Merged
merged 9 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
252 changes: 158 additions & 94 deletions integration-tests/modules/wasm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 0 additions & 15 deletions x/wasm/types/prunner.go

This file was deleted.

Loading