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

test: add e2e test for stake vesting token #186

Merged
merged 2 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
18 changes: 9 additions & 9 deletions tests/e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func NewIBCCoordinator(t *testing.T, n int, opts ...[]wasmkeeper.Option) *ibctes
)
}

func submitGovProposal(t *testing.T, chain *ibctesting.TestChain, msgs ...sdk.Msg) uint64 {
func submitGovProposal(t *testing.T, chain *TestChain, msgs ...sdk.Msg) uint64 {
chainApp := chain.App.(*app.MeshApp)
govParams := chainApp.GovKeeper.GetParams(chain.GetContext())
govMsg, err := govv1.NewMsgSubmitProposal(msgs, govParams.MinDeposit, chain.SenderAccount.GetAddress().String(), "", "my title", "my summary")
Expand All @@ -57,7 +57,7 @@ func submitGovProposal(t *testing.T, chain *ibctesting.TestChain, msgs ...sdk.Ms
return id
}

func voteAndPassGovProposal(t *testing.T, chain *ibctesting.TestChain, proposalID uint64) {
func voteAndPassGovProposal(t *testing.T, chain *TestChain, proposalID uint64) {
vote := govv1.NewMsgVote(chain.SenderAccount.GetAddress(), proposalID, govv1.OptionYes, "testing")
_, err := chain.SendMsgs(vote)
require.NoError(t, err)
Expand All @@ -67,14 +67,14 @@ func voteAndPassGovProposal(t *testing.T, chain *ibctesting.TestChain, proposalI

coord := chain.Coordinator
coord.IncrementTimeBy(*govParams.VotingPeriod)
coord.CommitBlock(chain)
coord.CommitBlock(chain.IBCTestChain())

rsp, err := chainApp.GovKeeper.Proposal(sdk.WrapSDKContext(chain.GetContext()), &govv1.QueryProposalRequest{ProposalId: proposalID})
require.NoError(t, err)
require.Equal(t, rsp.Proposal.Status, govv1.ProposalStatus_PROPOSAL_STATUS_PASSED)
}

func InstantiateContract(t *testing.T, chain *ibctesting.TestChain, codeID uint64, initMsg []byte, funds ...sdk.Coin) sdk.AccAddress {
func InstantiateContract(t *testing.T, chain *TestChain, codeID uint64, initMsg []byte, funds ...sdk.Coin) sdk.AccAddress {
instantiateMsg := &wasmtypes.MsgInstantiateContract{
Sender: chain.SenderAccount.GetAddress().String(),
Admin: chain.SenderAccount.GetAddress().String(),
Expand All @@ -96,8 +96,8 @@ func InstantiateContract(t *testing.T, chain *ibctesting.TestChain, codeID uint6

type example struct {
Coordinator *ibctesting.Coordinator
ConsumerChain *ibctesting.TestChain
ProviderChain *ibctesting.TestChain
ConsumerChain *TestChain
ProviderChain *TestChain
ConsumerApp *app.MeshApp
ProviderApp *app.MeshApp
IbcPath *ibctesting.Path
Expand All @@ -112,8 +112,8 @@ func setupExampleChains(t *testing.T) example {
consChain := coord.GetChain(ibctesting2.GetChainID(2))
return example{
Coordinator: coord,
ConsumerChain: consChain,
ProviderChain: provChain,
ConsumerChain: NewTestChain(t, consChain),
ProviderChain: NewTestChain(t, provChain),
ConsumerApp: consChain.App.(*app.MeshApp),
ProviderApp: provChain.App.(*app.MeshApp),
IbcPath: ibctesting.NewPath(consChain, provChain),
Expand All @@ -138,7 +138,7 @@ func setupMeshSecurity(t *testing.T, x example) (*TestConsumerClient, ConsumerCo

// setup ibc control path: consumer -> provider (direction matters)
x.IbcPath.EndpointB.ChannelConfig = &ibctesting2.ChannelConfig{
PortID: wasmkeeper.PortIDForContract(providerContracts.externalStaking),
PortID: wasmkeeper.PortIDForContract(providerContracts.ExternalStaking),
Order: types2.UNORDERED,
}
x.IbcPath.EndpointA.ChannelConfig = &ibctesting2.ChannelConfig{
Expand Down
61 changes: 61 additions & 0 deletions tests/e2e/stake_vesting_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package e2e

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

func TestStakeVestingTokenScenario1(t *testing.T) {
// Slashing scenario:
// Permanent lock vesting account should be able to bond in vault contract
// Unbond the amount, balance should keep being locked
x := setupExampleChains(t)
_, _, providerCli := setupMeshSecurity(t, x)

// Generate vesting address
vestingPrivKey := secp256k1.GenPrivKey()
vestingBacc := authtypes.NewBaseAccount(vestingPrivKey.PubKey().Address().Bytes(), vestingPrivKey.PubKey(), uint64(19), 0)

vestingBalance := sdk.NewCoin(x.ProviderDenom, sdk.NewInt(100000000))
providerCli.MustCreatePermanentLockedAccount(vestingBacc.GetAddress().String(), vestingBalance)

balance := x.ProviderChain.Balance(vestingBacc.GetAddress(), x.ProviderDenom)
assert.Equal(t, balance, vestingBalance)

// Exec contract by vesting account
execMsg := fmt.Sprintf(`{"bond":{"amount":{"denom":"%s", "amount":"100000000"}}}`, x.ProviderDenom)
providerCli.MustExecVaultWithSigner(vestingPrivKey, vestingBacc, execMsg)

balance = x.ProviderChain.Balance(providerCli.Contracts.Vault, x.ProviderDenom)
assert.Equal(t, balance, vestingBalance)

assert.Equal(t, 100_000_000, providerCli.QuerySpecificAddressVaultBalance(vestingBacc.GetAddress().String()))

// Try to exec msg bond again, should fail
err := providerCli.ExecVaultWithSigner(vestingPrivKey, vestingBacc, execMsg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "dispatch: submessages: failed to delegate")

// Make sure vault balance doesn't change
assert.Equal(t, 100_000_000, providerCli.QuerySpecificAddressVaultBalance(vestingBacc.GetAddress().String()))

// Unbond vault
execMsg = fmt.Sprintf(`{"unbond":{"amount":{"denom":"%s", "amount": "30000000"}}}`, x.ProviderDenom)
providerCli.MustExecVaultWithSigner(vestingPrivKey, vestingBacc, execMsg)

vestingBalance = sdk.NewCoin(x.ProviderDenom, sdk.NewInt(30000000))

balance = x.ProviderChain.Balance(vestingBacc.GetAddress(), x.ProviderDenom)
assert.Equal(t, balance, vestingBalance)

// Vesting account is still locked
err = providerCli.BankSendWithSigner(vestingPrivKey, vestingBacc, x.ProviderChain.SenderAccount.GetAddress().String(), vestingBalance)
assert.Error(t, err)
assert.Contains(t, err.Error(), "insufficient funds")
}
150 changes: 130 additions & 20 deletions tests/e2e/test_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import (
"cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/baseapp"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

"github.com/osmosis-labs/mesh-security-sdk/demo/app"
"github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity"
Expand Down Expand Up @@ -54,7 +58,7 @@ func (q QueryResponse) Array(key string) []QueryResponse {
return result
}

func Querier(t *testing.T, chain *ibctesting.TestChain) func(contract string, query Query) QueryResponse {
func Querier(t *testing.T, chain *TestChain) func(contract string, query Query) QueryResponse {
return func(contract string, query Query) QueryResponse {
qRsp := make(map[string]any)
err := chain.SmartQuery(contract, query, &qRsp)
Expand All @@ -65,20 +69,68 @@ func Querier(t *testing.T, chain *ibctesting.TestChain) func(contract string, qu

type TestProviderClient struct {
t *testing.T
chain *ibctesting.TestChain
contracts ProviderContracts
chain *TestChain
Contracts ProviderContracts
}

func NewProviderClient(t *testing.T, chain *ibctesting.TestChain) *TestProviderClient {
type TestChain struct {
*ibctesting.TestChain
t *testing.T
}

func NewTestChain(t *testing.T, chain *ibctesting.TestChain) *TestChain {
return &TestChain{
t: t,
TestChain: chain,
}
}

func (tc *TestChain) IBCTestChain() *ibctesting.TestChain {
return tc.TestChain
}

func NewProviderClient(t *testing.T, chain *TestChain) *TestProviderClient {
return &TestProviderClient{t: t, chain: chain}
}

func (tc *TestChain) SendMsgsWithSigner(privKey cryptotypes.PrivKey, signer *authtypes.BaseAccount, msgs ...sdk.Msg) (*sdk.Result, error) {
// ensure the chain has the latest time
tc.Coordinator.UpdateTimeForChain(tc.TestChain)
_, r, gotErr := app.SignAndDeliverWithoutCommit(
tc.t,
tc.TxConfig,
tc.App.GetBaseApp(),
msgs,
tc.DefaultMsgFees,
tc.ChainID,
[]uint64{signer.GetAccountNumber()},
[]uint64{signer.GetSequence()},
privKey,
)

// NextBlock calls app.Commit()
tc.NextBlock()

// increment sequence for successful and failed transaction execution
require.NoError(tc.t, signer.SetSequence(signer.GetSequence()+1))
tc.Coordinator.IncrementTime()

if gotErr != nil {
return nil, gotErr
}

tc.CaptureIBCEvents(r.Events)

return r, nil
}

type ProviderContracts struct {
vault sdk.AccAddress
externalStaking sdk.AccAddress
Vault sdk.AccAddress
ExternalStaking sdk.AccAddress
}

func (p *TestProviderClient) BootstrapContracts(provApp *app.MeshApp, connId, portID string) ProviderContracts { var (
func (p *TestProviderClient) BootstrapContracts(provApp *app.MeshApp, connId, portID string) ProviderContracts {
var (
unbondingPeriod = 21 * 24 * 60 * 60 // 21 days - make configurable?
localSlashRatioDoubleSign = "0.20"
localSlashRatioOffline = "0.10"
Expand Down Expand Up @@ -106,19 +158,53 @@ func (p *TestProviderClient) BootstrapContracts(provApp *app.MeshApp, connId, po
externalStakingContract := InstantiateContract(p.t, p.chain, extStakingCodeID, initMsg)

r := ProviderContracts{
vault: vaultContract,
externalStaking: externalStakingContract,
Vault: vaultContract,
ExternalStaking: externalStakingContract,
}
p.contracts = r
p.Contracts = r
return r
}

func (p TestProviderClient) MustCreatePermanentLockedAccount(acc string, coins ...sdk.Coin) *sdk.Result {
rsp, err := p.chain.SendMsgs(&vestingtypes.MsgCreatePermanentLockedAccount{
FromAddress: p.chain.SenderAccount.GetAddress().String(),
ToAddress: acc,
Amount: coins,
})
require.NoError(p.t, err)
return rsp
}

func (p TestProviderClient) BankSendWithSigner(privKey cryptotypes.PrivKey, signer *authtypes.BaseAccount, to string, coins ...sdk.Coin) error {
_, err := p.chain.SendMsgsWithSigner(
privKey,
signer,
&banktypes.MsgSend{
FromAddress: signer.GetAddress().String(),
ToAddress: to,
Amount: coins,
},
)
return err
}

func (p TestProviderClient) MustExecVault(payload string, funds ...sdk.Coin) *sdk.Result {
return p.mustExec(p.contracts.vault, payload, funds)
return p.mustExec(p.Contracts.Vault, payload, funds)
}

func (p TestProviderClient) MustExecVaultWithSigner(privKey cryptotypes.PrivKey, signer *authtypes.BaseAccount, payload string, funds ...sdk.Coin) *sdk.Result {
rsp, err := p.ExecWithSigner(privKey, signer, p.Contracts.Vault, payload, funds...)
require.NoError(p.t, err)
return rsp
}

func (p TestProviderClient) ExecVaultWithSigner(privKey cryptotypes.PrivKey, signer *authtypes.BaseAccount, payload string, funds ...sdk.Coin) error {
_, err := p.ExecWithSigner(privKey, signer, p.Contracts.Vault, payload, funds...)
return err
}

func (p TestProviderClient) MustExecExtStaking(payload string, funds ...sdk.Coin) *sdk.Result {
return p.mustExec(p.contracts.externalStaking, payload, funds)
return p.mustExec(p.Contracts.ExternalStaking, payload, funds)
}

func (p TestProviderClient) mustExec(contract sdk.AccAddress, payload string, funds []sdk.Coin) *sdk.Result {
Expand All @@ -137,8 +223,22 @@ func (p TestProviderClient) Exec(contract sdk.AccAddress, payload string, funds
return rsp, err
}

func (p TestProviderClient) ExecWithSigner(privKey cryptotypes.PrivKey, signer *authtypes.BaseAccount, contract sdk.AccAddress, payload string, funds ...sdk.Coin) (*sdk.Result, error) {
rsp, err := p.chain.SendMsgsWithSigner(
privKey,
signer,
&wasmtypes.MsgExecuteContract{
Sender: signer.GetAddress().String(),
Contract: contract.String(),
Msg: []byte(payload),
Funds: funds,
},
)
return rsp, err
}

func (p TestProviderClient) MustFailExecVault(payload string, funds ...sdk.Coin) error {
rsp, err := p.Exec(p.contracts.vault, payload, funds...)
rsp, err := p.Exec(p.Contracts.Vault, payload, funds...)
require.Error(p.t, err, "Response: %v", rsp)
return err
}
Expand All @@ -149,10 +249,10 @@ func (p TestProviderClient) MustExecStakeRemote(val string, amt sdk.Coin) {

func (p TestProviderClient) ExecStakeRemote(val string, amt sdk.Coin) error {
payload := fmt.Sprintf(`{"stake_remote":{"contract":"%s", "amount": {"denom":%q, "amount":"%s"}, "msg":%q}}`,
p.contracts.externalStaking.String(),
p.Contracts.ExternalStaking.String(),
amt.Denom, amt.Amount.String(),
base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"validator": "%s"}`, val))))
_, err := p.Exec(p.contracts.vault, payload)
_, err := p.Exec(p.Contracts.Vault, payload)
return err
}

Expand All @@ -168,11 +268,11 @@ func (p TestProviderClient) QueryExtStakingAmount(user, validator string) int {
}

func (p TestProviderClient) QueryExtStaking(q Query) QueryResponse {
return Querier(p.t, p.chain)(p.contracts.externalStaking.String(), q)
return Querier(p.t, p.chain)(p.Contracts.ExternalStaking.String(), q)
}

func (p TestProviderClient) QueryVault(q Query) QueryResponse {
return Querier(p.t, p.chain)(p.contracts.vault.String(), q)
return Querier(p.t, p.chain)(p.Contracts.Vault.String(), q)
}

type HighLowType struct {
Expand Down Expand Up @@ -210,6 +310,16 @@ func (p TestProviderClient) QueryVaultBalance() int {
return b
}

func (p TestProviderClient) QuerySpecificAddressVaultBalance(address string) int {
qRsp := p.QueryVault(Query{
"account_details": {"account": address},
})
require.NotEmpty(p.t, qRsp["bonded"], qRsp)
b, err := strconv.Atoi(qRsp["bonded"].(string))
require.NoError(p.t, err)
return b
}

func (p TestProviderClient) QueryMaxLien() int {
qRsp := p.QueryVault(Query{
"account_details": {"account": p.chain.SenderAccount.GetAddress().String()},
Expand All @@ -228,12 +338,12 @@ func (p TestProviderClient) QuerySlashableAmount() int {

type TestConsumerClient struct {
t *testing.T
chain *ibctesting.TestChain
chain *TestChain
contracts ConsumerContract
app *app.MeshApp
}

func NewConsumerClient(t *testing.T, chain *ibctesting.TestChain) *TestConsumerClient {
func NewConsumerClient(t *testing.T, chain *TestChain) *TestConsumerClient {
return &TestConsumerClient{t: t, chain: chain, app: chain.App.(*app.MeshApp)}
}

Expand Down Expand Up @@ -278,7 +388,7 @@ func (p *TestConsumerClient) ExecNewEpoch() {
execHeight, ok := p.app.MeshSecKeeper.GetNextScheduledTaskHeight(p.chain.GetContext(), types.SchedulerTaskHandleEpoch, p.contracts.staking)
require.True(p.t, ok)
if ch := uint64(p.chain.GetContext().BlockHeight()); ch < execHeight {
p.chain.Coordinator.CommitNBlocks(p.chain, execHeight-ch)
p.chain.Coordinator.CommitNBlocks(p.chain.IBCTestChain(), execHeight-ch)
}
rsp := p.chain.NextBlock()
// ensure capture events do not contain a contract error
Expand Down
Loading
Loading