-
Notifications
You must be signed in to change notification settings - Fork 0
/
resource_manager_test.go
177 lines (145 loc) · 5.62 KB
/
resource_manager_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package lrc
import (
"testing"
"time"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var (
testParams = ManagerParams{
RevenueWindow: time.Hour,
ReputationMultiplier: 24,
BlockTime: 10,
ProtectedPercentage: 50,
ResolutionPeriod: time.Second * 90,
}
)
// setup creates a resource manager for testing with some sane values set.
// If a channel history function is not provided, we stub it out with an
// empty impl.
func setup(t *testing.T, mockDeps *mockDeps) (*clock.TestClock,
*ResourceManager) {
testClock := clock.NewTestClock(testTime)
r, err := NewResourceManager(
testParams,
testClock,
// Don't return any start values for reputation or revenue.
func(_ lnwire.ShortChannelID) (*DecayingAverageStart, error) {
return nil, nil
},
func(_ lnwire.ShortChannelID) (*DecayingAverageStart, error) {
return nil, nil
}, &TestLogger{},
)
require.NoError(t, err)
// Replace constructors with our mocks.
r.newTargetMonitor = mockDeps.newTargetMonitor
r.newReputationMonitor = mockDeps.newReputationMonitor
return testClock, r
}
// mockDeps is used to mock out constructors for fetching our other mocked
// dependencies, so that we can return different values on different calls.
type mockDeps struct {
mock.Mock
}
func (m *mockDeps) newTargetMonitor(start *DecayingAverageStart,
chanInfo *ChannelInfo) (targetMonitor, error) {
args := m.Called(start, chanInfo)
return args.Get(0).(targetMonitor), args.Error(1)
}
func (m *mockDeps) newReputationMonitor(
start *DecayingAverageStart) reputationMonitor {
args := m.Called(start)
return args.Get(0).(reputationMonitor)
}
// TestResourceManager tests resource manager's handling of HTLCs
func TestResourceManager(t *testing.T) {
// Create a mock resource bucket and
targetBucket := &MockBucket{}
defer targetBucket.AssertExpectations(t)
// We don't care about a history bootstrap function here.
deps := &mockDeps{}
defer deps.AssertExpectations(t)
_, mgr := setup(t, deps)
htlc0 := mockProposedHtlc(100, 200, 0, true)
chanOutInfo := &ChannelInfo{
InFlightHTLC: 483,
InFlightLiquidity: 100000,
}
// Start with a HTLC that overflows.
htlc0.OutgoingAmount = MaxMilliSatoshi + 1
_, err := mgr.ForwardHTLC(htlc0, chanOutInfo)
require.ErrorIs(t, err, ErrAmtOverflow)
// Start from scratch, when trackers need to be made for both of our
// channels.
chan100Incoming := &MockReputation{}
defer chan100Incoming.AssertExpectations(t)
chan200Outgoing := &MockTarget{}
defer chan200Outgoing.AssertExpectations(t)
deps.On("newReputationMonitor", mock.Anything).Once().Return(
chan100Incoming,
)
deps.On("newTargetMonitor", mock.Anything, chanOutInfo).Once().Return(
chan200Outgoing, nil,
)
// We don't actually use our incoming reputation info anywhere, so
// we can just set it to return a single value every time.
incomingRep := IncomingReputation{}
chan100Incoming.On("IncomingReputation").Return(incomingRep)
// Add a HTLC which is assessed to be able to forward, but not
// endorsed - assert that it's forwarded without endorsement.
htlc1 := mockProposedHtlc(100, 200, 0, true)
chan200Outgoing.On("AddInFlight", incomingRep, htlc1).Once().Return(
ForwardDecision{ForwardOutcome: ForwardOutcomeUnendorsed},
)
chan100Incoming.On("AddInFlight", htlc1, ForwardOutcomeUnendorsed).Once().Return(nil)
f, err := mgr.ForwardHTLC(htlc1, chanOutInfo)
require.NoError(t, err)
require.Equal(t, ForwardOutcomeUnendorsed, f.ForwardOutcome)
// Next, add a HTLC that can't be forwarded due to lack of resources
// and assert that it is not added to the incoming channel's in-flight.
// Using the same channels, do we don't need any setup assertions.
htlc2 := mockProposedHtlc(100, 200, 0, true)
chan200Outgoing.On("AddInFlight", incomingRep, htlc2).Once().Return(
ForwardDecision{ForwardOutcome: ForwardOutcomeNoResources},
)
chan100Incoming.On("AddInFlight", htlc2, ForwardOutcomeNoResources).Once().Return(nil)
f, err = mgr.ForwardHTLC(htlc2, chanOutInfo)
require.NoError(t, err)
require.Equal(t, ForwardOutcomeNoResources, f.ForwardOutcome)
// Now, test resolving of HTLCs, staring with the obvious error cases
// where the channels are not known to us (incoming).
htlc3 := mockProposedHtlc(300, 400, 1, true)
htlc3res := resolutionForProposed(htlc3, true, testTime)
_, err = mgr.ResolveHTLC(htlc3res)
require.ErrorIs(t, err, ErrChannelNotFound)
// And the case where the incoming channel is known to us, but the
// outgoing channel is not (and the htlc _was_ supposedly forwarded).
// We'll call our resolution on the incoming channel then fail on the
// outgoing channel (note that a non-mocked incoming channel would fail
// on ResolveInFlight, so we're really just testing coverage here).
htlc4 := mockProposedHtlc(100, 500, 1, false)
htlc4res := resolutionForProposed(htlc4, false, testTime)
chan100Incoming.On("ResolveInFlight", htlc4res).Once().Return(
&InFlightHTLC{
OutgoingDecision: ForwardOutcomeEndorsed,
ProposedHTLC: &ProposedHTLC{},
}, nil,
)
_, err = mgr.ResolveHTLC(htlc4res)
require.ErrorIs(t, err, ErrChannelNotFound)
// Finally, test resolving of a htlc that was only added on the
// incoming link, and not the outgoing link due to lack of resources.
htlc5 := mockProposedHtlc(100, 500, 1, false)
htlc5res := resolutionForProposed(htlc5, false, testTime)
chan100Incoming.On("ResolveInFlight", htlc5res).Once().Return(
&InFlightHTLC{
OutgoingDecision: ForwardOutcomeNoResources,
ProposedHTLC: &ProposedHTLC{},
}, nil,
)
_, err = mgr.ResolveHTLC(htlc5res)
require.NoError(t, err)
}