github.com/lbryio/lbcd@v0.22.119/blockchain/chainquery.go (about) 1 package blockchain 2 3 import ( 4 "sort" 5 "strings" 6 7 btcutil "github.com/lbryio/lbcutil" 8 ) 9 10 type ChainTip struct { // duplicate of btcjson.GetChainTipsResult to avoid circular reference 11 Height int64 12 Hash string 13 BranchLen int64 14 Status string 15 } 16 17 // nodeHeightSorter implements sort.Interface to allow a slice of nodes to 18 // be sorted by height in ascending order. 19 type nodeHeightSorter []ChainTip 20 21 // Len returns the number of nodes in the slice. It is part of the 22 // sort.Interface implementation. 23 func (s nodeHeightSorter) Len() int { 24 return len(s) 25 } 26 27 // Swap swaps the nodes at the passed indices. It is part of the 28 // sort.Interface implementation. 29 func (s nodeHeightSorter) Swap(i, j int) { 30 s[i], s[j] = s[j], s[i] 31 } 32 33 // Less returns whether the node with index i should sort before the node with 34 // index j. It is part of the sort.Interface implementation. 35 func (s nodeHeightSorter) Less(i, j int) bool { 36 // To ensure stable order when the heights are the same, fall back to 37 // sorting based on hash. 38 if s[i].Height == s[j].Height { 39 return strings.Compare(s[i].Hash, s[j].Hash) < 0 40 } 41 return s[i].Height < s[j].Height 42 } 43 44 // ChainTips returns information, in JSON-RPC format, about all the currently 45 // known chain tips in the block index. 46 func (b *BlockChain) ChainTips() []ChainTip { 47 // we need our current tip 48 // we also need all of our orphans that aren't in the prevOrphans 49 var results []ChainTip 50 51 tip := b.bestChain.Tip() 52 results = append(results, ChainTip{ 53 Height: int64(tip.height), 54 Hash: tip.hash.String(), 55 BranchLen: 0, 56 Status: "active", 57 }) 58 59 b.orphanLock.RLock() 60 defer b.orphanLock.RUnlock() 61 62 notInBestChain := func(block *btcutil.Block) bool { 63 node := b.bestChain.NodeByHeight(block.Height()) 64 if node == nil { 65 return false 66 } 67 return node.hash.IsEqual(block.Hash()) 68 } 69 70 for hash, orphan := range b.orphans { 71 if len(b.prevOrphans[hash]) > 0 { 72 continue 73 } 74 fork := orphan.block 75 for fork != nil && notInBestChain(fork) { 76 fork = b.orphans[*fork.Hash()].block 77 } 78 79 result := ChainTip{ 80 Height: int64(orphan.block.Height()), 81 Hash: hash.String(), 82 BranchLen: int64(orphan.block.Height() - fork.Height()), 83 } 84 85 // Determine the status of the chain tip. 86 // 87 // active: 88 // The current best chain tip. 89 // 90 // invalid: 91 // The block or one of its ancestors is invalid. 92 // 93 // headers-only: 94 // The block or one of its ancestors does not have the full block data 95 // available which also means the block can't be validated or 96 // connected. 97 // 98 // valid-fork: 99 // The block is fully validated which implies it was probably part of 100 // main chain at one point and was reorganized. 101 // 102 // valid-headers: 103 // The full block data is available and the header is valid, but the 104 // block was never validated which implies it was probably never part 105 // of the main chain. 106 tipStatus := b.index.LookupNode(&hash).status 107 if tipStatus.KnownInvalid() { 108 result.Status = "invalid" 109 } else if !tipStatus.HaveData() { 110 result.Status = "headers-only" 111 } else if tipStatus.KnownValid() { 112 result.Status = "valid-fork" 113 } else { 114 result.Status = "valid-headers" 115 } 116 117 results = append(results, result) 118 } 119 120 // Generate the results sorted by descending height. 121 sort.Sort(sort.Reverse(nodeHeightSorter(results))) 122 return results 123 }