diff --git a/itest/opstackl2/op_e2e_test.go b/itest/opstackl2/op_e2e_test.go index bf11669e..4e3be08a 100644 --- a/itest/opstackl2/op_e2e_test.go +++ b/itest/opstackl2/op_e2e_test.go @@ -4,15 +4,7 @@ package e2etest_op import ( - "encoding/json" "testing" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - opcc "github.com/babylonlabs-io/finality-provider/clientcontroller/opstackl2" - e2eutils "github.com/babylonlabs-io/finality-provider/itest" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" ) // This test case will be removed by the final PR @@ -20,36 +12,9 @@ func TestOpTestManagerSetup(t *testing.T) { ctm := StartOpL2ConsumerManager(t) defer ctm.Stop(t) - // setup logger - config := zap.NewDevelopmentConfig() - config.Level = zap.NewAtomicLevelAt(zapcore.Level(zap.DebugLevel)) - logger, err := config.Build() - require.NoError(t, err) - - // create cosmwasm client - cwConfig := ctm.OpConsumerController.Cfg.ToCosmwasmConfig() - cwClient, err := opcc.NewCwClient(&cwConfig, logger) - require.NoError(t, err) - - // query cw contract config - queryMsg := map[string]interface{}{ - "config": struct{}{}, - } - queryMsgBytes, err := json.Marshal(queryMsg) - require.NoError(t, err) - - var queryConfigResponse *wasmtypes.QuerySmartContractStateResponse - require.Eventually(t, func() bool { - queryConfigResponse, err = cwClient.QuerySmartContractState( - ctm.OpConsumerController.Cfg.OPFinalityGadgetAddress, - string(queryMsgBytes), - ) - return err == nil - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) + // create and register Babylon FP and OP consumer FP + fps := ctm.setupBabylonAndConsumerFp(t) - var cfgResp Config - err = json.Unmarshal(queryConfigResponse.Data, &cfgResp) - require.NoError(t, err) - t.Logf("Response config query from CW contract: %+v", cfgResp) - require.Equal(t, opConsumerChainId, cfgResp.ConsumerId) + // send a BTC delegation and wait for activation + ctm.delegateBTCAndWaitForActivation(t, fps[0], fps[1]) } diff --git a/itest/opstackl2/op_test_manager.go b/itest/opstackl2/op_test_manager.go index a98ab72f..42f0f4ac 100644 --- a/itest/opstackl2/op_test_manager.go +++ b/itest/opstackl2/op_test_manager.go @@ -13,6 +13,7 @@ import ( wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" bbncfg "github.com/babylonlabs-io/babylon/client/config" + bbntypes "github.com/babylonlabs-io/babylon/types" bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" opcc "github.com/babylonlabs-io/finality-provider/clientcontroller/opstackl2" cwclient "github.com/babylonlabs-io/finality-provider/cosmwasmclient/client" @@ -24,8 +25,10 @@ import ( "github.com/babylonlabs-io/finality-provider/metrics" "github.com/babylonlabs-io/finality-provider/testutil/log" "github.com/babylonlabs-io/finality-provider/types" + "github.com/btcsuite/btcd/btcec/v2" sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/lightningnetwork/lnd/signal" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -44,6 +47,9 @@ type OpL2ConsumerTestManager struct { BaseDir string BabylonHandler *e2eutils.BabylonNodeHandler OpConsumerController *opcc.OPStackL2ConsumerController + EOTSServerHandler *e2eutils.EOTSServerHandler + BabylonFpApp *service.FinalityProviderApp + ConsumerFpApp *service.FinalityProviderApp } // Config is the config of the OP finality gadget cw contract @@ -80,8 +86,7 @@ func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { t.Logf(log.Prefix("Register consumer %s to Babylon"), opConsumerChainId) // create cosmwasm client - // consumer FP config will be defined and updated later - _, opConsumerCfg := createConsumerFpConfig(t, testDir, babylonHandler) + consumerFpCfg, opConsumerCfg := createConsumerFpConfig(t, testDir, babylonHandler) cwConfig := opConsumerCfg.ToCosmwasmConfig() cwClient, err := opcc.NewCwClient(&cwConfig, logger) require.NoError(t, err) @@ -93,10 +98,35 @@ func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { // update opConsumerCfg with opFinalityGadgetAddress opConsumerCfg.OPFinalityGadgetAddress = opFinalityGadgetAddress + // update consumer FP config with opConsumerCfg + consumerFpCfg.OPStackL2Config = opConsumerCfg + + // create Babylon FP config + babylonFpCfg := createBabylonFpConfig(t, testDir, babylonHandler) + + // create shutdown interceptor + shutdownInterceptor, err := signal.Intercept() + require.NoError(t, err) + + // create EOTS handler and EOTS gRPC clients for Babylon and consumer + eotsHandler, EOTSClients := base_test_manager.StartEotsManagers(t, logger, testDir, babylonFpCfg, consumerFpCfg, &shutdownInterceptor) + + // create Babylon consumer controller + babylonConsumerController, err := bbncc.NewBabylonConsumerController(babylonFpCfg.BabylonConfig, &babylonFpCfg.BTCNetParams, logger) + require.NoError(t, err) + + // create and start Babylon FP app + babylonFpApp := base_test_manager.CreateAndStartFpApp(t, logger, babylonFpCfg, babylonConsumerController, EOTSClients[0]) + t.Log(log.Prefix("Started Babylon FP App")) + // create op consumer controller opConsumerController, err := opcc.NewOPStackL2ConsumerController(opConsumerCfg, logger) require.NoError(t, err) + // create and start consumer FP app + consumerFpApp := base_test_manager.CreateAndStartFpApp(t, logger, consumerFpCfg, opConsumerController, EOTSClients[1]) + t.Log(log.Prefix("Started Consumer FP App")) + ctm := &OpL2ConsumerTestManager{ BaseTestManager: BaseTestManager{ BBNClient: babylonController, @@ -106,6 +136,9 @@ func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { BaseDir: testDir, BabylonHandler: babylonHandler, OpConsumerController: opConsumerController, + EOTSServerHandler: eotsHandler, + BabylonFpApp: babylonFpApp, + ConsumerFpApp: consumerFpApp, } return ctm @@ -278,6 +311,47 @@ func deployCwContract(t *testing.T, cwClient *cwclient.Client) string { return listContractsResponse.Contracts[0] } +func (ctm *OpL2ConsumerTestManager) setupBabylonAndConsumerFp(t *testing.T) []*bbntypes.BIP340PubKey { + // create and register Babylon FP + babylonFpPk := base_test_manager.CreateAndRegisterFinalityProvider(t, ctm.BabylonFpApp, e2eutils.ChainID) + t.Logf(log.Prefix("Registered Finality Provider %s for %s"), babylonFpPk.MarshalHex(), e2eutils.ChainID) + + // wait for Babylon FP registration + require.Eventually(t, func() bool { + _, err := ctm.BBNClient.QueryFinalityProviders() + return err == nil + }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime, "Failed to wait for Babylon FP registration") + + // create and register consumer FP + consumerFpPk := base_test_manager.CreateAndRegisterFinalityProvider(t, ctm.ConsumerFpApp, opConsumerChainId) + t.Logf(log.Prefix("Registered Finality Provider %s for %s"), consumerFpPk.MarshalHex(), opConsumerChainId) + + // wait for consumer FP registration + require.Eventually(t, func() bool { + _, err := ctm.BBNClient.QueryConsumerFinalityProviders(opConsumerChainId) + return err == nil + }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime, "Failed to wait for consumer FP registration") + + return []*bbntypes.BIP340PubKey{babylonFpPk, consumerFpPk} +} + +func (ctm *OpL2ConsumerTestManager) delegateBTCAndWaitForActivation(t *testing.T, babylonFpPk *bbntypes.BIP340PubKey, consumerFpPk *bbntypes.BIP340PubKey) { + // send a BTC delegation + ctm.InsertBTCDelegation(t, []*btcec.PublicKey{babylonFpPk.MustToBTCPK(), consumerFpPk.MustToBTCPK()}, + e2eutils.StakingTime, e2eutils.StakingAmount) + + // check the BTC delegation is pending + delsResp := ctm.WaitForNPendingDels(t, 1) + del, err := e2eutils.ParseRespBTCDelToBTCDel(delsResp[0]) + require.NoError(t, err) + + // send covenant sigs + ctm.InsertCovenantSigForDelegation(t, del) + + // check the BTC delegation is active + ctm.WaitForNActiveDels(t, 1) +} + func (ctm *OpL2ConsumerTestManager) Stop(t *testing.T) { t.Log("Stopping test manager") var err error diff --git a/itest/test-manager/base_test_manager.go b/itest/test-manager/base_test_manager.go index 16a2b1bd..8b04aa22 100644 --- a/itest/test-manager/base_test_manager.go +++ b/itest/test-manager/base_test_manager.go @@ -2,10 +2,13 @@ package test_manager import ( "encoding/hex" + "fmt" "math/rand" + "path/filepath" "testing" "time" + sdkmath "cosmossdk.io/math" "github.com/babylonlabs-io/babylon/btcstaking" txformat "github.com/babylonlabs-io/babylon/btctxformatter" asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" @@ -15,17 +18,24 @@ import ( btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" + "github.com/babylonlabs-io/finality-provider/clientcontroller/api" + bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" + "github.com/babylonlabs-io/finality-provider/eotsmanager/client" + eotsclient "github.com/babylonlabs-io/finality-provider/eotsmanager/client" + eotsconfig "github.com/babylonlabs-io/finality-provider/eotsmanager/config" + fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" + "github.com/babylonlabs-io/finality-provider/finality-provider/service" e2eutils "github.com/babylonlabs-io/finality-provider/itest" + "github.com/babylonlabs-io/finality-provider/metrics" + "github.com/babylonlabs-io/finality-provider/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" + "github.com/lightningnetwork/lnd/signal" "github.com/stretchr/testify/require" - - bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" - "github.com/babylonlabs-io/finality-provider/finality-provider/service" - "github.com/babylonlabs-io/finality-provider/types" + "go.uber.org/zap" ) type BaseTestManager struct { @@ -533,3 +543,94 @@ func (tm *BaseTestManager) FinalizeUntilEpoch(t *testing.T, epoch uint64) { t.Logf("epoch %d is finalised", epoch) } + +func StartEotsManagers( + t *testing.T, + logger *zap.Logger, + testDir string, + babylonFpCfg *fpcfg.Config, + consumerFpCfg *fpcfg.Config, + shutdownInterceptor *signal.Interceptor, +) (*e2eutils.EOTSServerHandler, []*eotsclient.EOTSManagerGRpcClient) { + fpCfgs := []*fpcfg.Config{babylonFpCfg, consumerFpCfg} + eotsClients := make([]*eotsclient.EOTSManagerGRpcClient, len(fpCfgs)) + eotsHomeDirs := []string{filepath.Join(testDir, "babylon-eots-home"), filepath.Join(testDir, "consumer-eots-home")} + eotsConfigs := make([]*eotsconfig.Config, len(fpCfgs)) + for i := 0; i < len(fpCfgs); i++ { + eotsCfg := eotsconfig.DefaultConfigWithHomePathAndPorts( + eotsHomeDirs[i], + eotsconfig.DefaultRPCPort+i, + metrics.DefaultEotsConfig().Port+i, + ) + eotsConfigs[i] = eotsCfg + } + + eh := e2eutils.NewEOTSServerHandlerMultiFP(t, logger, eotsConfigs, eotsHomeDirs, shutdownInterceptor) + eh.Start() + + // create EOTS clients + for i := 0; i < len(fpCfgs); i++ { + // wait for EOTS servers to start + // see https://github.com/babylonchain/finality-provider/pull/517 + var eotsCli *eotsclient.EOTSManagerGRpcClient + var err error + require.Eventually(t, func() bool { + eotsCli, err = eotsclient.NewEOTSManagerGRpcClient(fpCfgs[i].EOTSManagerAddress) + if err != nil { + t.Logf("Error creating EOTS client: %v", err) + return false + } + eotsClients[i] = eotsCli + return true + }, 5*time.Second, time.Second, "Failed to create EOTS client") + } + return eh, eotsClients +} + +func CreateAndStartFpApp( + t *testing.T, + logger *zap.Logger, + cfg *fpcfg.Config, + cc api.ConsumerController, + eotsCli *client.EOTSManagerGRpcClient, +) *service.FinalityProviderApp { + bc, err := bbncc.NewBabylonController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) + require.NoError(t, err) + + fpdb, err := cfg.DatabaseConfig.GetDbBackend() + require.NoError(t, err) + + fpApp, err := service.NewFinalityProviderApp(cfg, bc, cc, eotsCli, fpdb, logger) + require.NoError(t, err) + + err = fpApp.Start() + require.NoError(t, err) + + return fpApp +} + +func CreateAndRegisterFinalityProvider(t *testing.T, fpApp *service.FinalityProviderApp, chainId string) *bbntypes.BIP340PubKey { + fpCfg := fpApp.GetConfig() + keyName := fpCfg.BabylonConfig.Key + moniker := fmt.Sprintf("%s-%s", chainId, e2eutils.MonikerPrefix) + commission := sdkmath.LegacyZeroDec() + desc := e2eutils.NewDescription(moniker) + + res, err := fpApp.CreateFinalityProvider( + keyName, + chainId, + e2eutils.Passphrase, + e2eutils.HdPath, + nil, + desc, + &commission, + ) + require.NoError(t, err) + + fpPk, err := bbntypes.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) + require.NoError(t, err) + + _, err = fpApp.RegisterFinalityProvider(fpPk.MarshalHex()) + require.NoError(t, err) + return fpPk +}