lbcwallet/wtxmgr/kahnsort.go
2018-05-23 19:38:56 -07:00

114 lines
2.8 KiB
Go

// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wtxmgr
import "github.com/roasbeef/btcd/chaincfg/chainhash"
type graphNode struct {
value *TxRecord
outEdges []*chainhash.Hash
inDegree int
}
type hashGraph map[chainhash.Hash]graphNode
func makeGraph(set map[chainhash.Hash]*TxRecord) hashGraph {
graph := make(hashGraph)
for _, rec := range set {
// Add a node for every transaction record. The output edges
// and input degree are set by iterating over each record's
// inputs below.
if _, ok := graph[rec.Hash]; !ok {
graph[rec.Hash] = graphNode{value: rec}
}
inputLoop:
for _, input := range rec.MsgTx.TxIn {
// Transaction inputs that reference transactions not
// included in the set do not create any (local) graph
// edges.
if _, ok := set[input.PreviousOutPoint.Hash]; !ok {
continue
}
inputNode := graph[input.PreviousOutPoint.Hash]
// Skip duplicate edges.
for _, outEdge := range inputNode.outEdges {
if *outEdge == input.PreviousOutPoint.Hash {
continue inputLoop
}
}
// Mark a directed edge from the previous transaction
// hash to this transaction record and increase the
// input degree for this record's node.
inputRec := inputNode.value
if inputRec == nil {
inputRec = set[input.PreviousOutPoint.Hash]
}
graph[input.PreviousOutPoint.Hash] = graphNode{
value: inputRec,
outEdges: append(inputNode.outEdges, &rec.Hash),
inDegree: inputNode.inDegree,
}
node := graph[rec.Hash]
graph[rec.Hash] = graphNode{
value: rec,
outEdges: node.outEdges,
inDegree: node.inDegree + 1,
}
}
}
return graph
}
// graphRoots returns the roots of the graph. That is, it returns the node's
// values for all nodes which contain an input degree of 0.
func graphRoots(graph hashGraph) []*TxRecord {
roots := make([]*TxRecord, 0, len(graph))
for _, node := range graph {
if node.inDegree == 0 {
roots = append(roots, node.value)
}
}
return roots
}
// dependencySort topologically sorts a set of transaction records by their
// dependency order. It is implemented using Kahn's algorithm.
func dependencySort(txs map[chainhash.Hash]*TxRecord) []*TxRecord {
graph := makeGraph(txs)
s := graphRoots(graph)
// If there are no edges (no transactions from the map reference each
// other), then Kahn's algorithm is unnecessary.
if len(s) == len(txs) {
return s
}
sorted := make([]*TxRecord, 0, len(txs))
for len(s) != 0 {
rec := s[0]
s = s[1:]
sorted = append(sorted, rec)
n := graph[rec.Hash]
for _, mHash := range n.outEdges {
m := graph[*mHash]
if m.inDegree != 0 {
m.inDegree--
graph[*mHash] = m
if m.inDegree == 0 {
s = append(s, m.value)
}
}
}
}
return sorted
}