// Copyright (c) 2018 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package psbt

// The Extractor requires provision of a single PSBT
// in which all necessary signatures are encoded, and
// uses it to construct a fully valid network serialized
// transaction.

import (
	"bytes"

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

// Extract takes a finalized psbt.Packet and outputs a finalized transaction
// instance. Note that if the PSBT is in-complete, then an error
// ErrIncompletePSBT will be returned. As the extracted transaction has been
// fully finalized, it will be ready for network broadcast once returned.
func Extract(p *Packet) (*wire.MsgTx, error) {
	// If the packet isn't complete, then we'll return an error as it
	// doesn't have all the required witness data.
	if !p.IsComplete() {
		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()

	// For each input, we'll now populate any relevant witness and
	// sigScript data.
	for i, tin := range finalTx.TxIn {
		// We'll grab the corresponding internal packet input which
		// matches this materialized transaction input and emplace that
		// final sigScript (if present).
		pInput := p.Inputs[i]
		if pInput.FinalScriptSig != nil {
			tin.SignatureScript = pInput.FinalScriptSig
		}

		// Similarly, if there's a final witness, then we'll also need
		// to extract that as well, parsing the lower-level transaction
		// encoding.
		if pInput.FinalScriptWitness != nil {
			// In order to set the witness, need to re-deserialize
			// the field as encoded within the PSBT packet.  For
			// each input, the witness is encoded as a stack with
			// one or more items.
			witnessReader := bytes.NewReader(
				pInput.FinalScriptWitness,
			)

			// First we extract the number of witness elements
			// encoded in the above witnessReader.
			witCount, err := wire.ReadVarInt(witnessReader, 0)
			if err != nil {
				return nil, err
			}

			// Now that we know how many inputs we'll need, we'll
			// construct a packing slice, then read out each input
			// (with a varint prefix) from the witnessReader.
			tin.Witness = make(wire.TxWitness, witCount)
			for j := uint64(0); j < witCount; j++ {
				wit, err := wire.ReadVarBytes(
					witnessReader, 0,
					txscript.MaxScriptSize, "witness",
				)
				if err != nil {
					return nil, err
				}
				tin.Witness[j] = wit
			}
		}
	}

	return finalTx, nil
}
