Skip to content

Commit

Permalink
wip: ibc testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Lockwarr committed Oct 15, 2024
1 parent 542d592 commit 516908c
Show file tree
Hide file tree
Showing 2 changed files with 335 additions and 0 deletions.
18 changes: 18 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,3 +510,21 @@ func (app *App) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper {
func (app *App) GetStakingKeeper() ibctestingtypes.StakingKeeper {
return app.StakingKeeper
}

// InitChainer application update at chain initialization
// ONLY FOR TESTING PURPOSES
func (app *App) TestInitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
var genesisState GenesisState
if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
panic(err)
}

// manually set consensus params here, cause there is no way to set it using ibctesting stuff for now
// TODO: app.ConsensusParamsKeeper.Set(ctx, sims.DefaultConsensusParams)

err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap())
if err != nil {
return nil, fmt.Errorf("failed to set module version map: %w", err)
}
return app.mm.InitGenesis(ctx, app.appCodec, genesisState)
}
317 changes: 317 additions & 0 deletions testutil/test_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
package testutil

import (
"context"
"encoding/json"
"fmt"
"os"
"time"

"cosmossdk.io/log"

icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types"
"github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
ibctesting "github.com/cosmos/ibc-go/v8/testing"

"github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"

db2 "github.com/cosmos/cosmos-db"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
"github.com/stretchr/testify/suite"

"github.com/Nolus-Protocol/nolus-core/app"
"github.com/Nolus-Protocol/nolus-core/app/params"

tmos "github.com/cometbft/cometbft/libs/os"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"

ictxstypes "github.com/neutron-org/neutron/v4/x/interchaintxs/types"
)

var (
// TestOwnerAddress defines a reusable bech32 address for testing purposes.
TestOwnerAddress = "nolus17dtl0mjt3t77kpuhg2edqzjpszulwhgzcdvagh"

TestInterchainID = "owner_id"

Connection = "connection-0"

// TestVersion defines a reusable interchainaccounts version string for testing purposes.
TestVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&icatypes.Metadata{
Version: icatypes.Version,
ControllerConnectionId: Connection,
HostConnectionId: Connection,
Encoding: icatypes.EncodingProtobuf,
TxType: icatypes.TxTypeSDKMultiMsg,
}))
)

func init() {
ibctesting.DefaultTestingAppInit = SetupTestingApp
params.GetDefaultConfig()
// Disable cache since enabled cache triggers test errors when `AccAddress.String()`
// gets called before setting nolus bech32 prefix
sdk.SetAddrCacheEnabled(false)
}

type IBCConnectionTestSuite struct {
suite.Suite
Coordinator *ibctesting.Coordinator

// testing chains used for convenience and readability
ChainA *ibctesting.TestChain
ChainB *ibctesting.TestChain

Path *ibctesting.Path
TransferPath *ibctesting.Path
}

func (suite *IBCConnectionTestSuite) SetupTest() {
// we need to redefine this variable to make tests work cause we use unls as default bond denom in nolus
sdk.DefaultBondDenom = params.DefaultBondDenom

suite.Coordinator = ibctesting.NewCoordinator(suite.T(), 2) // initialize 2 test chains
suite.ChainA = suite.Coordinator.GetChain(ibctesting.GetChainID(1))
suite.ChainB = suite.Coordinator.GetChain(ibctesting.GetChainID(2))

// move chains to the next block
suite.ChainA.NextBlock()
suite.ChainB.NextBlock()

// path := ibctesting.NewPath(suite.ChainA, suite.ChainB) // clientID, connectionID, channelID empty
// suite.Coordinator.Setup(path) // clientID, connectionID, channelID filled
// suite.Require().Equal("07-tendermint-0", path.EndpointA.ClientID)
// suite.Require().Equal("connection-0", path.EndpointA.ClientID)
// suite.Require().Equal("channel-0", path.EndpointA.ClientID)

suite.Path = NewICAPath(suite.ChainA, suite.ChainB)

// suite.ConfigureTransferChannel()
suite.Coordinator.Setup(suite.Path)
}

func (suite *IBCConnectionTestSuite) ConfigureTransferChannel() {
suite.TransferPath = NewTransferPath(suite.ChainA, suite.ChainB)
suite.Coordinator.SetupConnections(suite.TransferPath)
err := SetupTransferPath(suite.TransferPath)
suite.Require().NoError(err)
}

func (suite *IBCConnectionTestSuite) GetNolusZoneApp(chain *ibctesting.TestChain) *app.App {
testApp, ok := chain.App.(*app.App)
if !ok {
panic("not NolusZone app")
}

return testApp
}

func (suite *IBCConnectionTestSuite) StoreTestCode(ctx sdk.Context, addr sdk.AccAddress, path string) uint64 {
// wasm file built with https://github.com/neutron-org/neutron-sdk/tree/main/contracts/reflect
// wasm file built with https://github.com/neutron-org/neutron-dev-contracts/tree/feat/ica-register-fee-update/contracts/neutron_interchain_txs
wasmCode, err := os.ReadFile(path)
suite.Require().NoError(err)

codeID, _, err := keeper.NewDefaultPermissionKeeper(suite.GetNolusZoneApp(suite.ChainA).WasmKeeper).Create(ctx, addr, wasmCode, &wasmtypes.AccessConfig{Permission: wasmtypes.AccessTypeEverybody})
suite.Require().NoError(err)

return codeID
}

func (suite *IBCConnectionTestSuite) InstantiateTestContract(ctx sdk.Context, funder sdk.AccAddress, codeID uint64) sdk.AccAddress {
initMsgBz := []byte("{}")
contractKeeper := keeper.NewDefaultPermissionKeeper(suite.GetNolusZoneApp(suite.ChainA).WasmKeeper)
addr, _, err := contractKeeper.Instantiate(ctx, codeID, funder, funder, initMsgBz, "demo contract", nil)
suite.Require().NoError(err)

return addr
}

func NewICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path {
path := ibctesting.NewPath(chainA, chainB)
path.EndpointA.Counterparty = path.EndpointB
path.EndpointB.Counterparty = path.EndpointA

path.EndpointA.ChannelConfig.PortID = icatypes.HostPortID
path.EndpointB.ChannelConfig.PortID = icatypes.HostPortID
path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED
path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED
path.EndpointA.ChannelConfig.Version = TestVersion
path.EndpointB.ChannelConfig.Version = TestVersion

path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).UnbondingPeriod = 3600000000000
path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod = 1200000000000

path.EndpointB.ClientConfig.(*ibctesting.TendermintConfig).UnbondingPeriod = 3600000000000
path.EndpointB.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod = 1200000000000
return path
}

// SetupICAPath invokes the InterchainAccounts entrypoint and subsequent channel handshake handlers.
func SetupICAPath(path *ibctesting.Path, owner string) error {
if err := RegisterInterchainAccount(path.EndpointA, owner); err != nil {
return err
}

if err := path.EndpointB.ChanOpenTry(); err != nil {
return err
}

if err := path.EndpointA.ChanOpenAck(); err != nil {
return err
}

return path.EndpointB.ChanOpenConfirm()
}

// RegisterInterchainAccount is a helper function for starting the channel handshake.
func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) error {
icaOwner, _ := ictxstypes.NewICAOwner(owner, TestInterchainID)
portID, err := icatypes.NewControllerPortID(icaOwner.String())
if err != nil {
return err
}

ctx := endpoint.Chain.GetContext()

channelSequence := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(ctx)

a, ok := endpoint.Chain.App.(*app.App)
if !ok {
return fmt.Errorf("not NolusZoneApp")
}

// TODO(pr0n00gler): are we sure it's okay?
if err := a.ICAControllerKeeper.RegisterInterchainAccount(ctx, endpoint.ConnectionID, icaOwner.String(), ""); err != nil {
return err
}

// commit state changes for proof verification
endpoint.Chain.NextBlock()

// update port/channel ids
endpoint.ChannelID = channeltypes.FormatChannelIdentifier(channelSequence)
endpoint.ChannelConfig.PortID = portID

return nil
}

var tempDir = func() string {
dir, err := os.MkdirTemp("", "nolusd")
if err != nil {
panic("failed to create temp dir: " + err.Error())
}
defer os.RemoveAll(dir)

return dir
}

// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of
// an IAVLStore for faster simulation speed.
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
bapp.SetFauxMerkleMode()
}

// SetupTestingApp initializes the IBC-go testing application.
func SetupTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) {
encoding := app.MakeEncodingConfig(app.ModuleBasics)
db := db2.NewMemDB()

temp := tempDir()

testApp := app.New(
log.NewNopLogger(),
db,
nil,
false,
map[int64]bool{},
temp,
0,
encoding,
sims.EmptyAppOptions{},
fauxMerkleModeOpt,
)

ctx := testApp.BaseApp.NewContextLegacy(false, tmproto.Header{Height: 1, ChainID: "testchain-1", Time: time.Now().UTC()})

// Manually set validator signing info, otherwise we panic
vals, err := testApp.StakingKeeper.GetAllValidators(context.TODO())
if err != nil {
panic(err)
}
for _, val := range vals {
consAddr, _ := val.GetConsAddr()
signingInfo := slashingtypes.NewValidatorSigningInfo(
consAddr,
ctx.BlockHeight(),
0,
time.Unix(0, 0),
false,
0,
)
err := testApp.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, signingInfo)
if err != nil {
panic(err)
}
}

// we need to set up a TestInitChainer where we can redefine MaxBlockGas in ConsensusParamsKeeper
testApp.SetInitChainer(testApp.TestInitChainer)
// and then we manually init baseapp and load states
if err := testApp.LoadLatestVersion(); err != nil {
tmos.Exit(err.Error())
}

// Initialize pinned codes in wasmvm as they are not persisted there
if err := testApp.WasmKeeper.InitializePinnedCodes(ctx); err != nil {
panic(err)
}

genesisState := app.NewDefaultGenesisState(encoding)

// genesisState := app.NewDefaultGenesisState(encoding)

return testApp, genesisState
}

func NewTransferPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path {
path := ibctesting.NewPath(chainA, chainB)
path.EndpointA.ChannelConfig.PortID = types.PortID
path.EndpointB.ChannelConfig.PortID = types.PortID
path.EndpointA.ChannelConfig.Order = channeltypes.UNORDERED
path.EndpointB.ChannelConfig.Order = channeltypes.UNORDERED
path.EndpointA.ChannelConfig.Version = types.Version
path.EndpointB.ChannelConfig.Version = types.Version

return path
}

// SetupTransferPath.
func SetupTransferPath(path *ibctesting.Path) error {
channelSequence := path.EndpointA.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(path.EndpointA.Chain.GetContext())
channelSequenceB := path.EndpointB.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(path.EndpointB.Chain.GetContext())

// update port/channel ids
path.EndpointA.ChannelID = channeltypes.FormatChannelIdentifier(channelSequence)
path.EndpointB.ChannelID = channeltypes.FormatChannelIdentifier(channelSequenceB)

if err := path.EndpointA.ChanOpenInit(); err != nil {
return err
}

if err := path.EndpointB.ChanOpenTry(); err != nil {
return err
}

if err := path.EndpointA.ChanOpenAck(); err != nil {
return err
}

return path.EndpointB.ChanOpenConfirm()
}

0 comments on commit 516908c

Please sign in to comment.