github.com/decred/dcrlnd@v0.7.6/routing/probability_estimator_test.go (about)

     1  package routing
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/decred/dcrlnd/lnwire"
     8  	"github.com/decred/dcrlnd/routing/route"
     9  )
    10  
    11  const (
    12  	// Define node identifiers
    13  	node1 = 1
    14  	node2 = 2
    15  	node3 = 3
    16  
    17  	// untriedNode is a node id for which we don't record any results in
    18  	// this test. This can be used to assert the probability for untried
    19  	// ndoes.
    20  	untriedNode = 255
    21  
    22  	// Define test estimator parameters.
    23  	aprioriHopProb     = 0.6
    24  	aprioriWeight      = 0.75
    25  	aprioriPrevSucProb = 0.95
    26  )
    27  
    28  type estimatorTestContext struct {
    29  	t         *testing.T
    30  	estimator *probabilityEstimator
    31  
    32  	// results contains a list of last results. Every element in the list
    33  	// corresponds to the last result towards a node. The list index equals
    34  	// the node id. So the first element in the list is the result towards
    35  	// node 0.
    36  	results map[int]TimedPairResult
    37  }
    38  
    39  func newEstimatorTestContext(t *testing.T) *estimatorTestContext {
    40  	return &estimatorTestContext{
    41  		t: t,
    42  		estimator: &probabilityEstimator{
    43  			ProbabilityEstimatorCfg: ProbabilityEstimatorCfg{
    44  				AprioriHopProbability: aprioriHopProb,
    45  				AprioriWeight:         aprioriWeight,
    46  				PenaltyHalfLife:       time.Hour,
    47  			},
    48  			prevSuccessProbability: aprioriPrevSucProb,
    49  		},
    50  	}
    51  }
    52  
    53  // assertPairProbability asserts that the calculated success probability is
    54  // correct.
    55  func (c *estimatorTestContext) assertPairProbability(now time.Time,
    56  	toNode byte, amt lnwire.MilliAtom, expectedProb float64) {
    57  
    58  	c.t.Helper()
    59  
    60  	results := make(NodeResults)
    61  	for i, r := range c.results {
    62  		results[route.Vertex{byte(i)}] = r
    63  	}
    64  
    65  	const tolerance = 0.01
    66  
    67  	p := c.estimator.getPairProbability(now, results, route.Vertex{toNode}, amt)
    68  	diff := p - expectedProb
    69  	if diff > tolerance || diff < -tolerance {
    70  		c.t.Fatalf("expected probability %v for node %v, but got %v",
    71  			expectedProb, toNode, p)
    72  	}
    73  }
    74  
    75  // TestProbabilityEstimatorNoResults tests the probability estimation when no
    76  // results are available.
    77  func TestProbabilityEstimatorNoResults(t *testing.T) {
    78  	ctx := newEstimatorTestContext(t)
    79  
    80  	ctx.assertPairProbability(testTime, 0, 0, aprioriHopProb)
    81  }
    82  
    83  // TestProbabilityEstimatorOneSuccess tests the probability estimation for nodes
    84  // that have a single success result.
    85  func TestProbabilityEstimatorOneSuccess(t *testing.T) {
    86  	ctx := newEstimatorTestContext(t)
    87  
    88  	ctx.results = map[int]TimedPairResult{
    89  		node1: {
    90  			SuccessAmt: lnwire.MilliAtom(1000),
    91  		},
    92  	}
    93  
    94  	// Because of the previous success, this channel keep reporting a high
    95  	// probability.
    96  	ctx.assertPairProbability(
    97  		testTime, node1, 100, aprioriPrevSucProb,
    98  	)
    99  
   100  	// Untried channels are also influenced by the success. With a
   101  	// aprioriWeight of 0.75, the a priori probability is assigned weight 3.
   102  	expectedP := (3*aprioriHopProb + 1*aprioriPrevSucProb) / 4
   103  	ctx.assertPairProbability(testTime, untriedNode, 100, expectedP)
   104  }
   105  
   106  // TestProbabilityEstimatorOneFailure tests the probability estimation for nodes
   107  // that have a single failure.
   108  func TestProbabilityEstimatorOneFailure(t *testing.T) {
   109  	ctx := newEstimatorTestContext(t)
   110  
   111  	ctx.results = map[int]TimedPairResult{
   112  		node1: {
   113  			FailTime: testTime.Add(-time.Hour),
   114  			FailAmt:  lnwire.MilliAtom(50),
   115  		},
   116  	}
   117  
   118  	// For an untried node, we expected the node probability. The weight for
   119  	// the failure after one hour is 0.5. This makes the node probability
   120  	// 0.51:
   121  	expectedNodeProb := (3*aprioriHopProb + 0.5*0) / 3.5
   122  	ctx.assertPairProbability(testTime, untriedNode, 100, expectedNodeProb)
   123  
   124  	// The pair probability decays back to the node probability. With the
   125  	// weight at 0.5, we expected a pair probability of 0.5 * 0.51 = 0.25.
   126  	ctx.assertPairProbability(testTime, node1, 100, expectedNodeProb/2)
   127  }
   128  
   129  // TestProbabilityEstimatorMix tests the probability estimation for nodes for
   130  // which a mix of successes and failures is recorded.
   131  func TestProbabilityEstimatorMix(t *testing.T) {
   132  	ctx := newEstimatorTestContext(t)
   133  
   134  	ctx.results = map[int]TimedPairResult{
   135  		node1: {
   136  			SuccessAmt: lnwire.MilliAtom(1000),
   137  		},
   138  		node2: {
   139  			FailTime: testTime.Add(-2 * time.Hour),
   140  			FailAmt:  lnwire.MilliAtom(50),
   141  		},
   142  		node3: {
   143  			FailTime: testTime.Add(-3 * time.Hour),
   144  			FailAmt:  lnwire.MilliAtom(50),
   145  		},
   146  	}
   147  
   148  	// We expect the probability for a previously successful channel to
   149  	// remain high.
   150  	ctx.assertPairProbability(testTime, node1, 100, prevSuccessProbability)
   151  
   152  	// For an untried node, we expected the node probability to be returned.
   153  	// This is a weighted average of the results above and the a priori
   154  	// probability: 0.62.
   155  	expectedNodeProb := (3*aprioriHopProb + 1*prevSuccessProbability) /
   156  		(3 + 1 + 0.25 + 0.125)
   157  
   158  	ctx.assertPairProbability(testTime, untriedNode, 100, expectedNodeProb)
   159  
   160  	// For the previously failed connection with node 1, we expect 0.75 *
   161  	// the node probability = 0.47.
   162  	ctx.assertPairProbability(testTime, node2, 100, expectedNodeProb*0.75)
   163  }