Skip to content

Commit

Permalink
Use bip322 to build pop through bitcoind (#186)
Browse files Browse the repository at this point in the history
* Use bip322 to build pop through bitcoind
  • Loading branch information
KonradStaniec authored Jul 18, 2024
1 parent 6b67265 commit 130fd68
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 31 deletions.
38 changes: 38 additions & 0 deletions babylonclient/pop.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package babylonclient
import (
"fmt"

"github.com/babylonchain/babylon/crypto/bip322"
bbn "github.com/babylonchain/babylon/types"
btcstypes "github.com/babylonchain/babylon/x/btcstaking/types"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand All @@ -23,6 +26,8 @@ type BabylonPop struct {
BtcSig []byte
}

// NewBabylonPop Generic constructor for BabylonPop that do as little validation
// as possible. It assumes passed btcSigOverBbnAddr is matching the popType `t`
func NewBabylonPop(t BabylonBtcPopType, btcSigOverBbnAddr []byte) (*BabylonPop, error) {
if len(btcSigOverBbnAddr) == 0 {
return nil, fmt.Errorf("cannot create BabylonPop with empty signatures")
Expand All @@ -34,6 +39,39 @@ func NewBabylonPop(t BabylonBtcPopType, btcSigOverBbnAddr []byte) (*BabylonPop,
}, nil
}

// NewBabylonBip322Pop build proper BabylonPop in BIP322 style, it verifies the
// the bip322 signature validity
func NewBabylonBip322Pop(
msg []byte,
w wire.TxWitness,
a btcutil.Address) (*BabylonPop, error) {
// TODO: bip322.Verify does not use it last parameter and this parameter should
// be removed from the function signature upstream.
// after that, we can remove the nil parameter here
if err := bip322.Verify(msg, w, a, nil); err != nil {
return nil, fmt.Errorf("invalid bip322 pop parameters: %w", err)
}

serializedWitness, err := bip322.SerializeWitness(w)

if err != nil {
return nil, fmt.Errorf("failed to serialize bip322 witness: %w", err)
}

bip322Sig := btcstypes.BIP322Sig{
Sig: serializedWitness,
Address: a.EncodeAddress(),
}

m, err := bip322Sig.Marshal()

if err != nil {
return nil, fmt.Errorf("failed to serialize btcstypes.BIP322Sig proto: %w", err)
}

return NewBabylonPop(Bip322Type, m)
}

func NewBTCSigType(t BabylonBtcPopType) (btcstypes.BTCSigType, error) {
switch t {
case SchnorrType:
Expand Down
46 changes: 17 additions & 29 deletions staker/stakerapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import (
"sync/atomic"
"time"

pv "github.com/cosmos/relayer/v2/relayer/provider"
"go.uber.org/zap"

"github.com/avast/retry-go/v4"
staking "github.com/babylonchain/babylon/btcstaking"
cl "github.com/babylonchain/btc-staker/babylonclient"
Expand All @@ -21,6 +18,8 @@ import (
"github.com/babylonchain/btc-staker/types"
"github.com/babylonchain/btc-staker/utils"
"github.com/babylonchain/btc-staker/walletcontroller"
pv "github.com/cosmos/relayer/v2/relayer/provider"
"go.uber.org/zap"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
Expand Down Expand Up @@ -1274,28 +1273,6 @@ func (app *StakerApp) BabylonController() cl.BabylonClient {
return app.babylonClient
}

// Generate proof of possessions for staker address.
// Requires btc wallet to be unlocked!
func (app *StakerApp) generatePop(stakerPrivKey *btcec.PrivateKey) (*cl.BabylonPop, error) {
// build proof of possession, no point moving forward if staker does not have all
// the necessary keys
babylonAddrHash := tmhash.Sum(app.babylonClient.GetKeyAddress().Bytes())
btcSig, err := schnorr.Sign(stakerPrivKey, babylonAddrHash)
if err != nil {
return nil, err
}

pop, err := cl.NewBabylonPop(
cl.SchnorrType,
btcSig.Serialize(),
)
if err != nil {
return nil, fmt.Errorf("failed to generate pop: %w", err)
}

return pop, nil
}

func GetMinStakingTime(p *cl.StakingParams) uint32 {
// Actual minimum staking time in babylon is k+w, but setting it to that would
// result in delegation which have voting power for 0 btc blocks.
Expand Down Expand Up @@ -1450,21 +1427,32 @@ func (app *StakerApp) StakeFunds(

// build proof of possesion, no point moving forward if staker do not have all
// the necessary keys
stakerPrivKey, err := app.wc.DumpPrivateKey(stakerAddress)
stakerPubKey, err := app.wc.AddressPublicKey(stakerAddress)

if err != nil {
return nil, err
}

// We build pop ourselves so no need to verify it
pop, err := app.generatePop(stakerPrivKey)
babylonAddrHash := tmhash.Sum(app.babylonClient.GetKeyAddress().Bytes())

sig, err := app.wc.SignBip322NativeSegwit(babylonAddrHash, stakerAddress)

if err != nil {
return nil, err
}

pop, err := cl.NewBabylonBip322Pop(
babylonAddrHash,
sig,
stakerAddress,
)

if err != nil {
return nil, err
}

stakingInfo, err := staking.BuildStakingInfo(
stakerPrivKey.PubKey(),
stakerPubKey,
fpPks,
params.CovenantPks,
params.CovenantQuruomThreshold,
Expand Down
16 changes: 14 additions & 2 deletions walletcontroller/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,25 @@ func (w *RpcWalletController) UnlockWallet(timoutSec int64) error {
}

func (w *RpcWalletController) AddressPublicKey(address btcutil.Address) (*btcec.PublicKey, error) {
privKey, err := w.DumpPrivKey(address)
encoded := address.EncodeAddress()

info, err := w.GetAddressInfo(encoded)

if err != nil {
return nil, err
}

if info.PubKey == nil {
return nil, fmt.Errorf("address %s has no public key", encoded)
}

decodedHex, err := hex.DecodeString(*info.PubKey)

if err != nil {
return nil, err
}

return privKey.PrivKey.PubKey(), nil
return btcec.ParsePubKey(decodedHex)
}

func (w *RpcWalletController) DumpPrivateKey(address btcutil.Address) (*btcec.PrivateKey, error) {
Expand Down

0 comments on commit 130fd68

Please sign in to comment.