github.com/decred/dcrlnd@v0.7.6/routing/mock_graph_test.go (about) 1 package routing 2 3 import ( 4 "bytes" 5 "fmt" 6 "testing" 7 8 "github.com/decred/dcrd/dcrutil/v4" 9 "github.com/decred/dcrlnd/channeldb" 10 "github.com/decred/dcrlnd/lnwire" 11 "github.com/decred/dcrlnd/routing/route" 12 ) 13 14 // createPubkey return a new test pubkey. 15 func createPubkey(id byte) route.Vertex { 16 pubkey := route.Vertex{id} 17 return pubkey 18 } 19 20 // mockChannel holds the channel state of a channel in the mock graph. 21 type mockChannel struct { 22 id uint64 23 capacity dcrutil.Amount 24 balance lnwire.MilliAtom 25 } 26 27 // mockNode holds a set of mock channels and routing policies for a node in the 28 // mock graph. 29 type mockNode struct { 30 channels map[route.Vertex]*mockChannel 31 baseFee lnwire.MilliAtom 32 pubkey route.Vertex 33 } 34 35 // newMockNode instantiates a new mock node with a newly generated pubkey. 36 func newMockNode(id byte) *mockNode { 37 pubkey := createPubkey(id) 38 return &mockNode{ 39 channels: make(map[route.Vertex]*mockChannel), 40 pubkey: pubkey, 41 } 42 } 43 44 // fwd simulates an htlc forward through this node. If the from parameter is 45 // nil, this node is considered to be the sender of the payment. The route 46 // parameter describes the remaining route from this node onwards. If route.next 47 // is nil, this node is the final hop. 48 func (m *mockNode) fwd(from *mockNode, route *hop) (htlcResult, error) { 49 next := route.next 50 51 // Get the incoming channel, if any. 52 var inChan *mockChannel 53 if from != nil { 54 inChan = m.channels[from.pubkey] 55 } 56 57 // If there is no next node, this is the final node and we can settle the htlc. 58 if next == nil { 59 // Update the incoming balance. 60 inChan.balance += route.amtToFwd 61 62 return htlcResult{}, nil 63 } 64 65 // Check if the outgoing channel has enough balance. 66 outChan, ok := m.channels[next.node.pubkey] 67 if !ok { 68 return htlcResult{}, 69 fmt.Errorf("%v: unknown next %v", 70 m.pubkey, next.node.pubkey) 71 } 72 if outChan.balance < route.amtToFwd { 73 return htlcResult{ 74 failureSource: m.pubkey, 75 failure: lnwire.NewTemporaryChannelFailure(nil), 76 }, nil 77 } 78 79 // Htlc can be forwarded, update channel balances. 80 outChan.balance -= route.amtToFwd 81 if inChan != nil { 82 inChan.balance += route.amtToFwd 83 } 84 85 // Recursively forward down the given route. 86 result, err := next.node.fwd(m, route.next) 87 if err != nil { 88 return htlcResult{}, err 89 } 90 91 // Revert balances when a failure occurs. 92 if result.failure != nil { 93 outChan.balance += route.amtToFwd 94 if inChan != nil { 95 inChan.balance -= route.amtToFwd 96 } 97 } 98 99 return result, nil 100 } 101 102 // mockGraph contains a set of nodes that together for a mocked graph. 103 type mockGraph struct { 104 t *testing.T 105 nodes map[route.Vertex]*mockNode 106 source *mockNode 107 } 108 109 // newMockGraph instantiates a new mock graph. 110 func newMockGraph(t *testing.T) *mockGraph { 111 return &mockGraph{ 112 nodes: make(map[route.Vertex]*mockNode), 113 t: t, 114 } 115 } 116 117 // addNode adds the given mock node to the network. 118 func (m *mockGraph) addNode(node *mockNode) { 119 m.t.Helper() 120 121 if _, exists := m.nodes[node.pubkey]; exists { 122 m.t.Fatal("node already exists") 123 } 124 m.nodes[node.pubkey] = node 125 } 126 127 // addChannel adds a new channel between two existing nodes on the network. It 128 // sets the channel balance to 50/50%. 129 // 130 // Ignore linter error because addChannel isn't yet called with different 131 // capacities. 132 // nolint:unparam 133 func (m *mockGraph) addChannel(id uint64, node1id, node2id byte, 134 capacity dcrutil.Amount) { 135 136 node1pubkey := createPubkey(node1id) 137 node2pubkey := createPubkey(node2id) 138 139 if _, exists := m.nodes[node1pubkey].channels[node2pubkey]; exists { 140 m.t.Fatal("channel already exists") 141 } 142 if _, exists := m.nodes[node2pubkey].channels[node1pubkey]; exists { 143 m.t.Fatal("channel already exists") 144 } 145 146 m.nodes[node1pubkey].channels[node2pubkey] = &mockChannel{ 147 capacity: capacity, 148 id: id, 149 balance: lnwire.NewMAtomsFromAtoms(capacity / 2), 150 } 151 m.nodes[node2pubkey].channels[node1pubkey] = &mockChannel{ 152 capacity: capacity, 153 id: id, 154 balance: lnwire.NewMAtomsFromAtoms(capacity / 2), 155 } 156 } 157 158 // forEachNodeChannel calls the callback for every channel of the given node. 159 // 160 // NOTE: Part of the routingGraph interface. 161 func (m *mockGraph) forEachNodeChannel(nodePub route.Vertex, 162 cb func(channel *channeldb.DirectedChannel) error) error { 163 164 // Look up the mock node. 165 node, ok := m.nodes[nodePub] 166 if !ok { 167 return channeldb.ErrGraphNodeNotFound 168 } 169 170 // Iterate over all of its channels. 171 for peer, channel := range node.channels { 172 // Lexicographically sort the pubkeys. 173 var node1 route.Vertex 174 if bytes.Compare(nodePub[:], peer[:]) == -1 { 175 node1 = peer 176 } else { 177 node1 = nodePub 178 } 179 180 peerNode := m.nodes[peer] 181 182 // Call the per channel callback. 183 err := cb( 184 &channeldb.DirectedChannel{ 185 ChannelID: channel.id, 186 IsNode1: nodePub == node1, 187 OtherNode: peer, 188 Capacity: channel.capacity, 189 OutPolicySet: true, 190 InPolicy: &channeldb.CachedEdgePolicy{ 191 ChannelID: channel.id, 192 ToNodePubKey: func() route.Vertex { 193 return nodePub 194 }, 195 ToNodeFeatures: lnwire.EmptyFeatureVector(), 196 FeeBaseMAtoms: peerNode.baseFee, 197 }, 198 }, 199 ) 200 if err != nil { 201 return err 202 } 203 } 204 return nil 205 } 206 207 // sourceNode returns the source node of the graph. 208 // 209 // NOTE: Part of the routingGraph interface. 210 func (m *mockGraph) sourceNode() route.Vertex { 211 return m.source.pubkey 212 } 213 214 // fetchNodeFeatures returns the features of the given node. 215 // 216 // NOTE: Part of the routingGraph interface. 217 func (m *mockGraph) fetchNodeFeatures(nodePub route.Vertex) ( 218 *lnwire.FeatureVector, error) { 219 220 return lnwire.EmptyFeatureVector(), nil 221 } 222 223 // htlcResult describes the resolution of an htlc. If failure is nil, the htlc 224 // was settled. 225 type htlcResult struct { 226 failureSource route.Vertex 227 failure lnwire.FailureMessage 228 } 229 230 // hop describes one hop of a route. 231 type hop struct { 232 node *mockNode 233 amtToFwd lnwire.MilliAtom 234 next *hop 235 } 236 237 // sendHtlc sends out an htlc on the mock network and synchronously returns the 238 // final resolution of the htlc. 239 func (m *mockGraph) sendHtlc(route *route.Route) (htlcResult, error) { 240 var next *hop 241 242 // Convert the route into a structure that is suitable for recursive 243 // processing. 244 for i := len(route.Hops) - 1; i >= 0; i-- { 245 routeHop := route.Hops[i] 246 node := m.nodes[routeHop.PubKeyBytes] 247 next = &hop{ 248 node: node, 249 next: next, 250 amtToFwd: routeHop.AmtToForward, 251 } 252 } 253 254 // Create the starting hop instance. 255 source := m.nodes[route.SourcePubKey] 256 next = &hop{ 257 node: source, 258 next: next, 259 amtToFwd: route.TotalAmount, 260 } 261 262 // Recursively walk the path and obtain the htlc resolution. 263 return source.fwd(nil, next) 264 } 265 266 // Compile-time check for the routingGraph interface. 267 var _ routingGraph = &mockGraph{}