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
73 changes: 70 additions & 3 deletions psbt/creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ const MinTxVersion = 1
// input) and transaction version (must be 1 of 2) must be specified here. Note
// that the default nSequence value is wire.MaxTxInSequenceNum. Referencing
// the PSBT BIP, this function serves the roles of the Creator.
func New(inputs []*wire.OutPoint,
outputs []*wire.TxOut, version int32, nLockTime uint32,
nSequences []uint32) (*Packet, error) {
func New(inputs []*wire.OutPoint, outputs []*wire.TxOut, version int32,
nLockTime uint32, nSequences []uint32) (*Packet, error) {

// Create the new struct; the input and output lists will be empty, the
// unsignedTx object must be constructed and serialized, and that
Expand Down Expand Up @@ -50,6 +49,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 +63,68 @@ func New(inputs []*wire.OutPoint,
Unknowns: nil,
}, nil
}

// NewV2 creates a new PSBTv2 Packet.
func NewV2(txVersion int32, fallbackLocktime *uint32,
txModifiable *uint8) (*Packet, error) {

if txVersion < 2 {
return nil, ErrV2TxVersionBelowTwo
}

// For V2, UnsignedTx must be nil and TxVersion is explicitly required.
return &Packet{
Version: PsbtVersion2,
TxVersion: txVersion,
FallbackLocktime: fallbackLocktime,
TxModifiable: txModifiable,
Inputs: []PInput{},
Outputs: []POutput{},
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. As the
// Constructor, it enforces the PSBT_GLOBAL_TX_MODIFIABLE Inputs-Modifiable flag
// (Bit 0) per BIP 370.
func (p *Packet) AddInputV2(input PInput) error {
switch p.Version {
case PsbtVersion2:
// Valid, continue to add the input.

default:
return ErrUnsupportedDynamicAdd
}

// Check the TxModifiable rule before adding
if p.TxModifiable != nil && *p.TxModifiable&FlagInputsModifiable == 0 {
return ErrInputsNotModifiable
}
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.
// As the Constructor, it enforces the PSBT_GLOBAL_TX_MODIFIABLE
// Outputs-Modifiable flag (Bit 1) per BIP 370.
func (p *Packet) AddOutputV2(output POutput) error {
switch p.Version {
case PsbtVersion2:
// Valid, continue to add the output.

default:
return ErrUnsupportedDynamicAdd
}

// Check the TxModifiable rule before adding
if p.TxModifiable != nil && *p.TxModifiable&FlagOutputsModifiable == 0 {
return ErrOutputsNotModifiable
}
p.Outputs = append(p.Outputs, output)
p.OutputCount = uint32(len(p.Outputs))
return nil
}
10 changes: 7 additions & 3 deletions psbt/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ 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
32 changes: 27 additions & 5 deletions psbt/finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ func isFinalizableWitnessInput(pInput *PInput) bool {
if pInput.WitnessScript == nil {
return false
}
} else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) {
} else if txscript.IsPayToWitnessPubKeyHash(
pInput.RedeemScript,
) {

if pInput.WitnessScript != nil {
return false
}
Expand Down Expand Up @@ -111,8 +114,15 @@ 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
if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) {
if p.Version == PsbtVersion0 && p.UnsignedTx == nil {
return false
}
outIndex := p.outIndex(inIndex)

if txscript.IsPayToScriptHash(
pInput.NonWitnessUtxo.TxOut[outIndex].PkScript,
) {

if pInput.RedeemScript == nil {
return false
}
Expand Down Expand Up @@ -186,7 +196,7 @@ 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 {
for i := range p.Inputs {
success, err := MaybeFinalize(p, i)
if err != nil || !success {
return err
Expand Down Expand Up @@ -351,6 +361,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 +506,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 @@ -556,7 +572,10 @@ func finalizeTaprootInput(p *Packet, inIndex int) error {
for idx, scriptSpendSig := range pInput.TaprootScriptSpendSig {
// Make sure that if there are indeed multiple
// signatures, they all reference the same leaf hash.
if !bytes.Equal(scriptSpendSig.LeafHash, targetLeafHash) {
if !bytes.Equal(
scriptSpendSig.LeafHash, targetLeafHash,
) {

return fmt.Errorf("script spend signature %d "+
"references different target leaf "+
"hash than first signature; only one "+
Expand Down Expand Up @@ -590,6 +609,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
Loading