func (c *dagView) nodesByHeight(height int32) []*blockNode {
	if height < 0 || height >= int32(len(c.nodes)) {
		return nil
	}
	nodes := c.nodes[height]
	if nodes != nil && len(nodes) > 1 {
		sort.Sort(blockSorter(nodes))
	}
	return nodes
}
nodeByHeight returns the block node at the specified height.  Nil will be
returned if the height does not exist.  This only differs from the exported
version in that it is up to the caller to ensure the lock is held.

This function MUST be called with the view mutex locked (for reads).
package blockdag
import (
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"sort"
	"sync"
)
const approxNodesPerWeek = 6 * 24 * 7  
var log2FloorMasks = []uint32{0xffff0000, 0xff00, 0xf0, 0xc, 0x2}
func fastLog2Floor(n uint32) uint8 {
	rv := uint8(0)
	exponent := uint8(16)
	for i := 0; i < 5; i++ {
		if n&log2FloorMasks[i] != 0 {
			rv += exponent
			n >>= exponent
		}
		exponent >>= 1
	}
	return rv
}
type dagView struct {
	mtx      sync.Mutex
	dagTips  map[*blockNode]struct{}
	nodes    [][]*blockNode
}
func newDAGView(tips []*blockNode) *dagView {
	var c dagView
	c.nodes = make([][]*blockNode, 0)
	c.dagTips = make(map[*blockNode]struct{})
	if tips != nil && len(tips) > 0 {
		for _, tip := range tips {
			c.addTip(tip)
		}
	}
	return &c
}
func (c *dagView) genesis() *blockNode {
	if len(c.nodes) == 0 {
		return nil
	}
	return c.nodes[0][0]
}
func (c *dagView) Genesis() *blockNode {
	c.mtx.Lock()
	genesis := c.genesis()
	c.mtx.Unlock()
	return genesis
}
func (c *dagView) tips() []*blockNode {
	if len(c.nodes) == 0 {
		return nil
	}
	tips := make([]*blockNode, 0, len(c.dagTips))
	for tip := range c.dagTips {
		tips = append(tips, tip)
	}
	sort.Sort(blockSorter(tips))
	return tips
}
func (c *dagView) Tips() []*blockNode {
	c.mtx.Lock()
	tip := c.tips()
	c.mtx.Unlock()
	return tip
}
func (c *dagView) addTip(node *blockNode) {
	needed := node.height + 1
	if int32(cap(c.nodes)) < needed {
		nodes := make([][]*blockNode, needed, needed+approxNodesPerWeek)
		copy(nodes, c.nodes)
		c.nodes = nodes
	} else {
		prevLen := int32(len(c.nodes))
		if prevLen < needed {
			c.nodes = c.nodes[0:needed]
		}
	}
	if node != nil {
		c.checkAndAddNode(node.height, node)
		c.dagTips[node] = struct{}{}
	}
}
func (c *dagView) AddTip(node *blockNode) {
	c.mtx.Lock()
	c.addTip(node)
	c.mtx.Unlock()
}
func (c *dagView) checkAndAddNode(height int32, node *blockNode) bool {
	if node != nil && !c.containsAtHeight(node.height, node) {
		nodes := c.nodes[node.height]
		nodes = append(nodes, node)
		for i, nodeH := range nodes {
			if node.hash.String() < nodeH.hash.String() {  
				copy(nodes[i+1:], nodes[i:])
				nodes[i] = node
				break
			}
		}
		c.nodes[node.height] = nodes
		parents := node.parents
		for _, parent := range parents {
			c.checkAndAddNode(parent.height, parent)
		}
		return true
	}
	return false
}
func (c *dagView) removeTip(node *blockNode) {
	for tip := range c.dagTips {
		if node == tip {
			delete(c.dagTips, node)
		}
	}
}
func (c *dagView) RemoveTip(node *blockNode) {
	c.mtx.Lock()
	c.removeTip(node)
	c.mtx.Unlock()
}
func (c *dagView) containsAtHeight(height int32, node *blockNode) bool {
	if height > int32(len(c.nodes) - 1) {
		return false
	}
	nodes := c.nodes[height]
	for _, nodeH := range nodes {
		if nodeH == node {
			return true
		}
	}
	return false
}
func (c *dagView) height() int32 {
	return int32(len(c.nodes) - 1)
}
func (c *dagView) Height() int32 {
	c.mtx.Lock()
	height := c.height()
	c.mtx.Unlock()
	return height
}