Skip to content

Commit

Permalink
feat: partial refactory in generating signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
RafilxTenfen committed Oct 10, 2024
1 parent 1f0d9ae commit 185e43f
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 38 deletions.
200 changes: 162 additions & 38 deletions covenant/covenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"go.uber.org/zap"

Expand Down Expand Up @@ -84,7 +85,8 @@ func (ce *CovenantEmulator) AddCovenantSignatures(btcDels []*types.Delegation) (
if len(btcDels) == 0 {
return nil, fmt.Errorf("no delegations")
}
covenantSigs := make([]*types.CovenantSigs, 0, len(btcDels))

signingReq := make(map[chainhash.Hash]SigningTxsRequest)
for _, btcDel := range btcDels {
// 0. nil checks
if btcDel == nil {
Expand Down Expand Up @@ -177,7 +179,6 @@ func (ce *CovenantEmulator) AddCovenantSignatures(btcDels []*types.Delegation) (

// 7. Check unbonding fee
unbondingFee := stakingTx.TxOut[btcDel.StakingOutputIdx].Value - unbondingTx.TxOut[0].Value

if unbondingFee != int64(params.UnbondingFee) {
ce.logger.Error("invalid unbonding fee",
zap.Int64("expected_unbonding_fee", int64(params.UnbondingFee)),
Expand All @@ -186,54 +187,113 @@ func (ce *CovenantEmulator) AddCovenantSignatures(btcDels []*types.Delegation) (
continue
}

// 8. sign covenant staking sigs
// record metrics
startSignTime := time.Now()
metricsTimeKeeper.SetPreviousSignStart(&startSignTime)
// Generate signing request data.

slashSigs, unbondingSig, err := signSlashAndUnbondSignatures(
btcDel,
stakingTx,
slashingTx,
unbondingTx,
ce.signer,
params,
&ce.config.BTCNetParams,
)
fpsEncKeys := make([]*asig.EncryptionKey, 0, len(btcDel.FpBtcPks))
for _, fpPk := range btcDel.FpBtcPks {
encKey, err := asig.NewEncryptionKeyFromBTCPK(fpPk)
if err != nil {
continue
// fpPkHex := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk).MarshalHex()
// return nil, nil, fmt.Errorf("failed to get encryption key from finality provider public key %s: %w", fpPkHex, err)
}

fpsEncKeys = append(fpsEncKeys, encKey)
}

slashingPkScriptPath, stakingTxUnbondingPkScriptPath, err := generatePkScriptPathSlashAndUnbond(btcDel, params, &ce.config.BTCNetParams)
if err != nil {
ce.logger.Error("failed to sign signatures or unbonding signature", zap.Error(err))
ce.logger.Error("failed to generate pk script path for slash and unbond", zap.Error(err))
continue
}

// 7. sign covenant slash unbonding signatures
slashUnbondingSigs, err := signSlashUnbondingSignatures(
btcDel,
unbondingTx,
slashUnbondingTx,
ce.signer,
params,
&ce.config.BTCNetParams,
)
// slashSigs, unbondingSig, err := signSlashAndUnbondSignatures(
// btcDel,
// stakingTx,
// slashingTx,
// unbondingTx,
// ce.signer,
// params,
// &ce.config.BTCNetParams,
// )
// if err != nil {
// ce.logger.Error("failed to sign signatures or unbonding signature", zap.Error(err))
// continue
// }

unbondingTxSlashingScriptPath, err := pkScriptPathSlashUnbonding(btcDel, unbondingTx, params, &ce.config.BTCNetParams)
if err != nil {
ce.logger.Error("failed to slash unbonding signature", zap.Error(err))
ce.logger.Error("failed to generate pk script path for slash unbonding", zap.Error(err))
continue
}
// 7. sign covenant slash unbonding signatures
// slashUnbondingSigs, err := signSlashUnbondingSignatures(
// btcDel,
// unbondingTx,
// slashUnbondingTx,
// ce.signer,
// params,
// &ce.config.BTCNetParams,
// )
// if err != nil {
// ce.logger.Error("failed to slash unbonding signature", zap.Error(err))
// continue
// }

signingReq[stakingTx.TxHash()] = SigningTxsRequest{
StakingTx: stakingTx,
SlashingTx: slashingTx,
UnbondingTx: unbondingTx,
SlashUnbondingTx: slashUnbondingTx,
StakingOutputIdx: btcDel.StakingOutputIdx,
SlashingPkScriptPath: slashingPkScriptPath,
StakingTxUnbondingPkScriptPath: stakingTxUnbondingPkScriptPath,
UnbondingTxSlashingScriptPath: unbondingTxSlashingScriptPath,
FpEncKeys: fpsEncKeys,
}

}

// Calls the signer to sign all
// builds the covenant signatures

// record metrics
finishSignTime := time.Now()
metricsTimeKeeper.SetPreviousSignFinish(&finishSignTime)
timedSignDelegationLag.Observe(time.Since(startSignTime).Seconds())
// 8. sign covenant staking sigs
// record metrics
startSignTime := time.Now()
metricsTimeKeeper.SetPreviousSignStart(&startSignTime)

// 8. collect covenant sigs
// calls the signer
respSignatures, err := ce.signer.SignTransactions(SigningRequest{SigningTxsReqByStkTxHash: signingReq})
if err != nil {
ce.recordMetricsFailedSignDelegations(len(btcDels))
return nil, err
}

covenantSigs := make([]*types.CovenantSigs, 0, len(btcDels))
for stkTxHash, signatures := range respSignatures.SignaturesByStkTxHash {
covenantSigs = append(covenantSigs, &types.CovenantSigs{
PublicKey: ce.pk,
StakingTxHash: stakingTx.TxHash(),
SlashingSigs: slashSigs,
UnbondingSig: unbondingSig,
SlashingUnbondingSigs: slashUnbondingSigs,
StakingTxHash: stkTxHash,
SlashingSigs: signatures.SlashSigs,
UnbondingSig: signatures.UnbondingSig,
SlashingUnbondingSigs: signatures.SlashUnbondingSigs,
})
}

// record metrics
finishSignTime := time.Now()
metricsTimeKeeper.SetPreviousSignFinish(&finishSignTime)
timedSignDelegationLag.Observe(time.Since(startSignTime).Seconds())

// builds the covenant signs to submit
// 8. collect covenant sigs
// covenantSigs = append(covenantSigs, &types.CovenantSigs{
// PublicKey: ce.pk,
// StakingTxHash: stakingTx.TxHash(),
// SlashingSigs: slashSigs,
// UnbondingSig: unbondingSig,
// SlashingUnbondingSigs: slashUnbondingSigs,
// })

// 9. submit covenant sigs
res, err := ce.cc.SubmitCovenantSigs(covenantSigs)
if err != nil {
Expand All @@ -249,6 +309,34 @@ func (ce *CovenantEmulator) AddCovenantSignatures(btcDels []*types.Delegation) (
return res, nil
}

func pkScriptPathSlashUnbonding(
del *types.Delegation,
unbondingTx *wire.MsgTx,
params *types.StakingParams,
btcNet *chaincfg.Params,
) (unbondingTxSlashingScriptPath []byte, err error) {
unbondingInfo, err := btcstaking.BuildUnbondingInfo(
del.BtcPk,
del.FpBtcPks,
params.CovenantPks,
params.CovenantQuorum,
del.UnbondingTime,
btcutil.Amount(unbondingTx.TxOut[0].Value),
btcNet,
)
if err != nil {
return nil, err
}

unbondingTxSlashingPathInfo, err := unbondingInfo.SlashingPathSpendInfo()
if err != nil {
return nil, err
}
unbondingTxSlashingScriptPath = unbondingTxSlashingPathInfo.GetPkScriptPath()

return unbondingTxSlashingScriptPath, nil
}

func signSlashUnbondingSignatures(

Check failure on line 340 in covenant/covenant.go

View workflow job for this annotation

GitHub Actions / lint_test / lint

func `signSlashUnbondingSignatures` is unused (unused)
del *types.Delegation,
unbondingTx *wire.MsgTx,
Expand All @@ -274,6 +362,7 @@ func signSlashUnbondingSignatures(
if err != nil {
return nil, err
}
unbondingTxSlashingPathInfo := unbondingTxSlashingPath.GetPkScriptPath()

slashUnbondingSigs := make([][]byte, 0, len(del.FpBtcPks))
for _, fpPk := range del.FpBtcPks {
Expand All @@ -285,7 +374,7 @@ func signSlashUnbondingSignatures(
slashUnbondingTx,
unbondingTx,
0, // 0th output is always the unbonding script output
unbondingTxSlashingPath.GetPkScriptPath(),
unbondingTxSlashingPathInfo,
encKey,
)
if err != nil {
Expand All @@ -297,6 +386,41 @@ func signSlashUnbondingSignatures(
return slashUnbondingSigs, nil
}

func generatePkScriptPathSlashAndUnbond(
del *types.Delegation,
params *types.StakingParams,
btcNet *chaincfg.Params,
) (slashingPkScriptPath, stakingTxUnbondingPkScriptPath []byte, err error) {
// sign slash signatures with every finality providers
stakingInfo, err := btcstaking.BuildStakingInfo(
del.BtcPk,
del.FpBtcPks,
params.CovenantPks,
params.CovenantQuorum,
del.GetStakingTime(),
del.TotalSat,
btcNet,
)
if err != nil {
return nil, nil, fmt.Errorf("failed to build staking info: %w", err)
}

slashingPathInfo, err := stakingInfo.SlashingPathSpendInfo()
if err != nil {
return nil, nil, fmt.Errorf("failed to get slashing path info: %w", err)
}
slashingPkScriptPath = slashingPathInfo.GetPkScriptPath()

// sign unbonding sig
stakingTxUnbondingPathInfo, err := stakingInfo.UnbondingPathSpendInfo()
if err != nil {
return nil, nil, fmt.Errorf("failed to get unbonding path spend info")
}
stakingTxUnbondingPkScriptPath = stakingTxUnbondingPathInfo.GetPkScriptPath()

return slashingPkScriptPath, stakingTxUnbondingPkScriptPath, nil
}

func signSlashAndUnbondSignatures(

Check failure on line 424 in covenant/covenant.go

View workflow job for this annotation

GitHub Actions / lint_test / lint

func `signSlashAndUnbondSignatures` is unused (unused)
del *types.Delegation,
stakingTx *wire.MsgTx,
Expand Down Expand Up @@ -341,7 +465,7 @@ func signSlashAndUnbondSignatures(
encKey,
)
if err != nil {
return nil, nil, fmt.Errorf("failed to sign adaptor signature with finaliyt provider public key %s: %w",
return nil, nil, fmt.Errorf("failed to sign adaptor signature with finality provider public key %s: %w",
fpPkHex, err)
}
slashSigs = append(slashSigs, slashSig.MustMarshal())
Expand Down
28 changes: 28 additions & 0 deletions covenant/expected_interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,40 @@ package covenant
import (
asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
)

type SigningTxsRequest struct {
StakingTx *wire.MsgTx
SlashingTx *wire.MsgTx
UnbondingTx *wire.MsgTx
SlashUnbondingTx *wire.MsgTx
StakingOutputIdx uint32
SlashingPkScriptPath []byte
StakingTxUnbondingPkScriptPath []byte
UnbondingTxSlashingScriptPath []byte
FpEncKeys []*asig.EncryptionKey
}

type SigningRequest struct {
SigningTxsReqByStkTxHash map[chainhash.Hash]SigningTxsRequest
}

type SignaturesResponse struct {
SlashSigs [][]byte
UnbondingSig *schnorr.Signature
SlashUnbondingSigs [][]byte
}

type SigningResponse struct {
SignaturesByStkTxHash map[chainhash.Hash]SignaturesResponse
}

// Signer wrapper interface to sign messages
type Signer interface {
SignTransactions(req SigningRequest) (*SigningResponse, error)
// PubKey returns the current secp256k1 public key
PubKey() (*secp.PublicKey, error)
// EncSignTxWithOneScriptSpendInputStrict is encrypted version of
Expand Down
70 changes: 70 additions & 0 deletions keyring/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
)
Expand Down Expand Up @@ -118,3 +119,72 @@ func (kcs KeyringSigner) SignTxWithOneScriptSpendInputStrict(
covenantPrivKey,
)
}

// SignTransactions receives a batch of transactions to sign and returns all the signatures if nothing fails.
func (kcs KeyringSigner) SignTransactions(req covenant.SigningRequest) (*covenant.SigningResponse, error) {
resp := make(map[chainhash.Hash]covenant.SignaturesResponse, len(req.SigningTxsReqByStkTxHash))

covenantPrivKey, err := kcs.getPrivKey()
if err != nil {
return nil, fmt.Errorf("failed to get Covenant private key: %w", err)
}

for stakingTxHash, signingTxReq := range req.SigningTxsReqByStkTxHash {
// for each signing tx request

slashSigs := make([][]byte, 0, len(signingTxReq.FpEncKeys))
slashUnbondingSigs := make([][]byte, 0, len(signingTxReq.FpEncKeys))
for _, fpEncKey := range signingTxReq.FpEncKeys {
// creates slash sigs
// TODO: split to diff func
slashSig, err := btcstaking.EncSignTxWithOneScriptSpendInputStrict(
signingTxReq.SlashingTx,
signingTxReq.StakingTx,
signingTxReq.StakingOutputIdx,
signingTxReq.SlashingPkScriptPath,
covenantPrivKey,
fpEncKey,
)
if err != nil {
return nil, fmt.Errorf("failed to sign adaptor slash signature with finality provider public key %s: %w", fpEncKey.ToBytes(), err)
}
slashSigs = append(slashSigs, slashSig.MustMarshal())

// TODO: split to diff func
// creates slash unbonding sig
slashUnbondingSig, err := btcstaking.EncSignTxWithOneScriptSpendInputStrict(
signingTxReq.SlashUnbondingTx,
signingTxReq.UnbondingTx,
0, // 0th output is always the unbonding script output
signingTxReq.UnbondingTxSlashingScriptPath,
covenantPrivKey,
fpEncKey,
)
if err != nil {
return nil, fmt.Errorf("failed to sign adaptor slash unbonding signature with finality provider public key %s: %w", fpEncKey.ToBytes(), err)
}
slashUnbondingSigs = append(slashUnbondingSigs, slashUnbondingSig.MustMarshal())
}

unbondingSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict(
signingTxReq.UnbondingTx,
signingTxReq.StakingTx,
signingTxReq.StakingOutputIdx,
signingTxReq.StakingTxUnbondingPkScriptPath,
covenantPrivKey,
)
if err != nil {
return nil, fmt.Errorf("failed to sign unbonding tx: %w", err)
}

resp[stakingTxHash] = covenant.SignaturesResponse{
SlashSigs: slashSigs,
UnbondingSig: unbondingSig,
SlashUnbondingSigs: slashUnbondingSigs,
}
}

return &covenant.SigningResponse{
SignaturesByStkTxHash: resp,
}, nil
}

0 comments on commit 185e43f

Please sign in to comment.