From 3763c104eeb054445837d4842704c9318f9d7881 Mon Sep 17 00:00:00 2001 From: Wojtek Date: Thu, 7 Dec 2023 15:52:37 +0100 Subject: [PATCH 1/9] Stop rejecting contract instantiation if account holds funds --- integration-tests/modules/wasm_test.go | 50 ++++++++++++++++++-------- x/wasm/types/prunner.go | 2 +- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/integration-tests/modules/wasm_test.go b/integration-tests/modules/wasm_test.go index 375aa89e5..5636ab8b3 100644 --- a/integration-tests/modules/wasm_test.go +++ b/integration-tests/modules/wasm_test.go @@ -2118,12 +2118,12 @@ func TestWASMBankSendContractWithMultipleFundsAttached(t *testing.T) { // TestWASMContractInstantiationIsRejectedIfThereAreTokensOnItsAccount verifies that smart contract instantiation // is rejected if account exists. -func TestWASMContractInstantiationIsRejectedIfAccountExists(t *testing.T) { +func TestWASMContractInstantiationIsNotRejectedIfAccountExists(t *testing.T) { t.Parallel() ctx, chain := integrationtests.NewCoreumTestingContext(t) - admin := chain.GenAccount() + amount := chain.NewCoin(sdkmath.NewInt(500)) requireT := require.New(t) chain.Faucet.FundAccounts(ctx, t, @@ -2162,15 +2162,15 @@ func TestWASMContractInstantiationIsRejectedIfAccountExists(t *testing.T) { msg := &banktypes.MsgSend{ FromAddress: admin.String(), ToAddress: contract.String(), - Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(500))), + Amount: sdk.NewCoins(amount), } _, 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. + // Instantiate the smart contract. - _, err = chain.Wasm.InstantiateWASMContract( + contractAddr1, err := chain.Wasm.InstantiateWASMContract( ctx, txf, admin, @@ -2182,7 +2182,7 @@ func TestWASMContractInstantiationIsRejectedIfAccountExists(t *testing.T) { Label: "bank_send", }, ) - requireT.ErrorContains(err, "contract account already exists") + requireT.NoError(err) // Predict the address of another smart contract. @@ -2197,16 +2197,14 @@ func TestWASMContractInstantiationIsRejectedIfAccountExists(t *testing.T) { ) requireT.NoError(err) - // Create vesting account using address of the smart cotntract. + // Create vesting account using address of the smart contract. createVestingAccMsg := &vestingtypes.MsgCreateVestingAccount{ FromAddress: admin.String(), ToAddress: contract.String(), - Amount: sdk.NewCoins( - chain.NewCoin(sdkmath.NewInt(10000)), - ), - EndTime: time.Now().Unix(), - Delayed: true, + Amount: sdk.NewCoins(amount), + EndTime: time.Now().Unix(), + Delayed: true, } _, err = client.BroadcastTx( @@ -2221,9 +2219,17 @@ func TestWASMContractInstantiationIsRejectedIfAccountExists(t *testing.T) { requireT.NoError(client.AwaitNextBlocks(ctx, clientCtx, 1)) - // Instantiate the smart contract. It should fail because its account holds some funds. + bankClient := banktypes.NewQueryClient(chain.ClientContext) + qres, err := bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: contract.String(), + Denom: amount.Denom, + }) + requireT.NoError(err) + requireT.Equal(amount.String(), qres.Balance.String()) + + // Instantiate the smart contract. - _, err = chain.Wasm.InstantiateWASMContract( + contractAddr2, err := chain.Wasm.InstantiateWASMContract( ctx, txf, admin, @@ -2235,7 +2241,21 @@ func TestWASMContractInstantiationIsRejectedIfAccountExists(t *testing.T) { Label: "bank_send", }, ) - requireT.ErrorContains(err, "contract account already exists") + requireT.NoError(err) + + qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: contractAddr1, + Denom: amount.Denom, + }) + requireT.NoError(err) + requireT.Equal(amount.String(), qres.Balance.String()) + + qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: contractAddr2, + Denom: amount.Denom, + }) + requireT.NoError(err) + requireT.Equal(amount.String(), qres.Balance.String()) } func randStringWithLength(n int) string { diff --git a/x/wasm/types/prunner.go b/x/wasm/types/prunner.go index a28652bca..94a15a1e2 100644 --- a/x/wasm/types/prunner.go +++ b/x/wasm/types/prunner.go @@ -11,5 +11,5 @@ 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 + return true, nil } From 30a562698e426528d4d17e730958dae5e292fd38 Mon Sep 17 00:00:00 2001 From: Wojtek Date: Thu, 7 Dec 2023 16:14:46 +0100 Subject: [PATCH 2/9] Test vesting on smart contract amount --- integration-tests/modules/wasm_test.go | 119 +++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/integration-tests/modules/wasm_test.go b/integration-tests/modules/wasm_test.go index 5636ab8b3..cdc795571 100644 --- a/integration-tests/modules/wasm_test.go +++ b/integration-tests/modules/wasm_test.go @@ -2258,6 +2258,125 @@ func TestWASMContractInstantiationIsNotRejectedIfAccountExists(t *testing.T) { requireT.Equal(amount.String(), qres.Balance.String()) } +// TestVestingToWASMContract verifies that smart contract instantiated on top of vesting accunt receives funds correctly. +func TestVestingToWASMContract(t *testing.T) { + t.Parallel() + + ctx, chain := integrationtests.NewCoreumTestingContext(t) + admin := chain.GenAccount() + recipient := chain.GenAccount() + amount := chain.NewCoin(sdkmath.NewInt(500)) + + requireT := require.New(t) + chain.Faucet.FundAccounts(ctx, t, + integration.NewFundedAccount(admin, chain.NewCoin(sdkmath.NewInt(5000000000))), + ) + + txf := chain.TxFactory(). + WithSimulateAndExecute(true) + + // Deploy smart contract. + + codeID, err := chain.Wasm.DeployWASMContract( + ctx, + txf, + admin, + moduleswasm.BankSendWASM, + ) + requireT.NoError(err) + + // Predict the address of smart contract. + + salt, err := chain.Wasm.GenerateSalt() + requireT.NoError(err) + + contract, err := chain.Wasm.PredictWASMContractAddress( + ctx, + admin, + salt, + codeID, + ) + requireT.NoError(err) + + // Create vesting account using address of the smart contract. + + vestingDuration := 30 * time.Second + createVestingAccMsg := &vestingtypes.MsgCreateVestingAccount{ + FromAddress: admin.String(), + ToAddress: contract.String(), + Amount: sdk.NewCoins(amount), + EndTime: time.Now().Add(vestingDuration).Unix(), + Delayed: true, + } + + _, err = client.BroadcastTx( + ctx, + chain.ClientContext.WithFromAddress(admin), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(createVestingAccMsg)), + createVestingAccMsg, + ) + requireT.NoError(err) + + // Instantiate the smart contract. + + contractAddr, err := chain.Wasm.InstantiateWASMContract( + ctx, + txf, + admin, + salt, + integration.InstantiateConfig{ + CodeID: codeID, + AccessType: wasmtypes.AccessTypeUnspecified, + Payload: moduleswasm.EmptyPayload, + Label: "bank_send", + }, + ) + requireT.NoError(err) + + // Check that this is still a vesting account. + + authClient := authtypes.NewQueryClient(chain.ClientContext) + accountRes, err := authClient.Account(ctx, &authtypes.QueryAccountRequest{ + Address: contractAddr, + }) + requireT.NoError(err) + requireT.Equal("/cosmos.vesting.v1beta1.DelayedVestingAccount", accountRes.Account.TypeUrl) + + // Await vesting time to unlock the vesting coins + + select { + case <-ctx.Done(): + return + case <-time.After(vestingDuration): + } + + // Verify funds are there. + + bankClient := banktypes.NewQueryClient(chain.ClientContext) + qres, err := bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: contractAddr, + Denom: amount.Denom, + }) + requireT.NoError(err) + requireT.Equal(amount.String(), qres.Balance.String()) + + // Send sth to verify that funds has been vested. + + msgSend := &banktypes.MsgSend{ + FromAddress: contractAddr, + ToAddress: recipient.String(), + Amount: sdk.NewCoins(chain.NewCoin(sdk.OneInt())), + } + + _, err = client.BroadcastTx( + ctx, + chain.ClientContext.WithFromAddress(contract), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(msgSend)), + msgSend, + ) + requireT.NoError(err) +} + func randStringWithLength(n int) string { letterRunes := []rune("abcdefghijklmnopqrstuvwxyz") b := make([]rune, n) From d4ae60b8e28f9715ac1658c23e23d75ccc2a54c8 Mon Sep 17 00:00:00 2001 From: Wojtek Date: Thu, 7 Dec 2023 16:29:55 +0100 Subject: [PATCH 3/9] fixes --- app/app.go | 6 +- integration-tests/modules/wasm_test.go | 85 ++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/app/app.go b/app/app.go index a5810ef0f..feedd5a65 100644 --- a/app/app.go +++ b/app/app.go @@ -590,7 +590,11 @@ func New( } wasmOpts := []wasmkeeper.Option{ - wasmkeeper.WithAcceptedAccountTypesOnContractInstantiation(), + wasmkeeper.WithAcceptedAccountTypesOnContractInstantiation( + &authtypes.BaseAccount{}, + &vestingtypes.ContinuousVestingAccount{}, + &vestingtypes.DelayedVestingAccount{}, + ), wasmkeeper.WithAccountPruner(cwasmtypes.AccountPruner{}), wasmkeeper.WithCoinTransferrer(cwasmtypes.NewBankCoinTransferrer(app.BankKeeper)), wasmkeeper.WithMessageHandler(wasmcustomhandler.NewMessengerWrapper(wasmkeeper.NewDefaultMessageHandler( diff --git a/integration-tests/modules/wasm_test.go b/integration-tests/modules/wasm_test.go index cdc795571..5c658a43c 100644 --- a/integration-tests/modules/wasm_test.go +++ b/integration-tests/modules/wasm_test.go @@ -2197,7 +2197,7 @@ func TestWASMContractInstantiationIsNotRejectedIfAccountExists(t *testing.T) { ) requireT.NoError(err) - // Create vesting account using address of the smart contract. + // Create delayed vesting account using address of the smart contract. createVestingAccMsg := &vestingtypes.MsgCreateVestingAccount{ FromAddress: admin.String(), @@ -2243,6 +2243,63 @@ func TestWASMContractInstantiationIsNotRejectedIfAccountExists(t *testing.T) { ) requireT.NoError(err) + // Predict the address of another smart contract. + + salt, err = chain.Wasm.GenerateSalt() + requireT.NoError(err) + + contract, err = chain.Wasm.PredictWASMContractAddress( + ctx, + admin, + salt, + codeID, + ) + requireT.NoError(err) + + // Create continuous vesting account using address of the smart contract. + + createVestingAccMsg = &vestingtypes.MsgCreateVestingAccount{ + FromAddress: admin.String(), + ToAddress: contract.String(), + Amount: sdk.NewCoins(amount), + EndTime: time.Now().Unix(), + } + + _, 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)) + + qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: contract.String(), + Denom: amount.Denom, + }) + requireT.NoError(err) + requireT.Equal(amount.String(), qres.Balance.String()) + + // Instantiate the smart contract. + + contractAddr3, err := chain.Wasm.InstantiateWASMContract( + ctx, + txf, + admin, + salt, + integration.InstantiateConfig{ + CodeID: codeID, + AccessType: wasmtypes.AccessTypeUnspecified, + Payload: moduleswasm.EmptyPayload, + Label: "bank_send", + }, + ) + requireT.NoError(err) + qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ Address: contractAddr1, Denom: amount.Denom, @@ -2256,6 +2313,13 @@ func TestWASMContractInstantiationIsNotRejectedIfAccountExists(t *testing.T) { }) requireT.NoError(err) requireT.Equal(amount.String(), qres.Balance.String()) + + qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: contractAddr3, + Denom: amount.Denom, + }) + requireT.NoError(err) + requireT.Equal(amount.String(), qres.Balance.String()) } // TestVestingToWASMContract verifies that smart contract instantiated on top of vesting accunt receives funds correctly. @@ -2360,20 +2424,15 @@ func TestVestingToWASMContract(t *testing.T) { requireT.NoError(err) requireT.Equal(amount.String(), qres.Balance.String()) - // Send sth to verify that funds has been vested. + // Verify that funds has been vested. - msgSend := &banktypes.MsgSend{ - FromAddress: contractAddr, - ToAddress: recipient.String(), - Amount: sdk.NewCoins(chain.NewCoin(sdk.OneInt())), - } - - _, err = client.BroadcastTx( + _, err = chain.Wasm.ExecuteWASMContract( ctx, - chain.ClientContext.WithFromAddress(contract), - chain.TxFactory().WithGas(chain.GasLimitByMsgs(msgSend)), - msgSend, - ) + txf, + admin, + contractAddr, + moduleswasm.BankSendExecuteWithdrawRequest(amount, recipient), + sdk.Coin{}) requireT.NoError(err) } From 3d163d7d7a7d5e280ba8d852b10d598ca3961e36 Mon Sep 17 00:00:00 2001 From: Yaroslav Savchuk Date: Fri, 8 Dec 2023 16:34:58 +0100 Subject: [PATCH 4/9] Change WASM behaviour to default: burn vesting balance when instantiating contract --- app/app.go | 6 - integration-tests/modules/wasm_test.go | 440 +++++++++---------------- 2 files changed, 153 insertions(+), 293 deletions(-) diff --git a/app/app.go b/app/app.go index feedd5a65..71e09e56d 100644 --- a/app/app.go +++ b/app/app.go @@ -590,12 +590,6 @@ func New( } wasmOpts := []wasmkeeper.Option{ - wasmkeeper.WithAcceptedAccountTypesOnContractInstantiation( - &authtypes.BaseAccount{}, - &vestingtypes.ContinuousVestingAccount{}, - &vestingtypes.DelayedVestingAccount{}, - ), - 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 5c658a43c..3ee8e2ebd 100644 --- a/integration-tests/modules/wasm_test.go +++ b/integration-tests/modules/wasm_test.go @@ -2122,318 +2122,184 @@ func TestWASMContractInstantiationIsNotRejectedIfAccountExists(t *testing.T) { t.Parallel() ctx, chain := integrationtests.NewCoreumTestingContext(t) - admin := chain.GenAccount() - amount := chain.NewCoin(sdkmath.NewInt(500)) + contractAdmin := chain.GenAccount() + vestingAccCreator := 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(amount), - } - - _, err = client.BroadcastTx(ctx, clientCtx.WithFromAddress(admin), txf, msg) - requireT.NoError(err) - - // Instantiate the smart contract. - - contractAddr1, 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.NoError(err) - - // Predict the address of another smart contract. - - salt, err = chain.Wasm.GenerateSalt() - requireT.NoError(err) - - contract, err = chain.Wasm.PredictWASMContractAddress( - ctx, - admin, - salt, - codeID, - ) - requireT.NoError(err) - - // Create delayed vesting account using address of the smart contract. - - createVestingAccMsg := &vestingtypes.MsgCreateVestingAccount{ - FromAddress: admin.String(), - ToAddress: contract.String(), - Amount: sdk.NewCoins(amount), - EndTime: time.Now().Unix(), - Delayed: true, - } - - _, 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)) - - bankClient := banktypes.NewQueryClient(chain.ClientContext) - qres, err := bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ - Address: contract.String(), - Denom: amount.Denom, - }) - requireT.NoError(err) - requireT.Equal(amount.String(), qres.Balance.String()) - - // Instantiate the smart contract. + { + 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)) + }, - contractAddr2, err := chain.Wasm.InstantiateWASMContract( - ctx, - txf, - admin, - salt, - integration.InstantiateConfig{ - CodeID: codeID, - AccessType: wasmtypes.AccessTypeUnspecified, - Payload: moduleswasm.EmptyPayload, - Label: "bank_send", + expectedBalanceAfterInstantiation: chain.NewCoin(sdk.ZeroInt()), }, - ) - requireT.NoError(err) - - // Predict the address of another smart contract. - - salt, err = chain.Wasm.GenerateSalt() - requireT.NoError(err) - - contract, err = chain.Wasm.PredictWASMContractAddress( - ctx, - admin, - salt, - codeID, - ) - requireT.NoError(err) - - // Create continuous vesting account using address of the smart contract. - - createVestingAccMsg = &vestingtypes.MsgCreateVestingAccount{ - FromAddress: admin.String(), - ToAddress: contract.String(), - Amount: sdk.NewCoins(amount), - EndTime: time.Now().Unix(), - } - - _, 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)) - - qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ - Address: contract.String(), - Denom: amount.Denom, - }) - requireT.NoError(err) - requireT.Equal(amount.String(), qres.Balance.String()) - - // Instantiate the smart contract. + { + 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)) + }, - contractAddr3, err := chain.Wasm.InstantiateWASMContract( - ctx, - txf, - admin, - salt, - integration.InstantiateConfig{ - CodeID: codeID, - AccessType: wasmtypes.AccessTypeUnspecified, - Payload: moduleswasm.EmptyPayload, - Label: "bank_send", + expectedBalanceAfterInstantiation: chain.NewCoin(sdk.ZeroInt()), }, - ) - requireT.NoError(err) - - qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ - Address: contractAddr1, - Denom: amount.Denom, - }) - requireT.NoError(err) - requireT.Equal(amount.String(), qres.Balance.String()) - - qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ - Address: contractAddr2, - Denom: amount.Denom, - }) - requireT.NoError(err) - requireT.Equal(amount.String(), qres.Balance.String()) - - qres, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ - Address: contractAddr3, - Denom: amount.Denom, - }) - requireT.NoError(err) - requireT.Equal(amount.String(), qres.Balance.String()) -} - -// TestVestingToWASMContract verifies that smart contract instantiated on top of vesting accunt receives funds correctly. -func TestVestingToWASMContract(t *testing.T) { - t.Parallel() - - ctx, chain := integrationtests.NewCoreumTestingContext(t) - admin := chain.GenAccount() - recipient := chain.GenAccount() - amount := chain.NewCoin(sdkmath.NewInt(500)) - - requireT := require.New(t) - chain.Faucet.FundAccounts(ctx, t, - integration.NewFundedAccount(admin, chain.NewCoin(sdkmath.NewInt(5000000000))), - ) - - txf := chain.TxFactory(). - WithSimulateAndExecute(true) - - // Deploy smart contract. - - codeID, err := chain.Wasm.DeployWASMContract( - ctx, - txf, - admin, - moduleswasm.BankSendWASM, - ) - requireT.NoError(err) - - // Predict the address of smart contract. - - salt, err := chain.Wasm.GenerateSalt() - requireT.NoError(err) - - contract, err := chain.Wasm.PredictWASMContractAddress( - ctx, - admin, - salt, - codeID, - ) - requireT.NoError(err) - - // Create vesting account using address of the smart contract. - - vestingDuration := 30 * time.Second - createVestingAccMsg := &vestingtypes.MsgCreateVestingAccount{ - FromAddress: admin.String(), - ToAddress: contract.String(), - Amount: sdk.NewCoins(amount), - EndTime: time.Now().Add(vestingDuration).Unix(), - Delayed: true, - } - - _, err = client.BroadcastTx( - ctx, - chain.ClientContext.WithFromAddress(admin), - chain.TxFactory().WithGas(chain.GasLimitByMsgs(createVestingAccMsg)), - createVestingAccMsg, - ) - requireT.NoError(err) - - // Instantiate the smart contract. + { + 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.Minute).Unix(), + Delayed: true, + } + + _, err := client.BroadcastTx( + ctx, + clientCtx.WithFromAddress(vestingAccCreator), + chain.TxFactory().WithGas(chain.GasLimitByMsgs(msg)), + msg, + ) + requireT.NoError(err) + }, - contractAddr, err := chain.Wasm.InstantiateWASMContract( - ctx, - txf, - admin, - salt, - integration.InstantiateConfig{ - CodeID: codeID, - AccessType: wasmtypes.AccessTypeUnspecified, - Payload: moduleswasm.EmptyPayload, - Label: "bank_send", + expectedBalanceAfterInstantiation: chain.NewCoin(sdk.ZeroInt()), }, - ) - requireT.NoError(err) - - // Check that this is still a vesting account. - - authClient := authtypes.NewQueryClient(chain.ClientContext) - accountRes, err := authClient.Account(ctx, &authtypes.QueryAccountRequest{ - Address: contractAddr, - }) - requireT.NoError(err) - requireT.Equal("/cosmos.vesting.v1beta1.DelayedVestingAccount", accountRes.Account.TypeUrl) - - // Await vesting time to unlock the vesting coins - - select { - case <-ctx.Done(): - return - case <-time.After(vestingDuration): } - // Verify funds are there. - - bankClient := banktypes.NewQueryClient(chain.ClientContext) - qres, err := bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ - Address: contractAddr, - Denom: amount.Denom, - }) - requireT.NoError(err) - requireT.Equal(amount.String(), qres.Balance.String()) - - // Verify that funds has been vested. - - _, err = chain.Wasm.ExecuteWASMContract( - ctx, - txf, - admin, - contractAddr, - moduleswasm.BankSendExecuteWithdrawRequest(amount, recipient), - sdk.Coin{}) - requireT.NoError(err) + 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 { From 8e0f20327271a83cf5ea0adfccd1e3d7269bae9c Mon Sep 17 00:00:00 2001 From: Yaroslav Savchuk Date: Fri, 8 Dec 2023 16:36:56 +0100 Subject: [PATCH 5/9] remove account pruner --- x/wasm/types/prunner.go | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 x/wasm/types/prunner.go diff --git a/x/wasm/types/prunner.go b/x/wasm/types/prunner.go deleted file mode 100644 index 94a15a1e2..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 true, nil -} From 485375caa2dfc8c9ab4566f52e0a3c2bbe5a0e72 Mon Sep 17 00:00:00 2001 From: Yaroslav Savchuk Date: Fri, 8 Dec 2023 16:51:15 +0100 Subject: [PATCH 6/9] increase period to 1h --- integration-tests/modules/wasm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/modules/wasm_test.go b/integration-tests/modules/wasm_test.go index 3ee8e2ebd..6d96dfed2 100644 --- a/integration-tests/modules/wasm_test.go +++ b/integration-tests/modules/wasm_test.go @@ -2232,7 +2232,7 @@ func TestWASMContractInstantiationIsNotRejectedIfAccountExists(t *testing.T) { FromAddress: vestingAccCreator.String(), ToAddress: predictedContractAddr.String(), Amount: sdk.NewCoins(amount2), - EndTime: time.Now().Add(time.Minute).Unix(), + EndTime: time.Now().Add(time.Hour).Unix(), Delayed: true, } From 35b5a7d222b49e1bbf43c113bed35824ffbe8a8a Mon Sep 17 00:00:00 2001 From: Yaroslav Savchuk Date: Fri, 8 Dec 2023 17:04:13 +0100 Subject: [PATCH 7/9] Change func name --- integration-tests/modules/wasm_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/modules/wasm_test.go b/integration-tests/modules/wasm_test.go index 6d96dfed2..7fc401154 100644 --- a/integration-tests/modules/wasm_test.go +++ b/integration-tests/modules/wasm_test.go @@ -2116,9 +2116,9 @@ 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 TestWASMContractInstantiationIsNotRejectedIfAccountExists(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) From 47725fbdc354a71d640c14323a027168c8911f8e Mon Sep 17 00:00:00 2001 From: Yaroslav Savchuk Date: Fri, 8 Dec 2023 17:51:24 +0100 Subject: [PATCH 8/9] Fix CI to run proper versions --- .github/workflows/ci.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79fc47f23..0333a2ba0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,10 +47,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 From 0ba617ef58fd80f3a07e8ec223ee7b296989bece Mon Sep 17 00:00:00 2001 From: Yaroslav Savchuk Date: Fri, 8 Dec 2023 18:02:04 +0100 Subject: [PATCH 9/9] Fix CI file one more time --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0333a2ba0..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: