Skip to content

Commit

Permalink
[backport] feat: add new cmd fpd commit-pubrand (#153)
Browse files Browse the repository at this point in the history
  • Loading branch information
bap2pecs authored Nov 28, 2024
1 parent b4cd6ac commit 36f1f9d
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* [#149](https://github.com/babylonlabs-io/finality-provider/pull/149) Remove update of config after `fpd keys add`
* [#148](https://github.com/babylonlabs-io/finality-provider/pull/148) Allow command `eotsd keys add` to use
empty HD path to derive new key and use master private key.
* [#153](https://github.com/babylonlabs-io/finality-provider/pull/153) Add `unsafe-commit-pubrand` command
* [#154](https://github.com/babylonlabs-io/finality-provider/pull/154) Use sign schnorr instead of getting private key from EOTS manager
* [#167](https://github.com/babylonlabs-io/finality-provider/pull/167) Remove last processed height

Expand Down
103 changes: 103 additions & 0 deletions finality-provider/cmd/fpd/daemon/commit_pr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package daemon

import (
"fmt"
"math"
"path/filepath"
"strconv"

bbntypes "github.com/babylonlabs-io/babylon/types"
fpcc "github.com/babylonlabs-io/finality-provider/clientcontroller"
eotsclient "github.com/babylonlabs-io/finality-provider/eotsmanager/client"
fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config"
"github.com/babylonlabs-io/finality-provider/finality-provider/service"
"github.com/babylonlabs-io/finality-provider/finality-provider/store"
"github.com/babylonlabs-io/finality-provider/log"
"github.com/babylonlabs-io/finality-provider/metrics"
"github.com/babylonlabs-io/finality-provider/util"
"github.com/cosmos/cosmos-sdk/client"
"github.com/spf13/cobra"
)

// CommandCommitPubRand returns the commit-pubrand command
func CommandCommitPubRand() *cobra.Command {
var cmd = &cobra.Command{
Use: "unsafe-commit-pubrand [fp-eots-pk-hex] [target-height]",
Aliases: []string{"unsafe-cpr"},
Short: "[UNSAFE] Manually trigger public randomness commitment for a finality provider",
Long: `[UNSAFE] Manually trigger public randomness commitment for a finality provider.
WARNING: this can drain the finality provider's balance if the target height is too high.`,
Example: `fpd unsafe-commit-pubrand --home /home/user/.fpd [fp-eots-pk-hex] [target-height]`,
Args: cobra.ExactArgs(2),
RunE: runCommandCommitPubRand,
}
cmd.Flags().Uint64("start-height", math.MaxUint64, "The block height to start committing pubrand from (optional)")
return cmd
}

func runCommandCommitPubRand(cmd *cobra.Command, args []string) error {
fpPk, err := bbntypes.NewBIP340PubKeyFromHex(args[0])
if err != nil {
return err
}
targetHeight, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return err
}
startHeight, err := cmd.Flags().GetUint64("start-height")
if err != nil {
return err
}

// Get homePath from context like in start.go
clientCtx := client.GetClientContextFromCmd(cmd)
homePath, err := filepath.Abs(clientCtx.HomeDir)
if err != nil {
return err
}
homePath = util.CleanAndExpandPath(homePath)

cfg, err := fpcfg.LoadConfig(homePath)
if err != nil {
return fmt.Errorf("failed to load configuration: %w", err)
}

logger, err := log.NewRootLoggerWithFile(fpcfg.LogFile(homePath), cfg.LogLevel)
if err != nil {
return fmt.Errorf("failed to initialize the logger: %w", err)
}

db, err := cfg.DatabaseConfig.GetDBBackend()
if err != nil {
return fmt.Errorf("failed to create db backend: %w", err)
}

fpStore, err := store.NewFinalityProviderStore(db)
if err != nil {
return fmt.Errorf("failed to initiate finality provider store: %w", err)
}
pubRandStore, err := store.NewPubRandProofStore(db)
if err != nil {
return fmt.Errorf("failed to initiate public randomness store: %w", err)
}
cc, err := fpcc.NewClientController(cfg.ChainType, cfg.BabylonConfig, &cfg.BTCNetParams, logger)
if err != nil {
return fmt.Errorf("failed to create rpc client for the Babylon chain: %w", err)
}
em, err := eotsclient.NewEOTSManagerGRpcClient(cfg.EOTSManagerAddress)
if err != nil {
return fmt.Errorf("failed to create EOTS manager client: %w", err)
}

fp, err := service.NewFinalityProviderInstance(
fpPk, cfg, fpStore, pubRandStore, cc, em, metrics.NewFpMetrics(), "",
make(chan<- *service.CriticalError), logger)
if err != nil {
return fmt.Errorf("failed to create finality-provider %s instance: %w", fpPk.MarshalHex(), err)
}

if startHeight == math.MaxUint64 {
return fp.TestCommitPubRand(targetHeight)
}
return fp.TestCommitPubRandWithStartHeight(startHeight, targetHeight)
}
9 changes: 5 additions & 4 deletions finality-provider/cmd/fpd/daemon/daemon_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,11 @@ func runCommandRegisterFP(cmd *cobra.Command, args []string) error {
// CommandAddFinalitySig returns the add-finality-sig command by connecting to the fpd daemon.
func CommandAddFinalitySig() *cobra.Command {
var cmd = &cobra.Command{
Use: "add-finality-sig [fp-eots-pk-hex] [block-height]",
Aliases: []string{"afs"},
Short: "Send a finality signature to the consumer chain. This command should only be used for presentation/testing purposes",
Example: fmt.Sprintf(`fpd add-finality-sig --daemon-address %s`, defaultFpdDaemonAddress),
Use: "unsafe-add-finality-sig [fp-eots-pk-hex] [block-height]",
Aliases: []string{"unsafe-afs"},
Short: "[UNSAFE] Send a finality signature to the consumer chain.",
Long: "[UNSAFE] Send a finality signature to the consumer chain. This command should only be used for presentation/testing purposes",
Example: fmt.Sprintf(`fpd unsafe-add-finality-sig [fp-eots-pk-hex] [block-height] --daemon-address %s`, defaultFpdDaemonAddress),
Args: cobra.ExactArgs(2),
RunE: runCommandAddFinalitySig,
}
Expand Down
1 change: 1 addition & 0 deletions finality-provider/cmd/fpd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func main() {
daemon.CommandInfoFP(), daemon.CommandRegisterFP(), daemon.CommandAddFinalitySig(),
daemon.CommandExportFP(), daemon.CommandTxs(), daemon.CommandUnjailFP(),
daemon.CommandEditFinalityDescription(), daemon.CommandVersion(),
daemon.CommandCommitPubRand(),
)

if err := cmd.Execute(); err != nil {
Expand Down
100 changes: 99 additions & 1 deletion finality-provider/service/fp_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ func NewFinalityProviderInstance(
return nil, fmt.Errorf("the finality provider instance cannot be initiated with status %s", sfp.Status.String())
}

return newFinalityProviderInstanceFromStore(sfp, cfg, s, prStore, cc, em, metrics, passphrase, errChan, logger)
}

// Helper function to create FinalityProviderInstance from store data
func newFinalityProviderInstanceFromStore(
sfp *store.StoredFinalityProvider,
cfg *fpcfg.Config,
s *store.FinalityProviderStore,
prStore *store.PubRandProofStore,
cc clientcontroller.ClientController,
em eotsmanager.EOTSManager,
metrics *metrics.FpMetrics,
passphrase string,
errChan chan<- *CriticalError,
logger *zap.Logger,
) (*FinalityProviderInstance, error) {
return &FinalityProviderInstance{
btcPk: bbntypes.NewBIP340PubKeyFromBTCPK(sfp.BtcPk),
fpState: newFpState(sfp, s),
Expand Down Expand Up @@ -456,6 +472,9 @@ func (fp *FinalityProviderInstance) retryCommitPubRandUntilBlockFinalized(target
// CommitPubRand generates a list of Schnorr rand pairs,
// commits the public randomness for the managed finality providers,
// and save the randomness pair to DB
// Note:
// - if there is no pubrand committed before, it will start from the tipHeight
// - if the tipHeight is too large, it will only commit fp.cfg.NumPubRand pairs
func (fp *FinalityProviderInstance) CommitPubRand(tipHeight uint64) (*types.TxResponse, error) {
lastCommittedHeight, err := fp.GetLastCommittedHeight()
if err != nil {
Expand All @@ -481,6 +500,11 @@ func (fp *FinalityProviderInstance) CommitPubRand(tipHeight uint64) (*types.TxRe
return nil, nil
}

return fp.commitPubRandPairs(startHeight)
}

// it will commit fp.cfg.NumPubRand pairs of public randomness starting from startHeight
func (fp *FinalityProviderInstance) commitPubRandPairs(startHeight uint64) (*types.TxResponse, error) {
activationBlkHeight, err := fp.cc.QueryFinalityActivationBlockHeight()
if err != nil {
return nil, err
Expand Down Expand Up @@ -520,12 +544,86 @@ func (fp *FinalityProviderInstance) CommitPubRand(tipHeight uint64) (*types.TxRe

// Update metrics
fp.metrics.RecordFpRandomnessTime(fp.GetBtcPkHex())
fp.metrics.RecordFpLastCommittedRandomnessHeight(fp.GetBtcPkHex(), lastCommittedHeight)
fp.metrics.RecordFpLastCommittedRandomnessHeight(fp.GetBtcPkHex(), startHeight+numPubRand-1)
fp.metrics.AddToFpTotalCommittedRandomness(fp.GetBtcPkHex(), float64(len(pubRandList)))

return res, nil
}

// TestCommitPubRand is exposed for devops/testing purpose to allow manual committing public randomness in cases
// where FP is stuck due to lack of public randomness.
//
// Note:
// - this function is similar to `CommitPubRand` but should not be used in the main pubrand submission loop.
// - it will always start from the last committed height + 1
// - if targetBlockHeight is too large, it will commit multiple fp.cfg.NumPubRand pairs in a loop until reaching the targetBlockHeight
func (fp *FinalityProviderInstance) TestCommitPubRand(targetBlockHeight uint64) error {
var startHeight, lastCommittedHeight uint64

lastCommittedHeight, err := fp.GetLastCommittedHeight()
if err != nil {
return err
}

if lastCommittedHeight >= targetBlockHeight {
return fmt.Errorf(
"finality provider has already committed pubrand to target block height (pk: %s, target: %d, last committed: %d)",
fp.GetBtcPkHex(),
targetBlockHeight,
lastCommittedHeight,
)
}

if lastCommittedHeight == uint64(0) {
// Note: it can also be the case that the finality-provider has committed 1 pubrand before (but in practice, we
// will never set cfg.NumPubRand to 1. so we can safely assume it has never committed before)
startHeight = 0
} else {
startHeight = lastCommittedHeight + 1
}

return fp.TestCommitPubRandWithStartHeight(startHeight, targetBlockHeight)
}

// TestCommitPubRandWithStartHeight is exposed for devops/testing purpose to allow manual committing public randomness
// in cases where FP is stuck due to lack of public randomness.
func (fp *FinalityProviderInstance) TestCommitPubRandWithStartHeight(startHeight uint64, targetBlockHeight uint64) error {
if startHeight > targetBlockHeight {
return fmt.Errorf("start height should not be greater than target block height")
}

var lastCommittedHeight uint64
lastCommittedHeight, err := fp.GetLastCommittedHeight()
if err != nil {
return err
}
if lastCommittedHeight >= startHeight {
return fmt.Errorf(
"finality provider has already committed pubrand at the start height (pk: %s, startHeight: %d, lastCommittedHeight: %d)",
fp.GetBtcPkHex(),
startHeight,
lastCommittedHeight,
)
}

fp.logger.Info("Start committing pubrand from block height", zap.Uint64("start_height", startHeight))

// TODO: instead of sending multiple txs, a better way is to bundle all the commit messages into
// one like we do for batch finality signatures. see discussion https://bit.ly/3OmbjkN
for startHeight <= targetBlockHeight {
_, err = fp.commitPubRandPairs(startHeight)
if err != nil {
return err
}
lastCommittedHeight = startHeight + uint64(fp.cfg.NumPubRand) - 1
startHeight = lastCommittedHeight + 1
fp.logger.Info("Committed pubrand to block height", zap.Uint64("height", lastCommittedHeight))
}

// no error. success
return nil
}

// SubmitFinalitySignature builds and sends a finality signature over the given block to the consumer chain
func (fp *FinalityProviderInstance) SubmitFinalitySignature(b *types.BlockInfo) (*types.TxResponse, error) {
return fp.SubmitBatchFinalitySignatures([]*types.BlockInfo{b})
Expand Down

0 comments on commit 36f1f9d

Please sign in to comment.