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{}