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 }