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  }