func (w *Wallet) authorTx(ctx context.Context, op errors.Op, a *authorTx) error {
	var unlockOutpoints []*wire.OutPoint
	defer func() {
		for _, op := range unlockOutpoints {
			delete(w.lockedOutpoints, outpoint{op.Hash, op.Index})
		}
		w.lockedOutpointMu.Unlock()
	}()
	ignoreInput := func(op *wire.OutPoint) bool {
		_, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}]
		return ok
	}
	w.lockedOutpointMu.Lock()
	var atx *txauthor.AuthoredTx
	var changeSourceUpdates []func(walletdb.ReadWriteTx) error
	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
		_, tipHeight := w.txStore.MainChainTip(dbtx)
		inputSource := w.txStore.MakeInputSource(txmgrNs, addrmgrNs,
			a.account, a.minconf, tipHeight, ignoreInput)
		var changeSource txauthor.ChangeSource
		if a.isTreasury {
			changeSource = &p2PKHTreasuryChangeSource{
				persist: w.deferPersistReturnedChild(ctx,
					&changeSourceUpdates),
				account: a.changeAccount,
				wallet:  w,
				ctx:     ctx,
			}
		} else {
			changeSource = &p2PKHChangeSource{
				persist: w.deferPersistReturnedChild(ctx,
					&changeSourceUpdates),
				account:   a.changeAccount,
				wallet:    w,
				ctx:       ctx,
				gapPolicy: gapPolicyWrap,
			}
		}
		var err error
		atx, err = txauthor.NewUnsignedTransaction(a.outputs, a.txFee,
			randomInputSource(inputSource.SelectInputs), changeSource,
			w.chainParams.MaxTxSize)
		if err != nil {
			return err
		}
		for _, in := range atx.Tx.TxIn {
			prev := &in.PreviousOutPoint
			w.lockedOutpoints[outpoint{prev.Hash, prev.Index}] = struct{}{}
			unlockOutpoints = append(unlockOutpoints, prev)
		}
		if atx.ChangeIndex >= 0 && a.randomizeChangeIdx {
			atx.RandomizeChangePosition()
		}
		if a.isTreasury {
			if atx.Tx.Version != wire.TxVersion {
				return errors.E(op, "violated assumption: "+
					"expected unsigned tx to be version 1")
			}
			atx.Tx.Version = wire.TxVersionTreasury
		}
		if !a.dontSignTx {
			secrets := &secretSource{Manager: w.manager, addrmgrNs: addrmgrNs}
			err = atx.AddAllInputScripts(secrets)
			for _, done := range secrets.doneFuncs {
				done()
			}
		}
		return err
	})
	if err != nil {
		return errors.E(op, err)
	}
	if atx.ChangeIndex >= 0 && a.account == udb.ImportedAddrAccount {
		changeAmount := dcrutil.Amount(atx.Tx.TxOut[atx.ChangeIndex].Value)
		log.Warnf("Spend from imported account produced change: moving"+
			" %v from imported account into default account.", changeAmount)
	}
	err = w.checkHighFees(atx.TotalInput, atx.Tx)
	if err != nil {
		return errors.E(op, err)
	}
	if !a.dontSignTx {
		err = validateMsgTx(op, atx.Tx, atx.PrevScripts)
		if err != nil {
			return errors.E(op, err)
		}
	}
	a.atx = atx
	a.changeSourceUpdates = changeSourceUpdates
	return nil
}
authorTx creates a (typically signed) transaction which includes each output
from outputs.  Previous outputs to redeem are chosen from the passed
account's UTXO set and minconf policy. An additional output may be added to
return change to the wallet.  An appropriate fee is included based on the
wallet's current relay fee.  The wallet must be unlocked to create the
transaction.
package wallet
import (
	"context"
	"crypto/rand"
	"crypto/tls"
	"encoding/binary"
	"net"
	"sort"
	"time"
	"decred.org/cspp"
	"decred.org/cspp/coinjoin"
	"decred.org/dcrwallet/v2/errors"
	"decred.org/dcrwallet/v2/wallet/txauthor"
	"decred.org/dcrwallet/v2/wallet/txrules"
	"decred.org/dcrwallet/v2/wallet/txsizes"
	"decred.org/dcrwallet/v2/wallet/udb"
	"decred.org/dcrwallet/v2/wallet/walletdb"
	"github.com/decred/dcrd/blockchain/stake/v4"
	blockchain "github.com/decred/dcrd/blockchain/standalone/v2"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/dcrec"
	"github.com/decred/dcrd/dcrutil/v4"
	"github.com/decred/dcrd/txscript/v4"
	"github.com/decred/dcrd/txscript/v4/sign"
	"github.com/decred/dcrd/txscript/v4/stdaddr"
	"github.com/decred/dcrd/wire"
)
const (
	revocationFeeLimit = 1 << 14
	maxStandardTxSize = 100000
	sanityVerifyFlags = txscript.ScriptDiscourageUpgradableNops |
		txscript.ScriptVerifyCleanStack |
		txscript.ScriptVerifyCheckLockTimeVerify |
		txscript.ScriptVerifyCheckSequenceVerify |
		txscript.ScriptVerifyTreasury
)
type Input struct {
	OutPoint wire.OutPoint
	PrevOut  wire.TxOut
}
type OutputSelectionAlgorithm uint
const (
	OutputSelectionAlgorithmDefault = iota
	OutputSelectionAlgorithmAll
)
func (w *Wallet) NewUnsignedTransaction(ctx context.Context, outputs []*wire.TxOut,
	relayFeePerKb dcrutil.Amount, account uint32, minConf int32,
	algo OutputSelectionAlgorithm, changeSource txauthor.ChangeSource, inputSource txauthor.InputSource) (*txauthor.AuthoredTx, error) {
	const op errors.Op = "wallet.NewUnsignedTransaction"
	ignoreInput := func(op *wire.OutPoint) bool {
		_, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}]
		return ok
	}
	defer w.lockedOutpointMu.Unlock()
	w.lockedOutpointMu.Lock()
	var authoredTx *txauthor.AuthoredTx
	var changeSourceUpdates []func(walletdb.ReadWriteTx) error
	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
		_, tipHeight := w.txStore.MainChainTip(dbtx)
		if account != udb.ImportedAddrAccount {
			lastAcct, err := w.manager.LastAccount(addrmgrNs)
			if err != nil {
				return err
			}
			if account > lastAcct {
				return errors.E(errors.NotExist, "missing account")
			}
		}
		if inputSource == nil {
			sourceImpl := w.txStore.MakeInputSource(txmgrNs, addrmgrNs, account,
				minConf, tipHeight, ignoreInput)
			switch algo {
			case OutputSelectionAlgorithmDefault:
				inputSource = randomInputSource(sourceImpl.SelectInputs)
			case OutputSelectionAlgorithmAll:
				inputSource = func(dcrutil.Amount) (*txauthor.InputDetail, error) {
					inputDetail, err := sourceImpl.SelectInputs(dcrutil.MaxAmount)
					if errors.Is(err, errors.InsufficientBalance) {
						err = nil
					}
					return inputDetail, err
				}
			default:
				return errors.E(errors.Invalid,
					errors.Errorf("unknown output selection algorithm %v", algo))
			}
		}
		if changeSource == nil {
			changeSource = &p2PKHChangeSource{
				persist: w.deferPersistReturnedChild(ctx, &changeSourceUpdates),
				account: account,
				wallet:  w,
				ctx:     context.Background(),
			}
		}
		var err error
		authoredTx, err = txauthor.NewUnsignedTransaction(outputs, relayFeePerKb,
			inputSource, changeSource, w.chainParams.MaxTxSize)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, errors.E(op, err)
	}
	if len(changeSourceUpdates) != 0 {
		err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
			for _, up := range changeSourceUpdates {
				err := up(tx)
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			return nil, errors.E(op, err)
		}
	}
	return authoredTx, nil
}
type secretSource struct {
	*udb.Manager
	addrmgrNs walletdb.ReadBucket
	doneFuncs []func()
}
func (s *secretSource) GetKey(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
	privKey, done, err := s.Manager.PrivateKey(s.addrmgrNs, addr)
	if err != nil {
		return nil, 0, false, err
	}
	s.doneFuncs = append(s.doneFuncs, done)
	return privKey.Serialize(), dcrec.STEcdsaSecp256k1, true, nil
}
func (s *secretSource) GetScript(addr stdaddr.Address) ([]byte, error) {
	return s.Manager.RedeemScript(s.addrmgrNs, addr)
}
type CreatedTx struct {
	MsgTx       *wire.MsgTx
	ChangeAddr  stdaddr.Address
	ChangeIndex int  
	Fee         dcrutil.Amount
}
func (w *Wallet) insertIntoTxMgr(dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx) (*udb.TxRecord, error) {
	rec, err := udb.NewTxRecordFromMsgTx(msgTx, time.Now())
	if err != nil {
		return nil, err
	}
	err = w.txStore.InsertMemPoolTx(dbtx, rec)
	if err != nil {
		return nil, err
	}
	return rec, nil
}
func (w *Wallet) insertCreditsIntoTxMgr(op errors.Op, dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx, rec *udb.TxRecord) error {
	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
	for i, output := range msgTx.TxOut {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version,
			output.PkScript, w.chainParams, true)  
		if err != nil {
			continue
		}
		for _, addr := range addrs {
			ma, err := w.manager.Address(addrmgrNs, addr)
			if err == nil {
				err = w.txStore.AddCredit(dbtx, rec, nil,
					uint32(i), ma.Internal(), ma.Account())
				if err != nil {
					return errors.E(op, err)
				}
				err = w.markUsedAddress(op, dbtx, ma)
				if err != nil {
					return err
				}
				log.Debugf("Marked address %v used", addr)
				continue
			}
			if !errors.Is(err, errors.NotExist) {
				return errors.E(op, err)
			}
		}
	}
	return nil
}
func (w *Wallet) insertMultisigOutIntoTxMgr(dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx, index uint32) error {
	rec, err := udb.NewTxRecordFromMsgTx(msgTx, time.Now())
	if err != nil {
		return err
	}
	return w.txStore.AddMultisigOut(dbtx, rec, nil, index)
}
func (w *Wallet) checkHighFees(totalInput dcrutil.Amount, tx *wire.MsgTx) error {
	if w.allowHighFees {
		return nil
	}
	if txrules.PaysHighFees(totalInput, tx) {
		return errors.E(errors.Policy, "high fee")
	}
	return nil
}
func (w *Wallet) publishAndWatch(ctx context.Context, op errors.Op, n NetworkBackend, tx *wire.MsgTx,
	watch []wire.OutPoint) error {
	if n == nil {
		var err error
		n, err = w.NetworkBackend()
		if err != nil {
			return errors.E(op, err)
		}
	}
	err := n.PublishTransactions(ctx, tx)
	if err != nil {
		hash := tx.TxHash()
		log.Errorf("Abandoning transaction %v which failed to publish", &hash)
		if err := w.AbandonTransaction(ctx, &hash); err != nil {
			log.Errorf("Cannot abandon %v: %v", &hash, err)
		}
		return errors.E(op, err)
	}
	_, err = w.watchHDAddrs(ctx, false, n)
	if err != nil {
		log.Errorf("Failed to watch for future address usage after publishing "+
			"transaction: %v", err)
	}
	if len(watch) > 0 {
		err := n.LoadTxFilter(ctx, false, nil, watch)
		if err != nil {
			log.Errorf("Failed to watch outpoints: %v", err)
		}
	}
	return nil
}
type authorTx struct {
	outputs            []*wire.TxOut
	account            uint32
	changeAccount      uint32
	minconf            int32
	randomizeChangeIdx bool
	txFee              dcrutil.Amount
	dontSignTx         bool
	isTreasury         bool
	atx                 *txauthor.AuthoredTx
	changeSourceUpdates []func(walletdb.ReadWriteTx) error
	watch               []wire.OutPoint
}