diff --git a/Makefile b/Makefile index 84b53cba..15e8f6fb 100644 --- a/Makefile +++ b/Makefile @@ -112,8 +112,9 @@ test-e2e-bcd: clean-e2e install-babylond install-bcd test-e2e-wasmd: clean-e2e install-babylond install-wasmd @go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e_wasmd +FILTER ?= . test-e2e-op: clean-e2e install-babylond - @go test -race -mod=readonly -timeout=25m -v $(PACKAGES_E2E_OP) -count=1 --tags=e2e_op + @go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E_OP) -count=1 --tags=e2e_op --run ^$(FILTER)$ test-e2e-op-ci: clean-e2e install-babylond go test -list . ./itest/opstackl2 --tags=e2e_op | grep Test \ diff --git a/finality-provider/config/opstackl2.go b/finality-provider/config/opstackl2.go index 17aa510c..eb7505a6 100644 --- a/finality-provider/config/opstackl2.go +++ b/finality-provider/config/opstackl2.go @@ -11,8 +11,9 @@ import ( ) type OPStackL2Config struct { - OPStackL2RPCAddress string `long:"opstackl2-rpc-address" description:"the rpc address of the op-stack-l2 node to connect to"` - OPFinalityGadgetAddress string `long:"op-finality-gadget" description:"the contract address of the op-finality-gadget"` + OPStackL2RPCAddress string `long:"opstackl2-rpc-address" description:"the rpc address of the op-stack-l2 node to connect to"` + OPFinalityGadgetAddress string `long:"op-finality-gadget" description:"the contract address of the op-finality-gadget"` + BabylonFinalityGadgetRpc string `long:"babylon-finality-gadget-rpc" description:"the rpc address of babylon op finality gadget"` // Below configurations are needed for the Babylon client Key string `long:"key" description:"name of the babylon key to sign transactions with"` ChainID string `long:"chain-id" description:"chain id of the babylon chain to connect to"` @@ -41,6 +42,12 @@ func (cfg *OPStackL2Config) Validate() error { if !strings.HasPrefix(cfg.OPFinalityGadgetAddress, cfg.AccountPrefix) { return fmt.Errorf("op-finality-gadget: invalid address prefix: %w", err) } + if cfg.BabylonFinalityGadgetRpc == "" { + return fmt.Errorf("babylon-finality-gadget-rpc is required") + } + if _, err := url.Parse(cfg.BabylonFinalityGadgetRpc); err != nil { + return fmt.Errorf("babylon-finality-gadget-rpc is not correctly formatted: %w", err) + } if _, err := url.Parse(cfg.RPCAddr); err != nil { return fmt.Errorf("rpc-addr is not correctly formatted: %w", err) } diff --git a/go.mod b/go.mod index 2dad27ae..37d950cd 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/CosmWasm/wasmd v0.52.0 github.com/avast/retry-go/v4 v4.5.1 github.com/babylonlabs-io/babylon v0.9.0 - github.com/babylonlabs-io/babylon-finality-gadget v0.0.0-20240801002122-6d6bca47ec56 github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240802071655-9fd2ddd5158b + github.com/babylonlabs-io/finality-gadget v0.1.0 github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.5 @@ -383,7 +383,7 @@ require ( github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect - go.etcd.io/bbolt v1.3.8 // indirect + go.etcd.io/bbolt v1.3.10 // indirect go.etcd.io/etcd/api/v3 v3.5.12 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect go.etcd.io/etcd/client/v2 v2.305.12 // indirect @@ -442,8 +442,9 @@ replace ( // use cosmos fork of keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 github.com/babylonlabs-io/babylon => github.com/babylonlabs-io/babylon-private v0.9.0-rc.3.0.20240801001431-74a24c962ce2 + github.com/babylonlabs-io/finality-gadget => github.com/babylonlabs-io/finality-gadget v0.0.0-20240810191700-9ddea258bce5 github.com/cockroachdb/pebble => github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4 - github.com/ethereum-optimism/optimism => github.com/babylonlabs-io/optimism v1.8.1-0.20240731214220-e7d71bf38866 + github.com/ethereum-optimism/optimism => github.com/babylonlabs-io/optimism v1.8.1-0.20240808190817-3279057d5250 github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101315.1-rc.5 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 diff --git a/go.sum b/go.sum index 500709ca..0a1c74cc 100644 --- a/go.sum +++ b/go.sum @@ -296,16 +296,16 @@ github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX github.com/aws/aws-sdk-go v1.44.312 h1:llrElfzeqG/YOLFFKjg1xNpZCFJ2xraIi3PqSuP+95k= github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/babylonlabs-io/babylon-finality-gadget v0.0.0-20240801002122-6d6bca47ec56 h1:xZ+x4nfezT99jbBYDfqxQx+KBmqICbJtu8WLrjS1sVY= -github.com/babylonlabs-io/babylon-finality-gadget v0.0.0-20240801002122-6d6bca47ec56/go.mod h1:IZUMNT4MCzKPzsniq5EeNITWy/HeNX5k8hPoJNjFSqQ= github.com/babylonlabs-io/babylon-private v0.9.0-rc.3.0.20240801001431-74a24c962ce2 h1:sFsAJkrYe1eIkKzWTn4sj7G6QrPQ3IK33/UlOVgeA5M= github.com/babylonlabs-io/babylon-private v0.9.0-rc.3.0.20240801001431-74a24c962ce2/go.mod h1:pyFZgF85gUhfiCJKbYjnxo5+9prDkti48tW4FDEu9js= github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240802071655-9fd2ddd5158b h1:RctaFr4Svn/gbTa6FkPi78Jkm81J/NFFtp2V2ogMyl0= github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240802071655-9fd2ddd5158b/go.mod h1:8t6U0E4bCQlRQQSKIXNTpTOjACVihJd5wdMLxL55jFE= github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240802071655-9fd2ddd5158b h1:DeP6CzLozsd1MFSOf/Xo3FoBqROW6YZ8fq5ayR3KPwM= github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240802071655-9fd2ddd5158b/go.mod h1:fCwb573m5JgCpuAZ+MK3Kx4WIV+q60TaPRu/m8fCcr0= -github.com/babylonlabs-io/optimism v1.8.1-0.20240731214220-e7d71bf38866 h1:FjrkthFS4JRGCckPh/uTdznhqwQOzjD8JJ0blcrS1VY= -github.com/babylonlabs-io/optimism v1.8.1-0.20240731214220-e7d71bf38866/go.mod h1:MAppo9HosEUwyUCNnvJxJb3GQr5iP72HYb0Xv5YY4JE= +github.com/babylonlabs-io/finality-gadget v0.0.0-20240810191700-9ddea258bce5 h1:PgJJS7ae2fFJKRRmb5epXabwuVl0SYIDIRz4n+NUNG0= +github.com/babylonlabs-io/finality-gadget v0.0.0-20240810191700-9ddea258bce5/go.mod h1:+BICJA7mUJpfYOSYds1G5w6nLO0gKVe9XwdmpIUiXmk= +github.com/babylonlabs-io/optimism v1.8.1-0.20240808190817-3279057d5250 h1:3FNsa55pbil/Ff7NICtaZzcdLwRs9HsCXJNmW7hDzPs= +github.com/babylonlabs-io/optimism v1.8.1-0.20240808190817-3279057d5250/go.mod h1:ZZVrpyP7weyHNHnN2Wr6A4lnMpqcJHyL5Ke7LQ0zzHs= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -1679,8 +1679,8 @@ github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfU github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= diff --git a/itest/eotsmanager_handler.go b/itest/eotsmanager_handler.go index 5e65e470..f0b38f3e 100644 --- a/itest/eotsmanager_handler.go +++ b/itest/eotsmanager_handler.go @@ -19,11 +19,8 @@ type EOTSServerHandler struct { } func NewEOTSServerHandlerMultiFP( - t *testing.T, configs []*config.Config, eotsHomeDirs []string, logger *zap.Logger, + t *testing.T, configs []*config.Config, eotsHomeDirs []string, logger *zap.Logger, shutdownInterceptor *signal.Interceptor, ) *EOTSServerHandler { - shutdownInterceptor, err := signal.Intercept() - require.NoError(t, err) - eotsServers := make([]*service.Server, 0, len(configs)) for i, cfg := range configs { dbBackend, err := cfg.DatabaseConfig.GetDbBackend() @@ -32,21 +29,24 @@ func NewEOTSServerHandlerMultiFP( eotsManager, err := eotsmanager.NewLocalEOTSManager(eotsHomeDirs[i], cfg.KeyringBackend, dbBackend, logger) require.NoError(t, err) - eotsServer := service.NewEOTSManagerServer(cfg, logger, eotsManager, dbBackend, shutdownInterceptor) + eotsServer := service.NewEOTSManagerServer(cfg, logger, eotsManager, dbBackend, *shutdownInterceptor) eotsServers = append(eotsServers, eotsServer) } return &EOTSServerHandler{ t: t, eotsServers: eotsServers, - interceptor: &shutdownInterceptor, + interceptor: shutdownInterceptor, } } func NewEOTSServerHandler(t *testing.T, cfg *config.Config, eotsHomeDir string) *EOTSServerHandler { // TODO: no-op logger makes it hard to debug. replace w real logger. + // create shutdown interceptor + shutdownInterceptor, err := signal.Intercept() + require.NoError(t, err) // this need refactor of NewEOTSServerHandler - return NewEOTSServerHandlerMultiFP(t, []*config.Config{cfg}, []string{eotsHomeDir}, zap.NewNop()) + return NewEOTSServerHandlerMultiFP(t, []*config.Config{cfg}, []string{eotsHomeDir}, zap.NewNop(), &shutdownInterceptor) } func (eh *EOTSServerHandler) Start() { diff --git a/itest/opstackl2/README.md b/itest/opstackl2/README.md index bcd495f9..c90deccd 100644 --- a/itest/opstackl2/README.md +++ b/itest/opstackl2/README.md @@ -6,10 +6,11 @@ To run the e2e tests, first you need to set up the devnet data: $ make op-e2e-devnet ``` -Next, replace `babylonFinalityGadgetBitcoinRpc` in `itest/opstackl2/devnet-data/devnetL1.json` with your own Bitcoin mainnet RPC URL to avoid potential rate limit issues. - Then run the following command to start the e2e tests: ```bash $ make test-e2e-op -``` \ No newline at end of file + +# Filter specific test +$ make test-e2e-op Filter=TestFinalityGadget +``` diff --git a/itest/opstackl2/op_e2e_test.go b/itest/opstackl2/op_e2e_test.go index 445c7fe9..ec39acca 100644 --- a/itest/opstackl2/op_e2e_test.go +++ b/itest/opstackl2/op_e2e_test.go @@ -4,12 +4,12 @@ package e2etest_op import ( + "context" "encoding/hex" "testing" "time" - sdkclient "github.com/babylonlabs-io/babylon-finality-gadget/sdk/client" - "github.com/babylonlabs-io/babylon-finality-gadget/sdk/cwclient" + fgtypes "github.com/babylonlabs-io/finality-gadget/types" e2eutils "github.com/babylonlabs-io/finality-provider/itest" "github.com/babylonlabs-io/finality-provider/testutil/log" "github.com/stretchr/testify/require" @@ -37,7 +37,7 @@ func TestOpSubmitFinalitySignature(t *testing.T) { // wait for the fp sign ctm.WaitForFpVoteReachHeight(t, fpInstance, testBlock.Height) - queryParams := cwclient.L2Block{ + queryBlock := &fgtypes.Block{ BlockHeight: testBlock.Height, BlockHash: hex.EncodeToString(testBlock.Hash), BlockTimestamp: 12345, // doesn't matter b/c the BTC client is mocked @@ -45,8 +45,8 @@ func TestOpSubmitFinalitySignature(t *testing.T) { // note: QueryFinalityProviderHasPower is hardcode to return true so FPs can still submit finality sigs even if they // don't have voting power. But the finality sigs will not be counted at tally time. - _, err = ctm.SdkClient.QueryIsBlockBabylonFinalized(queryParams) - require.ErrorIs(t, err, sdkclient.ErrNoFpHasVotingPower) + _, err = ctm.FinalityGadget.QueryIsBlockBabylonFinalized(queryBlock) + require.ErrorIs(t, err, fgtypes.ErrBtcStakingNotActivated) t.Logf(log.Prefix("Expected no voting power")) } @@ -88,12 +88,12 @@ func TestOpMultipleFinalityProviders(t *testing.T) { testBlock, err := ctm.getOpCCAtIndex(1).QueryBlock(targetBlockHeight) require.NoError(t, err) - queryParams := cwclient.L2Block{ + queryBlock := &fgtypes.Block{ BlockHeight: testBlock.Height, BlockHash: hex.EncodeToString(testBlock.Hash), BlockTimestamp: 12345, // doesn't matter b/c the BTC client is mocked } - finalized, err := ctm.SdkClient.QueryIsBlockBabylonFinalized(queryParams) + finalized, err := ctm.FinalityGadget.QueryIsBlockBabylonFinalized(queryBlock) require.NoError(t, err) require.Equal(t, true, finalized) t.Logf(log.Prefix("Test case 1: block %d is finalized"), testBlock.Height) @@ -112,13 +112,13 @@ func TestOpMultipleFinalityProviders(t *testing.T) { testNextBlock, err := ctm.getOpCCAtIndex(1).QueryBlock(testNextBlockHeight) require.NoError(t, err) - queryNextParams := cwclient.L2Block{ + queryNextBlock := &fgtypes.Block{ BlockHeight: testNextBlock.Height, BlockHash: hex.EncodeToString(testNextBlock.Hash), BlockTimestamp: 12345, // doesn't matter b/c the BTC client is mocked } // testNextBlock only have 1/4 total voting power - nextFinalized, err := ctm.SdkClient.QueryIsBlockBabylonFinalized(queryNextParams) + nextFinalized, err := ctm.FinalityGadget.QueryIsBlockBabylonFinalized(queryNextBlock) require.NoError(t, err) require.Equal(t, false, nextFinalized) t.Logf(log.Prefix("Test case 2: block %d is not finalized"), testNextBlock.Height) @@ -185,3 +185,58 @@ func TestFinalityStuckAndRecover(t *testing.T) { "OP chain fianlity is recovered, the latest finalized block height %d", ), nextFinalizedHeight) } + +func TestFinalityGadgetServer(t *testing.T) { + // start the consumer manager + ctm := StartOpL2ConsumerManager(t, 2) + defer ctm.Stop(t) + + // register, get BTC delegations, and start FPs + n := 2 + fpList := ctm.SetupFinalityProviders(t, n, []stakingParam{ + // for the first FP, we give it more power b/c it will be used later + {e2eutils.StakingTime, 3 * e2eutils.StakingAmount}, + {e2eutils.StakingTime, e2eutils.StakingAmount}, + }) + + // check both FPs have committed their first public randomness + // TODO: we might use go routine to do this in parallel + for i := 0; i < n; i++ { + e2eutils.WaitForFpPubRandCommitted(t, fpList[i]) + } + + // wait until the BTC staking is activated + l2BlockAfterActivation := ctm.waitForBTCStakingActivation(t) + + // both FP will sign the first block + targetBlockHeight := ctm.WaitForTargetBlockPubRand(t, fpList, l2BlockAfterActivation) + ctm.WaitForFpVoteReachHeight(t, fpList[0], targetBlockHeight) + ctm.WaitForFpVoteReachHeight(t, fpList[1], targetBlockHeight) + t.Logf(log.Prefix("Both FP instances signed the first block")) + + // both FP will sign the second block + ctm.WaitForFpVoteReachHeight(t, fpList[0], targetBlockHeight+1) + ctm.WaitForFpVoteReachHeight(t, fpList[1], targetBlockHeight+1) + t.Logf(log.Prefix("Both FP instances signed the second block")) + + // start finality gadget processing blocks + ctx, cancel := context.WithCancel(context.Background()) + go func() { + t.Logf(log.Prefix("Starting finality gadget")) + err := ctm.FinalityGadget.ProcessBlocks(ctx) + require.NoError(t, err) + }() + + // check latest block + require.Eventually(t, func() bool { + block, err := ctm.FinalityGadget.QueryLatestFinalizedBlock() + if block == nil { + return false + } + require.NoError(t, err) + return block.BlockHeight > targetBlockHeight+6 + }, 40*time.Second, 5*time.Second, "Failed to process blocks") + + // stop the finality gadget + cancel() +} diff --git a/itest/opstackl2/op_test_manager.go b/itest/opstackl2/op_test_manager.go index 2156d18d..44b565f9 100644 --- a/itest/opstackl2/op_test_manager.go +++ b/itest/opstackl2/op_test_manager.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + defaultlog "log" "os" "path/filepath" "strings" @@ -14,11 +15,13 @@ import ( "time" sdkmath "cosmossdk.io/math" - "github.com/babylonlabs-io/babylon-finality-gadget/sdk/btcclient" - sdkclient "github.com/babylonlabs-io/babylon-finality-gadget/sdk/client" - sdkcfg "github.com/babylonlabs-io/babylon-finality-gadget/sdk/config" bbncfg "github.com/babylonlabs-io/babylon/client/config" bbntypes "github.com/babylonlabs-io/babylon/types" + fgclient "github.com/babylonlabs-io/finality-gadget/client" + fgcfg "github.com/babylonlabs-io/finality-gadget/config" + "github.com/babylonlabs-io/finality-gadget/db" + "github.com/babylonlabs-io/finality-gadget/finalitygadget" + fgsrv "github.com/babylonlabs-io/finality-gadget/server" api "github.com/babylonlabs-io/finality-provider/clientcontroller/api" bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" "github.com/babylonlabs-io/finality-provider/clientcontroller/opstackl2" @@ -37,6 +40,7 @@ import ( ope2e "github.com/ethereum-optimism/optimism/op-e2e" optestlog "github.com/ethereum-optimism/optimism/op-service/testlog" gethlog "github.com/ethereum/go-ethereum/log" + "github.com/lightningnetwork/lnd/signal" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -52,16 +56,21 @@ type BaseTestManager = base_test_manager.BaseTestManager type OpL2ConsumerTestManager struct { BaseTestManager - BabylonHandler *e2eutils.BabylonNodeHandler - BabylonFpApp *service.FinalityProviderApp - ConsumerFpApps []*service.FinalityProviderApp - EOTSServerHandler *e2eutils.EOTSServerHandler - BaseDir string - SdkClient *sdkclient.SdkClient - OpSystem *ope2e.System + BabylonHandler *e2eutils.BabylonNodeHandler + BabylonFpApp *service.FinalityProviderApp + ConsumerFpApps []*service.FinalityProviderApp + EOTSServerHandler *e2eutils.EOTSServerHandler + BaseDir string + FinalityGadget *finalitygadget.FinalityGadget + FinalityGadgetClient *fgclient.FinalityGadgetGrpcClient + Db db.IDatabaseHandler + DbPath string + OpSystem *ope2e.System } func StartOpL2ConsumerManager(t *testing.T, numOfConsumerFPs uint8) *OpL2ConsumerTestManager { + defaultlog.SetOutput(os.Stdout) + // Setup base dir and logger testDir, err := e2eutils.BaseDir("fpe2etest") require.NoError(t, err) @@ -78,14 +87,19 @@ func StartOpL2ConsumerManager(t *testing.T, numOfConsumerFPs uint8) *OpL2Consume err = bh.Start() require.NoError(t, err) + // specify Babylon finality gadget rpc + babylonFinalityGadgetRpcPort := "8080" + babylonFinalityGadgetRpc := "localhost:" + babylonFinalityGadgetRpcPort + // deploy op-finality-gadget contract and start op stack system - opL2ConsumerConfig, opSys := startExtSystemsAndCreateConsumerCfg(t, logger, bh) + opL2ConsumerConfig, opSys := startExtSystemsAndCreateConsumerCfg(t, logger, bh, babylonFinalityGadgetRpc) // TODO: this is a hack to try to fix a flaky data race // https://github.com/babylonchain/finality-provider/issues/528 time.Sleep(5 * time.Second) - // init SDK client - sdkClient := initSdkClient(opSys, opL2ConsumerConfig, t) + // create shutdown interceptor + shutdownInterceptor, err := signal.Intercept() + require.NoError(t, err) // start multiple FPs. each FP has its own EOTS manager and finality provider app // there is one Babylon FP and multiple Consumer FPs @@ -95,6 +109,7 @@ func StartOpL2ConsumerManager(t *testing.T, numOfConsumerFPs uint8) *OpL2Consume opL2ConsumerConfig, numOfConsumerFPs+1, logger, + &shutdownInterceptor, t, ) @@ -109,18 +124,58 @@ func StartOpL2ConsumerManager(t *testing.T, numOfConsumerFPs uint8) *OpL2Consume require.NoError(t, err) t.Logf(log.Prefix("Register consumer %s to Babylon"), opConsumerId) + // define finality gadget configs + cfg := fgcfg.Config{ + L2RPCHost: opL2ConsumerConfig.OPStackL2RPCAddress, + BitcoinRPCHost: "rpc.ankr.com/btc", + FGContractAddress: opL2ConsumerConfig.OPFinalityGadgetAddress, + BBNChainID: "chain-test", + BBNRPCAddress: opL2ConsumerConfig.RPCAddr, + DBFilePath: "data.db", + GRPCServerPort: babylonFinalityGadgetRpcPort, + PollInterval: time.Second * time.Duration(10), + } + + // Init local DB for storing and querying blocks + db, err := db.NewBBoltHandler(cfg.DBFilePath, logger) + require.NoError(t, err) + err = db.CreateInitialSchema() + require.NoError(t, err) + t.Logf(log.Prefix("Init local DB for finality gadget server")) + + // Start finality gadget + fg, err := finalitygadget.NewFinalityGadget(&cfg, db, logger) + require.NoError(t, err) + t.Logf(log.Prefix("Created finality gadget")) + + // Start finality gadget server + srv := fgsrv.NewFinalityGadgetServer(&cfg, db, fg, shutdownInterceptor, logger) + go func() { + err = srv.RunUntilShutdown() + require.NoError(t, err) + }() + t.Logf(log.Prefix("Started finality gadget grpc server")) + + // Create grpc client + client, err := fgclient.NewFinalityGadgetGrpcClient(babylonFinalityGadgetRpc) + require.NoError(t, err) + t.Logf(log.Prefix("Started finality gadget grpc client")) + ctm := &OpL2ConsumerTestManager{ BaseTestManager: BaseTestManager{ BBNClient: babylonClient, CovenantPrivKeys: covenantPrivKeys, }, - BabylonHandler: bh, - EOTSServerHandler: eotsHandler, - BabylonFpApp: babylonFpApp, - ConsumerFpApps: consumerFpApps, - BaseDir: testDir, - SdkClient: sdkClient, - OpSystem: opSys, + BabylonHandler: bh, + EOTSServerHandler: eotsHandler, + BabylonFpApp: babylonFpApp, + ConsumerFpApps: consumerFpApps, + BaseDir: testDir, + FinalityGadget: fg, + FinalityGadgetClient: client, + Db: db, + DbPath: cfg.DBFilePath, + OpSystem: opSys, } ctm.WaitForServicesStart(t) @@ -133,6 +188,7 @@ func createMultiFps( opL2ConsumerConfig *fpcfg.OPStackL2Config, numOfConsumerFPs uint8, logger *zap.Logger, + shutdownInterceptor *signal.Interceptor, t *testing.T, ) (*service.FinalityProviderApp, []*service.FinalityProviderApp, *e2eutils.EOTSServerHandler) { babylonFpCfg, consumerFpCfgs := createFpConfigs( @@ -144,7 +200,7 @@ func createMultiFps( t, ) - eotsHandler, eotsClients := startEotsManagers(testDir, t, babylonFpCfg, consumerFpCfgs, logger) + eotsHandler, eotsClients := startEotsManagers(testDir, t, babylonFpCfg, consumerFpCfgs, logger, shutdownInterceptor) babylonFpApp, consumerFpApps := createFpApps( babylonFpCfg, @@ -352,6 +408,7 @@ func startEotsManagers( babylonFpCfg *fpcfg.Config, consumerFpCfgs []*fpcfg.Config, logger *zap.Logger, + shutdownInterceptor *signal.Interceptor, ) (*e2eutils.EOTSServerHandler, []*client.EOTSManagerGRpcClient) { allConfigs := append([]*fpcfg.Config{babylonFpCfg}, consumerFpCfgs...) eotsClients := make([]*client.EOTSManagerGRpcClient, len(allConfigs)) @@ -373,7 +430,7 @@ func startEotsManagers( ) eotsConfigs[i] = eotsCfg } - eh := e2eutils.NewEOTSServerHandlerMultiFP(t, eotsConfigs, eotsHomeDirs, logger) + eh := e2eutils.NewEOTSServerHandlerMultiFP(t, eotsConfigs, eotsHomeDirs, logger, shutdownInterceptor) eh.Start() // create EOTS clients @@ -394,24 +451,6 @@ func startEotsManagers( return eh, eotsClients } -func initSdkClient( - opSys *ope2e.System, - opL2ConsumerConfig *fpcfg.OPStackL2Config, - t *testing.T, -) *sdkclient.SdkClient { - // We pass in an external Bitcoin RPC address but otherwise use the default configs. - btcConfig := btcclient.DefaultBTCConfig() - // The RPC url must be trimmed to remove the http:// or https:// prefix. - btcConfig.RPCHost = trimLeadingHttp(opSys.Cfg.DeployConfig.BabylonFinalityGadgetBitcoinRpc) - sdkClient, err := sdkclient.NewClient(&sdkcfg.Config{ - ChainID: opSys.Cfg.DeployConfig.BabylonFinalityGadgetChainID, - ContractAddr: opL2ConsumerConfig.OPFinalityGadgetAddress, - BTCConfig: btcConfig, - }) - require.NoError(t, err) - return sdkClient -} - func deployCwContract( t *testing.T, logger *zap.Logger, @@ -662,6 +701,7 @@ func startExtSystemsAndCreateConsumerCfg( t *testing.T, logger *zap.Logger, bh *e2eutils.BabylonNodeHandler, + babylonFinalityGadgetRpc string, ) (*fpcfg.OPStackL2Config, *ope2e.System) { // create consumer config // TODO: using babylon node key dir is a hack. we should fix it @@ -669,24 +709,20 @@ func startExtSystemsAndCreateConsumerCfg( // DefaultSystemConfig load the op deploy config from devnet-data folder opSysCfg := ope2e.DefaultSystemConfig(t) - require.Equal( - t, - e2eutils.ChainID, - opSysCfg.DeployConfig.BabylonFinalityGadgetChainID, - "should be chain-test in devnetL1.json that means to connect with the Babylon localnet", - ) opConsumerId := getConsumerChainId(&opSysCfg) // deploy op-finality-gadget contract cwContractAddress := deployCwContract(t, logger, opL2ConsumerConfig, opConsumerId) - // replace the contract address - opSysCfg.DeployConfig.BabylonFinalityGadgetContractAddress = cwContractAddress // supress OP system logs opSysCfg.Loggers["verifier"] = optestlog.Logger(t, gethlog.LevelError).New("role", "verifier") opSysCfg.Loggers["sequencer"] = optestlog.Logger(t, gethlog.LevelError).New("role", "sequencer") opSysCfg.Loggers["batcher"] = optestlog.Logger(t, gethlog.LevelError).New("role", "watcher") + // specify babylon finality gadget rpc address + opL2ConsumerConfig.BabylonFinalityGadgetRpc = babylonFinalityGadgetRpc + opSysCfg.DeployConfig.BabylonFinalityGadgetRpc = babylonFinalityGadgetRpc + // start op stack system opSys, err := opSysCfg.Start(t) require.NoError(t, err, "Error starting up op stack system") @@ -801,6 +837,10 @@ func (ctm *OpL2ConsumerTestManager) WaitForBlockFinalized( require.Eventually(t, func() bool { // doesn't matter which FP we use to query. so we use the first consumer FP latestFinalizedBlock, err := ctm.getOpCCAtIndex(0).QueryLatestFinalizedBlock() + if err != nil { + t.Logf(log.Prefix("failed to query latest finalized block %s"), err.Error()) + return false + } require.NoError(t, err) finalizedBlockHeight = latestFinalizedBlock.Height return finalizedBlockHeight >= checkedHeight @@ -914,7 +954,7 @@ func (ctm *OpL2ConsumerTestManager) waitForBTCStakingActivation(t *testing.T) ui require.NoError(t, err) l2BlockAfterActivation = latestBlock.Number.Uint64() - activatedTimestamp, err := ctm.SdkClient.QueryBtcStakingActivatedTimestamp() + activatedTimestamp, err := ctm.FinalityGadget.QueryBtcStakingActivatedTimestamp() if err != nil { t.Logf(log.Prefix("Failed to query BTC staking activated timestamp: %v"), err) return false @@ -928,6 +968,12 @@ func (ctm *OpL2ConsumerTestManager) waitForBTCStakingActivation(t *testing.T) ui return l2BlockAfterActivation } +func (ctm *OpL2ConsumerTestManager) checkLatestBlock(t *testing.T, exp uint64) { + block, err := ctm.FinalityGadget.QueryLatestFinalizedBlock() + require.NoError(t, err) + require.Equal(t, exp, block.BlockHeight) +} + func (ctm *OpL2ConsumerTestManager) Stop(t *testing.T) { t.Log("Stopping test manager") var err error @@ -943,6 +989,17 @@ func (ctm *OpL2ConsumerTestManager) Stop(t *testing.T) { t.Logf(log.Prefix("Stopped Consumer FP App %d"), i) } + err = ctm.FinalityGadgetClient.Close() + require.NoError(t, err) + if ctm.DbPath != "" { + err = os.Remove(ctm.DbPath) + require.NoError(t, err) + } + if ctm.Db != nil { + err = ctm.Db.Close() + require.NoError(t, err) + } + ctm.FinalityGadget.Close() ctm.OpSystem.Close() err = ctm.BabylonHandler.Stop() require.NoError(t, err)