func countSpentOutputs(block *soterutil.Block) int {
	var numSpent int
	for _, tx := range block.Transactions()[1:] {
		numSpent += len(tx.MsgTx().TxIn)
	}
	return numSpent
}
countSpentOutputs returns the number of utxos the passed block spends.
package blockchain
import (
	"container/list"
	"fmt"
	"sync"
	"time"
	"github.com/soteria-dag/soterd/chaincfg"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/database"
	"github.com/soteria-dag/soterd/soterutil"
	"github.com/soteria-dag/soterd/txscript"
	"github.com/soteria-dag/soterd/wire"
)
const (
	maxOrphanBlocks = 100
)
type BlockLocator []*chainhash.Hash
type orphanBlock struct {
	block      *soterutil.Block
	expiration time.Time
}
type BestState struct {
	Hash        chainhash.Hash  
	Height      int32           
	Bits        uint32          
	BlockSize   uint64          
	BlockWeight uint64          
	NumTxns     uint64          
	TotalTxns   uint64          
	MedianTime  time.Time       
}
func newBestState(node *blockNode, blockSize, blockWeight, numTxns,
	totalTxns uint64, medianTime time.Time) *BestState {
	return &BestState{
		Hash:        node.hash,
		Height:      node.height,
		Bits:        node.bits,
		BlockSize:   blockSize,
		BlockWeight: blockWeight,
		NumTxns:     numTxns,
		TotalTxns:   totalTxns,
		MedianTime:  medianTime,
	}
}
type BlockChain struct {
	db           database.DB
	chainParams  *chaincfg.Params
	timeSource   MedianTimeSource
	sigCache     *txscript.SigCache
	indexManager IndexManager
	hashCache    *txscript.HashCache
	minRetargetTimespan int64  
	maxRetargetTimespan int64  
	blocksPerRetarget   int32  
	chainLock sync.RWMutex
	index     *blockIndex
	bestChain *chainView
	orphanLock   sync.RWMutex
	orphans      map[chainhash.Hash]*orphanBlock
	prevOrphans  map[chainhash.Hash][]*orphanBlock
	oldestOrphan *orphanBlock
	stateLock     sync.RWMutex
	stateSnapshot *BestState
	warningCaches    []thresholdStateCache
	deploymentCaches []thresholdStateCache
	unknownRulesWarned    bool
	unknownVersionsWarned bool
	notificationsLock sync.RWMutex
	notifications     []NotificationCallback
}
func (b *BlockChain) HaveBlock(hash *chainhash.Hash) (bool, error) {
	exists, err := b.blockExists(hash)
	if err != nil {
		return false, err
	}
	return exists || b.IsKnownOrphan(hash), nil
}
func (b *BlockChain) IsKnownOrphan(hash *chainhash.Hash) bool {
	b.orphanLock.RLock()
	_, exists := b.orphans[*hash]
	b.orphanLock.RUnlock()
	return exists
}
func (b *BlockChain) GetOrphanRoot(hash *chainhash.Hash) *chainhash.Hash {
	b.orphanLock.RLock()
	defer b.orphanLock.RUnlock()
	orphanRoot := hash
	prevHash := hash
	for {
		orphan, exists := b.orphans[*prevHash]
		if !exists {
			break
		}
		orphanRoot = prevHash
		prevHash = &orphan.block.MsgBlock().Header.PrevBlock
	}
	return orphanRoot
}
func (b *BlockChain) removeOrphanBlock(orphan *orphanBlock) {
	b.orphanLock.Lock()
	defer b.orphanLock.Unlock()
	orphanHash := orphan.block.Hash()
	delete(b.orphans, *orphanHash)
	prevHash := &orphan.block.MsgBlock().Header.PrevBlock
	orphans := b.prevOrphans[*prevHash]
	for i := 0; i < len(orphans); i++ {
		hash := orphans[i].block.Hash()
		if hash.IsEqual(orphanHash) {
			copy(orphans[i:], orphans[i+1:])
			orphans[len(orphans)-1] = nil
			orphans = orphans[:len(orphans)-1]
			i--
		}
	}
	b.prevOrphans[*prevHash] = orphans
	if len(b.prevOrphans[*prevHash]) == 0 {
		delete(b.prevOrphans, *prevHash)
	}
}
func (b *BlockChain) addOrphanBlock(block *soterutil.Block) {
	for _, oBlock := range b.orphans {
		if time.Now().After(oBlock.expiration) {
			b.removeOrphanBlock(oBlock)
			continue
		}
		if b.oldestOrphan == nil || oBlock.expiration.Before(b.oldestOrphan.expiration) {
			b.oldestOrphan = oBlock
		}
	}
	if len(b.orphans)+1 > maxOrphanBlocks {
		b.removeOrphanBlock(b.oldestOrphan)
		b.oldestOrphan = nil
	}
	b.orphanLock.Lock()
	defer b.orphanLock.Unlock()
	expiration := time.Now().Add(time.Hour)
	oBlock := &orphanBlock{
		block:      block,
		expiration: expiration,
	}
	b.orphans[*block.Hash()] = oBlock
	prevHash := &block.MsgBlock().Header.PrevBlock
	b.prevOrphans[*prevHash] = append(b.prevOrphans[*prevHash], oBlock)
}
type SequenceLock struct {
	Seconds     int64
	BlockHeight int32
}
func (b *BlockChain) CalcSequenceLock(tx *soterutil.Tx, utxoView *UtxoViewpoint, mempool bool) (*SequenceLock, error) {
	b.chainLock.Lock()
	defer b.chainLock.Unlock()
	return b.calcSequenceLock(b.bestChain.Tip(), tx, utxoView, mempool)
}
func (b *BlockChain) calcSequenceLock(node *blockNode, tx *soterutil.Tx, utxoView *UtxoViewpoint, mempool bool) (*SequenceLock, error) {
	sequenceLock := &SequenceLock{Seconds: -1, BlockHeight: -1}
	csvSoftforkActive := mempool
	if !csvSoftforkActive {
		csvState, err := b.deploymentState(node.parent, chaincfg.DeploymentCSV)
		if err != nil {
			return nil, err
		}
		csvSoftforkActive = csvState == ThresholdActive
	}
	mTx := tx.MsgTx()
	sequenceLockActive := mTx.Version >= 2 && csvSoftforkActive
	if !sequenceLockActive || IsCoinBase(tx) {
		return sequenceLock, nil
	}
	nextHeight := node.height + 1
	for txInIndex, txIn := range mTx.TxIn {
		utxo := utxoView.LookupEntry(txIn.PreviousOutPoint)
		if utxo == nil {
			str := fmt.Sprintf("output %v referenced from "+
				"transaction %s:%d either does not exist or "+
				"has already been spent", txIn.PreviousOutPoint,
				tx.Hash(), txInIndex)
			return sequenceLock, ruleError(ErrMissingTxOut, str)
		}
		inputHeight := utxo.BlockHeight()
		if inputHeight == 0x7fffffff {
			inputHeight = nextHeight
		}
		sequenceNum := txIn.Sequence
		relativeLock := int64(sequenceNum & wire.SequenceLockTimeMask)
		switch {
		case sequenceNum&wire.SequenceLockTimeDisabled == wire.SequenceLockTimeDisabled:
			continue
		case sequenceNum&wire.SequenceLockTimeIsSeconds == wire.SequenceLockTimeIsSeconds:
			prevInputHeight := inputHeight - 1
			if prevInputHeight < 0 {
				prevInputHeight = 0
			}
			blockNode := node.Ancestor(prevInputHeight)
			medianTime := blockNode.CalcPastMedianTime()
			timeLockSeconds := (relativeLock << wire.SequenceLockTimeGranularity) - 1
			timeLock := medianTime.Unix() + timeLockSeconds
			if timeLock > sequenceLock.Seconds {
				sequenceLock.Seconds = timeLock
			}
		default:
			blockHeight := inputHeight + int32(relativeLock-1)
			if blockHeight > sequenceLock.BlockHeight {
				sequenceLock.BlockHeight = blockHeight
			}
		}
	}
	return sequenceLock, nil
}
func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 {
	if !isSeconds {
		return locktime
	}
	return wire.SequenceLockTimeIsSeconds |
		locktime>>wire.SequenceLockTimeGranularity
}
func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List) {
	attachNodes := list.New()
	detachNodes := list.New()
	if b.index.NodeStatus(node.parent).KnownInvalid() {
		b.index.SetStatusFlags(node, statusInvalidAncestor)
		return detachNodes, attachNodes
	}
	forkNode := b.bestChain.FindFork(node)
	invalidChain := false
	for n := node; n != nil && n != forkNode; n = n.parent {
		if b.index.NodeStatus(n).KnownInvalid() {
			invalidChain = true
			break
		}
		attachNodes.PushFront(n)
	}
	if invalidChain {
		var next *list.Element
		for e := attachNodes.Front(); e != nil; e = next {
			next = e.Next()
			n := attachNodes.Remove(e).(*blockNode)
			b.index.SetStatusFlags(n, statusInvalidAncestor)
		}
		return detachNodes, attachNodes
	}
	for n := b.bestChain.Tip(); n != nil && n != forkNode; n = n.parent {
		detachNodes.PushBack(n)
	}
	return detachNodes, attachNodes
}
func (b *BlockChain) connectBlock(node *blockNode, block *soterutil.Block,
	view *UtxoViewpoint, stxos []SpentTxOut) error {
	prevHash := &block.MsgBlock().Header.PrevBlock
	if !prevHash.IsEqual(&b.bestChain.Tip().hash) {
		return AssertError("connectBlock must be called with a block " +
			"that extends the main chain")
	}
	if len(stxos) != countSpentOutputs(block) {
		return AssertError("connectBlock called with inconsistent " +
			"spent transaction out information")
	}
	if b.isCurrent() {
		if err := b.warnUnknownRuleActivations(node); err != nil {
			return err
		}
		if err := b.warnUnknownVersions(node); err != nil {
			return err
		}
	}
	err := b.index.flushToDB()
	if err != nil {
		return err
	}
	b.stateLock.RLock()
	curTotalTxns := b.stateSnapshot.TotalTxns
	b.stateLock.RUnlock()
	numTxns := uint64(len(block.MsgBlock().Transactions))
	blockSize := uint64(block.MsgBlock().SerializeSize())
	blockWeight := uint64(GetBlockWeight(block))
	state := newBestState(node, blockSize, blockWeight, numTxns,
		curTotalTxns+numTxns, node.CalcPastMedianTime())
	err = b.db.Update(func(dbTx database.Tx) error {
		err := dbPutBestState(dbTx, state, node.workSum)
		if err != nil {
			return err
		}
		err = dbPutBlockIndex(dbTx, block.Hash(), node.height)
		if err != nil {
			return err
		}
		err = dbPutUtxoView(dbTx, view)
		if err != nil {
			return err
		}
		err = dbPutSpendJournalEntry(dbTx, block.Hash(), stxos)
		if err != nil {
			return err
		}
		if b.indexManager != nil {
			err := b.indexManager.ConnectBlock(dbTx, block, stxos)
			if err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return err
	}
	view.commit()
	b.bestChain.SetTip(node)
	b.stateLock.Lock()
	b.stateSnapshot = state
	b.stateLock.Unlock()
	b.chainLock.Unlock()
	b.sendNotification(NTBlockConnected, block)
	b.chainLock.Lock()
	return nil
}
func (b *BlockChain) disconnectBlock(node *blockNode, block *soterutil.Block, view *UtxoViewpoint) error {
	if !node.hash.IsEqual(&b.bestChain.Tip().hash) {
		return AssertError("disconnectBlock must be called with the " +
			"block at the end of the main chain")
	}
	prevNode := node.parent
	var prevBlock *soterutil.Block
	err := b.db.View(func(dbTx database.Tx) error {
		var err error
		prevBlock, err = dbFetchBlockByNode(dbTx, prevNode)
		return err
	})
	if err != nil {
		return err
	}
	err = b.index.flushToDB()
	if err != nil {
		return err
	}
	b.stateLock.RLock()
	curTotalTxns := b.stateSnapshot.TotalTxns
	b.stateLock.RUnlock()
	numTxns := uint64(len(prevBlock.MsgBlock().Transactions))
	blockSize := uint64(prevBlock.MsgBlock().SerializeSize())
	blockWeight := uint64(GetBlockWeight(prevBlock))
	newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions))
	state := newBestState(prevNode, blockSize, blockWeight, numTxns,
		newTotalTxns, prevNode.CalcPastMedianTime())
	err = b.db.Update(func(dbTx database.Tx) error {
		err := dbPutBestState(dbTx, state, node.workSum)
		if err != nil {
			return err
		}
		err = dbRemoveBlockIndex(dbTx, block.Hash(), node.height)
		if err != nil {
			return err
		}
		err = dbPutUtxoView(dbTx, view)
		if err != nil {
			return err
		}
		stxos, err := dbFetchSpendJournalEntry(dbTx, block)
		if err != nil {
			return err
		}
		err = dbRemoveSpendJournalEntry(dbTx, block.Hash())
		if err != nil {
			return err
		}
		if b.indexManager != nil {
			err := b.indexManager.DisconnectBlock(dbTx, block, stxos)
			if err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return err
	}
	view.commit()
	b.bestChain.SetTip(node.parent)
	b.stateLock.Lock()
	b.stateSnapshot = state
	b.stateLock.Unlock()
	b.chainLock.Unlock()
	b.sendNotification(NTBlockDisconnected, block)
	b.chainLock.Lock()
	return nil
}