diff --git a/clientcontroller/babylon.go b/clientcontroller/babylon.go index e109b5c..d4dd9af 100644 --- a/clientcontroller/babylon.go +++ b/clientcontroller/babylon.go @@ -247,16 +247,17 @@ func ConvertDelegationType(del *btcstakingtypes.BTCDelegation) *types.Delegation } return &types.Delegation{ - BtcPk: del.BtcPk.MustToBTCPK(), - FpBtcPks: fpBtcPks, - TotalSat: del.TotalSat, - StartHeight: del.StartHeight, - EndHeight: del.EndHeight, - StakingTxHex: stakingTxHex, - SlashingTxHex: slashingTxHex, - CovenantSigs: covenantSigs, - UnbondingTime: del.UnbondingTime, - BtcUndelegation: undelegation, + BtcPk: del.BtcPk.MustToBTCPK(), + FpBtcPks: fpBtcPks, + TotalSat: del.TotalSat, + StartHeight: del.StartHeight, + EndHeight: del.EndHeight, + StakingTxHex: stakingTxHex, + SlashingTxHex: slashingTxHex, + StakingOutputIdx: del.StakingOutputIdx, + CovenantSigs: covenantSigs, + UnbondingTime: del.UnbondingTime, + BtcUndelegation: undelegation, } } diff --git a/covenant/covenant.go b/covenant/covenant.go index 0684404..83ab5ba 100644 --- a/covenant/covenant.go +++ b/covenant/covenant.go @@ -3,11 +3,12 @@ package covenant import ( "bytes" "fmt" - "github.com/btcsuite/btcd/btcec/v2/schnorr" "strings" "sync" "time" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/avast/retry-go/v4" "github.com/btcsuite/btcd/btcec/v2" @@ -169,6 +170,13 @@ func (ce *CovenantEmulator) AddCovenantSignatures(btcDels []*types.Delegation) ( uint16(unbondingTime), &ce.config.BTCNetParams, ); err != nil { + ce.logger.Error("invalid delegation", + zap.String("staker_pk", bbntypes.NewBIP340PubKeyFromBTCPK(btcDel.BtcPk).MarshalHex()), + zap.String("finality_provider_pk", bbntypes.NewBIP340PubKeyFromBTCPK(btcDel.FpBtcPks[0]).MarshalHex()), + zap.String("staking_tx_hex", btcDel.StakingTxHex), + zap.String("slashing_tx_hex", btcDel.SlashingTxHex), + zap.Error(err), + ) return nil, fmt.Errorf("invalid txs in the delegation: %w", err) } diff --git a/covenant/covenant_test.go b/covenant/covenant_test.go index 3112a45..37965d7 100644 --- a/covenant/covenant_test.go +++ b/covenant/covenant_test.go @@ -83,6 +83,8 @@ func FuzzAddCovenantSig(f *testing.F) { stakingTxBytes, err := bbntypes.SerializeBTCTx(testInfo.StakingTx) require.NoError(t, err) startHeight := datagen.RandomInt(r, 1000) + 100 + stakingOutputIdx, err := bbntypes.GetOutputIdxInBTCTx(testInfo.StakingTx, testInfo.StakingInfo.StakingOutput) + require.NoError(t, err) btcDel := &types.Delegation{ BtcPk: delPK, FpBtcPks: fpPks, @@ -91,7 +93,7 @@ func FuzzAddCovenantSig(f *testing.F) { TotalSat: uint64(stakingValue), UnbondingTime: uint32(unbondingTime), StakingTxHex: hex.EncodeToString(stakingTxBytes), - StakingOutputIdx: 0, + StakingOutputIdx: stakingOutputIdx, SlashingTxHex: testInfo.SlashingTx.ToHexStr(), } btcDels = append(btcDels, btcDel) diff --git a/itest/test_manager.go b/itest/test_manager.go index 60cb525..2680bf6 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -24,6 +24,7 @@ import ( covcc "github.com/babylonchain/covenant-emulator/clientcontroller" covcfg "github.com/babylonchain/covenant-emulator/config" "github.com/babylonchain/covenant-emulator/covenant" + "github.com/babylonchain/covenant-emulator/testutil" "github.com/babylonchain/covenant-emulator/types" ) @@ -238,7 +239,7 @@ func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKe require.NoError(t, err) unbondingTime := uint16(tm.StakingParams.MinimumUnbondingTime()) + 1 - testStakingInfo := datagen.GenBTCStakingSlashingInfo( + testStakingInfo := testutil.GenBTCStakingSlashingInfo( r, t, btcNetworkParams, @@ -293,7 +294,7 @@ func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKe // delegator sig delegatorSig, err := testStakingInfo.SlashingTx.Sign( testStakingInfo.StakingTx, - 0, + 1, slashignSpendInfo.GetPkScriptPath(), delBtcPrivKey, ) @@ -310,7 +311,7 @@ func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKe fpPks, params.CovenantPks, params.CovenantQuorum, - wire.NewOutPoint(&stakingTxHash, 0), + wire.NewOutPoint(&stakingTxHash, 1), unbondingTime, unbondingValue, params.SlashingAddress.String(), diff --git a/testutil/datagen.go b/testutil/datagen.go index a02be2b..eae3875 100644 --- a/testutil/datagen.go +++ b/testutil/datagen.go @@ -7,14 +7,31 @@ import ( "time" sdkmath "cosmossdk.io/math" + "github.com/babylonchain/babylon/btcstaking" "github.com/babylonchain/babylon/testutil/datagen" + bstypes "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/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/stretchr/testify/require" "github.com/babylonchain/covenant-emulator/types" ) +type TestStakingSlashingInfo struct { + StakingTx *wire.MsgTx + SlashingTx *bstypes.BTCSlashingTx + StakingInfo *btcstaking.StakingInfo +} + +type spendableOut struct { + prevOut wire.OutPoint + amount btcutil.Amount +} + func GenRandomHexStr(r *rand.Rand, length uint64) string { randBytes := datagen.GenRandomByteArray(r, length) return hex.EncodeToString(randBytes) @@ -67,3 +84,124 @@ func GenBtcPublicKeys(r *rand.Rand, t *testing.T, num int) []*btcec.PublicKey { return pks } + +func GenBTCStakingSlashingInfo( + r *rand.Rand, + t testing.TB, + btcNet *chaincfg.Params, + stakerSK *btcec.PrivateKey, + fpPKs []*btcec.PublicKey, + covenantPKs []*btcec.PublicKey, + covenantQuorum uint32, + stakingTimeBlocks uint16, + stakingValue int64, + slashingAddress string, + slashingRate sdkmath.LegacyDec, + slashingChangeLockTime uint16, +) *TestStakingSlashingInfo { + // an arbitrary input + unbondingTxFee := r.Int63n(1000) + 1 + spend := makeSpendableOutWithRandOutPoint(r, btcutil.Amount(stakingValue+unbondingTxFee)) + outPoint := &spend.prevOut + return GenBTCStakingSlashingInfoWithOutPoint( + r, + t, + btcNet, + outPoint, + stakerSK, + fpPKs, + covenantPKs, + covenantQuorum, + stakingTimeBlocks, + stakingValue, + slashingAddress, + slashingRate, + slashingChangeLockTime, + ) +} + +func makeSpendableOutWithRandOutPoint(r *rand.Rand, amount btcutil.Amount) spendableOut { + out := randOutPoint(r) + + return spendableOut{ + prevOut: out, + amount: amount, + } +} + +func GenBTCStakingSlashingInfoWithOutPoint( + r *rand.Rand, + t testing.TB, + btcNet *chaincfg.Params, + outPoint *wire.OutPoint, + stakerSK *btcec.PrivateKey, + fpPKs []*btcec.PublicKey, + covenantPKs []*btcec.PublicKey, + covenantQuorum uint32, + stakingTimeBlocks uint16, + stakingValue int64, + slashingAddress string, + slashingRate sdkmath.LegacyDec, + slashingChangeLockTime uint16, +) *TestStakingSlashingInfo { + + stakingInfo, err := btcstaking.BuildStakingInfo( + stakerSK.PubKey(), + fpPKs, + covenantPKs, + covenantQuorum, + stakingTimeBlocks, + btcutil.Amount(stakingValue), + btcNet, + ) + + require.NoError(t, err) + tx := wire.NewMsgTx(2) + + // 2 outputs for changes and staking output + changeAddrScript, err := datagen.GenRandomPubKeyHashScript(r, btcNet) + require.NoError(t, err) + require.False(t, txscript.GetScriptClass(changeAddrScript) == txscript.NonStandardTy) + + tx.AddTxOut(wire.NewTxOut(10000, changeAddrScript)) // output for change + + // add the given tx input + txIn := wire.NewTxIn(outPoint, nil, nil) + tx.AddTxIn(txIn) + tx.AddTxOut(stakingInfo.StakingOutput) + + // construct slashing tx + slashingAddrBtc, err := btcutil.DecodeAddress(slashingAddress, btcNet) + require.NoError(t, err) + + slashingMsgTx, err := btcstaking.BuildSlashingTxFromStakingTxStrict( + tx, + uint32(1), + slashingAddrBtc, + stakerSK.PubKey(), + slashingChangeLockTime, + 2000, + slashingRate, + btcNet) + require.NoError(t, err) + slashingTx, err := bstypes.NewBTCSlashingTxFromMsgTx(slashingMsgTx) + require.NoError(t, err) + + return &TestStakingSlashingInfo{ + StakingTx: tx, + SlashingTx: slashingTx, + StakingInfo: stakingInfo, + } +} + +func randOutPoint(r *rand.Rand) wire.OutPoint { + hash, _ := chainhash.NewHash(datagen.GenRandomByteArray(r, chainhash.HashSize)) + // TODO this will be deterministic without seed but for now it is not that + // important + idx := r.Uint32() + + return wire.OutPoint{ + Hash: *hash, + Index: idx, + } +}