func (s *AttestService) doStateInit() {
	log.Infoln("*AttestService* INITIATING ATTESTATION PROCESS")
	unconfirmed, unconfirmedTxid, unconfirmedErr := s.attester.getUnconfirmedTx()
	if s.setFailure(unconfirmedErr) {
		return  
	} else if unconfirmed {  
		s.stateInitUnconfirmed(unconfirmedTxid)
	} else {
		success, unspent, unspentErr := s.attester.findLastUnspent()
		if s.setFailure(unspentErr) {
			return  
		} else if success {
			s.stateInitUnspent(unspent)
		} else {
			s.stateInitWalletFailure()
		}
	}
}
AStateInit
- Check if there are unconfirmed or unspent transactions in the client
- Update server with latest attestation information
- If no transaction found wait, else initiate new attestation
- If no attestation found, check last unconfirmed from db
package attestation
import (
	"bytes"
	"context"
	"errors"
	"sync"
	"time"
	confpkg "mainstay/config"
	"mainstay/log"
	"mainstay/models"
	"github.com/btcsuite/btcd/btcjson"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	_ "github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)
type AttestationState int
const (
	AStateError             AttestationState = -1
	AStateInit              AttestationState = 0
	AStateNextCommitment    AttestationState = 1
	AStateNewAttestation    AttestationState = 2
	AStateSignAttestation   AttestationState = 3
	AStatePreSendStore      AttestationState = 4
	AStateSendAttestation   AttestationState = 5
	AStateAwaitConfirmation AttestationState = 6
	AStateHandleUnconfirmed AttestationState = 7
)
const (
	ErroUnspentNotFound = "No valid unspent found"
	WarningInvalidATimeNewAttestationArg    = "Invalid new attestation time config value"
	WarningInvalidATimeHandleUnconfirmedArg = "Invalid handle unconfirmed time config value"
)
const (
	ATimeFixed = 1 * time.Second
	ATimeSigs = 1 * time.Minute
	ATimeSkip = 1 * time.Minute
	ATimeConfirmation = 15 * time.Minute
	DefaultATimeNewAttestation = 60 * time.Minute
	DefaultATimeHandleUnconfirmed = 60 * time.Minute
)
type AttestService struct {
	ctx context.Context
	wg *sync.WaitGroup
	config *confpkg.Config
	attester *AttestClient
	server *AttestServer
	signer AttestSigner
	state       AttestationState
	attestation *models.Attestation
	errorState  error
	isRegtest   bool
}
var (
	atimeNewAttestation    time.Duration  
	atimeHandleUnconfirmed time.Duration  
	attestDelay time.Duration  
	confirmTime time.Time      
	isFeeBumped bool  
)
func NewAttestService(ctx context.Context, wg *sync.WaitGroup, server *AttestServer, signer AttestSigner, config *confpkg.Config) *AttestService {
	_, errInitTx := chainhash.NewHashFromStr(config.InitTx())
	if errInitTx != nil {
		log.Errorf("Incorrect initial transaction id %s\n", config.InitTx())
	}
	attester := NewAttestClient(config)
	isFeeBumped = false
	atimeNewAttestation = DefaultATimeNewAttestation
	if config.TimingConfig().NewAttestationMinutes > 0 {
		atimeNewAttestation = time.Duration(config.TimingConfig().NewAttestationMinutes) * time.Minute
	} else {
		log.Warnf("%s (%v)\n", WarningInvalidATimeNewAttestationArg, config.TimingConfig().NewAttestationMinutes)
	}
	log.Infof("Time new attestation set to: %v\n", atimeNewAttestation)
	atimeHandleUnconfirmed = DefaultATimeHandleUnconfirmed
	if config.TimingConfig().HandleUnconfirmedMinutes > 0 {
		atimeHandleUnconfirmed = time.Duration(config.TimingConfig().HandleUnconfirmedMinutes) * time.Minute
	} else {
		log.Warnf("%s (%v)\n", WarningInvalidATimeHandleUnconfirmedArg, config.TimingConfig().HandleUnconfirmedMinutes)
	}
	log.Infof("Time handle unconfirmed set to: %v\n", atimeHandleUnconfirmed)
	return &AttestService{ctx, wg, config, attester, server, signer, AStateInit, models.NewAttestationDefault(), nil, config.Regtest()}
}
func (s *AttestService) Run() {
	defer s.wg.Done()
	attestDelay = 10 * time.Second  
	for {  
		timer := time.NewTimer(attestDelay)
		select {
		case <-s.ctx.Done():
			log.Infoln("Shutting down Attestation Service...")
			return
		case <-timer.C:
			s.doAttestation()
			if s.isRegtest {
				attestDelay = 5 * time.Second
			}
			log.Infof("********** sleeping for: %s ...\n", attestDelay.String())
		}
	}
}
func (s *AttestService) doStateError() {
	log.Warnln("*AttestService* ATTESTATION SERVICE FAILURE")
	log.Warnln(s.errorState)
	s.state = AStateInit  
}
func (s *AttestService) stateInitUnconfirmed(unconfirmedTxid chainhash.Hash) {
	commitment, commitmentErr := s.server.GetAttestationCommitment(unconfirmedTxid, false)
	if s.setFailure(commitmentErr) {
		return  
	}
	log.Warnf("********** found unconfirmed attestation: %s\n", unconfirmedTxid.String())
	s.attestation = models.NewAttestation(unconfirmedTxid, &commitment)  
	rawTx, _ := s.config.MainClient().GetRawTransaction(&unconfirmedTxid)
	s.attestation.Tx = *rawTx.MsgTx()  
	lastCommitmentHash, latestErr := s.server.GetLatestAttestationCommitmentHash()
	if s.setFailure(latestErr) {
		return  
	}
	s.signer.SendConfirmedHash((&lastCommitmentHash).CloneBytes())  
	s.state = AStateAwaitConfirmation  
	walletTx, getTxError := s.config.MainClient().GetMempoolEntry(unconfirmedTxid.String())
	if s.setFailure(getTxError) {
		log.Info("********** failed to find unconfirmed transaction in mempool, re-initialising attestation")
		return  
	}
	confirmTime = time.Unix(walletTx.Time, 0)
	feePerByte := int(walletTx.Fee*float64(Coin)) / s.attestation.Tx.SerializeSize()  
	s.attester.Fees.setCurrentFee(feePerByte)
	isFeeBumped = false  
}
func (s *AttestService) stateInitUnspent(unspent btcjson.ListUnspentResult) {
	unspentTxid, _ := chainhash.NewHashFromStr(unspent.TxID)
	commitment, commitmentErr := s.server.GetAttestationCommitment(*unspentTxid)
	if s.setFailure(commitmentErr) {
		return  
	} else if (commitment.GetCommitmentHash() != chainhash.Hash{}) {
		log.Infof("********** found confirmed attestation: %s\n", unspentTxid.String())
		s.attestation = models.NewAttestation(*unspentTxid, &commitment)
		s.attestation.Confirmed = true
		rawTx, _ := s.config.MainClient().GetRawTransaction(unspentTxid)
		walletTx, _ := s.config.MainClient().GetTransaction(unspentTxid)
		s.attestation.Tx = *rawTx.MsgTx()   
		s.attestation.UpdateInfo(walletTx)  
		errUpdate := s.server.UpdateLatestAttestation(*s.attestation)
		if s.setFailure(errUpdate) {
			return  
		}
		s.attester.Fees.ResetFee(s.isRegtest)  
		lastDelay := time.Since(time.Unix(s.attestation.Info.Time, 0))
		if atimeNewAttestation > lastDelay {
			attestDelay = atimeNewAttestation - lastDelay
		}
	} else {
		log.Infoln("********** found unspent transaction, initiating staychain")
		s.attestation = models.NewAttestationDefault()
	}
	confirmedHash := s.attestation.CommitmentHash()
	if (s.attester.txid0 == unspentTxid.String()) {
		log.Infoln("********** found base transaction, blank attestation")		
		confirmedHash = chainhash.Hash{}
	}
	s.signer.SendConfirmedHash((&confirmedHash).CloneBytes())  
	s.state = AStateNextCommitment  
}
func (s *AttestService) stateInitWalletFailure() {
	log.Warnln("********** wallet failure")
	lastCommitmentHash, latestErr := s.server.GetLatestAttestationCommitmentHash()
	if s.setFailure(latestErr) {
		return  
	}
	paytoaddr, _, addrErr := s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), lastCommitmentHash)
	if s.setFailure(addrErr) {
		return  
	}
	log.Infof("********** importing latest confirmed addr: %s ...\n", paytoaddr.String())
	importErr := s.attester.ImportAttestationAddr(paytoaddr)
	if s.setFailure(importErr) {
		return  
	}
	lastCommitmentHash, latestErr = s.server.GetLatestAttestationCommitmentHash(false)
	if s.setFailure(latestErr) {
		return  
	}
	paytoaddr, _, addrErr = s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), lastCommitmentHash)
	if s.setFailure(addrErr) {
		return  
	}
	log.Infof("********** importing latest unconfirmed addr: %s ...\n", paytoaddr.String())
	importErr = s.attester.ImportAttestationAddr(paytoaddr)
	if s.setFailure(importErr) {
		return  
	}
	paytoaddr, _, addrErr = s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), chainhash.Hash{})
	if s.setFailure(addrErr) {
		return  
	}
	log.Infof("********** importing base init addr: %s ...\n", paytoaddr.String())
	importErr = s.attester.ImportAttestationAddr(paytoaddr)
	if s.setFailure(importErr) {
		return  
	}
	s.state = AStateInit  
}