Skip to content

Commit

Permalink
Merge pull request #186 from osmosis-labs/trinity/e2etest-for-stake-v…
Browse files Browse the repository at this point in the history
…esting-token

test: add e2e test for stake vesting token
  • Loading branch information
trinitys7 authored Aug 21, 2024
2 parents 1e2b9e4 + 2922c1e commit c0bd5c7
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 33 deletions.
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

0 comments on commit c0bd5c7

Please sign in to comment.