func NewUnsignedTransaction(outputs []*wire.TxOut, relayFeePerKb dcrutil.Amount,
	fetchInputs InputSource, fetchChange ChangeSource, maxTxSize int) (*AuthoredTx, error) {
	const op errors.Op = "txauthor.NewUnsignedTransaction"
	targetAmount := sumOutputValues(outputs)
	scriptSizes := []int{txsizes.RedeemP2PKHSigScriptSize}
	changeScript, changeScriptVersion, err := fetchChange.Script()
	if err != nil {
		return nil, errors.E(op, err)
	}
	changeScriptSize := fetchChange.ScriptSize()
	maxSignedSize := txsizes.EstimateSerializeSize(scriptSizes, outputs, changeScriptSize)
	targetFee := txrules.FeeForSerializeSize(relayFeePerKb, maxSignedSize)
	for {
		inputDetail, err := fetchInputs(targetAmount + targetFee)
		if err != nil {
			return nil, errors.E(op, err)
		}
		if inputDetail.Amount < targetAmount+targetFee {
			return nil, errors.E(op, errors.InsufficientBalance)
		}
		scriptSizes := make([]int, 0, len(inputDetail.RedeemScriptSizes))
		scriptSizes = append(scriptSizes, inputDetail.RedeemScriptSizes...)
		maxSignedSize = txsizes.EstimateSerializeSize(scriptSizes, outputs, changeScriptSize)
		maxRequiredFee := txrules.FeeForSerializeSize(relayFeePerKb, maxSignedSize)
		remainingAmount := inputDetail.Amount - targetAmount
		if remainingAmount < maxRequiredFee {
			targetFee = maxRequiredFee
			continue
		}
		if maxSignedSize > maxTxSize {
			return nil, errors.E(errors.Invalid, "signed tx size exceeds allowed maximum")
		}
		unsignedTransaction := &wire.MsgTx{
			SerType:  wire.TxSerializeFull,
			Version:  generatedTxVersion,
			TxIn:     inputDetail.Inputs,
			TxOut:    outputs,
			LockTime: 0,
			Expiry:   0,
		}
		changeIndex := -1
		changeAmount := inputDetail.Amount - targetAmount - maxRequiredFee
		if changeAmount != 0 && !txrules.IsDustAmount(changeAmount,
			changeScriptSize, relayFeePerKb) {
			if len(changeScript) > txscript.MaxScriptElementSize {
				return nil, errors.E(errors.Invalid, "script size exceed maximum bytes "+
					"pushable to the stack")
			}
			change := &wire.TxOut{
				Value:    int64(changeAmount),
				Version:  changeScriptVersion,
				PkScript: changeScript,
			}
			l := len(outputs)
			unsignedTransaction.TxOut = append(outputs[:l:l], change)
			changeIndex = l
		} else {
			maxSignedSize = txsizes.EstimateSerializeSize(scriptSizes,
				unsignedTransaction.TxOut, 0)
		}
		return &AuthoredTx{
			Tx:                           unsignedTransaction,
			PrevScripts:                  inputDetail.Scripts,
			TotalInput:                   inputDetail.Amount,
			ChangeIndex:                  changeIndex,
			EstimatedSignedSerializeSize: maxSignedSize,
		}, nil
	}
}
NewUnsignedTransaction creates an unsigned transaction paying to one or more
non-change outputs.  An appropriate transaction fee is included based on the
transaction size.

Transaction inputs are chosen from repeated calls to fetchInputs with
increasing targets amounts.

If any remaining output value can be returned to the wallet via a change
output without violating mempool dust rules, a P2PKH change output is
appended to the transaction outputs.  Since the change output may not be
necessary, fetchChange is called zero or one times to generate this script.
This function must return a P2PKH script or smaller, otherwise fee estimation
will be incorrect.

If successful, the transaction, total input value spent, and all previous
output scripts are returned.  If the input source was unable to provide
enough input value to pay for every output any any necessary fees, an
InputSourceError is returned.
package txauthor
import (
	"decred.org/dcrwallet/v2/errors"
	"decred.org/dcrwallet/v2/wallet/txrules"
	"decred.org/dcrwallet/v2/wallet/txsizes"
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/dcrutil/v4"
	"github.com/decred/dcrd/txscript/v4"
	"github.com/decred/dcrd/txscript/v4/sign"
	"github.com/decred/dcrd/wire"
)
const (
	generatedTxVersion = 1
)
type InputDetail struct {
	Amount            dcrutil.Amount
	Inputs            []*wire.TxIn
	Scripts           [][]byte
	RedeemScriptSizes []int
}
type InputSource func(target dcrutil.Amount) (detail *InputDetail, err error)
type AuthoredTx struct {
	Tx                           *wire.MsgTx
	PrevScripts                  [][]byte
	TotalInput                   dcrutil.Amount
	ChangeIndex                  int  
	EstimatedSignedSerializeSize int
}
type ChangeSource interface {
	Script() (script []byte, version uint16, err error)
	ScriptSize() int
}
func sumOutputValues(outputs []*wire.TxOut) (totalOutput dcrutil.Amount) {
	for _, txOut := range outputs {
		totalOutput += dcrutil.Amount(txOut.Value)
	}
	return totalOutput
}