decred.org/dcrwallet/v3@v3.1.0/wallet/udb/kahnsort.go (about)

     1  // Copyright (c) 2016 The btcsuite developers
     2  // Copyright (c) 2016 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package udb
     7  
     8  import (
     9  	"github.com/decred/dcrd/chaincfg/chainhash"
    10  )
    11  
    12  type graphNode struct {
    13  	value    *TxRecord
    14  	outEdges []*chainhash.Hash
    15  	inDegree int
    16  }
    17  
    18  type hashGraph map[chainhash.Hash]graphNode
    19  
    20  func makeGraph(set map[chainhash.Hash]*TxRecord) hashGraph {
    21  	graph := make(hashGraph)
    22  
    23  	for _, rec := range set {
    24  		// Add a node for every transaction record.  The output edges
    25  		// and input degree are set by iterating over each record's
    26  		// inputs below.
    27  		if _, ok := graph[rec.Hash]; !ok {
    28  			graph[rec.Hash] = graphNode{value: rec}
    29  		}
    30  
    31  	inputLoop:
    32  		for _, input := range rec.MsgTx.TxIn {
    33  			// Transaction inputs that reference transactions not
    34  			// included in the set do not create any (local) graph
    35  			// edges.
    36  			if _, ok := set[input.PreviousOutPoint.Hash]; !ok {
    37  				continue
    38  			}
    39  
    40  			inputNode := graph[input.PreviousOutPoint.Hash]
    41  
    42  			// Skip duplicate edges.
    43  			for _, outEdge := range inputNode.outEdges {
    44  				if *outEdge == input.PreviousOutPoint.Hash {
    45  					continue inputLoop
    46  				}
    47  			}
    48  
    49  			// Mark a directed edge from the previous transaction
    50  			// hash to this transaction record and increase the
    51  			// input degree for this record's node.
    52  			inputRec := inputNode.value
    53  			if inputRec == nil {
    54  				inputRec = set[input.PreviousOutPoint.Hash]
    55  			}
    56  			graph[input.PreviousOutPoint.Hash] = graphNode{
    57  				value:    inputRec,
    58  				outEdges: append(inputNode.outEdges, &rec.Hash),
    59  				inDegree: inputNode.inDegree,
    60  			}
    61  			node := graph[rec.Hash]
    62  			graph[rec.Hash] = graphNode{
    63  				value:    rec,
    64  				outEdges: node.outEdges,
    65  				inDegree: node.inDegree + 1,
    66  			}
    67  		}
    68  	}
    69  
    70  	return graph
    71  }
    72  
    73  // graphRoots returns the roots of the graph.  That is, it returns the node's
    74  // values for all nodes which contain an input degree of 0.
    75  func graphRoots(graph hashGraph) []*TxRecord {
    76  	roots := make([]*TxRecord, 0, len(graph))
    77  	for _, node := range graph {
    78  		if node.inDegree == 0 {
    79  			roots = append(roots, node.value)
    80  		}
    81  	}
    82  	return roots
    83  }
    84  
    85  // dependencySort topologically sorts a set of transaction records by their
    86  // dependency order.  It is implemented using Kahn's algorithm.
    87  func dependencySort(txs map[chainhash.Hash]*TxRecord) []*TxRecord {
    88  	graph := makeGraph(txs)
    89  	s := graphRoots(graph)
    90  
    91  	// If there are no edges (no transactions from the map reference each
    92  	// other), then Kahn's algorithm is unnecessary.
    93  	if len(s) == len(txs) {
    94  		return s
    95  	}
    96  
    97  	sorted := make([]*TxRecord, 0, len(txs))
    98  	for len(s) != 0 {
    99  		rec := s[0]
   100  		s = s[1:]
   101  		sorted = append(sorted, rec)
   102  
   103  		n := graph[rec.Hash]
   104  		for _, mHash := range n.outEdges {
   105  			m := graph[*mHash]
   106  			if m.inDegree != 0 {
   107  				m.inDegree--
   108  				graph[*mHash] = m
   109  				if m.inDegree == 0 {
   110  					s = append(s, m.value)
   111  				}
   112  			}
   113  		}
   114  	}
   115  	return sorted
   116  }