Skip to content

Commit

Permalink
chore: add finality activation height (#101)
Browse files Browse the repository at this point in the history
- Add checks for finality activation height
- Pub rand commit start height at activation height or last commited
height whatever is higher
- Finality vote, only vote in blocks after the finality activation
height
- Update babylon to v014
  • Loading branch information
RafilxTenfen authored Oct 24, 2024
1 parent d56d0f8 commit 2dbba5a
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 149 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## Unreleased

### Improvements

* [#101](https://github.com/babylonlabs-io/finality-provider/pull/101) Add finality activation
height check in finality voting and commit pub rand start height

## v0.8.0

### Misc Improvements
### Improvements

* [#97](https://github.com/babylonlabs-io/finality-provider/pull/97) Bump Babylon version to v0.13.0
* [#90](https://github.com/babylonlabs-io/finality-provider/pull/90) CLI edit finality provider
Expand Down
12 changes: 11 additions & 1 deletion clientcontroller/babylon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package clientcontroller
import (
"context"
"fmt"
"github.com/babylonlabs-io/finality-provider/finality-provider/proto"
"strings"
"time"

"github.com/babylonlabs-io/finality-provider/finality-provider/proto"

sdkErr "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
bbnclient "github.com/babylonlabs-io/babylon/client/client"
Expand Down Expand Up @@ -387,6 +388,15 @@ func (bc *BabylonController) QueryActivatedHeight() (uint64, error) {
return res.Height, nil
}

func (bc *BabylonController) QueryFinalityActivationBlockHeight() (uint64, error) {
res, err := bc.bbnClient.QueryClient.FinalityParams()
if err != nil {
return 0, fmt.Errorf("failed to query finality params to get finality activation block height: %w", err)
}

return res.FinalityActivationHeight, nil
}

func (bc *BabylonController) QueryBestBlock() (*types.BlockInfo, error) {
blocks, err := bc.queryLatestBlocks(nil, 1, finalitytypes.QueriedBlockStatus_ANY, true)
if err != nil || len(blocks) != 1 {
Expand Down
10 changes: 9 additions & 1 deletion clientcontroller/interface.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package clientcontroller

import (
"cosmossdk.io/math"
"fmt"

"cosmossdk.io/math"
btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
Expand Down Expand Up @@ -71,6 +72,13 @@ type ClientController interface {
// error will be returned if the consumer chain has not been activated
QueryActivatedHeight() (uint64, error)

// QueryFinalityActivationBlockHeight returns the block height of the consumer chain
// starts to accept finality voting and pub rand commit as start height
// error will be returned if the consumer chain failed to get this value
// if the consumer chain wants to accept finality voting at any block height
// the value zero should be returned.
QueryFinalityActivationBlockHeight() (uint64, error)

Close() error
}

Expand Down
1 change: 0 additions & 1 deletion finality-provider/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ func PersistClientCtx(ctx client.Context) func(cmd *cobra.Command, _ []string) e
cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.ErrOrStderr())

ctx = ctx.WithCmdContext(cmd.Context())
if err := client.SetCmdClientContextHandler(ctx, cmd); err != nil {
return err
}
Expand Down
20 changes: 11 additions & 9 deletions finality-provider/cmd/cmd_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd_test

import (
"context"
"math/rand"
"path/filepath"
"testing"
Expand All @@ -19,30 +20,31 @@ import (

func TestPersistClientCtx(t *testing.T) {
ctx := client.Context{}
cmd := cobra.Command{}
cmd := &cobra.Command{}
cmd.SetContext(context.Background())

tempDir := t.TempDir()
defaultHome := filepath.Join(tempDir, "defaultHome")

cmd.Flags().String(flags.FlagHome, defaultHome, "The application home directory")
cmd.Flags().String(flags.FlagChainID, "", "chain id")

err := fpcmd.PersistClientCtx(ctx)(&cmd, []string{})
err := fpcmd.PersistClientCtx(ctx)(cmd, []string{})
require.NoError(t, err)

// verify that has the defaults to ctx
ctx = client.GetClientContextFromCmd(&cmd)
ctx = client.GetClientContextFromCmd(cmd)
require.Equal(t, defaultHome, ctx.HomeDir)
require.Equal(t, "", ctx.ChainID)

flagHomeValue := filepath.Join(tempDir, "flagHome")
err = cmd.Flags().Set(flags.FlagHome, flagHomeValue)
require.NoError(t, err)

err = fpcmd.PersistClientCtx(ctx)(&cmd, []string{})
err = fpcmd.PersistClientCtx(ctx)(cmd, []string{})
require.NoError(t, err)

ctx = client.GetClientContextFromCmd(&cmd)
ctx = client.GetClientContextFromCmd(cmd)
require.Equal(t, flagHomeValue, ctx.HomeDir)

r := rand.New(rand.NewSource(10))
Expand All @@ -60,10 +62,10 @@ func TestPersistClientCtx(t *testing.T) {
require.NoError(t, err)

// parses the ctx from cmd with config, should modify the chain ID
err = fpcmd.PersistClientCtx(ctx)(&cmd, []string{})
err = fpcmd.PersistClientCtx(ctx)(cmd, []string{})
require.NoError(t, err)

ctx = client.GetClientContextFromCmd(&cmd)
ctx = client.GetClientContextFromCmd(cmd)
require.Equal(t, flagHomeValue, ctx.HomeDir)
require.Equal(t, randChainID, ctx.ChainID)

Expand All @@ -73,10 +75,10 @@ func TestPersistClientCtx(t *testing.T) {

// parses the ctx from cmd with config, but it has set in flags which should give
// preference over the config set, so it should use from the flag value set.
err = fpcmd.PersistClientCtx(ctx)(&cmd, []string{})
err = fpcmd.PersistClientCtx(ctx)(cmd, []string{})
require.NoError(t, err)

ctx = client.GetClientContextFromCmd(&cmd)
ctx = client.GetClientContextFromCmd(cmd)
require.Equal(t, flagHomeValue, ctx.HomeDir)
require.Equal(t, flagChainID, ctx.ChainID)
}
6 changes: 3 additions & 3 deletions finality-provider/service/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func FuzzRegisterFinalityProvider(f *testing.F) {
// Create mocked babylon client
randomStartingHeight := uint64(r.Int63n(100) + 1)
currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0)
mockClientController.EXPECT().QueryLatestFinalizedBlocks(gomock.Any()).Return(nil, nil).AnyTimes()
mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(),
gomock.Any()).Return(uint64(0), nil).AnyTimes()
Expand Down Expand Up @@ -172,7 +172,7 @@ func FuzzSyncFinalityProviderStatus(f *testing.F) {

randomStartingHeight := uint64(r.Int63n(100) + 1)
currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0)

blkInfo := &types.BlockInfo{Height: currentHeight}

Expand Down Expand Up @@ -251,7 +251,7 @@ func FuzzUnjailFinalityProvider(f *testing.F) {

randomStartingHeight := uint64(r.Int63n(100) + 1)
currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0)

blkInfo := &types.BlockInfo{Height: currentHeight}

Expand Down
11 changes: 10 additions & 1 deletion finality-provider/service/fastsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"go.uber.org/zap"

"github.com/babylonlabs-io/finality-provider/lib/math"
"github.com/babylonlabs-io/finality-provider/types"
)

Expand All @@ -28,8 +29,16 @@ func (fp *FinalityProviderInstance) FastSync(startHeight, endHeight uint64) (*Fa
startHeight, endHeight)
}

var syncedHeight uint64
activationBlkHeight, err := fp.cc.QueryFinalityActivationBlockHeight()
if err != nil {
return nil, fmt.Errorf("failed to get activation height during fast sync %w", err)
}

responses := make([]*types.TxResponse, 0)
// make sure it starts at least at the finality activation height
startHeight = math.MaxUint64(startHeight, activationBlkHeight)
// the syncedHeight is at least the starting point
syncedHeight := startHeight
// we may need several rounds to catch-up as we need to limit
// the catch-up distance for each round to avoid memory overflow
for startHeight <= endHeight {
Expand Down
26 changes: 24 additions & 2 deletions finality-provider/service/fastsync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service_test
import (
"math/rand"
"testing"
"time"

"github.com/babylonlabs-io/babylon/testutil/datagen"
ftypes "github.com/babylonlabs-io/babylon/x/finality/types"
Expand All @@ -25,7 +26,7 @@ func FuzzFastSync_SufficientRandomness(f *testing.F) {
randomStartingHeight := uint64(r.Int63n(100) + 1)
finalizedHeight := randomStartingHeight + uint64(r.Int63n(10)+2)
currentHeight := finalizedHeight + uint64(r.Int63n(10)+1)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0)
mockClientController.EXPECT().QueryLatestFinalizedBlocks(uint64(1)).Return(nil, nil).AnyTimes()
_, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, randomStartingHeight)
defer cleanUp()
Expand Down Expand Up @@ -77,7 +78,7 @@ func FuzzFastSync_NoRandomness(f *testing.F) {
randomStartingHeight := uint64(r.Int63n(100) + 100)
finalizedHeight := randomStartingHeight + uint64(r.Int63n(10)+2)
currentHeight := finalizedHeight + uint64(r.Int63n(10)+1)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0)
mockClientController.EXPECT().QueryLatestFinalizedBlocks(uint64(1)).Return(nil, nil).AnyTimes()
_, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, randomStartingHeight)
defer cleanUp()
Expand Down Expand Up @@ -122,3 +123,24 @@ func FuzzFastSync_NoRandomness(f *testing.F) {
require.Equal(t, lastHeightWithPubRand, fpIns.GetLastProcessedHeight())
})
}

func TestFinalityActivationBlockHeight(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().Unix()))

randomStartingHeight := uint64(r.Int63n(100) + 100)
finalizedHeight := randomStartingHeight + uint64(r.Int63n(10)+2)
currentHeight := finalizedHeight + uint64(r.Int63n(10)+1)
finalityActvationBlockHeight := randomStartingHeight + 10

mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, finalityActvationBlockHeight)
mockClientController.EXPECT().QueryLatestFinalizedBlocks(uint64(1)).Return(nil, nil).AnyTimes()

mockClientController.EXPECT().QueryLastCommittedPublicRand(gomock.Any(), uint64(1)).Return(make(map[uint64]*ftypes.PubRandCommitResponse), nil).AnyTimes()
mockClientController.EXPECT().CommitPubRandList(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)

_, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, randomStartingHeight)
defer cleanUp()

_, err := fpIns.CommitPubRand(randomStartingHeight)
require.NoError(t, err)
}
45 changes: 34 additions & 11 deletions finality-provider/service/fp_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/avast/retry-go/v4"
bbntypes "github.com/babylonlabs-io/babylon/types"
ftypes "github.com/babylonlabs-io/babylon/x/finality/types"
fppath "github.com/babylonlabs-io/finality-provider/lib/math"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/gogo/protobuf/jsonpb"
"go.uber.org/atomic"
Expand Down Expand Up @@ -202,6 +203,18 @@ func (fp *FinalityProviderInstance) finalitySigSubmissionLoop() {
if fp.hasProcessed(b) {
continue
}

activationBlkHeight, err := fp.cc.QueryFinalityActivationBlockHeight()
if err != nil {
fp.reportCriticalErr(fmt.Errorf("failed to get activation height during fast sync %w", err))
continue
}

// check if it is allowed to send finality
if b.Height < activationBlkHeight {
continue
}

// check whether the finality provider has voting power
hasVp, err := fp.hasVotingPower(b)
if err != nil {
Expand Down Expand Up @@ -374,16 +387,9 @@ func (fp *FinalityProviderInstance) tryFastSync(targetBlock *types.BlockInfo) (*
return nil, nil
}

lastFinalizedHeight := lastFinalizedBlocks[0].Height
lastProcessedHeight := fp.GetLastProcessedHeight()

// get the startHeight from the maximum of the lastVotedHeight and
// the lastFinalizedHeight plus 1
var startHeight uint64
if lastFinalizedHeight < lastProcessedHeight {
startHeight = lastProcessedHeight + 1
} else {
startHeight = lastFinalizedHeight + 1
startHeight, err := fp.fastSyncStartHeight(lastFinalizedBlocks[0].Height)
if err != nil {
return nil, err
}

if startHeight > targetBlock.Height {
Expand All @@ -395,6 +401,18 @@ func (fp *FinalityProviderInstance) tryFastSync(targetBlock *types.BlockInfo) (*
return fp.FastSync(startHeight, targetBlock.Height)
}

func (fp *FinalityProviderInstance) fastSyncStartHeight(lastFinalizedHeight uint64) (uint64, error) {
lastProcessedHeight := fp.GetLastProcessedHeight()

finalityActivationBlkHeight, err := fp.cc.QueryFinalityActivationBlockHeight()
if err != nil {
return 0, err
}

// return the max start height by checking the finality activation block height
return fppath.MaxUint64(lastProcessedHeight+1, lastFinalizedHeight+1, finalityActivationBlkHeight), nil
}

func (fp *FinalityProviderInstance) hasProcessed(b *types.BlockInfo) bool {
if b.Height <= fp.GetLastProcessedHeight() {
fp.logger.Debug(
Expand Down Expand Up @@ -597,11 +615,16 @@ func (fp *FinalityProviderInstance) CommitPubRand(tipHeight uint64) (*types.TxRe
return nil, nil
}

activationBlkHeight, err := fp.cc.QueryFinalityActivationBlockHeight()
if err != nil {
return nil, err
}

// generate a list of Schnorr randomness pairs
// NOTE: currently, calling this will create and save a list of randomness
// in case of failure, randomness that has been created will be overwritten
// for safety reason as the same randomness must not be used twice
pubRandList, err := fp.getPubRandList(startHeight, fp.cfg.NumPubRand)
pubRandList, err := fp.getPubRandList(fppath.MaxUint64(startHeight, activationBlkHeight), fp.cfg.NumPubRand)
if err != nil {
return nil, fmt.Errorf("failed to generate randomness: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions finality-provider/service/fp_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func FuzzCommitPubRandList(f *testing.F) {
randomStartingHeight := uint64(r.Int63n(100) + 1)
currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2)
startingBlock := &types.BlockInfo{Height: randomStartingHeight, Hash: testutil.GenRandomByteArray(r, 32)}
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0)
mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(), gomock.Any()).
Return(uint64(0), nil).AnyTimes()
_, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, randomStartingHeight)
Expand All @@ -57,7 +57,7 @@ func FuzzSubmitFinalitySig(f *testing.F) {
randomStartingHeight := uint64(r.Int63n(100) + 1)
currentHeight := randomStartingHeight + uint64(r.Int63n(10)+1)
startingBlock := &types.BlockInfo{Height: randomStartingHeight, Hash: testutil.GenRandomByteArray(r, 32)}
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight)
mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0)
mockClientController.EXPECT().QueryLatestFinalizedBlocks(gomock.Any()).Return(nil, nil).AnyTimes()
_, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, randomStartingHeight)
defer cleanUp()
Expand Down
1 change: 1 addition & 0 deletions finality-provider/service/fp_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func FuzzStatusUpdate(f *testing.F) {
mockClientController.EXPECT().QueryLatestFinalizedBlocks(gomock.Any()).Return(nil, nil).AnyTimes()
mockClientController.EXPECT().QueryBestBlock().Return(currentBlockRes, nil).AnyTimes()
mockClientController.EXPECT().QueryActivatedHeight().Return(uint64(1), nil).AnyTimes()
mockClientController.EXPECT().QueryFinalityActivationBlockHeight().Return(uint64(0), nil).AnyTimes()
mockClientController.EXPECT().QueryBlock(gomock.Any()).Return(currentBlockRes, nil).AnyTimes()
mockClientController.EXPECT().QueryLastCommittedPublicRand(gomock.Any(), uint64(1)).Return(nil, nil).AnyTimes()

Expand Down
Loading

0 comments on commit 2dbba5a

Please sign in to comment.