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 }