From 914831e3c83f598d874bbefb0eaec8dff6688457 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Mon, 9 Oct 2023 15:32:44 +0200 Subject: [PATCH] tapfreighter+tapgarden: Add TX sanity checks --- tapfreighter/wallet.go | 17 +++++++++++++++++ tapgarden/caretaker.go | 14 ++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index fdcebf215..c7b29e0a6 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -9,8 +9,10 @@ import ( "sync" "time" + "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -1397,6 +1399,12 @@ func (f *AssetWallet) AnchorVirtualTransactions(ctx context.Context, return nil, fmt.Errorf("unable to extract psbt: %w", err) } + // Final TX sanity check. + err = blockchain.CheckTransactionSanity(btcutil.NewTx(finalTx)) + if err != nil { + return nil, fmt.Errorf("anchor TX failed final checks: %w", err) + } + return &AnchorTransaction{ FundedPsbt: &anchorPkt, FinalTx: finalTx, @@ -1654,6 +1662,15 @@ func addAnchorPsbtInputs(btcPkt *psbt.Packet, vPkt *tappsbt.VPacket, lastIdx := len(btcPkt.UnsignedTx.TxOut) - 1 currentFee := inputAmt - outputAmt feeDelta := int64(requiredFee) - currentFee + changeValue := btcPkt.UnsignedTx.TxOut[lastIdx].Value + + // The fee may exceed the total value of the change output, which means + // this spend is impossible with the given inputs and fee rate. + if changeValue-feeDelta < 0 { + return fmt.Errorf("fee of %d sats exceeds change amount of %d"+ + "sats", requiredFee, changeValue) + } + btcPkt.UnsignedTx.TxOut[lastIdx].Value -= feeDelta log.Infof("Adjusting send pkt by delta of %v from %d sats to %d sats", diff --git a/tapgarden/caretaker.go b/tapgarden/caretaker.go index 7264ee37f..22e4c1cd9 100644 --- a/tapgarden/caretaker.go +++ b/tapgarden/caretaker.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" @@ -697,6 +698,19 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) if err != nil { return 0, fmt.Errorf("unable to sign psbt: %w", err) } + + // Final TX sanity check. + signedTx, err := psbt.Extract(signedPkt) + if err != nil { + return 0, fmt.Errorf("unable to extract psbt: %w", err) + } + + err = blockchain.CheckTransactionSanity(btcutil.NewTx(signedTx)) + if err != nil { + return 0, fmt.Errorf("genesis TX failed final checks: "+ + "%w", err) + } + b.cfg.Batch.GenesisPacket.Pkt = signedPkt // Populate how much this tx paid in on-chain fees.