From 1e03cca1c3cd58e395084c735efea7815f99662d Mon Sep 17 00:00:00 2001 From: Rafael Tenfen Date: Fri, 9 Aug 2024 11:09:17 -0300 Subject: [PATCH] feat: e2e upgrade btc headers (#4) Add upgrade to insert BTC headers Should be merged after #16 --- .github/workflows/ci.yml | 43 ++++++- .github/workflows/publish.yml | 4 +- Makefile | 3 + app/e2e_include_upgrades.go | 7 +- app/test_helpers.go | 22 ++++ app/upgrades/signetlaunch/README.md | 4 + app/upgrades/signetlaunch/data_btc_headers.go | 51 ++++++++ app/upgrades/signetlaunch/upgrades.go | 107 ++++++++++++++++ app/upgrades/signetlaunch/upgrades_test.go | 120 ++++++++++++++++++ app/upgrades/vanilla/upgrades.go | 8 +- .../cmd/genhelpers/set_btc_headers.go | 26 +--- contrib/images/Makefile | 3 + test/e2e/configurer/base.go | 8 +- test/e2e/configurer/chain/chain.go | 22 ++++ .../chain/queries_btclightclient.go | 21 +++ test/e2e/configurer/config/constants.go | 5 +- test/e2e/configurer/current.go | 5 +- test/e2e/configurer/factory.go | 10 +- test/e2e/configurer/upgrade.go | 25 +--- test/e2e/containers/containers.go | 10 +- test/e2e/e2e_test.go | 5 + test/e2e/initialization/chain/main.go | 32 ++++- test/e2e/initialization/config.go | 31 +++-- test/e2e/initialization/init.go | 11 +- test/e2e/initialization/init_test.go | 4 +- ...software_upgrade_e2e_signet_launch_test.go | 72 +++++++++++ ...o => software_upgrade_e2e_vanilla_test.go} | 5 +- test/e2e/upgrades/signet-launch.json | 14 ++ testutil/datagen/btc_header_info.go | 2 +- x/btclightclient/keeper/keeper.go | 105 ++++++++++++--- x/btclightclient/keeper/keeper_test.go | 22 ++-- x/btclightclient/keeper/msg_server.go | 2 +- x/btclightclient/types/genesis.go | 18 +++ x/monitor/keeper/grpc_query_test.go | 4 +- .../keeper/ibc_packet_btc_timestamp_test.go | 2 +- x/zoneconcierge/types/btc_timestamp.go | 2 +- 36 files changed, 722 insertions(+), 113 deletions(-) create mode 100644 app/upgrades/signetlaunch/README.md create mode 100644 app/upgrades/signetlaunch/data_btc_headers.go create mode 100644 app/upgrades/signetlaunch/upgrades.go create mode 100644 app/upgrades/signetlaunch/upgrades_test.go create mode 100644 test/e2e/configurer/chain/queries_btclightclient.go create mode 100644 test/e2e/software_upgrade_e2e_signet_launch_test.go rename test/e2e/{software_upgrade_e2e_test.go => software_upgrade_e2e_vanilla_test.go} (90%) create mode 100644 test/e2e/upgrades/signet-launch.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3c378f44..b301b791a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -252,4 +252,45 @@ jobs: go-version: 1.21 - name: Run e2e TestSoftwareUpgradeTestSuite run: | - sudo make test-e2e-cache-upgrade-vanilla \ No newline at end of file + sudo make test-e2e-cache-upgrade-vanilla + + e2e-run-upgrade-signet: + needs: [e2e-docker-build-babylon, e2e-docker-build-babylon-before-upgrade, e2e-docker-build-e2e-init-chain] + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Download babylon artifact + uses: actions/download-artifact@v4 + with: + name: babylond-${{ github.sha }} + path: /tmp + - name: Download babylond-before-upgrade artifact + uses: actions/download-artifact@v4 + with: + name: babylond-before-upgrade + path: /tmp + - name: Download init-chain artifact + uses: actions/download-artifact@v4 + with: + name: init-chain + path: /tmp + - name: Docker load babylond + run: | + docker load < /tmp/docker-babylond.tar.gz + + - name: Docker load babylond-before-upgrade + run: | + docker load < /tmp/docker-babylond-before-upgrade.tar.gz + + - name: Docker load init chain + run: | + docker load < /tmp/docker-init-chain.tar.gz + + - name: Cache Go + uses: actions/setup-go@v5 + with: + go-version: 1.21 + - name: Run e2e TestSoftwareUpgradeSignetLaunchTestSuite + run: | + sudo make test-e2e-cache-upgrade-signet diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b5c3cdb81..e1081410b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,10 +13,8 @@ jobs: uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.1.0 with: run-unit-tests: true - run-integration-tests: true + run-integration-tests: false run-lint: true - integration-tests-command: | - sudo make test-e2e docker_pipeline: uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.1.0 diff --git a/Makefile b/Makefile index 170ad1640..d1105b12a 100644 --- a/Makefile +++ b/Makefile @@ -277,6 +277,9 @@ test-e2e-cache-btc-staking: test-e2e-cache-upgrade-vanilla: go test -run TestSoftwareUpgradeTestSuite -mod=readonly -timeout=60m -v $(PACKAGES_E2E) --tags=e2e +test-e2e-cache-upgrade-signet: + go test -run TestSoftwareUpgradeSignetLaunchTestSuite -mod=readonly -timeout=60m -v $(PACKAGES_E2E) --tags=e2e + test-sim-nondeterminism: @echo "Running non-determinism test..." @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ diff --git a/app/e2e_include_upgrades.go b/app/e2e_include_upgrades.go index 9d6af8d51..75e5d89de 100644 --- a/app/e2e_include_upgrades.go +++ b/app/e2e_include_upgrades.go @@ -2,8 +2,11 @@ package app -import "github.com/babylonlabs-io/babylon/app/upgrades/vanilla" +import ( + "github.com/babylonlabs-io/babylon/app/upgrades/signetlaunch" + "github.com/babylonlabs-io/babylon/app/upgrades/vanilla" +) func init() { - Upgrades = append(Upgrades, vanilla.Upgrade) + Upgrades = append(Upgrades, vanilla.Upgrade, signetlaunch.Upgrade) } diff --git a/app/test_helpers.go b/app/test_helpers.go index 08ff94d76..5a6d9cbd7 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -1,6 +1,7 @@ package app import ( + "bytes" "encoding/json" "os" "testing" @@ -15,6 +16,7 @@ import ( cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cosmosed "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -35,6 +37,7 @@ import ( "github.com/babylonlabs-io/babylon/crypto/bls12381" "github.com/babylonlabs-io/babylon/privval" bbn "github.com/babylonlabs-io/babylon/types" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" checkpointingtypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" ) @@ -354,3 +357,22 @@ func initAccountWithCoins(app *BabylonApp, ctx sdk.Context, addr sdk.AccAddress, panic(err) } } + +// SignetBtcHeaderGenesis returns the BTC Header block zero from signet. +func SignetBtcHeaderGenesis(cdc codec.Codec) (*btclighttypes.BTCHeaderInfo, error) { + var btcHeaderGenesis btclighttypes.BTCHeaderInfo + // signet btc header 0 + btcHeaderGenesisStr := `{ + "header": "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a008f4d5fae77031e8ad22203", + "hash": "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6", + "work": "77414720" + }` + buff := bytes.NewBufferString(btcHeaderGenesisStr) + + err := cdc.UnmarshalJSON(buff.Bytes(), &btcHeaderGenesis) + if err != nil { + return nil, err + } + + return &btcHeaderGenesis, nil +} diff --git a/app/upgrades/signetlaunch/README.md b/app/upgrades/signetlaunch/README.md new file mode 100644 index 000000000..26c89a050 --- /dev/null +++ b/app/upgrades/signetlaunch/README.md @@ -0,0 +1,4 @@ +# Signet Launch + +This folder contains a software upgrade for testing purposes. +DO NOT USE IN PRODUCTION! diff --git a/app/upgrades/signetlaunch/data_btc_headers.go b/app/upgrades/signetlaunch/data_btc_headers.go new file mode 100644 index 000000000..408de35f3 --- /dev/null +++ b/app/upgrades/signetlaunch/data_btc_headers.go @@ -0,0 +1,51 @@ +package signetlaunch + +const NewBtcHeadersStr = `{ + "btc_headers": [ + { + "header": "00000020f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000f575c83235984e7dc4afc1f30944c170462e84437ab6f2d52e16878a79e4678bd1914d5fae77031eccf40700" + }, + { + "header": "00000020533b53ded9bff4adc94101d32400a144c54edc5ed492a3b26c63b2d686000000b38fef50592017cfafbcab88eb3d9cf50b2c801711cad8299495d26df5e54812e7914d5fae77031ecfdd0b00" + }, + { + "header": "000000202960f3752f0bfa8858a3e333294aedc7808025e868c9dc03e71d88bb320000007765fcd3d5b4966beb338bba2675dc2cf2ad28d4ad1d83bdb6f286e7e27ac1f807924d5fae77031e81d60b00" + }, + { + "header": "00000020b06443a13ae1d3d50faef5ecad38c6818194dc46abca3e972e2aacdae800000069a5829097e80fee00ac49a56ea9f82d741a6af84d32b3bc455cf31871e2a8ac27924d5fae77031e9c910500" + }, + { + "header": "000000207ed403758a4f228a1939418a155e2ebd4ae6b26e5ffd0ae433123f7694010000542e80b609c5bc58af5bdf492e26d4f60cd43a3966c2e063c50444c29b3757a636924d5fae77031ee8601d00" + }, + { + "header": "000000205bea0a88d1422c3df08d766ad72df95084d0700e6f873b75dd4e986c7703000002b57516d33ed60c2bdd9f93d6d5614083324c837e68e5ba6e04287a7285633585924d5fae77031ed1719600" + }, + { + "header": "00000020daf3b60d374b19476461f97540498dcfa2eb7016238ec6b1d022f82fb60100007a7ae65b53cb988c2ec92d2384996713821d5645ffe61c9acea60da75cd5edfa1a944d5fae77031e9dbb0500" + }, + { + "header": "00000020457cc5f3c2e1a5655bc20e20e48d33e1b7ea68786c614032b5c518f0b6000000541f36942d82c6e7248275ff15c8933487fbe1819c67a9ecc0f4b70bb7e6cf672a944d5fae77031e8f398600" + }, + { + "header": "00000020a2eb61eb4f3831baa3a3363e1b42db4462663f756f07423e81ed30322102000077224de7dea0f8d0ec22b1d2e2e255f0a987b96fe7200e1a2e6373f48a2f5b7894954d5fae77031e36867e00" + }, + { + "header": "00000020a868e8514be5e46dabd6a122132f423f36a43b716a40c394e2a8d063e1010000f4c6c717e99d800c699c25a2006a75a0c5c09f432a936f385e6fce139cdbd1a5e9964d5fae77031e7d026e00" + }, + { + "header": "000000205b969d72d19a47f39703c89a0cdb9eada8c4db934064f30e31f89a8e41010000949eef89068ffc76bf4dca6762e26581d410d0df40edf147d4ffdc6dea404a1512984d5fae77031ee67c1200" + }, + { + "header": "000000209410d824b5c57e762922a4035d300bd1d24db4e57b130ff7762ae5df4c030000532299955b2dc6bd7c13c267d3c0990fefdf7aec3bcbab5b2c85d0d36316f93644984d5fae77031ecdea1600" + }, + { + "header": "0000002009f649453a4079cb1d1beb138ea915d2355788bd4689785ecf7a265d3700000010bd26b43a88350e614736674431e62cc7c77dc577d07edd80620a02339d5fab82984d5fae77031efe682400" + }, + { + "header": "0000002035b9ff9157a6e7b5b9ee3807b8246ab687d2ee340f5b0f86cd0e2798aa00000028ef48260b3c0d45bbe5321335b05dde8fcb130e063202457884585298b8a5dde4984d5fae77031ec0a08600" + }, + { + "header": "0000002086102ffb6fd14131a08faa1e87680d5470954ba9638f15b56b7345de500100009f423c63aa6d39330082b58808013a85af5a7f338a7a3587f0a85b587665e6174e9a4d5fae77031e79353a00" + } + ] +}` diff --git a/app/upgrades/signetlaunch/upgrades.go b/app/upgrades/signetlaunch/upgrades.go new file mode 100644 index 000000000..7460a7f2d --- /dev/null +++ b/app/upgrades/signetlaunch/upgrades.go @@ -0,0 +1,107 @@ +// This code is only for testing purposes. +// DO NOT USE IN PRODUCTION! + +package signetlaunch + +import ( + "bytes" + "context" + "errors" + "fmt" + + store "cosmossdk.io/store/types" + upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/btcsuite/btcd/chaincfg" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/babylonlabs-io/babylon/app/keepers" + appparams "github.com/babylonlabs-io/babylon/app/params" + "github.com/babylonlabs-io/babylon/app/upgrades" + bbn "github.com/babylonlabs-io/babylon/types" + btclightkeeper "github.com/babylonlabs-io/babylon/x/btclightclient/keeper" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" +) + +var Upgrade = upgrades.Upgrade{ + UpgradeName: "signet-launch", + CreateUpgradeHandler: CreateUpgradeHandler, + StoreUpgrades: store.StoreUpgrades{}, +} + +// CreateUpgradeHandler upgrade handler for launch. +func CreateUpgradeHandler( + mm *module.Manager, + cfg module.Configurator, + app upgrades.BaseAppParamManager, + keepers *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(context context.Context, _plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + ctx := sdk.UnwrapSDKContext(context) + + migrations, err := mm.RunMigrations(ctx, cfg, fromVM) + if err != nil { + return nil, err + } + + if err := propLaunch(ctx, &keepers.BTCLightClientKeeper); err != nil { + panic(err) + } + + return migrations, nil + } +} + +// propLaunch runs the proposal of launch that is meant to insert new BTC Headers. +func propLaunch( + ctx sdk.Context, + btcLigthK *btclightkeeper.Keeper, +) error { + newHeaders, err := LoadBTCHeadersFromData() + if err != nil { + return err + } + + return insertBtcHeaders(ctx, btcLigthK, newHeaders) +} + +// LoadBTCHeadersFromData returns the BTC headers load from the json string with the headers inside of it. +func LoadBTCHeadersFromData() ([]*btclighttypes.BTCHeaderInfo, error) { + cdc := appparams.DefaultEncodingConfig().Codec + buff := bytes.NewBufferString(NewBtcHeadersStr) + + var gs btclighttypes.GenesisState + err := cdc.UnmarshalJSON(buff.Bytes(), &gs) + if err != nil { + return nil, err + } + + return gs.BtcHeaders, nil +} + +func insertBtcHeaders( + ctx sdk.Context, + k *btclightkeeper.Keeper, + btcHeaders []*btclighttypes.BTCHeaderInfo, +) error { + if len(btcHeaders) == 0 { + return errors.New("no headers to insert") + } + + headersBytes := make([]bbn.BTCHeaderBytes, len(btcHeaders)) + for i, btcHeader := range btcHeaders { + h := btcHeader + headersBytes[i] = *h.Header + } + + if err := k.InsertHeaders(ctx, headersBytes); err != nil { + return err + } + + allBlocks := k.GetMainChainFromWithLimit(ctx, 0, 1) + isRetarget := btclighttypes.IsRetargetBlock(allBlocks[0], &chaincfg.SigNetParams) + if !isRetarget { + return fmt.Errorf("first header be a difficulty adjustment block") + } + return nil +} diff --git a/app/upgrades/signetlaunch/upgrades_test.go b/app/upgrades/signetlaunch/upgrades_test.go new file mode 100644 index 000000000..caf1409a1 --- /dev/null +++ b/app/upgrades/signetlaunch/upgrades_test.go @@ -0,0 +1,120 @@ +package signetlaunch_test + +import ( + "fmt" + "testing" + "time" + + "cosmossdk.io/core/appmodule" + "cosmossdk.io/core/header" + "cosmossdk.io/x/upgrade" + upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/babylonlabs-io/babylon/app" + v1 "github.com/babylonlabs-io/babylon/app/upgrades/signetlaunch" + "github.com/babylonlabs-io/babylon/x/btclightclient" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" +) + +const ( + DummyUpgradeHeight = 5 +) + +type UpgradeTestSuite struct { + suite.Suite + + ctx sdk.Context + app *app.BabylonApp + preModule appmodule.HasPreBlocker +} + +func (s *UpgradeTestSuite) SetupTest() { + // add the upgrade plan + app.Upgrades = append(app.Upgrades, v1.Upgrade) + + // set up app + s.app = app.Setup(s.T(), false) + s.ctx = s.app.BaseApp.NewContextLegacy(false, tmproto.Header{Height: 1, ChainID: "babylon-1", Time: time.Now().UTC()}) + s.preModule = upgrade.NewAppModule(s.app.UpgradeKeeper, s.app.AccountKeeper.AddressCodec()) + + btcHeaderGenesis, err := app.SignetBtcHeaderGenesis(s.app.EncodingConfig().Codec) + s.NoError(err) + + k := s.app.BTCLightClientKeeper + btclightclient.InitGenesis(s.ctx, s.app.BTCLightClientKeeper, btclighttypes.GenesisState{ + Params: k.GetParams(s.ctx), + BtcHeaders: []*btclighttypes.BTCHeaderInfo{btcHeaderGenesis}, + }) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(UpgradeTestSuite)) +} + +func (s *UpgradeTestSuite) TestUpgrade() { + oldHeadersLen := 0 + + testCases := []struct { + msg string + pre_update func() + update func() + post_update func() + }{ + { + "Test launch software upgrade gov prop", + func() { + allBtcHeaders := s.app.BTCLightClientKeeper.GetMainChainFrom(s.ctx, 0) + oldHeadersLen = len(allBtcHeaders) + }, + func() { + // inject upgrade plan + s.ctx = s.ctx.WithBlockHeight(DummyUpgradeHeight - 1) + plan := upgradetypes.Plan{Name: v1.Upgrade.UpgradeName, Height: DummyUpgradeHeight} + err := s.app.UpgradeKeeper.ScheduleUpgrade(s.ctx, plan) + s.NoError(err) + + // ensure upgrade plan exists + actualPlan, err := s.app.UpgradeKeeper.GetUpgradePlan(s.ctx) + s.NoError(err) + s.Equal(plan, actualPlan) + + // execute upgrade + s.ctx = s.ctx.WithHeaderInfo(header.Info{Height: DummyUpgradeHeight, Time: s.ctx.BlockTime().Add(time.Second)}).WithBlockHeight(DummyUpgradeHeight) + s.NotPanics(func() { + _, err := s.preModule.PreBlock(s.ctx) + s.NoError(err) + }) + }, + func() { + // ensure the btc headers were added + allBtcHeaders := s.app.BTCLightClientKeeper.GetMainChainFrom(s.ctx, 0) + + btcHeadersInserted, err := v1.LoadBTCHeadersFromData() + s.NoError(err) + lenHeadersInserted := len(btcHeadersInserted) + + newHeadersLen := len(allBtcHeaders) + s.Equal(newHeadersLen, oldHeadersLen+lenHeadersInserted) + + // ensure the headers were inserted as expected + for i, btcHeaderInserted := range btcHeadersInserted { + btcHeaderInState := allBtcHeaders[oldHeadersLen+i] + + s.EqualValues(btcHeaderInserted.Header.MarshalHex(), btcHeaderInState.Header.MarshalHex()) + } + }, + }, + } + + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case %s", tc.msg), func() { + s.SetupTest() // reset + + tc.pre_update() + tc.update() + tc.post_update() + }) + } +} diff --git a/app/upgrades/vanilla/upgrades.go b/app/upgrades/vanilla/upgrades.go index cb01322e5..99522243e 100644 --- a/app/upgrades/vanilla/upgrades.go +++ b/app/upgrades/vanilla/upgrades.go @@ -32,12 +32,16 @@ func CreateUpgradeHandler( keepers *keepers.AppKeepers, ) upgradetypes.UpgradeHandler { return func(context context.Context, _plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - ctx := sdk.UnwrapSDKContext(context) + migrations, err := mm.RunMigrations(ctx, cfg, fromVM) + if err != nil { + return nil, err + } + propVanilla(ctx, &keepers.AccountKeeper, &keepers.BTCStakingKeeper) - return mm.RunMigrations(ctx, cfg, fromVM) + return migrations, nil } } diff --git a/cmd/babylond/cmd/genhelpers/set_btc_headers.go b/cmd/babylond/cmd/genhelpers/set_btc_headers.go index db30b2f04..7df48e4fd 100644 --- a/cmd/babylond/cmd/genhelpers/set_btc_headers.go +++ b/cmd/babylond/cmd/genhelpers/set_btc_headers.go @@ -2,12 +2,8 @@ package genhelpers import ( "fmt" - "os" - "path/filepath" - cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -51,7 +47,7 @@ Possible content of 'btc_headers.json' is config := server.GetServerContextFromCmd(cmd).Config config.SetRoot(clientCtx.HomeDir) - inputBtcHeaders, err := getBtcLightGenStateFromFile(clientCtx.Codec, args[0]) + inputBtcHeaders, err := btclighttypes.LoadBtcLightGenStateFromFile(clientCtx.Codec, args[0]) if err != nil { return err } @@ -102,23 +98,3 @@ Possible content of 'btc_headers.json' is return cmd } - -func getBtcLightGenStateFromFile(cdc codec.Codec, inputFilePath string) (*btclighttypes.GenesisState, error) { - filePath := filepath.Clean(inputFilePath) - if !cmtos.FileExists(filePath) { - return nil, fmt.Errorf("input file %s does not exists", filePath) - } - - bz, err := os.ReadFile(filePath) - if err != nil { - return nil, err - } - - var genState btclighttypes.GenesisState - err = cdc.UnmarshalJSON(bz, &genState) - if err != nil { - return nil, err - } - - return &genState, nil -} diff --git a/contrib/images/Makefile b/contrib/images/Makefile index 5c9892904..47febc890 100644 --- a/contrib/images/Makefile +++ b/contrib/images/Makefile @@ -29,6 +29,9 @@ e2e-init-chain: @DOCKER_BUILDKIT=1 docker build -t babylonlabs-io/babylond-e2e-init-chain --build-arg E2E_SCRIPT_NAME=chain --platform=linux/x86_64 \ -f e2e-initialization/init.Dockerfile ${BABYLON_FULL_PATH} +e2e-init-chain-rmi: + docker rmi babylonlabs-io/babylond-e2e-init-chain 2>/dev/null; true + cosmos-relayer: cosmos-relayer-rmi docker build --tag babylonlabs-io/cosmos-relayer:${RELAYER_TAG} -f cosmos-relayer/Dockerfile \ ${BABYLON_FULL_PATH}/contrib/images/cosmos-relayer diff --git a/test/e2e/configurer/base.go b/test/e2e/configurer/base.go index de34baa2e..38ae526db 100644 --- a/test/e2e/configurer/base.go +++ b/test/e2e/configurer/base.go @@ -40,11 +40,11 @@ const defaultSyncUntilHeight = 3 func (bc *baseConfigurer) ClearResources() error { bc.t.Log("tearing down e2e integration test suite...") - g := new(errgroup.Group) - g.Go(func() error { - return bc.containerManager.ClearResources() - }) + if err := bc.containerManager.ClearResources(); err != nil { + return err + } + g := new(errgroup.Group) for _, chainConfig := range bc.chainConfigs { chainConfig := chainConfig g.Go(func() error { diff --git a/test/e2e/configurer/chain/chain.go b/test/e2e/configurer/chain/chain.go index c0a5ef195..137d002e0 100644 --- a/test/e2e/configurer/chain/chain.go +++ b/test/e2e/configurer/chain/chain.go @@ -1,7 +1,9 @@ package chain import ( + "encoding/hex" "fmt" + "strings" "testing" "time" @@ -15,6 +17,7 @@ import ( "github.com/babylonlabs-io/babylon/test/e2e/configurer/config" "github.com/babylonlabs-io/babylon/test/e2e/containers" "github.com/babylonlabs-io/babylon/test/e2e/initialization" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" ) type Config struct { @@ -29,6 +32,7 @@ type Config struct { LatestProposalNumber int LatestLockNumber int NodeConfigs []*NodeConfig + BTCHeaders []*btclighttypes.BTCHeaderInfo IBCConfig *ibctesting.ChannelConfig LatestCodeId int @@ -59,6 +63,7 @@ func New(t *testing.T, containerManager *containers.Manager, id string, initVali ExpeditedVotingPeriod: config.PropDepositBlocks + numVal*config.PropVoteBlocks + config.PropBufferBlocks - 2, t: t, containerManager: containerManager, + BTCHeaders: []*btclighttypes.BTCHeaderInfo{}, } } @@ -182,3 +187,20 @@ func (c *Config) TxGovVoteFromAllNodes(propID int, option govv1.VoteOption, over n.TxGovVote(n.WalletName, propID, option, overallFlags...) } } + +// BTCHeaderBytesHexJoined join all the btc headers as byte string hex +func (c *Config) BTCHeaderBytesHexJoined() string { + if c.BTCHeaders == nil || len(c.BTCHeaders) == 0 { + return "" + } + + strBtcHeaders := make([]string, len(c.BTCHeaders)) + for i, btcHeader := range c.BTCHeaders { + bz, err := btcHeader.Marshal() + if err != nil { + panic(err) + } + strBtcHeaders[i] = hex.EncodeToString(bz) + } + return strings.Join(strBtcHeaders, ",") +} diff --git a/test/e2e/configurer/chain/queries_btclightclient.go b/test/e2e/configurer/chain/queries_btclightclient.go new file mode 100644 index 000000000..2c4cfb570 --- /dev/null +++ b/test/e2e/configurer/chain/queries_btclightclient.go @@ -0,0 +1,21 @@ +package chain + +import ( + "net/url" + + "github.com/stretchr/testify/require" + + "github.com/babylonlabs-io/babylon/test/e2e/util" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" +) + +func (n *NodeConfig) QueryBtcLightClientMainchain() []*btclighttypes.BTCHeaderInfoResponse { + bz, err := n.QueryGRPCGateway("/babylon/btclightclient/v1/mainchain", url.Values{}) + require.NoError(n.t, err) + + var resp btclighttypes.QueryMainChainResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.Headers +} diff --git a/test/e2e/configurer/config/constants.go b/test/e2e/configurer/config/constants.go index 72ee9e9b4..75a5815c5 100644 --- a/test/e2e/configurer/config/constants.go +++ b/test/e2e/configurer/config/constants.go @@ -15,6 +15,7 @@ const ( MaxRetries = 60 // PropSubmitBlocks estimated number of blocks it takes to submit for a proposal PropSubmitBlocks float32 = 1 - // VanillaUpgradeFilePath upgrade vanilla testing - VanillaUpgradeFilePath = "/upgrades/vanilla.json" + // Upgrade prop files json + UpgradeVanillaFilePath = "/upgrades/vanilla.json" + UpgradeSignetLaunchFilePath = "/upgrades/signet-launch.json" ) diff --git a/test/e2e/configurer/current.go b/test/e2e/configurer/current.go index 2bb4c9081..cd463ba99 100644 --- a/test/e2e/configurer/current.go +++ b/test/e2e/configurer/current.go @@ -50,7 +50,10 @@ func (cb *CurrentBranchConfigurer) ConfigureChain(chainConfig *chain.Config) err tmpDir, chainConfig.ValidatorInitConfigs, time.Duration(chainConfig.VotingPeriod*1000000000), - time.Duration(chainConfig.ExpeditedVotingPeriod*1000000000), 0) + time.Duration(chainConfig.ExpeditedVotingPeriod*1000000000), + 0, + chainConfig.BTCHeaders, + ) if err != nil { return err } diff --git a/test/e2e/configurer/factory.go b/test/e2e/configurer/factory.go index f849b2693..1580903f7 100644 --- a/test/e2e/configurer/factory.go +++ b/test/e2e/configurer/factory.go @@ -10,6 +10,7 @@ import ( "github.com/babylonlabs-io/babylon/test/e2e/configurer/chain" "github.com/babylonlabs-io/babylon/test/e2e/containers" "github.com/babylonlabs-io/babylon/test/e2e/initialization" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" zctypes "github.com/babylonlabs-io/babylon/x/zoneconcierge/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) @@ -208,17 +209,22 @@ func NewBTCStakingConfigurer(t *testing.T, isDebugLogEnabled bool) (Configurer, } // NewSoftwareUpgradeConfigurer returns a new Configurer for Software Upgrade testing -func NewSoftwareUpgradeConfigurer(t *testing.T, isDebugLogEnabled bool, upgradePath string) (Configurer, error) { +func NewSoftwareUpgradeConfigurer(t *testing.T, isDebugLogEnabled bool, upgradePath string, btcHeaders []*btclighttypes.BTCHeaderInfo) (Configurer, error) { identifier := identifierName(t) containerManager, err := containers.NewManager(identifier, isDebugLogEnabled, false, true) if err != nil { return nil, err } + chainA := chain.New(t, containerManager, initialization.ChainAID, updateNodeConfigNameWithIdentifier(validatorConfigsChainA, identifier), nil) + if btcHeaders != nil { + chainA.BTCHeaders = btcHeaders + } + return NewUpgradeConfigurer(t, []*chain.Config{ // we only need 1 chain for testing upgrade - chain.New(t, containerManager, initialization.ChainAID, updateNodeConfigNameWithIdentifier(validatorConfigsChainA, identifier), nil), + chainA, }, withUpgrade(baseSetup), // base set up with upgrade containerManager, diff --git a/test/e2e/configurer/upgrade.go b/test/e2e/configurer/upgrade.go index 60ea7ded0..1218f242b 100644 --- a/test/e2e/configurer/upgrade.go +++ b/test/e2e/configurer/upgrade.go @@ -96,7 +96,10 @@ func (uc *UpgradeConfigurer) ConfigureChain(chainConfig *chain.Config) error { forkHeight = forkHeight - config.ForkHeightPreUpgradeOffset } - chainInitResource, err := uc.containerManager.RunChainInitResource(chainConfig.Id, int(chainConfig.VotingPeriod), int(chainConfig.ExpeditedVotingPeriod), validatorConfigBytes, tmpDir, int(forkHeight)) + chainInitResource, err := uc.containerManager.RunChainInitResource( + chainConfig.Id, int(chainConfig.VotingPeriod), int(chainConfig.ExpeditedVotingPeriod), + validatorConfigBytes, tmpDir, int(forkHeight), chainConfig.BTCHeaderBytesHexJoined(), + ) if err != nil { return err } @@ -238,26 +241,8 @@ func (uc *UpgradeConfigurer) upgradeContainers(chainConfig *chain.Config, propHe uc.t.Logf("starting upgrade for chain-id: %s...", chainConfig.Id) uc.containerManager.CurrentRepository = containers.BabylonContainerName - errCh := make(chan error, len(chainConfig.NodeConfigs)) - var wg sync.WaitGroup - for _, node := range chainConfig.NodeConfigs { - wg.Add(1) - go func(node *chain.NodeConfig) { - defer wg.Done() - if err := node.Run(); err != nil { - errCh <- err - } - }(node) - } - - // Wait for all goroutines to complete - wg.Wait() - close(errCh) - - // Check if any of the goroutines returned an error - for err := range errCh { - if err != nil { + if err := node.Run(); err != nil { return err } } diff --git a/test/e2e/containers/containers.go b/test/e2e/containers/containers.go index 143afbb80..cd09f1aec 100644 --- a/test/e2e/containers/containers.go +++ b/test/e2e/containers/containers.go @@ -357,7 +357,14 @@ func noRestart(config *docker.HostConfig) { // The genesis and configs are to be mounted on the init container as volume on mountDir path. // Returns the container resource and error if any. This method does not Purge the container. The caller // must deal with removing the resource. -func (m *Manager) RunChainInitResource(chainId string, chainVotingPeriod, chainExpeditedVotingPeriod int, validatorConfigBytes []byte, mountDir string, forkHeight int) (*dockertest.Resource, error) { +func (m *Manager) RunChainInitResource( + chainId string, + chainVotingPeriod, chainExpeditedVotingPeriod int, + validatorConfigBytes []byte, + mountDir string, + forkHeight int, + btcHeaders string, +) (*dockertest.Resource, error) { votingPeriodDuration := time.Duration(chainVotingPeriod * 1000000000) expeditedVotingPeriodDuration := time.Duration(chainExpeditedVotingPeriod * 1000000000) @@ -373,6 +380,7 @@ func (m *Manager) RunChainInitResource(chainId string, chainVotingPeriod, chainE fmt.Sprintf("--voting-period=%v", votingPeriodDuration), fmt.Sprintf("--expedited-voting-period=%v", expeditedVotingPeriodDuration), fmt.Sprintf("--fork-height=%v", forkHeight), + fmt.Sprintf("--btc-headers=%s", btcHeaders), }, User: "root:root", Mounts: []string{ diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index cc78d6b15..801e6d511 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -40,3 +40,8 @@ func TestBTCStakingTestSuite(t *testing.T) { func TestSoftwareUpgradeTestSuite(t *testing.T) { suite.Run(t, new(SoftwareUpgradeVanillaTestSuite)) } + +// TestSoftwareUpgradeSignetLaunchTestSuite tests software upgrade of signet launch end-to-end +func TestSoftwareUpgradeSignetLaunchTestSuite(t *testing.T) { + suite.Run(t, new(SoftwareUpgradeSignetLaunchTestSuite)) +} diff --git a/test/e2e/initialization/chain/main.go b/test/e2e/initialization/chain/main.go index 6d22e8e78..9f251c9f9 100644 --- a/test/e2e/initialization/chain/main.go +++ b/test/e2e/initialization/chain/main.go @@ -1,13 +1,16 @@ package main import ( + "encoding/hex" "encoding/json" "flag" "fmt" "os" + "strings" "time" "github.com/babylonlabs-io/babylon/test/e2e/initialization" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" ) func main() { @@ -16,6 +19,7 @@ func main() { dataDir string chainId string config string + btcHeadersBytesHexStr string votingPeriod time.Duration expeditedVotingPeriod time.Duration forkHeight int @@ -24,6 +28,7 @@ func main() { flag.StringVar(&dataDir, "data-dir", "", "chain data directory") flag.StringVar(&chainId, "chain-id", "", "chain ID") flag.StringVar(&config, "config", "", "serialized config") + flag.StringVar(&btcHeadersBytesHexStr, "btc-headers", "", "btc header bytes comma separated") flag.DurationVar(&votingPeriod, "voting-period", 30000000000, "voting period") flag.DurationVar(&expeditedVotingPeriod, "expedited-voting-period", 20000000000, "expedited voting period") flag.IntVar(&forkHeight, "fork-height", 0, "fork height") @@ -43,7 +48,8 @@ func main() { panic(err) } - createdChain, err := initialization.InitChain(chainId, dataDir, valConfig, votingPeriod, expeditedVotingPeriod, forkHeight) + btcHeaders := btcHeaderFromFlag(btcHeadersBytesHexStr) + createdChain, err := initialization.InitChain(chainId, dataDir, valConfig, votingPeriod, expeditedVotingPeriod, forkHeight, btcHeaders) if err != nil { panic(err) } @@ -54,3 +60,27 @@ func main() { panic(err) } } + +func btcHeaderFromFlag(btcHeadersBytesHexStr string) []*btclighttypes.BTCHeaderInfo { + btcHeaders := []*btclighttypes.BTCHeaderInfo{} + if len(btcHeadersBytesHexStr) == 0 { + return btcHeaders + } + + btcHeadersBytesHex := strings.Split(btcHeadersBytesHexStr, ",") + for _, btcHeaderBytesHex := range btcHeadersBytesHex { + btcHeaderBytes, err := hex.DecodeString(btcHeaderBytesHex) + if err != nil { + panic(err) + } + + btcHeader := &btclighttypes.BTCHeaderInfo{} + err = btcHeader.Unmarshal(btcHeaderBytes) + if err != nil { + panic(err) + } + + btcHeaders = append(btcHeaders, btcHeader) + } + return btcHeaders +} diff --git a/test/e2e/initialization/config.go b/test/e2e/initialization/config.go index 930f9d66a..cb074552d 100644 --- a/test/e2e/initialization/config.go +++ b/test/e2e/initialization/config.go @@ -27,6 +27,7 @@ import ( bbn "github.com/babylonlabs-io/babylon/types" btccheckpointtypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" blctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" checkpointingtypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" "github.com/babylonlabs-io/babylon/test/e2e/util" @@ -168,7 +169,12 @@ func updateModuleGenesis[V proto.Message](appGenState map[string]json.RawMessage return nil } -func initGenesis(chain *internalChain, votingPeriod, expeditedVotingPeriod time.Duration, forkHeight int) error { +func initGenesis( + chain *internalChain, + votingPeriod, expeditedVotingPeriod time.Duration, + forkHeight int, + btcHeaders []*btclighttypes.BTCHeaderInfo, +) error { // initialize a genesis file configDir := chain.nodes[0].configDir() @@ -248,7 +254,7 @@ func initGenesis(chain *internalChain, votingPeriod, expeditedVotingPeriod time. return err } - err = updateModuleGenesis(appGenState, blctypes.ModuleName, blctypes.DefaultGenesis(), updateBtcLightClientGenesis) + err = updateModuleGenesis(appGenState, blctypes.ModuleName, blctypes.DefaultGenesis(), updateBtcLightClientGenesis(btcHeaders)) if err != nil { return err } @@ -322,14 +328,21 @@ func updateCrisisGenesis(crisisGenState *crisistypes.GenesisState) { crisisGenState.ConstantFee.Denom = BabylonDenom } -func updateBtcLightClientGenesis(blcGenState *blctypes.GenesisState) { - btcSimnetGenesisHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a45068653ffff7f2002000000" - baseBtcHeader, err := bbn.NewBTCHeaderBytesFromHex(btcSimnetGenesisHex) - if err != nil { - panic(err) +func updateBtcLightClientGenesis(btcHeaders []*btclighttypes.BTCHeaderInfo) func(blcGenState *blctypes.GenesisState) { + return func(blcGenState *btclighttypes.GenesisState) { + if len(btcHeaders) > 0 { + blcGenState.BtcHeaders = btcHeaders + return + } + + btcSimnetGenesisHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a45068653ffff7f2002000000" + baseBtcHeader, err := bbn.NewBTCHeaderBytesFromHex(btcSimnetGenesisHex) + if err != nil { + panic(err) + } + work := blctypes.CalcWork(&baseBtcHeader) + blcGenState.BtcHeaders = []*blctypes.BTCHeaderInfo{blctypes.NewBTCHeaderInfo(&baseBtcHeader, baseBtcHeader.Hash(), 0, &work)} } - work := blctypes.CalcWork(&baseBtcHeader) - blcGenState.BtcHeaders = []*blctypes.BTCHeaderInfo{blctypes.NewBTCHeaderInfo(&baseBtcHeader, baseBtcHeader.Hash(), 0, &work)} } func updateBtccheckpointGenesis(btccheckpointGenState *btccheckpointtypes.GenesisState) { diff --git a/test/e2e/initialization/init.go b/test/e2e/initialization/init.go index 94e8ca3e5..af3b9e7bb 100644 --- a/test/e2e/initialization/init.go +++ b/test/e2e/initialization/init.go @@ -8,9 +8,16 @@ import ( appkeepers "github.com/babylonlabs-io/babylon/app/keepers" "github.com/babylonlabs-io/babylon/test/e2e/util" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" ) -func InitChain(id, dataDir string, nodeConfigs []*NodeConfig, votingPeriod, expeditedVotingPeriod time.Duration, forkHeight int) (*Chain, error) { +func InitChain( + id, dataDir string, + nodeConfigs []*NodeConfig, + votingPeriod, expeditedVotingPeriod time.Duration, + forkHeight int, + btcHeaders []*btclighttypes.BTCHeaderInfo, +) (*Chain, error) { chain, err := new(id, dataDir) if err != nil { return nil, err @@ -24,7 +31,7 @@ func InitChain(id, dataDir string, nodeConfigs []*NodeConfig, votingPeriod, expe chain.nodes = append(chain.nodes, newNode) } - if err := initGenesis(chain, votingPeriod, expeditedVotingPeriod, forkHeight); err != nil { + if err := initGenesis(chain, votingPeriod, expeditedVotingPeriod, forkHeight, btcHeaders); err != nil { return nil, err } diff --git a/test/e2e/initialization/init_test.go b/test/e2e/initialization/init_test.go index 8d21b1b4d..a4ba49dab 100644 --- a/test/e2e/initialization/init_test.go +++ b/test/e2e/initialization/init_test.go @@ -51,7 +51,7 @@ func TestChainInit(t *testing.T) { dataDir, err = os.MkdirTemp("", "bbn-e2e-testnet-test") ) - chain, err := initialization.InitChain(id, dataDir, nodeConfigs, time.Second*3, time.Second, forkHeight) + chain, err := initialization.InitChain(id, dataDir, nodeConfigs, time.Second*3, time.Second, forkHeight, nil) require.NoError(t, err) require.Equal(t, chain.ChainMeta.DataDir, dataDir) @@ -109,7 +109,7 @@ func TestSingleNodeInit(t *testing.T) { ) // Setup - existingChain, err := initialization.InitChain(id, dataDir, existingChainNodeConfigs, time.Second*3, time.Second, forkHeight) + existingChain, err := initialization.InitChain(id, dataDir, existingChainNodeConfigs, time.Second*3, time.Second, forkHeight, nil) require.NoError(t, err) actualNode, err := initialization.InitSingleNode(existingChain.ChainMeta.Id, dataDir, filepath.Join(existingChain.Nodes[0].ConfigDir, "config", "genesis.json"), expectedConfig, time.Second*3, 3, "testHash", []string{"some server"}, []string{"some server"}) diff --git a/test/e2e/software_upgrade_e2e_signet_launch_test.go b/test/e2e/software_upgrade_e2e_signet_launch_test.go new file mode 100644 index 000000000..96c85980c --- /dev/null +++ b/test/e2e/software_upgrade_e2e_signet_launch_test.go @@ -0,0 +1,72 @@ +package e2e + +import ( + "github.com/stretchr/testify/suite" + + "github.com/babylonlabs-io/babylon/app" + v1 "github.com/babylonlabs-io/babylon/app/upgrades/signetlaunch" + btclighttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + + "github.com/babylonlabs-io/babylon/test/e2e/configurer" + "github.com/babylonlabs-io/babylon/test/e2e/configurer/config" +) + +type SoftwareUpgradeSignetLaunchTestSuite struct { + suite.Suite + + configurer configurer.Configurer +} + +func (s *SoftwareUpgradeSignetLaunchTestSuite) SetupSuite() { + s.T().Log("setting up e2e integration test suite...") + var err error + + btcHeaderGenesis, err := app.SignetBtcHeaderGenesis(app.NewTmpBabylonApp().AppCodec()) + s.NoError(err) + s.configurer, err = configurer.NewSoftwareUpgradeConfigurer(s.T(), false, config.UpgradeSignetLaunchFilePath, []*btclighttypes.BTCHeaderInfo{btcHeaderGenesis}) + s.NoError(err) + err = s.configurer.ConfigureChains() + s.NoError(err) + err = s.configurer.RunSetup() // upgrade happens at the setup of configurer. + s.NoError(err) +} + +func (s *SoftwareUpgradeSignetLaunchTestSuite) TearDownSuite() { + err := s.configurer.ClearResources() + s.Require().NoError(err) +} + +// TestUpgradeSignetLaunch Checks if the BTC Headers were inserted. +func (s *SoftwareUpgradeSignetLaunchTestSuite) TestUpgradeSignetLaunch() { + // chain is already upgraded, only checks for differences in state are expected + chainA := s.configurer.GetChainConfig(0) + chainA.WaitUntilHeight(30) // five blocks more than upgrade + + n, err := chainA.GetDefaultNode() + s.NoError(err) + + expectedUpgradeHeight := int64(25) + + // makes sure that the upgrade was actually executed + resp := n.QueryAppliedPlan(v1.Upgrade.UpgradeName) + s.EqualValues(expectedUpgradeHeight, resp.Height, "the plan should be applied at the height 25") + + btcHeadersInserted, err := v1.LoadBTCHeadersFromData() + s.NoError(err) + + lenHeadersInserted := len(btcHeadersInserted) + oldHeadersStoredLen := 1 // only block zero is set by default in genesis for e2e test + + storedBtcHeadersResp := n.QueryBtcLightClientMainchain() + storedHeadersLen := len(storedBtcHeadersResp) + s.Equal(storedHeadersLen, oldHeadersStoredLen+lenHeadersInserted) + + // ensure the headers were inserted at the end + for i := 0; i < lenHeadersInserted; i++ { + headerInserted := btcHeadersInserted[i] + reversedStoredIndex := storedHeadersLen - (oldHeadersStoredLen + i + 1) + headerStoredResp := storedBtcHeadersResp[reversedStoredIndex] // reverse reading + + s.EqualValues(headerInserted.Header.MarshalHex(), headerStoredResp.HeaderHex) + } +} diff --git a/test/e2e/software_upgrade_e2e_test.go b/test/e2e/software_upgrade_e2e_vanilla_test.go similarity index 90% rename from test/e2e/software_upgrade_e2e_test.go rename to test/e2e/software_upgrade_e2e_vanilla_test.go index 8018065d8..5dba61c1f 100644 --- a/test/e2e/software_upgrade_e2e_test.go +++ b/test/e2e/software_upgrade_e2e_vanilla_test.go @@ -3,6 +3,7 @@ package e2e import ( "github.com/stretchr/testify/suite" + v1 "github.com/babylonlabs-io/babylon/app/upgrades/vanilla" "github.com/babylonlabs-io/babylon/test/e2e/configurer" "github.com/babylonlabs-io/babylon/test/e2e/configurer/config" ) @@ -17,7 +18,7 @@ func (s *SoftwareUpgradeVanillaTestSuite) SetupSuite() { s.T().Log("setting up e2e integration test suite...") var err error - s.configurer, err = configurer.NewSoftwareUpgradeConfigurer(s.T(), false, config.VanillaUpgradeFilePath) + s.configurer, err = configurer.NewSoftwareUpgradeConfigurer(s.T(), false, config.UpgradeVanillaFilePath, nil) s.NoError(err) err = s.configurer.ConfigureChains() s.NoError(err) @@ -44,7 +45,7 @@ func (s *SoftwareUpgradeVanillaTestSuite) TestUpgradeVanilla() { expectedUpgradeHeight := int64(25) // makes sure that the upgrade was actually executed - resp := n.QueryAppliedPlan("vanilla") + resp := n.QueryAppliedPlan(v1.Upgrade.UpgradeName) s.EqualValues(expectedUpgradeHeight, resp.Height, "the plan should be applied at the height 25") fps := n.QueryFinalityProviders() diff --git a/test/e2e/upgrades/signet-launch.json b/test/e2e/upgrades/signet-launch.json new file mode 100644 index 000000000..de4638b31 --- /dev/null +++ b/test/e2e/upgrades/signet-launch.json @@ -0,0 +1,14 @@ +{ + "title": "any title", + "summary": "any summary", + "messages": [ + { + "@type": "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade", + "authority": "bbn10d07y265gmmuvt4z0w9aw880jnsr700jduz5f2", + "plan": { "name": "signet-launch", "info": "Msg info", "height": 25 } + } + ], + "deposit": "500000000ubbn", + "initial_deposit": "500000000ubbn", + "initialDeposit": "500000000ubbn" +} \ No newline at end of file diff --git a/testutil/datagen/btc_header_info.go b/testutil/datagen/btc_header_info.go index 91230a1dd..c680acdef 100644 --- a/testutil/datagen/btc_header_info.go +++ b/testutil/datagen/btc_header_info.go @@ -294,7 +294,7 @@ func GenRandBtcChainInsertingInKeeper( genesisHeaderInfo, uint32(chainLength), ) - err := k.InsertHeaders(ctx, randomChain.ChainToBytes()) + err := k.InsertHeadersWithHookAndEvents(ctx, randomChain.ChainToBytes()) require.NoError(t, err) tip := k.GetTipInfo(ctx) randomChainTipInfo := randomChain.GetTipInfo() diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 64b7508ba..f3df98e84 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -75,11 +75,66 @@ func (k *Keeper) SetHooks(bh types.BTCLightClientHooks) *Keeper { return k } +func (k Keeper) insertHandler() func(ctx context.Context, s headersState, result *types.InsertResult) error { + return func(ctx context.Context, s headersState, result *types.InsertResult) error { + // if we receive rollback, should return error + if result.RollbackInfo != nil { + return fmt.Errorf("rollback should not happend %+v", result.RollbackInfo) + } + + for _, header := range result.HeadersToInsert { + h := header + s.insertHeader(h) + } + return nil + } +} + +func (k Keeper) triggerEventAndHandleHooksHandler() func(ctx context.Context, s headersState, result *types.InsertResult) error { + return func(ctx context.Context, s headersState, result *types.InsertResult) error { + // if we have rollback, first delete all headers up to the rollback point + if result.RollbackInfo != nil { + // roll back to the height + s.rollBackHeadersUpTo(result.RollbackInfo.HeaderToRollbackTo.Height) + // trigger rollback event + k.triggerRollBack(ctx, result.RollbackInfo.HeaderToRollbackTo) + } + + for _, header := range result.HeadersToInsert { + h := header + s.insertHeader(h) + k.triggerHeaderInserted(ctx, h) + k.triggerRollForward(ctx, h) + } + return nil + } +} + +func (k Keeper) insertHeadersWithHookAndEvents( + ctx context.Context, + headers []*wire.BlockHeader) error { + return k.insertHeadersInternal( + ctx, + headers, + k.triggerEventAndHandleHooksHandler(), + ) +} + func (k Keeper) insertHeaders( + ctx context.Context, + headers []*wire.BlockHeader) error { + return k.insertHeadersInternal( + ctx, + headers, + k.insertHandler(), + ) +} + +func (k Keeper) insertHeadersInternal( ctx context.Context, headers []*wire.BlockHeader, + handleInsertResult func(ctx context.Context, s headersState, result *types.InsertResult) error, ) error { - headerState := k.headersState(ctx) result, err := k.bl.InsertHeaders( @@ -91,21 +146,7 @@ func (k Keeper) insertHeaders( return err } - // if we have rollback, first delete all headers up to the rollback point - if result.RollbackInfo != nil { - // roll back to the height - headerState.rollBackHeadersUpTo(result.RollbackInfo.HeaderToRollbackTo.Height) - // trigger rollback event - k.triggerRollBack(ctx, result.RollbackInfo.HeaderToRollbackTo) - } - - for _, header := range result.HeadersToInsert { - h := header - headerState.insertHeader(h) - k.triggerHeaderInserted(ctx, h) - k.triggerRollForward(ctx, h) - } - return nil + return handleInsertResult(ctx, headerState, result) } // InsertHeaderInfos inserts multiple headers info at the store. @@ -116,17 +157,31 @@ func (k Keeper) InsertHeaderInfos(ctx context.Context, infos []*types.BTCHeaderI } } +func (k Keeper) InsertHeadersWithHookAndEvents(ctx context.Context, headers []bbn.BTCHeaderBytes) error { + if len(headers) == 0 { + return types.ErrEmptyMessage + } + + blockHeaders := btcHeadersBytesToBlockHeader(headers) + return k.insertHeadersWithHookAndEvents(ctx, blockHeaders) +} + func (k Keeper) InsertHeaders(ctx context.Context, headers []bbn.BTCHeaderBytes) error { if len(headers) == 0 { return types.ErrEmptyMessage } + blockHeaders := btcHeadersBytesToBlockHeader(headers) + return k.insertHeaders(ctx, blockHeaders) +} + +func btcHeadersBytesToBlockHeader(headers []bbn.BTCHeaderBytes) []*wire.BlockHeader { blockHeaders := make([]*wire.BlockHeader, len(headers)) for i, header := range headers { blockHeaders[i] = header.ToBlockHeader() } - return k.insertHeaders(ctx, blockHeaders) + return blockHeaders } // BlockHeight returns the height of the provided header @@ -206,6 +261,22 @@ func (k Keeper) GetMainChainFrom(ctx context.Context, startHeight uint64) []*typ return headers } +// GetMainChainFromWithLimit returns the current canonical chain from the given height up to the tip +// If the height is higher than the tip, it returns an empty slice +// If startHeight is 0, it returns the entire main chain +func (k Keeper) GetMainChainFromWithLimit(ctx context.Context, startHeight, limit uint64) []*types.BTCHeaderInfo { + headers := make([]*types.BTCHeaderInfo, 0, limit) + fn := func(header *types.BTCHeaderInfo) bool { + if len(headers) >= int(limit) { + return true + } + headers = append(headers, header) + return false + } + k.headersState(ctx).IterateForwardHeaders(startHeight, fn) + return headers +} + // GetMainChainUpTo returns the current canonical chain as a collection of block headers // starting from the tip and ending on the header that has `depth` distance from it. func (k Keeper) GetMainChainUpTo(ctx context.Context, depth uint64) []*types.BTCHeaderInfo { diff --git a/x/btclightclient/keeper/keeper_test.go b/x/btclightclient/keeper/keeper_test.go index 90ddeaa96..896ebf88a 100644 --- a/x/btclightclient/keeper/keeper_test.go +++ b/x/btclightclient/keeper/keeper_test.go @@ -160,7 +160,7 @@ func FuzzKeeperInsertValidChainExtension(f *testing.F) { extendedChainWork := oldTip.Work.Add(*chainExtensionWork) extendedChainHeight := uint64(uint32(oldTip.Height) + newChainLength) - err := blcKeeper.InsertHeaders(ctx, keepertest.NewBTCHeaderBytesList(chainToInsert)) + err := blcKeeper.InsertHeadersWithHookAndEvents(ctx, keepertest.NewBTCHeaderBytesList(chainToInsert)) require.NoError(t, err) // updated tip @@ -247,7 +247,7 @@ func FuzzKeeperInsertValidBetterChain(f *testing.F) { require.True(t, len(removedBranch) > 0) - err := blcKeeper.InsertHeaders(ctx, keepertest.NewBTCHeaderBytesList(chainToInsert)) + err := blcKeeper.InsertHeadersWithHookAndEvents(ctx, keepertest.NewBTCHeaderBytesList(chainToInsert)) require.NoError(t, err) // updated tip @@ -332,16 +332,16 @@ func FuzzKeeperInsertInvalidChain(f *testing.F) { require.NotNil(t, currentTip) // Inserting nil headers should result with error - errNil := blcKeeper.InsertHeaders(ctx, nil) + errNil := blcKeeper.InsertHeadersWithHookAndEvents(ctx, nil) require.Error(t, errNil) // Inserting empty headers should result with error - errEmpty := blcKeeper.InsertHeaders(ctx, []bbn.BTCHeaderBytes{}) + errEmpty := blcKeeper.InsertHeadersWithHookAndEvents(ctx, []bbn.BTCHeaderBytes{}) require.Error(t, errEmpty) // Inserting header without existing parent should result with error chain := datagen.NewBTCHeaderChainWithLength(r, 0, 0, 10) - errNoParent := blcKeeper.InsertHeaders(ctx, chain.ChainToBytes()[1:]) + errNoParent := blcKeeper.InsertHeadersWithHookAndEvents(ctx, chain.ChainToBytes()[1:]) require.Error(t, errNoParent) require.True(t, errors.Is(errNoParent, types.ErrHeaderParentDoesNotExist)) @@ -358,7 +358,7 @@ func FuzzKeeperInsertInvalidChain(f *testing.F) { // bump the nonce, it should fail validation and tip should not change chainToInsert[3].Nonce = chainToInsert[3].Nonce + 1 - errInvalidHeader := blcKeeper.InsertHeaders(ctx, keepertest.NewBTCHeaderBytesList(chainToInsert)) + errInvalidHeader := blcKeeper.InsertHeadersWithHookAndEvents(ctx, keepertest.NewBTCHeaderBytesList(chainToInsert)) require.Error(t, errInvalidHeader) newTip := blcKeeper.GetTipInfo(ctx) // tip did not change @@ -374,7 +374,7 @@ func FuzzKeeperInsertInvalidChain(f *testing.F) { nil, 1, ) - errWorseChain := blcKeeper.InsertHeaders(ctx, keepertest.NewBTCHeaderBytesList(worseChain)) + errWorseChain := blcKeeper.InsertHeadersWithHookAndEvents(ctx, keepertest.NewBTCHeaderBytesList(worseChain)) require.Error(t, errWorseChain) require.True(t, errors.Is(errWorseChain, types.ErrChainWithNotEnoughWork)) }) @@ -409,13 +409,13 @@ func FuzzKeeperValdateHeaderAtDifficultyAdjustmentBoundaries(f *testing.F) { // this will always fail as last header is at adjustment boundary, but we created // it without adjustment - err := blcKeeper.InsertHeaders(ctx, randomChain.ChainToBytes()) + err := blcKeeper.InsertHeadersWithHookAndEvents(ctx, randomChain.ChainToBytes()) require.Error(t, err) randomChainWithoutLastHeader := randomChain.Headers[:len(randomChain.Headers)-1] chain := keepertest.NewBTCHeaderBytesList(randomChainWithoutLastHeader) // now all headers are valid, and we are below adjustment boundary - err = blcKeeper.InsertHeaders(ctx, chain) + err = blcKeeper.InsertHeadersWithHookAndEvents(ctx, chain) require.NoError(t, err) currentTip := blcKeeper.GetTipInfo(ctx) @@ -429,7 +429,7 @@ func FuzzKeeperValdateHeaderAtDifficultyAdjustmentBoundaries(f *testing.F) { nil, ) // try to insert header at adjustment boundary without adjustment should fail - err = blcKeeper.InsertHeaders(ctx, []bbn.BTCHeaderBytes{bbn.NewBTCHeaderBytesFromBlockHeader(invalidAdjustedHeader)}) + err = blcKeeper.InsertHeadersWithHookAndEvents(ctx, []bbn.BTCHeaderBytes{bbn.NewBTCHeaderBytesFromBlockHeader(invalidAdjustedHeader)}) require.Error(t, err) // Inserting valid adjusted header should succeed @@ -446,7 +446,7 @@ func FuzzKeeperValdateHeaderAtDifficultyAdjustmentBoundaries(f *testing.F) { ) validAdjustedHeaderBytes := bbn.NewBTCHeaderBytesFromBlockHeader(validAdjustedHeader) - err = blcKeeper.InsertHeaders(ctx, []bbn.BTCHeaderBytes{bbn.NewBTCHeaderBytesFromBlockHeader(validAdjustedHeader)}) + err = blcKeeper.InsertHeadersWithHookAndEvents(ctx, []bbn.BTCHeaderBytes{bbn.NewBTCHeaderBytesFromBlockHeader(validAdjustedHeader)}) require.NoError(t, err) newTip := blcKeeper.GetTipInfo(ctx) diff --git a/x/btclightclient/keeper/msg_server.go b/x/btclightclient/keeper/msg_server.go index 90de17942..b40880e5f 100644 --- a/x/btclightclient/keeper/msg_server.go +++ b/x/btclightclient/keeper/msg_server.go @@ -47,7 +47,7 @@ func (m msgServer) InsertHeaders(ctx context.Context, msg *types.MsgInsertHeader return nil, types.ErrUnauthorizedReporter.Wrapf("reporter %s is not authorized to insert headers", reporterAddress) } - err := m.k.InsertHeaders(sdkCtx, msg.Headers) + err := m.k.InsertHeadersWithHookAndEvents(sdkCtx, msg.Headers) if err != nil { return nil, err diff --git a/x/btclightclient/types/genesis.go b/x/btclightclient/types/genesis.go index 13835fa2b..f8555fdcf 100644 --- a/x/btclightclient/types/genesis.go +++ b/x/btclightclient/types/genesis.go @@ -4,6 +4,8 @@ import ( "encoding/json" "errors" "fmt" + "os" + "path/filepath" bbn "github.com/babylonlabs-io/babylon/types" "github.com/btcsuite/btcd/chaincfg" @@ -86,3 +88,19 @@ func GenesisStateFromAppState(cdc codec.Codec, appState map[string]json.RawMessa return genesisState } + +func LoadBtcLightGenStateFromFile(cdc codec.Codec, inputFilePath string) (*GenesisState, error) { + filePath := filepath.Clean(inputFilePath) + bz, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } + + var genState GenesisState + err = cdc.UnmarshalJSON(bz, &genState) + if err != nil { + return nil, err + } + + return &genState, nil +} diff --git a/x/monitor/keeper/grpc_query_test.go b/x/monitor/keeper/grpc_query_test.go index 53ea0862f..9f47095b4 100644 --- a/x/monitor/keeper/grpc_query_test.go +++ b/x/monitor/keeper/grpc_query_test.go @@ -41,7 +41,7 @@ func FuzzQueryEndedEpochBtcHeight(f *testing.F) { 10, ) headerBytes := datagen.HeaderToHeaderBytes(chain) - err := lck.InsertHeaders(ctx, headerBytes) + err := lck.InsertHeadersWithHookAndEvents(ctx, headerBytes) require.NoError(t, err) // go to BeginBlock of block 11, and thus entering epoch 2 @@ -98,7 +98,7 @@ func FuzzQueryReportedCheckpointBtcHeight(f *testing.F) { 10, ) headerBytes := datagen.HeaderToHeaderBytes(chain) - err := lck.InsertHeaders(ctx, headerBytes) + err := lck.InsertHeadersWithHookAndEvents(ctx, headerBytes) require.NoError(t, err) // Add checkpoint diff --git a/x/zoneconcierge/keeper/ibc_packet_btc_timestamp_test.go b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp_test.go index 7a2d1a7c6..014203fe5 100644 --- a/x/zoneconcierge/keeper/ibc_packet_btc_timestamp_test.go +++ b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp_test.go @@ -32,7 +32,7 @@ func genRandomChain( initHeader, uint32(chainLength), ) - err := k.InsertHeaders(ctx, randomChain.ChainToBytes()) + err := k.InsertHeadersWithHookAndEvents(ctx, randomChain.ChainToBytes()) require.NoError(t, err) tip := k.GetTipInfo(ctx) randomChainTipInfo := randomChain.GetTipInfo() diff --git a/x/zoneconcierge/types/btc_timestamp.go b/x/zoneconcierge/types/btc_timestamp.go index c1cde804e..d54b04355 100644 --- a/x/zoneconcierge/types/btc_timestamp.go +++ b/x/zoneconcierge/types/btc_timestamp.go @@ -273,7 +273,7 @@ func (ts *BTCTimestamp) Verify( headerBytes := bbn.NewBTCHeaderBytesFromBlockHeader(headerInfo.Header.ToBlockHeader()) headersBytes = append(headersBytes, headerBytes) } - if err := btclcKeeper.InsertHeaders(ctx, headersBytes); err != nil { + if err := btclcKeeper.InsertHeadersWithHookAndEvents(ctx, headersBytes); err != nil { return err }