Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions btcutil/psbt/creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
package psbt

import (
"errors"

"github.com/btcsuite/btcd/wire"
)

Expand Down Expand Up @@ -50,6 +52,9 @@ func New(inputs []*wire.OutPoint,
// two lists, and each one must be of length matching the unsigned
// transaction; the unknown list can be nil.
pInputs := make([]PInput, len(unsignedTx.TxIn))
for i := range pInputs {
pInputs[i].Sequence = nSequences[i]
}
pOutputs := make([]POutput, len(unsignedTx.TxOut))

// This new Psbt is "raw" and contains no key-value fields, so sanity
Expand All @@ -61,3 +66,62 @@ func New(inputs []*wire.OutPoint,
Unknowns: nil,
}, nil
}

// NewV2 creates a new, empty Packet that is pre-configured to adhere to the
// BIP-0370 PSBT Version 2 specification.
func NewV2(txVersion uint32, fallbackLocktime uint32, txModifiable uint8) (*Packet, error) {

if txVersion < 2 {
return nil, errors.New("PSBTv2 requires a transaction version of at least 2")
}
return &Packet{
Version: 2,
TxVersion: txVersion,
FallbackLocktime: fallbackLocktime,
TxModifiable: txModifiable,
Inputs: nil,
Outputs: nil,
XPubs: nil,
Unknowns: nil,
}, nil
}

// AddInputV2 appends a new PInput to a Version 2 PSBT, incrementing the
// internal count. It returns an error if the PSBT is not Version 2.
func (p *Packet) AddInputV2(input PInput) error {
if p.Version != 2 {
return errors.New("cannot dynamically add inputs to a non-v2 PSBT")
}
p.Inputs = append(p.Inputs, input)
p.InputCount = uint32(len(p.Inputs))
return nil
}

// AddOutputV2 appends a new POutput to a Version 2 PSBT, incrementing the
// internal count. It returns an error if the PSBT is not Version 2.
func (p *Packet) AddOutputV2(output POutput) error {
if p.Version != 2 {
return errors.New("cannot dynamically add outputs to a non-v2 PSBT")
}
p.Outputs = append(p.Outputs, output)
p.OutputCount = uint32(len(p.Outputs))
return nil
}

// AddInput adds a new input to a Version 2 PSBT using a standard wire.OutPoint.
func (p *Packet) AddInput(outPoint wire.OutPoint, sequence uint32) error {
return p.AddInputV2(PInput{
PreviousTxid: outPoint.Hash[:],
OutputIndex: outPoint.Index,
Sequence: sequence,
})
}

// AddOutput adds a new output to a Version 2 PSBT using a standard amount and
// script.
func (p *Packet) AddOutput(amount int64, script []byte) error {
return p.AddOutputV2(POutput{
Amount: amount,
Script: script,
})
}
9 changes: 6 additions & 3 deletions btcutil/psbt/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ func Extract(p *Packet) (*wire.MsgTx, error) {
return nil, ErrIncompletePSBT
}

// First, we'll make a copy of the underlying unsigned transaction (the
// initial template) so we don't mutate it during our activates below.
finalTx := p.UnsignedTx.Copy()
// First, we'll get a fresh copy of the underlying unsigned transaction
// (the initial template) so we don't mutate it during our activates below.
finalTx, err := p.GetUnsignedTx()
if err != nil {
return nil, err
}

// For each input, we'll now populate any relevant witness and
// sigScript data.
Expand Down
24 changes: 22 additions & 2 deletions btcutil/psbt/finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,17 @@ func isFinalizableLegacyInput(p *Packet, pInput *PInput, inIndex int) bool {

// Otherwise, we'll verify that we only have a RedeemScript if the prev
// output script is P2SH.
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
var outIndex uint32
switch p.Version {
case 0:
if p.UnsignedTx == nil {
return false
}
outIndex = p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
default:
outIndex = pInput.OutputIndex
}

if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) {
if pInput.RedeemScript == nil {
return false
Expand Down Expand Up @@ -186,7 +196,8 @@ func MaybeFinalize(p *Packet, inIndex int) (bool, error) {
// MaybeFinalizeAll attempts to finalize all inputs of the psbt.Packet that are
// not already finalized, and returns an error if it fails to do so.
func MaybeFinalizeAll(p *Packet) error {
for i := range p.UnsignedTx.TxIn {
numInputs := len(p.Inputs)
for i := 0; i < numInputs; i++ {
success, err := MaybeFinalize(p, i)
if err != nil || !success {
return err
Expand Down Expand Up @@ -351,6 +362,9 @@ func finalizeNonWitnessInput(p *Packet, inIndex int) error {
newInput := NewPsbtInput(pInput.NonWitnessUtxo, nil)
newInput.FinalScriptSig = sigScript

// Preserve required PSBTv2 fields and unknowns as mandated by BIP-370
newInput.CopyInputFields(&pInput)

// Overwrite the entry in the input list at the correct index. Note
// that this removes all the other entries in the list for this input
// index.
Expand Down Expand Up @@ -493,6 +507,9 @@ func finalizeWitnessInput(p *Packet, inIndex int) error {

newInput.FinalScriptWitness = serializedWitness

// Preserve required PSBTv2 fields and unknowns as mandated by BIP-370
newInput.CopyInputFields(&pInput)

// Finally, we overwrite the entry in the input list at the correct
// index.
p.Inputs[inIndex] = *newInput
Expand Down Expand Up @@ -590,6 +607,9 @@ func finalizeTaprootInput(p *Packet, inIndex int) error {
newInput := NewPsbtInput(nil, pInput.WitnessUtxo)
newInput.FinalScriptWitness = serializedWitness

// Preserve required PSBTv2 fields and unknowns as mandated by BIP-370
newInput.CopyInputFields(pInput)

// Finally, we overwrite the entry in the input list at the correct
// index.
p.Inputs[inIndex] = *newInput
Expand Down
Loading