func (b *BlockDAG) connectBlock(node *blockNode, block *soterutil.Block,
	view *UtxoViewpoint, stxos []SpentTxOut) error {
	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())
	curTotalBlks := b.dagSnapshot.BlkCount
	tips := b.dView.tips()
	dagTips := append(tips, node)
	for _, parent := range node.parents {
		for j, node := range dagTips {
			if node == parent {
				copy(dagTips[j:], dagTips[j+1:])
				dagTips[len(dagTips)-1] = nil
				dagTips = dagTips[:len(dagTips)-1]
				break
			}
		}
	}
	dagState := newDAGState(dagTips, curTotalBlks + 1)
	newView := NewUtxoViewpoint()
	err = b.db.Update(func(dbTx database.Tx) error {
		err := dbPutBestState(dbTx, state, node.workSum)
		if err != nil {
			return err
		}
		err = dbPutDAGState(dbTx, dagState)
		if err != nil {
			return err
		}
		err = dbPutBlockIndex(dbTx, block.Hash(), node.height)
		if err != nil {
			return err
		}
		var strHash = block.Hash().String()
		var nodeAdded = b.graph.AddNodeById(strHash)
		if !nodeAdded {
			log.Infof("Node not added to graph: %s", strHash)
		 }
		for _, parent := range block.MsgBlock().Parents.ParentHashes() {
			var edgeAdded = b.graph.AddEdgeById(strHash, parent.String())
			if !edgeAdded {
				log.Infof("Edge not added to graph, from %s to %s", strHash, parent.String())
			}
		}
		genesisHash := b.dView.Genesis().hash.String()
		_, sortOrder, err := phantom.OrderDAG(b.graph, b.graph.GetNodeById(genesisHash), coloringK, b.blueSet, dagState.MinHeight, b.orderCache)
		if err != nil {
			return err
		}
		sortedHashes := make([]*chainhash.Hash, len(sortOrder))
		for i, node := range sortOrder {
			blockHash, err := chainhash.NewHashFromStr(node.GetId())
			if err != nil {
				return err
			}
			sortedHashes[i] = blockHash
			var soterBlock *soterutil.Block
			if block.Hash().IsEqual(blockHash) {
				soterBlock = block
			} else {
				soterBlock, err = b.BlockByHash(blockHash)
				if err != nil {
					return err
				}
			}
			err = newView.connectTransactionsForSorting(soterBlock, nil, b.chainParams)
			if err != nil {
				return err
			}
		}
		b.nodeOrder = sortedHashes
		err = dbPutUtxoView(dbTx, newView)
		if err != nil {
			return err
		}
		blockStxos, err := b.getAllInputTxos(block, newView)
		if err != nil {
			return err
		}
		err = dbPutSpendJournalEntry(dbTx, block.Hash(), blockStxos)
		if err != nil {
			return err
		}
		if b.indexManager != nil {
			err := b.indexManager.ConnectBlock(dbTx, block, blockStxos)
			if err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		b.graph.RemoveTipById(block.Hash().String())
		return err
	}
	newView.commit()
	b.dView.AddTip(node)
	for _, parent := range node.parents {
		b.dView.RemoveTip(parent)
	}
	b.stateLock.Lock()
	b.stateSnapshot = state
	b.dagSnapshot = dagState
	b.stateLock.Unlock()
	b.chainLock.Unlock()
	b.sendNotification(NTBlockConnected, block)
	b.chainLock.Lock()
	return nil
}
connectBlock handles connecting the passed node/block to the end of the main
(best) chain.

This passed utxo view must have all referenced txos the block spends marked
as spent and all of the new txos the block creates added to it.  In addition,
the passed stxos slice must be populated with all of the information for the
spent txos.  This approach is used because the connection validation that
must happen prior to calling this function requires the same details, so
it would be inefficient to repeat it.

This function MUST be called with the chain state lock held (for writes).
package blockdag
import (
	"container/list"
	"fmt"
	"github.com/soteria-dag/soterd/mining/cuckoo"
	"math"
	"sort"
	"sync"
	"time"
	"github.com/soteria-dag/soterd/blockdag/phantom"
	"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 = 700
	maxGenerationDifference = 70
	coloringK = 3
)
type BlockLocator []*int32
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 DAGState struct {
	Tips      []chainhash.Hash  
	Hash      chainhash.Hash  
	MinHeight int32
	MaxHeight int32
	BlkCount  uint32
}
func newDAGState(tips []*blockNode, blkCount uint32) *DAGState {
	tipHashes := make([]chainhash.Hash, len(tips))
	i := 0
	var maxHeight int32 = math.MinInt32
	var minHeight int32 = math.MaxInt32
	for _, tip := range tips {
		tipHashes[i] = tip.hash
		i++
		if tip.height > maxHeight {
			maxHeight = tip.height
		}
		if tip.height < minHeight {
			minHeight = tip.height
		}
	}
	hash := generateTipsHash(tips)
	return &DAGState{
		Tips: tipHashes,
		Hash: *hash,
		MinHeight: minHeight,
		MaxHeight: maxHeight,
		BlkCount: blkCount,
	}
}
type BlockDAG struct {
	db           database.DB
	chainParams  *chaincfg.Params
	timeSource   MedianTimeSource
	sigCache     *txscript.SigCache
	indexManager IndexManager
	hashCache    *txscript.HashCache
	Solver cuckoo.Solver
	minRetargetTimespan int64  
	maxRetargetTimespan int64  
	blocksPerRetarget   int64  
	chainLock sync.RWMutex
	index             *blockIndex
	dView             *dagView
	graph             *phantom.Graph
	blueSet           *phantom.BlueSetCache
	nodeOrder         []*chainhash.Hash
	orderCache        *phantom.OrderCache
	orphanLock   sync.RWMutex
	orphans      map[chainhash.Hash]*orphanBlock
	prevOrphans  map[chainhash.Hash][]*orphanBlock
	oldestOrphan *orphanBlock
	stateLock     sync.RWMutex
	stateSnapshot *BestState
	dagSnapshot   *DAGState
	warningCaches    []thresholdStateCache
	deploymentCaches []thresholdStateCache
	unknownRulesWarned    bool
	unknownVersionsWarned bool
	notificationsLock sync.RWMutex
	notifications     []NotificationCallback
}
func (b *BlockDAG) visitOrphan(hash *chainhash.Hash, order *[]chainhash.Hash, tempMark, done *map[chainhash.Hash]int) error {
	_, isDone := (*done)[*hash]
	if isDone {
		return nil
	}
	_, inProgress := (*tempMark)[*hash]
	if inProgress {
		return fmt.Errorf("Non-DAG block connectivity detected!")
	}
	(*tempMark)[*hash] = 0
	parents, hasParents := b.prevOrphans[*hash]
	if hasParents {
		for _, parent := range parents {
			err := b.visitOrphan(parent.block.Hash(), order, tempMark, done)
			if err != nil {
				return err
			}
		}
	}
	(*done)[*hash] = 0
	*order = append(*order, *hash)
	return nil
}
func (b *BlockDAG) 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 *BlockDAG) IsKnownOrphan(hash *chainhash.Hash) bool {
	b.orphanLock.RLock()
	_, exists := b.orphans[*hash]
	b.orphanLock.RUnlock()
	return exists
}
func (b *BlockDAG) GetOrphanBlocks() []*soterutil.Block {
	b.orphanLock.RLock()
	defer b.orphanLock.RUnlock()
	var blocks []*soterutil.Block
	for _, orphan := range b.orphans {
		blocks = append(blocks, orphan.block)
	}
	return blocks
}
func (b *BlockDAG) GetOrphanChildren(parent *chainhash.Hash) []*soterutil.Block {
	b.orphanLock.RLock()
	defer b.orphanLock.RUnlock()
	var children []*soterutil.Block
	for _, orphan := range b.orphans {
		if orphan.block.MsgBlock().Parents.IsParent(parent) {
			children = append(children, orphan.block)
		}
	}
	return children
}
func (b *BlockDAG) GetOrphanLocator(hashes []chainhash.Hash) BlockLocator {
	b.orphanLock.RLock()
	defer b.orphanLock.RUnlock()
	locatorDistance := int32(2)
	var minHeight int32
	heightSet := false
	for _, hash := range hashes {
		orphan, exists := b.orphans[hash]
		if !exists {
			continue
		}
		height := orphan.block.Height()
		header := &orphan.block.MsgBlock().Header
		if height == soterutil.BlockHeightUnknown {
			coinbaseTxs := orphan.block.Transactions()
			if !ShouldHaveSerializedBlockHeight(header) || len(coinbaseTxs) == 0 {
				continue
			} else {
				cbHeight, err := ExtractCoinbaseHeight(coinbaseTxs[0])
				if err != nil {
					log.Warnf("Unable to extract height from coinbase tx: %v", err)
					continue
				}
				height = cbHeight
			}
		}
		if !heightSet {
			minHeight = height
			heightSet = true
		} else if height < minHeight {
			minHeight = height
		}
	}
	locatorHeight := minHeight - locatorDistance
	if locatorHeight < int32(0) {
		locatorHeight = int32(0)
	}
	locator := b.BlockLocatorFromHeight(locatorHeight)
	return locator
}
func (b *BlockDAG) GetOrphanOrder() ([]chainhash.Hash, error) {
	b.orphanLock.RLock()
	defer b.orphanLock.RUnlock()
	order := make([]chainhash.Hash, 0)
	temp := make(map[chainhash.Hash]int)
	done := make(map[chainhash.Hash]int)
	for hash := range b.orphans {
		err := b.visitOrphan(&hash, &order, &temp, &done)
		if err != nil {
			return order, err
		}
	}
	return order, nil
}
func (b *BlockDAG) GetOrphanRoot(hash *chainhash.Hash) []chainhash.Hash {
	b.orphanLock.RLock()
	defer b.orphanLock.RUnlock()
	children := make(map[chainhash.Hash][]chainhash.Hash)
	orphanRootMap := make(map[chainhash.Hash]struct{})
	prevHashes := list.New()
	prevHashes.PushBack(*hash)
	for prevHashes.Len() > 0 {
		prevHash := prevHashes.Front().Value.(chainhash.Hash)
		prevHashes.Remove(prevHashes.Front())
		orphan, exists := b.orphans[prevHash]
		if !exists {
			continue
		}
		orphanRootMap[prevHash] = struct{}{}
		childNodes, childExists := children[prevHash]
		if childExists {
			for _, child := range childNodes {
				delete(orphanRootMap, child)
			}
		}
		for _, parentHash := range orphan.block.MsgBlock().Parents.ParentHashes() {
			if _, exists := orphanRootMap[parentHash]; !exists {
				prevHashes.PushBack(parentHash)
			}
			if children[parentHash] == nil {
				children[parentHash] = make([]chainhash.Hash, 0)
			}
			children[parentHash] = append(children[parentHash], prevHash)
		}
	}
	orphanRoot := make([]chainhash.Hash, 0)
	for k := range orphanRootMap {
		orphanRoot = append(orphanRoot, k)
	}
	return orphanRoot
}
func (b *BlockDAG) GetOrphans() []*orphanBlock {
	b.orphanLock.RLock()
	defer b.orphanLock.RUnlock()
	var orphans []*orphanBlock
	for _, orphan := range b.orphans {
		orphans = append(orphans, orphan)
	}
	return orphans
}
func (b *BlockDAG) removeOrphanBlock(orphan *orphanBlock) {
	b.orphanLock.Lock()
	defer b.orphanLock.Unlock()
	orphanHash := orphan.block.Hash()
	delete(b.orphans, *orphanHash)
	for _, parentHash := range orphan.block.MsgBlock().Parents.ParentHashes() {
		orphans := b.prevOrphans[parentHash]
		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[parentHash] = orphans
		if len(b.prevOrphans[parentHash]) == 0 {
			delete(b.prevOrphans, parentHash)
		}
	}
}
func (b *BlockDAG) 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
	for _, parentHash := range block.MsgBlock().Parents.ParentHashes() {
		b.prevOrphans[parentHash] = append(b.prevOrphans[parentHash], oBlock)
	}
}
type SequenceLock struct {
	Seconds     int64
	BlockHeight int32
}
func (b *BlockDAG) CalcSequenceLock(tx *soterutil.Tx, utxoView *UtxoViewpoint, mempool bool) (*SequenceLock, error) {
	b.chainLock.Lock()
	defer b.chainLock.Unlock()
	return b.calcSequenceLock(b.dView.Tips(), tx, utxoView, mempool)
}
func (b *BlockDAG) calcSequenceLock(nodes []*blockNode, tx *soterutil.Tx, utxoView *UtxoViewpoint, mempool bool) (*SequenceLock, error) {
	sequenceLock := &SequenceLock{Seconds: -1, BlockHeight: -1}
	csvSoftforkActive := mempool
	if !csvSoftforkActive {
		stateCount := 0
		activeCount := 0
		for _, node := range nodes {
			csvStates, err := b.deploymentStates(node.parents, chaincfg.DeploymentCSV)
			if err != nil {
				return nil, err
			}
			for _, state := range csvStates {
				stateCount++
				if state == ThresholdActive {
					activeCount++
				}
			}
		}
		csvSoftforkActive = stateCount == activeCount
	}
	mTx := tx.MsgTx()
	sequenceLockActive := mTx.Version >= 2 && csvSoftforkActive
	if !sequenceLockActive || IsCoinBase(tx) {
		return sequenceLock, nil
	}
	var highestParent int32
	for _, node := range nodes {
		maxHeight := node.parentsMaxHeight()
		if maxHeight > highestParent {
			highestParent = maxHeight
		}
	}
	nextHeight := highestParent + 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
			}
			var times []time.Time
			for _, node := range nodes {
				for _, blockNode := range node.Ancestors(prevInputHeight) {
					medianTime := blockNode.CalcPastMedianTime()
					times = append(times, medianTime)
				}
			}
			sort.Sort(ByTime(times))
			medianTime := times[len(times)/2]
			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 *BlockDAG) getAllInputTxos(block *soterutil.Block, view *UtxoViewpoint) ([]SpentTxOut, error) {
	stxos := make([]SpentTxOut, countSpentOutputs(block))
	index := 0
	for _, tx:= range block.Transactions() {
		if IsCoinBase(tx) {
			continue
		}
		for _, txIn := range tx.MsgTx().TxIn {
			entry := view.entries[txIn.PreviousOutPoint]
			if entry == nil {
				return nil, AssertError(fmt.Sprintf("view missing input %v",
					txIn.PreviousOutPoint))
			}
			var stxo = SpentTxOut{
				Amount:     entry.Amount(),
				PkScript:   entry.PkScript(),
				Height:     entry.BlockHeight(),
				IsCoinBase: entry.IsCoinBase(),
			}
			stxos[index] = stxo
			index++
		}
	}
	return stxos, nil
}