diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index ae05742bf..7d01f4653 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -1764,3 +1764,101 @@ func copyPsbt(packet *psbt.Packet) (*psbt.Packet, error) { return psbt.NewFromRawBytes(&buf, false) } + +// PrepareAnchoringTemplate creates a BTC level PSBT packet that can be used to +// anchor the given virtual packets. The PSBT packet is created with the +// necessary inputs and outputs to anchor the virtual packets, but without any +// signatures. +func PrepareAnchoringTemplate( + vPackets []*tappsbt.VPacket) (*psbt.Packet, error) { + + // Create output skeleton from the allocations. + var maxOutputIndex uint32 + for _, vPkt := range vPackets { + for _, vOut := range vPkt.Outputs { + if vOut.AnchorOutputIndex > maxOutputIndex { + maxOutputIndex = vOut.AnchorOutputIndex + } + } + } + + txTemplate := wire.NewMsgTx(2) + for i := uint32(0); i <= maxOutputIndex; i++ { + txTemplate.AddTxOut(createDummyOutput()) + } + + btcPacket, err := psbt.NewFromUnsignedTx(txTemplate) + if err != nil { + return nil, fmt.Errorf("unable to make psbt packet: %w", err) + } + + // Populate output keys and values. + for _, vPkt := range vPackets { + for _, vOut := range vPkt.Outputs { + btcOut := &btcPacket.Outputs[vOut.AnchorOutputIndex] + + btcOut.TaprootInternalKey = schnorr.SerializePubKey( + vOut.AnchorOutputInternalKey, + ) + + // TODO: Using the TaprootTapTree for storing the + // encode sibling preimage is wrong! We should require + // the full Tapscript tree to be provided and encode it + // in the BIP-0174 manner. + siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( + vOut.AnchorOutputTapscriptSibling, + ) + if err != nil { + return nil, fmt.Errorf("unable to encode "+ + "sibling preimage: %w", err) + } + btcOut.TaprootTapTree = siblingBytes + } + } + + // Populate inputs. + for pIdx := range vPackets { + for iIdx := range vPackets[pIdx].Inputs { + vIn := vPackets[pIdx].Inputs[iIdx] + anchor := vIn.Anchor + + if HasInput(btcPacket.UnsignedTx, vIn.PrevID.OutPoint) { + continue + } + + btcPacket.Inputs = append(btcPacket.Inputs, psbt.PInput{ + WitnessUtxo: &wire.TxOut{ + Value: int64(anchor.Value), + PkScript: anchor.PkScript, + }, + SighashType: anchor.SigHashType, + Bip32Derivation: anchor.Bip32Derivation, + TaprootBip32Derivation: anchor.TrBip32Derivation, + TaprootInternalKey: schnorr.SerializePubKey( + anchor.InternalKey, + ), + TaprootMerkleRoot: anchor.MerkleRoot, + }) + btcPacket.UnsignedTx.TxIn = append( + btcPacket.UnsignedTx.TxIn, &wire.TxIn{ + PreviousOutPoint: vIn.PrevID.OutPoint, + }, + ) + + } + } + + return btcPacket, nil +} + +// HasInput returns true if the given transaction has an input that spends the +// given outpoint. +func HasInput(tx *wire.MsgTx, outpoint wire.OutPoint) bool { + for _, in := range tx.TxIn { + if in.PreviousOutPoint == outpoint { + return true + } + } + + return false +}