diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7fd1e8637b..46432680b5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,4 +15,5 @@ # owner is fully deferring ownership to the directory owner docs @liamsi @adlerjohn @MSevey @evan-forbes x/qgb @SweeXordious @evan-forbes -pkg/shares @rootulp @evan-forbes \ No newline at end of file +pkg/shares @rootulp @evan-forbes +pkg/wrapper @staheri14 @evan-forbes diff --git a/.github/workflows/issue-label-automation.yml b/.github/workflows/issue-label-automation.yml index ac1258a132..6a8a0aaa6c 100644 --- a/.github/workflows/issue-label-automation.yml +++ b/.github/workflows/issue-label-automation.yml @@ -12,6 +12,7 @@ jobs: pull-requests: write steps: - name: Check if issue or PR was created by external contributor + if: ${{ github.actor != 'dependabot[bot]' }} uses: tspascoal/get-user-teams-membership@v2 id: teamCheck with: @@ -31,8 +32,16 @@ jobs: # If an issue or PR was created by someone that isn't part of # celestia-core, add the `external` label. - name: Maybe label issue or PR with `external` - if: ${{ steps.teamCheck.outputs.isTeamMember == 'false' }} + if: ${{ github.actor != 'dependabot[bot]' && steps.teamCheck.outputs.isTeamMember == 'false' }} uses: andymckay/labeler@master with: add-labels: "external" repo-token: ${{ secrets.GITHUB_TOKEN }} + + # If a PR was created by dependabot add the `bot` label. + - name: Maybe label PR with `bot` + if: ${{ github.actor == 'dependabot[bot]' }} + uses: andymckay/labeler@master + with: + add-labels: "bot" + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 51cb543195..c7472f0c7c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,17 +6,13 @@ name: Tests / Code Coverage on: workflow_call: -jobs: - cleanup-runs: - runs-on: ubuntu-latest - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: | - !startsWith(github.ref, 'refs/tags/') && - github.ref != 'refs/heads/master' +concurrency: + # do not cancel jobs from earlier commits for tags or the main branch + group: ${{ github.workflow }}-${{ !startsWith(github.ref, 'refs/tags/') && + github.ref != 'refs/heads/main' }} + cancel-in-progress: true +jobs: install-tparse: runs-on: ubuntu-latest steps: @@ -29,7 +25,7 @@ jobs: run: > export GO111MODULE="on" && go install github.com/mfridman/tparse@v0.8.3 - - uses: actions/cache@v3.2.6 + - uses: actions/cache@v3.3.1 with: path: ~/go/bin key: ${{ runner.os }}-go-tparse-binary @@ -228,7 +224,7 @@ jobs: with: name: "${{ github.sha }}-03-race-output" if: env.GIT_DIFF - - uses: actions/cache@v3.2.6 + - uses: actions/cache@v3.3.1 with: path: ~/go/bin key: ${{ runner.os }}-go-tparse-binary diff --git a/Makefile b/Makefile index d711bf176f..ebab648393 100644 --- a/Makefile +++ b/Makefile @@ -27,11 +27,9 @@ help: Makefile ## build: Build the celestia-appd binary into the ./build directory. build: mod - @go install github.com/gobuffalo/packr/v2/packr2@latest - @cd ./cmd/celestia-appd && packr2 + @cd ./cmd/celestia-appd @mkdir -p build/ @go build $(BUILD_FLAGS) -o build/ ./cmd/celestia-appd - @packr2 clean @go mod tidy -compat=1.18 .PHONY: build diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..2932abd987 --- /dev/null +++ b/NOTICE @@ -0,0 +1,2 @@ +Celestia App +Copyright 2021 and onwards Strange Loop Labs AG diff --git a/README.md b/README.md index 528d1dd84f..86a1814090 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ node | | | | ## Install -1. [Install Go](https://go.dev/doc/install) 1.18+ +1. [Install Go](https://go.dev/doc/install) 1.18 1. Clone this repo 1. Install the celestia-app CLI diff --git a/app/app.go b/app/app.go index 35ef5a754a..b262f7c35d 100644 --- a/app/app.go +++ b/app/app.go @@ -35,7 +35,6 @@ import ( crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" distr "github.com/cosmos/cosmos-sdk/x/distribution" - distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/evidence" @@ -47,7 +46,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/cosmos/cosmos-sdk/x/gov" - govclient "github.com/cosmos/cosmos-sdk/x/gov/client" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1beta2 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" @@ -56,7 +54,6 @@ import ( mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params" - paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" @@ -67,7 +64,6 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/cosmos-sdk/x/upgrade" - upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/ibc-go/v6/modules/apps/transfer" @@ -75,7 +71,6 @@ import ( ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" ibc "github.com/cosmos/ibc-go/v6/modules/core" ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" - ibcclientclient "github.com/cosmos/ibc-go/v6/modules/core/02-client/client" ibcclienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" ibcporttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types" ibchost "github.com/cosmos/ibc-go/v6/modules/core/24-host" @@ -97,6 +92,7 @@ import ( qgbmodule "github.com/celestiaorg/celestia-app/x/qgb" qgbmodulekeeper "github.com/celestiaorg/celestia-app/x/qgb/keeper" qgbmoduletypes "github.com/celestiaorg/celestia-app/x/qgb/types" + ibctestingtypes "github.com/cosmos/ibc-go/v6/testing/types" ) const ( @@ -143,7 +139,7 @@ var ( stakingModule{}, mintModule{}, distr.AppModuleBasic{}, - gov.NewAppModuleBasic(getGovProposalHandlers()), + newGovModule(), params.AppModuleBasic{}, crisisModule{}, slashing.AppModuleBasic{}, @@ -587,6 +583,31 @@ func (app *App) ModuleAccountAddrs() map[string]bool { return modAccAddrs } +// GetBaseApp implements the TestingApp interface. +func (app *App) GetBaseApp() *baseapp.BaseApp { + return app.BaseApp +} + +// GetStakingKeeper implements the TestingApp interface. +func (app *App) GetStakingKeeper() ibctestingtypes.StakingKeeper { + return app.StakingKeeper +} + +// GetIBCKeeper implements the TestingApp interface. +func (app *App) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +// GetScopedIBCKeeper implements the TestingApp interface. +func (app *App) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { + return app.ScopedIBCKeeper +} + +// GetTxConfig implements the TestingApp interface. +func (app *App) GetTxConfig() client.TxConfig { + return app.txConfig +} + // LegacyAmino returns SimApp's amino codec. // // NOTE: This is solely to be used for testing purposes as it may be desirable @@ -700,21 +721,6 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino return paramsKeeper } -func getGovProposalHandlers() []govclient.ProposalHandler { - var govProposalHandlers []govclient.ProposalHandler - - govProposalHandlers = append(govProposalHandlers, - paramsclient.ProposalHandler, - distrclient.ProposalHandler, - upgradeclient.LegacyProposalHandler, - upgradeclient.LegacyCancelProposalHandler, - ibcclientclient.UpdateClientProposalHandler, - ibcclientclient.UpgradeProposalHandler, - ) - - return govProposalHandlers -} - func moduleMapToSlice(m module.BasicManager) []encoding.ModuleRegister { // TODO: might be able to use some standard generics in go 1.18 s := make([]encoding.ModuleRegister, len(m)) diff --git a/app/genesis.go b/app/genesis.go index 69e3fb3666..5bf0c1da80 100644 --- a/app/genesis.go +++ b/app/genesis.go @@ -2,6 +2,8 @@ package app import ( "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" ) // The genesis state of the blockchain is represented here as a map of raw json @@ -12,3 +14,8 @@ import ( // the ModuleBasicManager which populates json from each BasicModule // object provided to it during init. type GenesisState map[string]json.RawMessage + +// NewDefaultGenesisState generates the default state for the application. +func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { + return ModuleBasics.DefaultGenesis(cdc) +} diff --git a/app/module.go b/app/module.go index a4447a39c3..6c9ad6eaca 100644 --- a/app/module.go +++ b/app/module.go @@ -9,10 +9,17 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/crisis" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" + "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/cosmos/cosmos-sdk/x/mint" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" "github.com/cosmos/cosmos-sdk/x/staking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + ibcclientclient "github.com/cosmos/ibc-go/v6/modules/core/02-client/client" ) // bankModule defines a custom wrapper around the x/bank module's AppModuleBasic @@ -89,3 +96,34 @@ func (mintModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { return cdc.MustMarshalJSON(genState) } + +func newGovModule() govModule { + return govModule{gov.NewAppModuleBasic(getLegacyProposalHandlers())} +} + +// govModule is a custom wrapper around the x/gov module's AppModuleBasic +// implementation to provide custom default genesis state. +type govModule struct { + gov.AppModuleBasic +} + +// DefaultGenesis returns custom x/gov module genesis state. +func (govModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + genState := govtypes.DefaultGenesisState() + genState.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(BondDenom, sdk.NewInt(10000000))) + + return cdc.MustMarshalJSON(genState) +} + +func getLegacyProposalHandlers() (result []govclient.ProposalHandler) { + result = append(result, + paramsclient.ProposalHandler, + distrclient.ProposalHandler, + upgradeclient.LegacyProposalHandler, + upgradeclient.LegacyCancelProposalHandler, + ibcclientclient.UpdateClientProposalHandler, + ibcclientclient.UpgradeProposalHandler, + ) + + return result +} diff --git a/app/module_test.go b/app/module_test.go new file mode 100644 index 0000000000..3dc8b81eb9 --- /dev/null +++ b/app/module_test.go @@ -0,0 +1,33 @@ +package app + +import ( + "encoding/json" + "testing" + + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/stretchr/testify/assert" +) + +// Test_newGovModule tests that the default genesis state for the gov module +// uses the utia denominiation. +func Test_newGovModule(t *testing.T) { + encCfg := encoding.MakeConfig(ModuleEncodingRegisters...) + + govModule := newGovModule() + raw := govModule.DefaultGenesis(encCfg.Codec) + govGenesisState := govtypes.GenesisState{} + + // HACKHACK explicitly ignore the error returned from json.Unmarshal because + // the error is a failure to unmarshal the string StartingProposalId as a + // uint which is unrelated to the test here. + _ = json.Unmarshal(raw, &govGenesisState) + + want := []types.Coin{{ + Denom: BondDenom, + Amount: types.NewInt(10000000), + }} + + assert.Equal(t, want, govGenesisState.DepositParams.MinDeposit) +} diff --git a/go.mod b/go.mod index 0095e71a21..99fd51e79a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/celestiaorg/nmt v0.14.0 github.com/celestiaorg/quantum-gravity-bridge v1.3.0 - github.com/ethereum/go-ethereum v1.11.3 + github.com/ethereum/go-ethereum v1.11.4 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.3.0 // indirect diff --git a/go.sum b/go.sum index 716c230ba5..57ecaad0d1 100644 --- a/go.sum +++ b/go.sum @@ -347,8 +347,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.10.17/go.mod h1:Lt5WzjM07XlXc95YzrhosmR4J9Ahd6X2wyEV2SvGhk0= -github.com/ethereum/go-ethereum v1.11.3 h1:uuBkYUJW9aY5JYi3+sqLHz+XWyo5fmn/ab9XcbtVDTU= -github.com/ethereum/go-ethereum v1.11.3/go.mod h1:rBUvAl5cdVrAei9q5lgOU7RSEuPJk1nlBDnS/YSoKQE= +github.com/ethereum/go-ethereum v1.11.4 h1:KG81SnUHXWk8LJB3mBcHg/E2yLvXoiPmRMCIRxgx3cE= +github.com/ethereum/go-ethereum v1.11.4/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= @@ -620,7 +620,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU= github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= diff --git a/pkg/da/data_availability_header.go b/pkg/da/data_availability_header.go index 15379e4eb8..1190a3c44b 100644 --- a/pkg/da/data_availability_header.go +++ b/pkg/da/data_availability_header.go @@ -5,13 +5,14 @@ import ( "errors" "fmt" + "github.com/celestiaorg/rsmt2d" + "github.com/tendermint/tendermint/crypto/merkle" + "github.com/tendermint/tendermint/types" + "github.com/celestiaorg/celestia-app/pkg/appconsts" "github.com/celestiaorg/celestia-app/pkg/shares" "github.com/celestiaorg/celestia-app/pkg/wrapper" daproto "github.com/celestiaorg/celestia-app/proto/celestia/da" - "github.com/celestiaorg/rsmt2d" - "github.com/tendermint/tendermint/crypto/merkle" - "github.com/tendermint/tendermint/types" ) const ( @@ -171,7 +172,7 @@ func (dah *DataAvailabilityHeader) IsZero() bool { // It is equal to the data availability header for a block with one tail padding // share. func MinDataAvailabilityHeader() DataAvailabilityHeader { - s := shares.ToBytes(shares.TailPaddingShares(appconsts.MinShareCount)) + s := MinShares() eds, err := ExtendShares(appconsts.DefaultMinSquareSize, s) if err != nil { panic(err) @@ -179,3 +180,8 @@ func MinDataAvailabilityHeader() DataAvailabilityHeader { dah := NewDataAvailabilityHeader(eds) return dah } + +// MinShares returns one tail-padded share. +func MinShares() [][]byte { + return shares.ToBytes(shares.TailPaddingShares(appconsts.MinShareCount)) +} diff --git a/testing/tokenfilter/setup.go b/testing/tokenfilter/setup.go new file mode 100644 index 0000000000..316a112219 --- /dev/null +++ b/testing/tokenfilter/setup.go @@ -0,0 +1,229 @@ +package tokenfilter + +import ( + "encoding/json" + "testing" + "time" + + "cosmossdk.io/math" + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/ethereum/go-ethereum/common" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + ibctesting "github.com/cosmos/ibc-go/v6/testing" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/ibc-go/v6/testing/mock" + "github.com/cosmos/ibc-go/v6/testing/simapp" +) + +// NewTestChainWithValSet initializes a new TestChain instance with the given validator set +// and signer array. It also initializes 10 Sender accounts with a balance of 10000000000000000000 coins of +// bond denom to use for tests. +// +// The first block height is committed to state in order to allow for client creations on +// counterparty chains. The TestChain will return with a block height starting at 2. +// +// Time management is handled by the Coordinator in order to ensure synchrony between chains. +// Each update of any chain increments the block header time for all chains by 5 seconds. +// +// NOTE: to use a custom sender privkey and account for testing purposes, replace and modify this +// constructor function. +// +// CONTRACT: Validator array must be provided in the order expected by Tendermint. +// i.e. sorted first by power and then lexicographically by address. +func NewTestChainWithValSet(t *testing.T, coord *ibctesting.Coordinator, chainID string, valSet *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *ibctesting.TestChain { + genAccs := []authtypes.GenesisAccount{} + genBals := []banktypes.Balance{} + senderAccs := []ibctesting.SenderAccount{} + + // generate genesis accounts + for i := 0; i < ibctesting.MaxAccounts; i++ { + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), uint64(i), 0) + amount, ok := sdk.NewIntFromString("10000000000000000000") + require.True(t, ok) + + // add sender account + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, amount)), + } + + genAccs = append(genAccs, acc) + genBals = append(genBals, balance) + + senderAcc := ibctesting.SenderAccount{ + SenderAccount: acc, + SenderPrivKey: senderPrivKey, + } + + senderAccs = append(senderAccs, senderAcc) + } + + app := SetupWithGenesisValSet(t, valSet, genAccs, chainID, sdk.DefaultPowerReduction, genBals...) + + // create current header and call begin block + header := tmproto.Header{ + ChainID: chainID, + Height: 1, + Time: coord.CurrentTime.UTC(), + } + + txConfig := app.GetTxConfig() + + chain := &ibctesting.TestChain{ + T: t, + Coordinator: coord, + ChainID: chainID, + App: app, + CurrentHeader: header, + QueryServer: app.GetIBCKeeper(), + TxConfig: txConfig, + Codec: app.AppCodec(), + Vals: valSet, + NextVals: valSet, + Signers: signers, + SenderPrivKey: senderAccs[0].SenderPrivKey, + SenderAccount: senderAccs[0].SenderAccount, + SenderAccounts: senderAccs, + } + + coord.CommitBlock(chain) + + return chain +} + +// NewTestChain initializes a new test chain with a default of 4 validators. +// Use this function if the tests do not need custom control over the validator set. +func NewTestChain(t *testing.T, coord *ibctesting.Coordinator, chainID string) *ibctesting.TestChain { + var ( + validatorsPerChain = 4 + validators []*tmtypes.Validator + signersByAddress = make(map[string]tmtypes.PrivValidator, validatorsPerChain) + ) + + // generate validators private/public key + for i := 0; i < validatorsPerChain; i++ { + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + validators = append(validators, tmtypes.NewValidator(pubKey, 1)) + signersByAddress[pubKey.Address().String()] = privVal + } + + // construct validator set; + // Note that the validators are sorted by voting power + // or, if equal, by address lexical order + valSet := tmtypes.NewValidatorSet(validators) + + return NewTestChainWithValSet(t, coord, chainID, valSet, signersByAddress) +} + +// SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts +// that also act as delegators. For simplicity, each validator is bonded with a delegation +// of one consensus engine unit (10^6) in the default token of the simapp from first genesis +// account. A Nop logger is set in SimApp. +func SetupWithGenesisValSet(t testing.TB, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, powerReduction math.Int, balances ...banktypes.Balance) ibctesting.TestingApp { + db := dbm.NewMemDB() + encCdc := encoding.MakeConfig(app.ModuleEncodingRegisters...) + genesisState := app.NewDefaultGenesisState(encCdc.Codec) + app := app.New( + log.NewNopLogger(), db, nil, true, map[int64]bool{}, app.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}, + ) + + // set genesis accounts + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) + + validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) + delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) + + bondAmt := sdk.TokensFromConsensusPower(1, powerReduction) + + for _, val := range valSet.Validators { + pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) + require.NoError(t, err) + pkAny, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + evmAddress := common.HexToAddress(crypto.CRandHex(common.AddressLength)) + validator := stakingtypes.Validator{ + OperatorAddress: sdk.ValAddress(val.Address).String(), + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondAmt, + DelegatorShares: sdk.OneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + MinSelfDelegation: sdk.ZeroInt(), + EvmAddress: evmAddress.Hex(), + } + + validators = append(validators, validator) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) + } + + // set validators and delegations + var stakingGenesis stakingtypes.GenesisState + app.AppCodec().MustUnmarshalJSON(genesisState[stakingtypes.ModuleName], &stakingGenesis) + + bondDenom := stakingGenesis.Params.BondDenom + + // add bonded amount to bonded pool module account + balances = append(balances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdk.Coins{sdk.NewCoin(bondDenom, bondAmt.Mul(sdk.NewInt(int64(len(valSet.Validators)))))}, + }) + + // set validators and delegations + stakingGenesis = *stakingtypes.NewGenesisState(stakingGenesis.Params, validators, delegations) + genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(&stakingGenesis) + + // update total supply + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(), []banktypes.Metadata{}) + genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // init chain will set the validator set and initialize the genesis accounts + app.InitChain( + abci.RequestInitChain{ + ChainId: chainID, + Validators: []abci.ValidatorUpdate{}, + AppStateBytes: stateBytes, + }, + ) + + // commit genesis changes + app.Commit() + app.BeginBlock( + abci.RequestBeginBlock{ + Header: tmproto.Header{ + ChainID: chainID, + Height: app.LastBlockHeight() + 1, + AppHash: app.LastCommitID().Hash, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + }, + }, + ) + + return app +} diff --git a/testing/tokenfilter/tokenfilter_test.go b/testing/tokenfilter/tokenfilter_test.go new file mode 100644 index 0000000000..f33a5b8ccb --- /dev/null +++ b/testing/tokenfilter/tokenfilter_test.go @@ -0,0 +1,137 @@ +package tokenfilter + +import ( + "testing" + "time" + + "github.com/celestiaorg/celestia-app/app" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v6/testing" + "github.com/stretchr/testify/suite" +) + +type TokenFilterTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // Celestia app including the tokenfilter middleware + celestiaChain *ibctesting.TestChain + + // Default IBC Simapp + otherChain *ibctesting.TestChain +} + +func (suite *TokenFilterTestSuite) SetupTest() { + chains := make(map[string]*ibctesting.TestChain) + suite.coordinator = &ibctesting.Coordinator{ + T: suite.T(), + CurrentTime: time.Now(), + Chains: chains, + } + suite.celestiaChain = NewTestChain(suite.T(), suite.coordinator, ibctesting.GetChainID(1)) + suite.otherChain = ibctesting.NewTestChain(suite.T(), suite.coordinator, ibctesting.GetChainID(2)) + + suite.coordinator.Chains[ibctesting.GetChainID(1)] = suite.celestiaChain + suite.coordinator.Chains[ibctesting.GetChainID(2)] = suite.otherChain +} + +func NewTransferPath(celestiaChain, otherChain *ibctesting.TestChain) *ibctesting.Path { + path := ibctesting.NewPath(celestiaChain, otherChain) + path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort + path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort + path.EndpointA.ChannelConfig.Version = types.Version + path.EndpointB.ChannelConfig.Version = types.Version + + return path +} + +// TestHandleOutboundTransfer asserts that native tokens on a celestia based chain can be transferred to +// another chain and can then return to the original celestia chain +func (suite *TokenFilterTestSuite) TestHandleOutboundTransfer() { + // setup between celestiaChain and otherChain + path := NewTransferPath(suite.celestiaChain, suite.otherChain) + suite.coordinator.Setup(path) + + celestiaApp := suite.celestiaChain.App.(*app.App) + originalBalance := celestiaApp.BankKeeper.GetBalance(suite.celestiaChain.GetContext(), suite.celestiaChain.SenderAccount.GetAddress(), sdk.DefaultBondDenom) + // take half of the original balance + amount := originalBalance.Amount.QuoRaw(2) + timeoutHeight := clienttypes.NewHeight(1, 110) + coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, amount) + + // send half the users balance from celestiaChain to otherChain + msg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.celestiaChain.SenderAccount.GetAddress().String(), suite.otherChain.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + res, err := suite.celestiaChain.SendMsgs(msg) + suite.Require().NoError(err) // message committed + + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + // relay send + err = path.RelayPacket(packet) + suite.Require().NoError(err) // relay committed + + // check that the token exists on chain B + voucherDenomTrace := types.ParseDenomTrace(types.GetPrefixedDenom(packet.GetDestPort(), packet.GetDestChannel(), sdk.DefaultBondDenom)) + balance := suite.otherChain.GetSimApp().BankKeeper.GetBalance(suite.otherChain.GetContext(), suite.otherChain.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom()) + coinSentFromAToB := types.GetTransferCoin(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.DefaultBondDenom, amount) + suite.Require().Equal(coinSentFromAToB, balance) + + // check that the account on celestiaChain has "amount" less tokens than before + intermediateBalance := celestiaApp.BankKeeper.GetBalance(suite.celestiaChain.GetContext(), suite.celestiaChain.SenderAccount.GetAddress(), sdk.DefaultBondDenom) + want := originalBalance.Amount.Sub(coinToSendToB.Amount) + suite.Require().Equal(want, intermediateBalance.Amount) + + // Send the native celestiaChain token on otherChain back to celestiaChain + msg = types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinSentFromAToB, suite.otherChain.SenderAccount.GetAddress().String(), suite.celestiaChain.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + res, err = suite.otherChain.SendMsgs(msg) + suite.Require().NoError(err) // message committed + + packet, err = ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + err = path.RelayPacket(packet) + suite.Require().NoError(err) // relay committed + + // check that the token was sent back i.e. the new balance is equal to the original balance + newBalance := celestiaApp.BankKeeper.GetBalance(suite.celestiaChain.GetContext(), suite.celestiaChain.SenderAccount.GetAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(originalBalance, newBalance) +} + +// TestHandleInboundTransfer asserts that inbound transfers to a celestia chain are rejected when they do not contain +// the celestia native token +func (suite *TokenFilterTestSuite) TestHandleInboundTransfer() { + // setup between celestiaChain and otherChain + path := NewTransferPath(suite.celestiaChain, suite.otherChain) + suite.coordinator.Setup(path) + + amount, ok := sdk.NewIntFromString("1000") + suite.Require().True(ok) + timeoutHeight := clienttypes.NewHeight(1, 110) + coinToSendToA := sdk.NewCoin(sdk.DefaultBondDenom, amount) + + // send from otherChain to celestiaChain + msg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinToSendToA, suite.otherChain.SenderAccount.GetAddress().String(), suite.celestiaChain.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + res, err := suite.otherChain.SendMsgs(msg) + suite.Require().NoError(err) // message committed + + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + // relay send + err = path.RelayPacket(packet) + suite.Require().NoError(err) // relay committed + + // check that the token does not exist on chain A (was rejected) + voucherDenomTrace := types.ParseDenomTrace(types.GetPrefixedDenom(packet.GetDestPort(), packet.GetDestChannel(), sdk.DefaultBondDenom)) + balance := suite.otherChain.GetSimApp().BankKeeper.GetBalance(suite.otherChain.GetContext(), suite.otherChain.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom()) + emptyCoin := sdk.NewInt64Coin(voucherDenomTrace.IBCDenom(), 0) + suite.Require().Equal(emptyCoin, balance) +} + +func TestTokenFilterTestSuite(t *testing.T) { + suite.Run(t, new(TokenFilterTestSuite)) +}