Skip to content

Commit

Permalink
feat: new command phase1 stake to BTC delegation (#90)
Browse files Browse the repository at this point in the history
* chore: init command draft

* chore: add get tx details from tx hash only

* chore: add initial impl to btc staking from phase 1

* feat: add cli to create babylon create btc delegation from staking tx

* chore: add #90 to changelog

* chore: add stake command to CLI

* fix: lint

* chore: move cli and manager to separated file

* chore: manager split btc data

* chore: split func to generate covenant pk

* chore: add staking of phase 1 test

* fix: lint

* trytest: stuck at including finality provider to running chain

* chore: add last err to debug

* pass json serializable params

* chore: refactory send transaction to phase 1

* chore: fix comment

* chore: address PR comments

* chore: rollback rand

* chore: use rand previous created

* chore: add possible responde of BTC delegation to consumer

* chore: try to refactory to receive BTC delegation tx hash

* chore: moved to use command

* chore: add todo

* chore: fix lint and allow nonamedreturns

* chore: remove go routine of mine empty blocks

* chore: add new field to transaction tracked of tx hash of btc delegation

* tryfix: get btc delegation tx hash

* fix: lint removed unused func

* chore: add goroutine to return the cmd request

* chore: address pr comment and simplified the stking tx migration

* chore: add btc del tx hash to delegation sent to babylon

* fix: lint removed unused func

* chore: removed txInclusionHeightFlag flag and getting the block height by querying the btc

* chore: rename BtcDelegationTxHash to BabylonBTCDelegationTxHash

* chore: update error msg

* fix: lint

* chore: add back tx-inclusion-height as optional, if not set queries the tx hash

---------

Co-authored-by: KonradStaniec <[email protected]>
  • Loading branch information
RafilxTenfen and KonradStaniec authored Nov 29, 2024
1 parent 5a1c0c2 commit 35c690a
Show file tree
Hide file tree
Showing 23 changed files with 2,238 additions and 1,435 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ linters:
- nilerr
# - nlreturn # Style wise I personally like this one, todo(lazar): unlax at somepoint, good practice
- noctx
- nonamedreturns
# - nonamedreturns
- nosprintfhostport
- paralleltest
- reassign
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

### Improvements

* [#90](https://github.com/babylonlabs-io/btc-staker/pull/90) Add CLI to create
babylon BTC delegation from phase-1 BTC staking transaction.
* [#99](https://github.com/babylonlabs-io/btc-staker/pull/99) Bump babylon version
and adapt staker to the changes

Expand Down
1 change: 0 additions & 1 deletion babylonclient/babyloncontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,6 @@ func (bc *BabylonController) RegisterFinalityProvider(
Pop: pop,
}

fpPrivKeyBBN.PubKey()
relayerMsgs := bbnclient.ToProviderMsgs([]sdk.Msg{registerMsg})

_, err := bc.bbnClient.SendMessageWithSigner(context.Background(), fpAddr, fpPrivKeyBBN, relayerMsgs)
Expand Down
83 changes: 83 additions & 0 deletions cmd/stakercli/daemon/daemoncommands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ package daemon

import (
"context"
"errors"
"fmt"
"strconv"

"github.com/babylonlabs-io/btc-staker/cmd/stakercli/helpers"
scfg "github.com/babylonlabs-io/btc-staker/stakercfg"
dc "github.com/babylonlabs-io/btc-staker/stakerservice/client"
"github.com/babylonlabs-io/networks/parameters/parser"
"github.com/cometbft/cometbft/libs/os"
"github.com/urfave/cli"
)

Expand All @@ -26,6 +30,7 @@ var DaemonCommands = []cli.Command{
listStakingTransactionsCmd,
withdrawableTransactionsCmd,
unbondCmd,
stakeFromPhase1Cmd,
},
},
}
Expand All @@ -37,6 +42,7 @@ const (
fpPksFlag = "finality-providers-pks"
stakingTransactionHashFlag = "staking-transaction-hash"
stakerAddressFlag = "staker-address"
txInclusionHeightFlag = "tx-inclusion-height"
)

var (
Expand Down Expand Up @@ -133,6 +139,36 @@ var stakeCmd = cli.Command{
Action: stake,
}

var stakeFromPhase1Cmd = cli.Command{
Name: "stake-from-phase1",
ShortName: "stfp1",
Usage: "\nstakercli daemon stake-from-phase1 [fullpath/to/global_parameters.json]" +
" --staking-transaction-hash [txHashHex] --staker-address [btcStakerAddrHex] --tx-inclusion-height [blockHeightTxInclusion]",
Description: "Creates a Babylon BTC delegation transaction from the Phase1 BTC staking transaction",
Flags: []cli.Flag{
cli.StringFlag{
Name: stakingDaemonAddressFlag,
Usage: "full address of the staker daemon in format tcp:://<host>:<port>",
Value: defaultStakingDaemonAddress,
},
cli.StringFlag{
Name: stakingTransactionHashFlag,
Usage: "Hash of original staking transaction in bitcoin hex format",
Required: true,
},
cli.StringFlag{
Name: stakerAddressFlag,
Usage: "BTC address of the staker in hex",
Required: true,
},
cli.Uint64Flag{
Name: txInclusionHeightFlag,
Usage: "Expected BTC height at which transaction was included. This value is important to choose correct global parameters for transaction, if set doesn't query bitcoin to get the block height from txHash",
},
},
Action: stakeFromPhase1TxBTC,
}

var unstakeCmd = cli.Command{
Name: "unstake",
ShortName: "ust",
Expand Down Expand Up @@ -335,6 +371,53 @@ func stake(ctx *cli.Context) error {
return nil
}

func stakeFromPhase1TxBTC(ctx *cli.Context) error {
daemonAddress := ctx.String(stakingDaemonAddressFlag)
client, err := dc.NewStakerServiceJSONRPCClient(daemonAddress)
if err != nil {
return err
}

sctx := context.Background()
stakingTransactionHash := ctx.String(stakingTransactionHashFlag)
if len(stakingTransactionHash) == 0 {
return errors.New("staking tx hash hex is empty")
}

inputGlobalParamsFilePath := ctx.Args().First()
if len(inputGlobalParamsFilePath) == 0 {
return errors.New("json file input is empty")
}

if !os.FileExists(inputGlobalParamsFilePath) {
return fmt.Errorf("json file input %s does not exist", inputGlobalParamsFilePath)
}

globalParams, err := parser.NewParsedGlobalParamsFromFile(inputGlobalParamsFilePath)
if err != nil {
return fmt.Errorf("error parsing file %s: %w", inputGlobalParamsFilePath, err)
}

blockHeighTxInclusion := ctx.Uint64(txInclusionHeightFlag)
if blockHeighTxInclusion == 0 {
resp, err := client.BtcTxDetails(sctx, stakingTransactionHash)
if err != nil {
return fmt.Errorf("error to get btc tx and block data from staking tx %s: %w", stakingTransactionHash, err)
}

blockHeighTxInclusion = uint64(resp.Blk.Height)
}

paramsForHeight := globalParams.GetVersionedGlobalParamsByHeight(blockHeighTxInclusion)
if paramsForHeight == nil {
return fmt.Errorf("error getting param version from global params %s with height %d", inputGlobalParamsFilePath, blockHeighTxInclusion)
}

stakerAddress := ctx.String(stakerAddressFlag)
_, err = client.BtcDelegationFromBtcStakingTx(sctx, stakerAddress, stakingTransactionHash, paramsForHeight)
return err
}

func unstake(ctx *cli.Context) error {
daemonAddress := ctx.String(stakingDaemonAddressFlag)
client, err := dc.NewStakerServiceJSONRPCClient(daemonAddress)
Expand Down
82 changes: 17 additions & 65 deletions cmd/stakercli/transaction/transactions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/urfave/cli"

cmdadmin "github.com/babylonlabs-io/btc-staker/cmd/stakercli/admin"
cmddaemon "github.com/babylonlabs-io/btc-staker/cmd/stakercli/daemon"
"github.com/babylonlabs-io/btc-staker/cmd/stakercli/transaction"
"github.com/babylonlabs-io/btc-staker/itest/testutil"
"github.com/babylonlabs-io/btc-staker/utils"
)

Expand Down Expand Up @@ -71,9 +70,6 @@ var (
Versions: []*parser.VersionedGlobalParams{&defaultParam},
}

//nolint:errchkjson
paramsMarshalled, _ = json.Marshal(globalParams)

parsedGlobalParams, _ = parser.ParseGlobalParams(&globalParams)
lastParams = parsedGlobalParams.Versions[len(parsedGlobalParams.Versions)-1]
)
Expand Down Expand Up @@ -110,36 +106,12 @@ func FuzzFinalityProviderDeposit(f *testing.F) {
fmt.Sprintf("--staking-time=%d", fpStakingTimeLock),
}

app := testApp()
stakingTx := appRunCreatePhase1StakingTx(r, t, app, append(createTxCmdArgs, commonFlags...))
app := testutil.TestApp()
stakingTx := testutil.AppRunCreatePhase1StakingTx(r, t, app, append(createTxCmdArgs, commonFlags...))
require.NotNil(t, stakingTx)
})
}

func appRunCreatePhase1StakingTxWithParams(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) transaction.CreatePhase1StakingTxResponse {
args := []string{"stakercli", "transaction", "create-phase1-staking-transaction-with-params"}
args = append(args, arguments...)
output := appRunWithOutput(r, t, app, args)

var data transaction.CreatePhase1StakingTxResponse
err := json.Unmarshal([]byte(output), &data)
require.NoError(t, err)

return data
}

func appRunCreatePhase1StakingTx(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) transaction.CreatePhase1StakingTxResponse {
args := []string{"stakercli", "transaction", "create-phase1-staking-transaction"}
args = append(args, arguments...)
output := appRunWithOutput(r, t, app, args)

var data transaction.CreatePhase1StakingTxResponse
err := json.Unmarshal([]byte(output), &data)
require.NoError(t, err)

return data
}

func appRunCheckPhase1StakingTxParams(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) transaction.CheckPhase1StakingTxResponse {
args := []string{"stakercli", "transaction", "check-phase1-staking-transaction-params"}
args = append(args, arguments...)
Expand Down Expand Up @@ -192,15 +164,6 @@ func readFromFile(t *testing.T, f *os.File) string {
return buf.String()
}

func testApp() *cli.App {
app := cli.NewApp()
app.Name = "stakercli"
app.Commands = append(app.Commands, cmddaemon.DaemonCommands...)
app.Commands = append(app.Commands, cmdadmin.AdminCommands...)
app.Commands = append(app.Commands, transaction.TransactionCommands...)
return app
}

func appRunCreatePhase1UnbondingTx(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) transaction.CreatePhase1UnbondingTxResponse {
args := []string{"stakercli", "transaction", "create-phase1-unbonding-transaction"}
args = append(args, arguments...)
Expand All @@ -223,19 +186,8 @@ func appRunCreatePhase1WithdrawalTx(r *rand.Rand, t *testing.T, app *cli.App, ar
return data
}

func randRange(_ *rand.Rand, min, max int) int {
return rand.Intn(max+1-min) + min
}

func createTempFileWithParams(f *testing.F) string {
file, err := os.CreateTemp("", "tmpParams-*.json")
require.NoError(f, err)
defer file.Close()
_, err = file.Write(paramsMarshalled)
require.NoError(f, err)
info, err := file.Stat()
require.NoError(f, err)
return filepath.Join(os.TempDir(), info.Name())
func randRange(r *rand.Rand, minV, maxV int) int {
return r.Intn(maxV+1-minV) + minV
}

type StakeParameters struct {
Expand Down Expand Up @@ -280,7 +232,7 @@ func createCustomValidStakeParams(

func TestCheckPhase1StakingTransactionCmd(t *testing.T) {
t.Parallel()
app := testApp()
app := testutil.TestApp()
stakerCliCheckP1StkTx := []string{
"stakercli", "transaction", "check-phase1-staking-transaction",
"--covenant-quorum=1",
Expand Down Expand Up @@ -386,12 +338,12 @@ func TestCheckPhase1StakingTransactionCmd(t *testing.T) {

// Property: Every create should end without error for valid params
func FuzzCreatPhase1Tx(f *testing.F) {
paramsFilePath := createTempFileWithParams(f)
paramsFilePath := testutil.CreateTempFileWithParams(f)

datagen.AddRandomSeedsToFuzzer(f, 5)
f.Fuzz(func(t *testing.T, seed int64) {
r := rand.New(rand.NewSource(seed))
app := testApp()
app := testutil.TestApp()

var args []string
args = append(args, paramsFilePath)
Expand All @@ -400,7 +352,7 @@ func FuzzCreatPhase1Tx(f *testing.F) {

args = append(args, createArgs...)

resCreate := appRunCreatePhase1StakingTxWithParams(
resCreate := testutil.AppRunCreatePhase1StakingTxWithParams(
r, t, app, args,
)
require.NotNil(t, resCreate)
Expand All @@ -412,12 +364,12 @@ func keyToSchnorrHex(key *btcec.PublicKey) string {
}

func FuzzCheckPhase1Tx(f *testing.F) {
paramsFilePath := createTempFileWithParams(f)
paramsFilePath := testutil.CreateTempFileWithParams(f)

datagen.AddRandomSeedsToFuzzer(f, 5)
f.Fuzz(func(t *testing.T, seed int64) {
r := rand.New(rand.NewSource(seed))
app := testApp()
app := testutil.TestApp()

stakerParams, _ := createCustomValidStakeParams(t, r, &globalParams, &chaincfg.RegressionNetParams)

Expand Down Expand Up @@ -462,7 +414,7 @@ func FuzzCheckPhase1Tx(f *testing.F) {
}

func FuzzCreateUnbondingTx(f *testing.F) {
paramsFilePath := createTempFileWithParams(f)
paramsFilePath := testutil.CreateTempFileWithParams(f)

datagen.AddRandomSeedsToFuzzer(f, 10)
f.Fuzz(func(t *testing.T, seed int64) {
Expand Down Expand Up @@ -495,7 +447,7 @@ func FuzzCreateUnbondingTx(f *testing.F) {
fmt.Sprintf("--network=%s", chaincfg.RegressionNetParams.Name),
}

app := testApp()
app := testutil.TestApp()
unbondingTxResponse := appRunCreatePhase1UnbondingTx(r, t, app, createTxCmdArgs)
require.NotNil(t, unbondingTxResponse)
utx, _, err := bbn.NewBTCTxFromHex(unbondingTxResponse.UnbondingTxHex)
Expand All @@ -512,7 +464,7 @@ func FuzzCreateUnbondingTx(f *testing.F) {
}

func FuzzCreateWithdrawalStaking(f *testing.F) {
paramsFilePath := createTempFileWithParams(f)
paramsFilePath := testutil.CreateTempFileWithParams(f)

datagen.AddRandomSeedsToFuzzer(f, 10)
f.Fuzz(func(t *testing.T, seed int64) {
Expand Down Expand Up @@ -557,7 +509,7 @@ func FuzzCreateWithdrawalStaking(f *testing.F) {
fmt.Sprintf("--network=%s", chaincfg.RegressionNetParams.Name),
}

app := testApp()
app := testutil.TestApp()
wr := appRunCreatePhase1WithdrawalTx(r, t, app, createTxCmdArgs)
require.NotNil(t, wr)

Expand Down Expand Up @@ -600,7 +552,7 @@ func FuzzCreateWithdrawalStaking(f *testing.F) {
}

func FuzzCreateWithdrawalUnbonding(f *testing.F) {
paramsFilePath := createTempFileWithParams(f)
paramsFilePath := testutil.CreateTempFileWithParams(f)

datagen.AddRandomSeedsToFuzzer(f, 10)
f.Fuzz(func(t *testing.T, seed int64) {
Expand Down Expand Up @@ -668,7 +620,7 @@ func FuzzCreateWithdrawalUnbonding(f *testing.F) {
fmt.Sprintf("--network=%s", chaincfg.RegressionNetParams.Name),
}

app := testApp()
app := testutil.TestApp()
wr := appRunCreatePhase1WithdrawalTx(r, t, app, createTxCmdArgs)
require.NotNil(t, wr)

Expand Down
3 changes: 2 additions & 1 deletion itest/bitcoind_node_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package e2etest
import (
"encoding/json"
"fmt"
"github.com/ory/dockertest/v3"
"os"
"strconv"
"strings"
"testing"
"time"

"github.com/ory/dockertest/v3"

"github.com/babylonlabs-io/btc-staker/itest/containers"
"github.com/stretchr/testify/require"
)
Expand Down
16 changes: 4 additions & 12 deletions itest/containers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,19 +179,11 @@ func (m *Manager) RunBabylondResource(
coventantQuorum int,
baseHeaderHex string,
slashingPkScript string,
covenantPk1 *btcec.PublicKey,
covenantPk2 *btcec.PublicKey,
covenantPk3 *btcec.PublicKey,
covenantPks ...*btcec.PublicKey,
) (*dockertest.Resource, error) {
covenantPks := []*bbn.BIP340PubKey{
bbn.NewBIP340PubKeyFromBTCPK(covenantPk1),
bbn.NewBIP340PubKeyFromBTCPK(covenantPk2),
bbn.NewBIP340PubKeyFromBTCPK(covenantPk3),
}

var covenantPksStr []string
for _, pk := range covenantPks {
covenantPksStr = append(covenantPksStr, pk.MarshalHex())
covenantPksStr := make([]string, len(covenantPks))
for i, cvPk := range covenantPks {
covenantPksStr[i] = bbn.NewBIP340PubKeyFromBTCPK(cvPk).MarshalHex()
}

cmd := []string{
Expand Down
Loading

0 comments on commit 35c690a

Please sign in to comment.