From 3abced43a5b232d2098515b5b419139545fb58dc Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Mon, 12 Aug 2024 08:30:46 +0200 Subject: [PATCH 1/3] Wip --- walletcontroller/client.go | 110 ++++++++++++++++++++++++++++++++++ walletcontroller/interface.go | 4 ++ 2 files changed, 114 insertions(+) diff --git a/walletcontroller/client.go b/walletcontroller/client.go index 46b9285..cf4143a 100644 --- a/walletcontroller/client.go +++ b/walletcontroller/client.go @@ -1,6 +1,8 @@ package walletcontroller import ( + "bytes" + "encoding/base64" "encoding/hex" "fmt" "sort" @@ -11,8 +13,10 @@ import ( scfg "github.com/babylonlabs-io/btc-staker/stakercfg" "github.com/babylonlabs-io/btc-staker/types" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" @@ -351,3 +355,109 @@ func (w *RpcWalletController) SignOneInputTaprootSpendingTransaction(req *Taproo Signature: sig, }, nil } + +func (w *RpcWalletController) foo(request *TaprootSigningRequest) (*TaprootSigningResult, error) { + if len(request.TxToSign.TxIn) != 1 { + return nil, fmt.Errorf("cannot sign transaction with more than one input") + } + + if !txscript.IsPayToTaproot(request.FundingOutput.PkScript) { + return nil, fmt.Errorf("cannot sign transaction spending non-taproot output") + } + + key, err := w.AddressPublicKey(request.SignerAddress) + + if err != nil { + return nil, fmt.Errorf("failed to get public key for address: %w", err) + } + + psbtPacket, err := psbt.New( + []*wire.OutPoint{&request.TxToSign.TxIn[0].PreviousOutPoint}, + request.TxToSign.TxOut, + request.TxToSign.Version, + request.TxToSign.LockTime, + []uint32{request.TxToSign.TxIn[0].Sequence}, + ) + + if err != nil { + return nil, fmt.Errorf("failed to create PSBT packet with transaction to sign: %w", err) + } + + psbtPacket.Inputs[0].SighashType = txscript.SigHashDefault + psbtPacket.Inputs[0].WitnessUtxo = request.FundingOutput + psbtPacket.Inputs[0].Bip32Derivation = []*psbt.Bip32Derivation{ + { + PubKey: key.SerializeCompressed(), + }, + } + + ctrlBlockBytes, err := request.SpendDescription.ControlBlock.ToBytes() + + if err != nil { + return nil, fmt.Errorf("failed to serialize control block: %w", err) + } + + psbtPacket.Inputs[0].TaprootLeafScript = []*psbt.TaprootTapLeafScript{ + { + ControlBlock: ctrlBlockBytes, + Script: request.SpendDescription.ScriptLeaf.Script, + LeafVersion: request.SpendDescription.ScriptLeaf.LeafVersion, + }, + } + + psbtEncoded, err := psbtPacket.B64Encode() + + if err != nil { + return nil, fmt.Errorf("failed to encode PSBT packet: %w", err) + } + + sign := true + signResult, err := w.Client.WalletProcessPsbt( + psbtEncoded, + &sign, + "DEFAULT", + nil, + ) + + if err != nil { + return nil, fmt.Errorf("failed to sign PSBT packet: %w", err) + } + + decodedBytes, err := base64.StdEncoding.DecodeString(signResult.Psbt) + + if err != nil { + return nil, fmt.Errorf("failed to decode signed PSBT packet from b64: %w", err) + } + + decodedPsbt, err := psbt.NewFromRawBytes(bytes.NewReader(decodedBytes), false) + + if err != nil { + return nil, fmt.Errorf("failed to decode signed PSBT packet from bytes: %w", err) + } + + + if err != nil { + return nil, fmt.Errorf("failed to decode signed PSBT packet: %w", err) + } + + if len(decodedSignedPacket.Inputs[0].TaprootScriptSpendSig) == 0 { + // this can happen if btcwallet does not maintain the private key for the + // for the public in signing request + return nil, fmt.Errorf("no signature found in PSBT packet. Wallet does not maintain covenant public key") + } + + schnorSignature := signedPacket.Inputs[0].TaprootScriptSpendSig[0].Signature + + parsedSignature, err := schnorr.ParseSignature(schnorSignature) + + if err != nil { + return nil, fmt.Errorf("failed to parse schnorr signature in psbt packet: %w", err) + + } + + result := &SigningResult{ + Signature: parsedSignature, + } + + return result, nil +} diff --git a/walletcontroller/interface.go b/walletcontroller/interface.go index 112fdad..935aec1 100644 --- a/walletcontroller/interface.go +++ b/walletcontroller/interface.go @@ -34,6 +34,10 @@ type TaprootSigningResult struct { Signature *schnorr.Signature } +type TaprootSigningResult struct { + Signature *schnorr.Signature +} + type WalletController interface { UnlockWallet(timeoutSecs int64) error AddressPublicKey(address btcutil.Address) (*btcec.PublicKey, error) From f8d7d00d255cad0c4007a141ac66bb951a8a47b9 Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Mon, 12 Aug 2024 12:38:31 +0200 Subject: [PATCH 2/3] Signing through psbts works --- itest/bitcoind_node_setup.go | 2 +- itest/containers/config.go | 2 +- itest/e2e_test.go | 43 +++++++++++-------- staker/babylontypes.go | 12 +++++- staker/stakerapp.go | 18 ++++---- walletcontroller/client.go | 77 ++++++++++++----------------------- walletcontroller/interface.go | 9 ++-- 7 files changed, 79 insertions(+), 84 deletions(-) diff --git a/itest/bitcoind_node_setup.go b/itest/bitcoind_node_setup.go index a8d7eea..f757a77 100644 --- a/itest/bitcoind_node_setup.go +++ b/itest/bitcoind_node_setup.go @@ -88,7 +88,7 @@ func (h *BitcoindTestHandler) CreateWallet(walletName string, passphrase string) // last false on the list will create legacy wallet. This is needed, as currently // we are signing all taproot transactions by dumping the private key and signing it // on app level. Descriptor wallets do not allow dumping private keys. - buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"createwallet", walletName, "false", "false", passphrase, "false", "false"}) + buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"createwallet", walletName, "false", "false", passphrase}) require.NoError(h.t, err) var response CreateWalletResponse diff --git a/itest/containers/config.go b/itest/containers/config.go index 2521e78..c93dbd0 100644 --- a/itest/containers/config.go +++ b/itest/containers/config.go @@ -10,7 +10,7 @@ type ImageConfig struct { //nolint:deadcode const ( dockerBitcoindRepository = "lncm/bitcoind" - dockerBitcoindVersionTag = "v24.0.1" + dockerBitcoindVersionTag = "v26.0" ) // NewImageConfig returns ImageConfig needed for running e2e test. diff --git a/itest/e2e_test.go b/itest/e2e_test.go index 1d95096..d519eda 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -139,7 +139,7 @@ type TestManager struct { Db kvdb.Backend Sa *staker.StakerApp BabylonClient *babylonclient.BabylonController - WalletPrivKey *btcec.PrivateKey + WalletPubKey *btcec.PublicKey MinerAddr btcutil.Address serverStopper *signal.Interceptor wg *sync.WaitGroup @@ -294,7 +294,13 @@ func StartManager( err = walletClient.UnlockWallet(20) require.NoError(t, err) - walletPrivKey, err := c.DumpPrivKey(minerAddressDecoded) + info, err := c.GetAddressInfo(br.Address) + require.NoError(t, err) + + pubKeyHex := *info.PubKey + pubKeyBytes, err := hex.DecodeString(pubKeyHex) + require.NoError(t, err) + walletPubKey, err := btcec.ParsePubKey(pubKeyBytes) require.NoError(t, err) interceptor, err := signal.Intercept() @@ -334,7 +340,7 @@ func StartManager( Db: dbbackend, Sa: stakerApp, BabylonClient: bl, - WalletPrivKey: walletPrivKey.PrivKey, + WalletPubKey: walletPubKey, MinerAddr: minerAddressDecoded, serverStopper: &interceptor, wg: &wg, @@ -750,6 +756,9 @@ func (tm *TestManager) sendWatchedStakingTx( testStakingData *testStakingData, params *babylonclient.StakingParams, ) *chainhash.Hash { + privKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + unbondingTme := uint16(params.FinalizationTimeoutBlocks) + 1 stakingInfo, err := staking.BuildStakingInfo( @@ -812,7 +821,7 @@ func (tm *TestManager) sendWatchedStakingTx( slashSig, err := staking.SignTxWithOneScriptSpendInputFromScript( slashingTx, tx.TxOut[stakingOutputIdx], - tm.WalletPrivKey, + privKey, stakingTxSlashingPathInfo.RevealedLeaf.Script, ) require.NoError(t, err) @@ -858,7 +867,7 @@ func (tm *TestManager) sendWatchedStakingTx( slashUnbondingSig, err := staking.SignTxWithOneScriptSpendInputFromScript( slashUnbondingTx, unbondingTx.TxOut[0], - tm.WalletPrivKey, + privKey, unbondingSlashingPathInfo.RevealedLeaf.Script, ) @@ -871,7 +880,7 @@ func (tm *TestManager) sendWatchedStakingTx( // easy way to generate bip322 sig on backend side pop, err := btcstypes.NewPoPBTC( testStakingData.StakerBabylonAddr, - tm.WalletPrivKey, + privKey, ) require.NoError(t, err) @@ -1077,7 +1086,7 @@ func TestStakingFailures(t *testing.T) { require.NoError(t, err) stakingTime := uint16(staker.GetMinStakingTime(params)) - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 10000, 1) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 10000, 1) fpKey := hex.EncodeToString(schnorr.SerializePubKey(testStakingData.FinalityProviderBtcKeys[0])) tm.createAndRegisterFinalityProviders(t, testStakingData) @@ -1117,7 +1126,7 @@ func TestSendingStakingTransaction(t *testing.T) { require.NoError(t, err) stakingTime := uint16(staker.GetMinStakingTime(params)) - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 10000, 1) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 10000, 1) hashed, err := chainhash.NewHash(datagen.GenRandomByteArray(r, 32)) require.NoError(t, err) @@ -1197,7 +1206,7 @@ func TestMultipleWithdrawableStakingTransactions(t *testing.T) { stakingTime4 := minStakingTime + 2 stakingTime5 := minStakingTime + 3 - testStakingData1 := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime1, 10000, 1) + testStakingData1 := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime1, 10000, 1) testStakingData2 := testStakingData1.withStakingTime(stakingTime2) testStakingData3 := testStakingData1.withStakingTime(stakingTime3) testStakingData4 := testStakingData1.withStakingTime(stakingTime4) @@ -1244,7 +1253,7 @@ func TestMultipleWithdrawableStakingTransactions(t *testing.T) { require.Equal(t, withdrawableTransactionsResp.Transactions[2].TransactionIdx, "4") } -func TestSendingWatchedStakingTransaction(t *testing.T) { +func ATestSendingWatchedStakingTransaction(t *testing.T) { // need to have at least 300 block on testnet as only then segwit is activated. // Mature output is out which has 100 confirmations, which means 200mature outputs // will generate 300 blocks @@ -1257,7 +1266,7 @@ func TestSendingWatchedStakingTransaction(t *testing.T) { params, err := cl.Params() require.NoError(t, err) stakingTime := uint16(staker.GetMinStakingTime(params)) - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 10000, 1) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 10000, 1) tm.createAndRegisterFinalityProviders(t, testStakingData) @@ -1279,7 +1288,7 @@ func TestRestartingTxNotDeepEnough(t *testing.T) { params, err := cl.Params() require.NoError(t, err) stakingTime := uint16(staker.GetMinStakingTime(params)) - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 10000, 1) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 10000, 1) tm.createAndRegisterFinalityProviders(t, testStakingData) txHash := tm.sendStakingTxBTC(t, testStakingData) @@ -1305,7 +1314,7 @@ func TestRestartingTxNotOnBabylon(t *testing.T) { require.NoError(t, err) stakingTime := uint16(staker.GetMinStakingTime(params)) - testStakingData1 := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 10000, 1) + testStakingData1 := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 10000, 1) testStakingData2 := testStakingData1.withStakingAmout(11000) tm.createAndRegisterFinalityProviders(t, testStakingData1) @@ -1347,7 +1356,7 @@ func TestStakingUnbonding(t *testing.T) { require.NoError(t, err) // large staking time stakingTime := uint16(1000) - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 50000, 1) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 50000, 1) tm.createAndRegisterFinalityProviders(t, testStakingData) @@ -1418,7 +1427,7 @@ func TestUnbondingRestartWaitingForSignatures(t *testing.T) { require.NoError(t, err) // large staking time stakingTime := uint16(1000) - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 50000, 1) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 50000, 1) tm.createAndRegisterFinalityProviders(t, testStakingData) @@ -1588,7 +1597,7 @@ func TestSendingStakingTransaction_Restaking(t *testing.T) { stakingTime := uint16(staker.GetMinStakingTime(params)) // restaked to 5 finality providers - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 10000, 5) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 10000, 5) hashed, err := chainhash.NewHash(datagen.GenRandomByteArray(r, 32)) require.NoError(t, err) @@ -1628,7 +1637,7 @@ func TestRecoverAfterRestartDuringWithdrawal(t *testing.T) { require.NoError(t, err) stakingTime := uint16(staker.GetMinStakingTime(params)) - testStakingData := tm.getTestStakingData(t, tm.WalletPrivKey.PubKey(), stakingTime, 10000, 1) + testStakingData := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime, 10000, 1) hashed, err := chainhash.NewHash(datagen.GenRandomByteArray(r, 32)) require.NoError(t, err) diff --git a/staker/babylontypes.go b/staker/babylontypes.go index 5f7b120..4178050 100644 --- a/staker/babylontypes.go +++ b/staker/babylontypes.go @@ -91,6 +91,10 @@ func (app *StakerApp) buildOwnedDelegation( return nil, fmt.Errorf("error signing slashing transaction for staking transaction: %w", err) } + if stakingSlashingSig.Signature == nil { + return nil, fmt.Errorf("failed to receive stakingSlashingSig.Signature ") + } + unbondingSlashingSig, err := app.signTaprootScriptSpendUsingWallet( undelegationDesc.SlashUnbondingTransaction, undelegationDesc.UnbondingTransaction.TxOut[0], @@ -103,13 +107,17 @@ func (app *StakerApp) buildOwnedDelegation( return nil, fmt.Errorf("error signing slashing transaction for unbonding transaction: %w", err) } + if unbondingSlashingSig.Signature == nil { + return nil, fmt.Errorf("failed to receive unbondingSlashingSig.Signature ") + } + dg := createDelegationData( externalData.stakerPublicKey, req.inclusionBlock, req.txIndex, storedTx, stakingSlashingTx, - stakingSlashingSig, + stakingSlashingSig.Signature, externalData.babylonStakerAddr, stakingTxInclusionProof, &cl.UndelegationData{ @@ -117,7 +125,7 @@ func (app *StakerApp) buildOwnedDelegation( UnbondingTxValue: undelegationDesc.UnbondingTxValue, UnbondingTxUnbondingTime: undelegationDesc.UnbondingTxUnbondingTime, SlashUnbondingTransaction: undelegationDesc.SlashUnbondingTransaction, - SlashUnbondingTransactionSig: unbondingSlashingSig, + SlashUnbondingTransactionSig: unbondingSlashingSig.Signature, }, ) diff --git a/staker/stakerapp.go b/staker/stakerapp.go index 57dfb7a..b36880e 100644 --- a/staker/stakerapp.go +++ b/staker/stakerapp.go @@ -944,6 +944,10 @@ func (app *StakerApp) sendUnbondingTxToBtcWithWitness( return fmt.Errorf("failed to send unbondingtx. wallet signing error: %w", err) } + if stakerUnbondingSig.Signature == nil { + return fmt.Errorf("failed to receive stakerUnbondingSig.Signature") + } + covenantSigantures := createWitnessSignaturesForPubKeys( params.CovenantPks, unbondingData.CovenantSignatures, @@ -951,7 +955,7 @@ func (app *StakerApp) sendUnbondingTxToBtcWithWitness( witness, err := unbondingSpendInfo.CreateUnbondingPathWitness( covenantSigantures, - stakerUnbondingSig, + stakerUnbondingSig.Signature, ) if err != nil { @@ -1720,7 +1724,7 @@ func (app *StakerApp) signTaprootScriptSpendUsingWallet( signerAddress btcutil.Address, leaf *txscript.TapLeaf, controlBlock *txscript.ControlBlock, -) (*schnorr.Signature, error) { +) (*walletcontroller.TaprootSigningResult, error) { if err := app.wc.UnlockWallet(defaultWalletUnlockTimeout); err != nil { return nil, fmt.Errorf("failed to unlock wallet before signing: %w", err) @@ -1742,7 +1746,7 @@ func (app *StakerApp) signTaprootScriptSpendUsingWallet( return nil, err } - return resp.Signature, nil + return resp, nil } // SpendStake spends stake identified by stakingTxHash. Stake can be currently locked in @@ -1832,15 +1836,15 @@ func (app *StakerApp) SpendStake(stakingTxHash *chainhash.Hash) (*chainhash.Hash return nil, nil, fmt.Errorf("cannot spend staking output. Error building signature: %w", err) } - witness, err := spendStakeTxInfo.fundingOutputSpendInfo.CreateTimeLockPathWitness( - stakerSig, - ) + if stakerSig.FullInputWitness == nil { + return nil, nil, fmt.Errorf("failed to recevie full witness to spend staking transactions") + } if err != nil { return nil, nil, fmt.Errorf("cannot spend staking output. Error building witness: %w", err) } - spendStakeTxInfo.spendStakeTx.TxIn[0].Witness = witness + spendStakeTxInfo.spendStakeTx.TxIn[0].Witness = stakerSig.FullInputWitness // We do not check if transaction is spendable i.e the staking time has passed // as this is validated in mempool so in of not meeting this time requirement diff --git a/walletcontroller/client.go b/walletcontroller/client.go index cf4143a..2b38122 100644 --- a/walletcontroller/client.go +++ b/walletcontroller/client.go @@ -7,7 +7,6 @@ import ( "fmt" "sort" - staking "github.com/babylonlabs-io/babylon/btcstaking" "github.com/babylonlabs-io/babylon/crypto/bip322" "github.com/babylonlabs-io/btc-staker/stakercfg" scfg "github.com/babylonlabs-io/btc-staker/stakercfg" @@ -323,40 +322,7 @@ func (w *RpcWalletController) OutputSpent( return res == nil, nil } -// TODO: Temporary implementation to encapsulate signing of taproot spending transaction, it will be replaced with PSBT -// signing in the future -func (w *RpcWalletController) SignOneInputTaprootSpendingTransaction(req *TaprootSigningRequest) (*TaprootSigningResult, error) { - if len(req.TxToSign.TxIn) != 1 { - return nil, fmt.Errorf("cannot sign transaction with more than one input") - } - - if !txscript.IsPayToTaproot(req.FundingOutput.PkScript) { - return nil, fmt.Errorf("cannot sign transaction spending non-taproot output") - } - - privKey, err := w.DumpPrivKey(req.SignerAddress) - - if err != nil { - return nil, err - } - - sig, err := staking.SignTxWithOneScriptSpendInputFromTapLeaf( - req.TxToSign, - req.FundingOutput, - privKey.PrivKey, - *req.SpendDescription.ScriptLeaf, - ) - - if err != nil { - return nil, err - } - - return &TaprootSigningResult{ - Signature: sig, - }, nil -} - -func (w *RpcWalletController) foo(request *TaprootSigningRequest) (*TaprootSigningResult, error) { +func (w *RpcWalletController) SignOneInputTaprootSpendingTransaction(request *TaprootSigningRequest) (*TaprootSigningResult, error) { if len(request.TxToSign.TxIn) != 1 { return nil, fmt.Errorf("cannot sign transaction with more than one input") } @@ -435,29 +401,38 @@ func (w *RpcWalletController) foo(request *TaprootSigningRequest) (*TaprootSigni return nil, fmt.Errorf("failed to decode signed PSBT packet from bytes: %w", err) } + // In our signing request we only handle transaction with one input, and request + // signature for one public key, thus we can receive at most one signature from btc + if len(decodedPsbt.Inputs[0].TaprootScriptSpendSig) == 1 { + schnorSignature := decodedPsbt.Inputs[0].TaprootScriptSpendSig[0].Signature - if err != nil { - return nil, fmt.Errorf("failed to decode signed PSBT packet: %w", err) - } + parsedSignature, err := schnorr.ParseSignature(schnorSignature) - if len(decodedSignedPacket.Inputs[0].TaprootScriptSpendSig) == 0 { - // this can happen if btcwallet does not maintain the private key for the - // for the public in signing request - return nil, fmt.Errorf("no signature found in PSBT packet. Wallet does not maintain covenant public key") - } + if err != nil { + return nil, fmt.Errorf("failed to parse schnorr signature in psbt packet: %w", err) + } - schnorSignature := signedPacket.Inputs[0].TaprootScriptSpendSig[0].Signature + return &TaprootSigningResult{ + Signature: parsedSignature, + }, nil + } - parsedSignature, err := schnorr.ParseSignature(schnorSignature) + // decodedPsbt.Inputs[0].TaprootScriptSpendSig was 0, it is possible that script + // required only one signature to build whole witness + if len(decodedPsbt.Inputs[0].FinalScriptWitness) > 0 { + // we go whole witness, return it to the caller + witness, err := bip322.SimpleSigToWitness(decodedPsbt.Inputs[0].FinalScriptWitness) - if err != nil { - return nil, fmt.Errorf("failed to parse schnorr signature in psbt packet: %w", err) + if err != nil { + return nil, fmt.Errorf("failed to parse witness in psbt packet: %w", err) + } + return &TaprootSigningResult{ + FullInputWitness: witness, + }, nil } - result := &SigningResult{ - Signature: parsedSignature, - } + // neither witness, nor signature is filled. + return nil, fmt.Errorf("no signature found in PSBT packet. Wallet can't sign given tx") - return result, nil } diff --git a/walletcontroller/interface.go b/walletcontroller/interface.go index 935aec1..8dd4f4c 100644 --- a/walletcontroller/interface.go +++ b/walletcontroller/interface.go @@ -30,12 +30,11 @@ type TaprootSigningRequest struct { SpendDescription *SpendPathDescription } +// TaprootSigningResult contains result of signing taproot spend through bitcoind +// wallet. It will contain either Signature or FullInputWitness, never both. type TaprootSigningResult struct { - Signature *schnorr.Signature -} - -type TaprootSigningResult struct { - Signature *schnorr.Signature + Signature *schnorr.Signature + FullInputWitness wire.TxWitness } type WalletController interface { From 4704047a47d1da73e724ba38c6f5058453bfba26 Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Mon, 12 Aug 2024 13:14:43 +0200 Subject: [PATCH 3/3] fix watching transactions test --- itest/e2e_test.go | 61 ++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/itest/e2e_test.go b/itest/e2e_test.go index d519eda..9be4f5d 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -20,6 +20,7 @@ import ( "github.com/babylonlabs-io/babylon/crypto/bip322" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" + "github.com/cometbft/cometbft/crypto/tmhash" staking "github.com/babylonlabs-io/babylon/btcstaking" txformat "github.com/babylonlabs-io/babylon/btctxformatter" @@ -756,9 +757,6 @@ func (tm *TestManager) sendWatchedStakingTx( testStakingData *testStakingData, params *babylonclient.StakingParams, ) *chainhash.Hash { - privKey, err := btcec.NewPrivateKey() - require.NoError(t, err) - unbondingTme := uint16(params.FinalizationTimeoutBlocks) + 1 stakingInfo, err := staking.BuildStakingInfo( @@ -818,13 +816,20 @@ func (tm *TestManager) sendWatchedStakingTx( stakingTxSlashingPathInfo, err := stakingInfo.SlashingPathSpendInfo() require.NoError(t, err) - slashSig, err := staking.SignTxWithOneScriptSpendInputFromScript( - slashingTx, - tx.TxOut[stakingOutputIdx], - privKey, - stakingTxSlashingPathInfo.RevealedLeaf.Script, + slashingSigResult, err := tm.Sa.Wallet().SignOneInputTaprootSpendingTransaction( + &walletcontroller.TaprootSigningRequest{ + FundingOutput: stakingInfo.StakingOutput, + TxToSign: slashingTx, + SignerAddress: tm.MinerAddr, + SpendDescription: &walletcontroller.SpendPathDescription{ + ControlBlock: &stakingTxSlashingPathInfo.ControlBlock, + ScriptLeaf: &stakingTxSlashingPathInfo.RevealedLeaf, + }, + }, ) + require.NoError(t, err) + require.NotNil(t, slashingSigResult.Signature) serializedStakingTx, err := utils.SerializeBtcTransaction(tx) require.NoError(t, err) @@ -864,23 +869,35 @@ func (tm *TestManager) sendWatchedStakingTx( ) require.NoError(t, err) - slashUnbondingSig, err := staking.SignTxWithOneScriptSpendInputFromScript( - slashUnbondingTx, - unbondingTx.TxOut[0], - privKey, - unbondingSlashingPathInfo.RevealedLeaf.Script, + slashingUnbondingSigResult, err := tm.Sa.Wallet().SignOneInputTaprootSpendingTransaction( + &walletcontroller.TaprootSigningRequest{ + FundingOutput: unbondingTx.TxOut[0], + TxToSign: slashUnbondingTx, + SignerAddress: tm.MinerAddr, + SpendDescription: &walletcontroller.SpendPathDescription{ + ControlBlock: &unbondingSlashingPathInfo.ControlBlock, + ScriptLeaf: &unbondingSlashingPathInfo.RevealedLeaf, + }, + }, ) + require.NoError(t, err) + require.NotNil(t, slashingUnbondingSigResult.Signature) + serializedUnbondingTx, err := utils.SerializeBtcTransaction(unbondingTx) require.NoError(t, err) serializedSlashUnbondingTx, err := utils.SerializeBtcTransaction(slashUnbondingTx) require.NoError(t, err) - // TODO: Update pop when new version will be ready, for now using schnorr as we don't have - // easy way to generate bip322 sig on backend side - pop, err := btcstypes.NewPoPBTC( - testStakingData.StakerBabylonAddr, - privKey, + babylonAddrHash := tmhash.Sum(testStakingData.StakerBabylonAddr.Bytes()) + + sig, err := tm.Sa.Wallet().SignBip322NativeSegwit(babylonAddrHash, tm.MinerAddr) + require.NoError(t, err) + + pop, err := babylonclient.NewBabylonBip322Pop( + babylonAddrHash, + sig, + tm.MinerAddr, ) require.NoError(t, err) @@ -897,16 +914,16 @@ func (tm *TestManager) sendWatchedStakingTx( hex.EncodeToString(schnorr.SerializePubKey(testStakingData.StakerKey)), fpBTCPKs, hex.EncodeToString(serializedSlashingTx), - hex.EncodeToString(slashSig.Serialize()), + hex.EncodeToString(slashingSigResult.Signature.Serialize()), testStakingData.StakerBabylonAddr.String(), tm.MinerAddr.String(), hex.EncodeToString(pop.BtcSig), hex.EncodeToString(serializedUnbondingTx), hex.EncodeToString(serializedSlashUnbondingTx), - hex.EncodeToString(slashUnbondingSig.Serialize()), + hex.EncodeToString(slashingUnbondingSigResult.Signature.Serialize()), int(unbondingTme), // Use schnor verification - int(btcstypes.BTCSigType_BIP340), + int(btcstypes.BTCSigType_BIP322), ) require.NoError(t, err) @@ -1253,7 +1270,7 @@ func TestMultipleWithdrawableStakingTransactions(t *testing.T) { require.Equal(t, withdrawableTransactionsResp.Transactions[2].TransactionIdx, "4") } -func ATestSendingWatchedStakingTransaction(t *testing.T) { +func TestSendingWatchedStakingTransaction(t *testing.T) { // need to have at least 300 block on testnet as only then segwit is activated. // Mature output is out which has 100 confirmations, which means 200mature outputs // will generate 300 blocks