func (w *AttestClient) findLastUnspent() (bool, btcjson.ListUnspentResult, error) {
	unspent, err := w.MainClient.ListUnspent()
	if err != nil {
		return false, btcjson.ListUnspentResult{}, err
	}
	for _, vout := range unspent {
		txhash, _ := chainhash.NewHashFromStr(vout.TxID)
		if w.verifyTxOnSubchain(*txhash) {
			return true, vout, nil
		}
	}
	return false, btcjson.ListUnspentResult{}, nil
}
Find the latest unspent vout that is on the tip of subchain attestations
package attestation
import (
	"encoding/hex"
	"errors"
	"fmt"
	"math"
	confpkg "mainstay/config"
	"mainstay/crypto"
	"mainstay/log"
	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/btcjson"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/rpcclient"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/hdkeychain"
)
const (
	WarningInsufficientFunds            = `Warning - Last unspent vout value low (less than 100*maxFee target)`
	WarningTopupInfoMissing             = `Warning - Topup Address and/or Topup Script not set in config`
	WarningTopupPkMissing               = `Warning - Topup Private Key not set in config`
	WarningFailureImportingTopupAddress = `Could not import topup address`
	WarningFailedDecodingTopupMultisig  = `Could not decode multisig topup script`
	ErrorInsufficientFunds          = `Insufficient unspent vout value (less than the 5*maxFee target)`
	ErrorMissingMultisig            = `No multisig used - Client must be signer and include private key`
	ErrorFailedDecodingInitMultisig = `Could not decode multisig init script`
	ErrorMissingAddress             = `Client address missing from multisig script`
	ErrorInvalidPk                  = `Invalid private key`
	ErrorFailureImportingPk         = `Could not import initial private key`
	ErrorSigsMissingForTx           = `Missing signatures for transaction`
	ErrorSigsMissingForVin          = `Missing signatures for transaction input`
	ErrorInputMissingForTx          = `Missing input for transaction`
	ErrorInvalidChaincode           = `Invalid chaincode provided`
	ErrorMissingChaincodes          = `Missing chaincodes for pubkeys`
	ErrorTopUpScriptNumSigs         = `Different number of signatures in Init script to top-up script`
)
const Coin = 100000000
type AttestClient struct {
	MainClient *rpcclient.Client
	MainChainCfg *chaincfg.Params
	Fees AttestFees
	txid0           string
	script0         string
	pubkeysExtended []*hdkeychain.ExtendedKey
	pubkeys         []*btcec.PublicKey
	chaincodes      [][]byte
	numOfSigs       int
	addrTopup       string
	scriptTopup     string
	WalletPriv      *btcutil.WIF
	WalletPrivTopup *btcutil.WIF
	WalletChainCode []byte
}
func parseTopupKeys(config *confpkg.Config, isSigner bool) *btcutil.WIF {
	if isSigner {
		pkTopup := config.TopupPK()
		if pkTopup != "" {
			topupWif, errPkWifTopup := crypto.GetWalletPrivKey(pkTopup)
			if errPkWifTopup != nil {
				log.Errorf("%s %s\n%v\n", ErrorInvalidPk, pkTopup, errPkWifTopup)
			}
			return topupWif
		} else {
			log.Warnln(WarningTopupPkMissing)
		}
	}
	return nil
}
func parseMainKeys(config *confpkg.Config, isSigner bool) *btcutil.WIF {
	if isSigner {  
		pk := config.InitPK()
		pkWif, errPkWif := crypto.GetWalletPrivKey(pk)
		if errPkWif != nil {
			log.Errorf("%s %s\n%v\n", ErrorInvalidPk, pk, errPkWif)
		}
		return pkWif
	} else if config.InitScript() == "" {
		log.Error(ErrorMissingMultisig)
	}
	return nil
}
func newNonMultisigAttestClient(config *confpkg.Config, isSigner bool, wif *btcutil.WIF, wifTopup *btcutil.WIF) *AttestClient {
	return &AttestClient{
		MainClient:      config.MainClient(),
		MainChainCfg:    config.MainChainCfg(),
		Fees:            NewAttestFees(config.FeesConfig()),
		txid0:           config.InitTx(),
		script0:         "",
		pubkeysExtended: nil,
		pubkeys:         nil,
		chaincodes:      nil,
		numOfSigs:       1,
		addrTopup:       config.TopupAddress(),
		scriptTopup:     config.TopupScript(),
		WalletPriv:      wif,
		WalletPrivTopup: wifTopup,
		WalletChainCode: []byte{}}
}
func newMultisigAttestClient(config *confpkg.Config, isSigner bool, wif *btcutil.WIF, wifTopup *btcutil.WIF) *AttestClient {
	multisig := config.InitScript()
	pubkeys, numOfSigs := crypto.ParseRedeemScript(multisig)
	if _, topUpnumOfSigs := crypto.ParseRedeemScript(config.TopupScript()); numOfSigs != topUpnumOfSigs {
		log.Errorf("%s. %d != %d", ErrorTopUpScriptNumSigs, numOfSigs, topUpnumOfSigs)
	}
	chaincodesStr := config.InitChaincodes()
	if len(chaincodesStr) != len(pubkeys) {
		log.Errorf("%s %d != %d", ErrorMissingChaincodes, len(chaincodesStr), len(pubkeys))
	}
	chaincodes := make([][]byte, len(pubkeys))
	for i_c := range chaincodesStr {
		ccBytes, ccBytesErr := hex.DecodeString(chaincodesStr[i_c])
		if ccBytesErr != nil || len(ccBytes) != 32 {
			log.Errorf("%s %s", ErrorInvalidChaincode, chaincodesStr[i_c])
		}
		chaincodes[i_c] = append(chaincodes[i_c], ccBytes...)
	}
	var myChaincode []byte
	if isSigner {
		myFound := false
		for i_p, pub := range pubkeys {
			if wif.PrivKey.PubKey().IsEqual(pub) {
				myFound = true
				myChaincode = chaincodes[i_p]
			}
		}
		if !myFound {
			log.Error(ErrorMissingAddress)
		}
	}
	var pubkeysExtended []*hdkeychain.ExtendedKey
	for i_p, pub := range pubkeys {
		pubkeysExtended = append(pubkeysExtended,
			hdkeychain.NewExtendedKey([]byte{}, pub.SerializeCompressed(), chaincodes[i_p], []byte{}, 0, 0, false))
	}
	return &AttestClient{
		MainClient:      config.MainClient(),
		MainChainCfg:    config.MainChainCfg(),
		Fees:            NewAttestFees(config.FeesConfig()),
		txid0:           config.InitTx(),
		script0:         multisig,
		pubkeysExtended: pubkeysExtended,
		pubkeys:         pubkeys,
		chaincodes:      chaincodes,
		numOfSigs:       numOfSigs,
		addrTopup:       config.TopupAddress(),
		scriptTopup:     config.TopupScript(),
		WalletPriv:      wif,
		WalletPrivTopup: wifTopup,
		WalletChainCode: myChaincode}
}
func NewAttestClient(config *confpkg.Config, signerFlag ...bool) *AttestClient {
	isSigner := false
	if len(signerFlag) > 0 {
		isSigner = signerFlag[0]
	}
	topupAddrStr := config.TopupAddress()
	topupScriptStr := config.TopupScript()
	var pkWifTopup *btcutil.WIF
	if topupAddrStr != "" && topupScriptStr != "" {
		log.Infof("*Client* importing top-up addr: %s ...\n", topupAddrStr)
		importErr := config.MainClient().ImportAddressRescan(topupAddrStr, "", false)
		if importErr != nil {
			log.Warnf("%s (%s)\n%v\n", WarningFailureImportingTopupAddress, topupAddrStr, importErr)
		}
		pkWifTopup = parseTopupKeys(config, isSigner)
	} else {
		log.Warnln(WarningTopupInfoMissing)
	}
	multisig := config.InitScript()
	var pkWif = parseMainKeys(config, isSigner)
	if multisig != "" {  
		return newMultisigAttestClient(config, isSigner, pkWif, pkWifTopup)
	}
	return newNonMultisigAttestClient(config, isSigner, pkWif, pkWifTopup)
}
func (w *AttestClient) GetNextAttestationKey(hash chainhash.Hash) (*btcutil.WIF, error) {
	if w.WalletPriv == nil {
		return nil, nil
	}
	extndKey := hdkeychain.NewExtendedKey([]byte{}, w.WalletPriv.PrivKey.Serialize(), w.WalletChainCode, []byte{}, 0, 0, true)
	tweakedExtndKey, tweakErr := crypto.TweakExtendedKey(extndKey, hash.CloneBytes())
	if tweakErr != nil {
		return nil, tweakErr
	}
	tweakedExtndPriv, tweakPrivErr := tweakedExtndKey.ECPrivKey()
	if tweakPrivErr != nil {
		return nil, tweakPrivErr
	}
	tweakedWalletPriv, err := btcutil.NewWIF(tweakedExtndPriv, w.MainChainCfg, w.WalletPriv.CompressPubKey)
	if err != nil {
		return nil, err
	}
	return tweakedWalletPriv, nil
}
func (w *AttestClient) GetNextAttestationAddr(key *btcutil.WIF, hash chainhash.Hash) (
	btcutil.Address, string, error) {
	if len(w.pubkeysExtended) > 0 {
		if hash.IsEqual(&chainhash.Hash{}) {
			multisigAddr, multisigScript := crypto.CreateMultisig(w.pubkeys, w.numOfSigs, w.MainChainCfg)
			return multisigAddr, multisigScript, nil
		}
		var tweakedPubs []*btcec.PublicKey
		hashBytes := hash.CloneBytes()
		for _, pub := range w.pubkeysExtended {
			tweakedKey, tweakErr := crypto.TweakExtendedKey(pub, hashBytes)
			if tweakErr != nil {
				return nil, "", tweakErr
			}
			tweakedPub, tweakPubErr := tweakedKey.ECPubKey()
			if tweakPubErr != nil {
				return nil, "", tweakPubErr
			}
			tweakedPubs = append(tweakedPubs, tweakedPub)
		}
		multisigAddr, redeemScript := crypto.CreateMultisig(
			tweakedPubs, w.numOfSigs, w.MainChainCfg)
		return multisigAddr, redeemScript, nil
	}
	myAddr, myAddrErr := crypto.GetAddressFromPrivKey(key, w.MainChainCfg)
	if myAddrErr != nil {
		return nil, "", myAddrErr
	}
	return myAddr, "", nil
}
func (w *AttestClient) ImportAttestationAddr(addr btcutil.Address, rescan ...bool) error {
	var isRescan = true
	if len(rescan) > 0 {
		isRescan = rescan[0]
	}
	importErr := w.MainClient.ImportAddressRescan(addr.String(), "", isRescan)
	if importErr != nil {
		return importErr
	}
	return nil
}
func (w *AttestClient) createAttestation(paytoaddr btcutil.Address, unspent []btcjson.ListUnspentResult) (
	*wire.MsgTx, error) {
	var inputs []btcjson.TransactionInput
	amounts := map[btcutil.Address]btcutil.Amount{
		paytoaddr: btcutil.Amount(0)}
	for i := 0; i < len(unspent); i++ {
		inputs = append(inputs, btcjson.TransactionInput{
			Txid: unspent[i].TxID,
			Vout: unspent[i].Vout,
		})
		amounts[paytoaddr] += btcutil.Amount(unspent[i].Amount * Coin)
	}
	msgTx, errCreate := w.MainClient.CreateRawTransaction(inputs, amounts, nil)
	if errCreate != nil {
		return nil, errCreate
	}
	msgTx.TxIn[0].Sequence = uint32(math.Pow(2, float64(32))) - 3
	maxFee := calcSignedTxFee(w.Fees.maxFee, msgTx.SerializeSize(),
		len(w.script0)/2, w.numOfSigs, len(inputs))
	if msgTx.TxOut[0].Value < 5*maxFee {
		return nil, errors.New(ErrorInsufficientFunds)
	}
	if msgTx.TxOut[0].Value < 100*maxFee {
		log.Warnln(WarningInsufficientFunds)
	}
	feePerByte := w.Fees.GetFee()
	fee := calcSignedTxFee(feePerByte, msgTx.SerializeSize(),
		len(w.script0)/2, w.numOfSigs, len(inputs))
	msgTx.TxOut[0].Value -= fee
	return msgTx, nil
}
func (w *AttestClient) bumpAttestationFees(msgTx *wire.MsgTx, isFeeBumped bool) error {
	for i := 0; i < len(msgTx.TxIn); i++ {
		msgTx.TxIn[i].SignatureScript = []byte{}
	}
	var prevFeePerByte int
	if isFeeBumped {
		prevFeePerByte = w.Fees.GetPrevFee()
	} else {
		prevFeePerByte = w.Fees.GetFee()
		w.Fees.BumpFee()
	}
	feePerByteIncrement := w.Fees.GetFee() - prevFeePerByte
	feeIncrement := calcSignedTxFee(feePerByteIncrement, msgTx.SerializeSize(),
		len(w.script0)/2, w.numOfSigs, len(msgTx.TxIn))
	msgTx.TxOut[0].Value -= feeIncrement
	return nil
}
func calcSignedTxSize(unsignedTxSize int, scriptSize int, numOfSigs int, numOfInputs int) int {
	var pushDataSize int
	if pushDataSize = 0; scriptSize > 75 {
		pushDataSize = 1
	}
	scriptSigSize :=   1 + pushDataSize + scriptSize +   1 + numOfSigs*(   1+72)
	scriptSigSizeSize := wire.VarIntSerializeSize(uint64(scriptSigSize)) - 1  
	return unsignedTxSize + (scriptSigSize+scriptSigSizeSize)*numOfInputs
}
func calcSignedTxFee(feePerByte int, unsignedTxSize int, scriptSize int, numOfSigs int, numOfInputs int) int64 {
	return int64(feePerByte * calcSignedTxSize(unsignedTxSize, scriptSize, numOfSigs, numOfInputs))
}
func (w *AttestClient) GetKeyFromHash(hash chainhash.Hash) btcutil.WIF {
	if !hash.IsEqual(&chainhash.Hash{}) {
		extndKey := hdkeychain.NewExtendedKey([]byte{}, w.WalletPriv.PrivKey.Serialize(), w.WalletChainCode, []byte{}, 0, 0, true)
		tweakedExtndKey, _ := crypto.TweakExtendedKey(extndKey, hash.CloneBytes())
		tweakedExtndPriv, _ := tweakedExtndKey.ECPrivKey()
		tweakedKey, _ := btcutil.NewWIF(tweakedExtndPriv, w.MainChainCfg, w.WalletPriv.CompressPubKey)
		return *tweakedKey
	}
	return *w.WalletPriv
}
func (w *AttestClient) GetScriptFromHash(hash chainhash.Hash) (string, error) {
	if !hash.IsEqual(&chainhash.Hash{}) {
		_, redeemScript, scriptErr := w.GetNextAttestationAddr(w.WalletPriv, hash)
		if scriptErr != nil {
			return "", scriptErr
		}
		return redeemScript, nil
	}
	return w.script0, nil
}
func (w *AttestClient) getTransactionPreImages(hash chainhash.Hash, msgTx *wire.MsgTx) ([]wire.MsgTx, error) {
	if len(msgTx.TxIn) <= 0 {
		return []wire.MsgTx{}, errors.New(ErrorInputMissingForTx)
	}
	var preImageTxs []wire.MsgTx
	script, scriptErr := w.GetScriptFromHash(hash)
	if scriptErr != nil {
		return nil, scriptErr
	}
	scriptSer, decodeErr := hex.DecodeString(script)
	if decodeErr != nil {
		return []wire.MsgTx{},
			errors.New(fmt.Sprintf("%s for init script:%s\n", ErrorFailedDecodingInitMultisig, script))
	}
	preImageTx0 := msgTx.Copy()
	preImageTx0.TxIn[0].SignatureScript = scriptSer
	preImageTxs = append(preImageTxs, *preImageTx0)
	if len(msgTx.TxIn) > 1 {
		topupScriptSer, topupDecodeErr := hex.DecodeString(w.scriptTopup)
		if topupDecodeErr != nil {
			log.Warnf("%s %s\n", WarningFailedDecodingTopupMultisig, w.scriptTopup)
			return preImageTxs, nil
		}
		for i := 1; i < len(msgTx.TxIn); i++ {
			preImageTxi := msgTx.Copy()
			preImageTxi.TxIn[i].SignatureScript = topupScriptSer
			preImageTxs = append(preImageTxs, *preImageTxi)
		}
	}
	return preImageTxs, nil
}
func (w *AttestClient) SignTransaction(hash chainhash.Hash, msgTx wire.MsgTx) (
	*wire.MsgTx, string, error) {
	key := w.GetKeyFromHash(hash)
	redeemScript, redeemScriptErr := w.GetScriptFromHash(hash)
	if redeemScriptErr != nil {
		return nil, "", redeemScriptErr
	}
	if len(msgTx.TxIn) <= 0 {
		return nil, "", errors.New(ErrorInputMissingForTx)
	}
	prevTxId := msgTx.TxIn[0].PreviousOutPoint.Hash
	prevTx, prevTxErr := w.MainClient.GetRawTransaction(&prevTxId)
	if prevTxErr != nil {
		return nil, "", prevTxErr
	}
	var inputs []btcjson.RawTxInput  
	var keys []string                
	inputs = append(inputs, btcjson.RawTxInput{prevTxId.String(), 0,
		hex.EncodeToString(prevTx.MsgTx().TxOut[0].PkScript), redeemScript})
	keys = append(keys, key.String())
	for i := 1; i < len(msgTx.TxIn); i++ {
		prevTxId = msgTx.TxIn[i].PreviousOutPoint.Hash
		prevTx, prevTxErr = w.MainClient.GetRawTransaction(&prevTxId)
		if prevTxErr != nil {
			return nil, "", prevTxErr
		}
		inputs = append(inputs, btcjson.RawTxInput{prevTxId.String(), 0,
			hex.EncodeToString(prevTx.MsgTx().TxOut[0].PkScript), w.scriptTopup})
		keys = append(keys, w.WalletPrivTopup.String())
	}
	signedMsgTx, _, errSign := w.MainClient.SignRawTransaction3(
		&msgTx, inputs, keys)
	if errSign != nil {
		return nil, "", errSign
	}
	return signedMsgTx, redeemScript, nil
}
func (w *AttestClient) signAttestation(msgtx *wire.MsgTx, sigs [][]crypto.Sig, hash chainhash.Hash) (
	*wire.MsgTx, error) {
	signedMsgTx := msgtx
	redeemScript, redeemScriptErr := w.GetScriptFromHash(hash)
	if redeemScriptErr != nil {
		return nil, redeemScriptErr
	}
	if w.WalletPriv != nil {  
		var errSign error
		signedMsgTx, redeemScript, errSign = w.SignTransaction(hash, *msgtx)
		if errSign != nil {
			return nil, errSign
		}
	}
	if redeemScript != "" {
		for i := 0; i < len(signedMsgTx.TxIn); i++ {
			mySigs, script := crypto.ParseScriptSig(signedMsgTx.TxIn[i].SignatureScript)
			if len(mySigs) > 0 && len(script) > 0 {
				if len(sigs) > i {
					mySigs = append(mySigs, sigs[i]...)
				}
				if len(mySigs) < w.numOfSigs {
					return nil, errors.New(ErrorSigsMissingForVin)
				}
				combinedScriptSig := crypto.CreateScriptSig(mySigs[:w.numOfSigs], script)
				signedMsgTx.TxIn[i].SignatureScript = combinedScriptSig
			} else {
				if len(sigs) < len(signedMsgTx.TxIn) {
					return nil, errors.New(ErrorSigsMissingForTx)
				}
				if len(sigs[i]) < w.numOfSigs {
					return nil, errors.New(ErrorSigsMissingForVin)
				}
				var redeemScriptBytes []byte
				if i == 0 {
					redeemScriptBytes, _ = hex.DecodeString(redeemScript)
				} else {
					redeemScriptBytes, _ = hex.DecodeString(w.scriptTopup)
				}
				combinedScriptSig := crypto.CreateScriptSig(sigs[i][:w.numOfSigs], redeemScriptBytes)
				signedMsgTx.TxIn[i].SignatureScript = combinedScriptSig
			}
		}
	}
	return signedMsgTx, nil
}
func (w *AttestClient) sendAttestation(msgtx *wire.MsgTx) (chainhash.Hash, error) {
	txhash, errSend := w.MainClient.SendRawTransaction(msgtx, false)
	if errSend != nil {
		return chainhash.Hash{}, errSend
	}
	return *txhash, nil
}
func (w *AttestClient) verifyTxOnSubchain(txid chainhash.Hash) bool {
	if txid.String() == w.txid0 {  
		return true
	} else {
		txraw, err := w.MainClient.GetRawTransaction(&txid)
		if err != nil {
			return false
		}
		prevtxid := txraw.MsgTx().TxIn[0].PreviousOutPoint.Hash
		return w.verifyTxOnSubchain(prevtxid)
	}
	return false
}