github.com/decred/dcrlnd@v0.7.6/chanacceptor/acceptor_test.go (about) 1 package chanacceptor 2 3 import ( 4 "errors" 5 "testing" 6 "time" 7 8 "github.com/decred/dcrd/chaincfg/v3" 9 "github.com/decred/dcrd/dcrec/secp256k1/v4" 10 "github.com/decred/dcrd/dcrutil/v4" 11 "github.com/decred/dcrlnd/lnrpc" 12 "github.com/decred/dcrlnd/lnwallet/chancloser" 13 "github.com/decred/dcrlnd/lnwire" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 const testTimeout = time.Second 19 20 type channelAcceptorCtx struct { 21 t *testing.T 22 23 // extRequests is the channel that we send our channel accept requests 24 // into, this channel mocks sending of a request to the rpc acceptor. 25 // This channel should be buffered with the number of requests we want 26 // to send so that it does not block (like a rpc stream). 27 extRequests chan []byte 28 29 // responses is a map of pending channel IDs to the response which we 30 // wish to mock the remote channel acceptor sending. 31 responses map[[32]byte]*lnrpc.ChannelAcceptResponse 32 33 // acceptor is the channel acceptor we create for the test. 34 acceptor *RPCAcceptor 35 36 // errChan is a channel that the error the channel acceptor exits with 37 // is sent into. 38 errChan chan error 39 40 // quit is a channel that can be used to shutdown the channel acceptor 41 // and return errShuttingDown. 42 quit chan struct{} 43 } 44 45 func newChanAcceptorCtx(t *testing.T, acceptCallCount int, 46 responses map[[32]byte]*lnrpc.ChannelAcceptResponse) *channelAcceptorCtx { 47 48 testCtx := &channelAcceptorCtx{ 49 t: t, 50 extRequests: make(chan []byte, acceptCallCount), 51 responses: responses, 52 errChan: make(chan error), 53 quit: make(chan struct{}), 54 } 55 56 testCtx.acceptor = NewRPCAcceptor( 57 testCtx.receiveResponse, testCtx.sendRequest, testTimeout*5, 58 chaincfg.TestNet3Params(), testCtx.quit, 59 ) 60 61 return testCtx 62 } 63 64 // sendRequest mocks sending a request to the channel acceptor. 65 func (c *channelAcceptorCtx) sendRequest(request *lnrpc.ChannelAcceptRequest) error { 66 select { 67 case c.extRequests <- request.PendingChanId: 68 69 case <-time.After(testTimeout): 70 c.t.Fatalf("timeout sending request: %v", request.PendingChanId) 71 } 72 73 return nil 74 } 75 76 // receiveResponse mocks sending of a response from the channel acceptor. 77 func (c *channelAcceptorCtx) receiveResponse() (*lnrpc.ChannelAcceptResponse, 78 error) { 79 80 select { 81 case id := <-c.extRequests: 82 scratch := [32]byte{} 83 copy(scratch[:], id) 84 85 resp, ok := c.responses[scratch] 86 assert.True(c.t, ok) 87 88 return resp, nil 89 90 case <-time.After(testTimeout): 91 c.t.Fatalf("timeout receiving request") 92 return nil, errors.New("receiveResponse timeout") 93 94 // Exit if our test acceptor closes the done channel, which indicates 95 // that the acceptor is shutting down. 96 case <-c.acceptor.done: 97 return nil, errors.New("acceptor shutting down") 98 } 99 } 100 101 // start runs our channel acceptor in a goroutine which sends its exit error 102 // into our test error channel. 103 func (c *channelAcceptorCtx) start() { 104 go func() { 105 c.errChan <- c.acceptor.Run() 106 }() 107 } 108 109 // stop shuts down the test's channel acceptor and asserts that it exits with 110 // our expected error. 111 func (c *channelAcceptorCtx) stop() { 112 close(c.quit) 113 114 select { 115 case actual := <-c.errChan: 116 assert.Equal(c.t, errShuttingDown, actual) 117 118 case <-time.After(testTimeout): 119 c.t.Fatal("timeout waiting for acceptor to exit") 120 } 121 } 122 123 // queryAndAssert takes a map of open channel requests which we want to call 124 // Accept for to the outcome we expect from the acceptor, dispatches each 125 // request in a goroutine and then asserts that we get the outcome we expect. 126 func (c *channelAcceptorCtx) queryAndAssert(queries map[*lnwire.OpenChannel]*ChannelAcceptResponse) { 127 var ( 128 node = &secp256k1.PublicKey{} 129 130 responses = make(chan struct{}) 131 ) 132 133 for request, expected := range queries { 134 request := request 135 expected := expected 136 137 go func() { 138 resp := c.acceptor.Accept(&ChannelAcceptRequest{ 139 Node: node, 140 OpenChanMsg: request, 141 }) 142 assert.Equal(c.t, expected, resp) 143 responses <- struct{}{} 144 }() 145 } 146 147 // Wait for each of our requests to return a response before we exit. 148 for i := 0; i < len(queries); i++ { 149 select { 150 case <-responses: 151 case <-time.After(testTimeout): 152 c.t.Fatalf("did not receive response") 153 } 154 } 155 } 156 157 // TestMultipleAcceptClients tests that the RPC acceptor is capable of handling 158 // multiple requests to its Accept function and responding to them correctly. 159 func TestMultipleAcceptClients(t *testing.T) { 160 testAddr := "TsZwLVNTYxwcT5tgcqMzGUujjPydy5nGMk2" 161 testUpfront, err := chancloser.ParseUpfrontShutdownAddress( 162 testAddr, chaincfg.TestNet3Params(), 163 ) 164 require.NoError(t, err) 165 166 var ( 167 chan1 = &lnwire.OpenChannel{ 168 PendingChannelID: [32]byte{1}, 169 } 170 chan2 = &lnwire.OpenChannel{ 171 PendingChannelID: [32]byte{2}, 172 } 173 chan3 = &lnwire.OpenChannel{ 174 PendingChannelID: [32]byte{3}, 175 } 176 177 customError = errors.New("go away") 178 179 // Queries is a map of the channel IDs we will query Accept 180 // with, and the set of outcomes we expect. 181 queries = map[*lnwire.OpenChannel]*ChannelAcceptResponse{ 182 chan1: NewChannelAcceptResponse( 183 true, nil, testUpfront, 1, 2, 3, 4, 5, 6, 184 ), 185 chan2: NewChannelAcceptResponse( 186 false, errChannelRejected, nil, 0, 0, 0, 187 0, 0, 0, 188 ), 189 chan3: NewChannelAcceptResponse( 190 false, customError, nil, 0, 0, 0, 0, 0, 0, 191 ), 192 } 193 194 // Responses is a mocked set of responses from the remote 195 // channel acceptor. 196 responses = map[[32]byte]*lnrpc.ChannelAcceptResponse{ 197 chan1.PendingChannelID: { 198 PendingChanId: chan1.PendingChannelID[:], 199 Accept: true, 200 UpfrontShutdown: testAddr, 201 CsvDelay: 1, 202 MaxHtlcCount: 2, 203 MinAcceptDepth: 3, 204 ReserveAtoms: 4, 205 InFlightMaxMatoms: 5, 206 MinHtlcIn: 6, 207 }, 208 chan2.PendingChannelID: { 209 PendingChanId: chan2.PendingChannelID[:], 210 Accept: false, 211 }, 212 chan3.PendingChannelID: { 213 PendingChanId: chan3.PendingChannelID[:], 214 Accept: false, 215 Error: customError.Error(), 216 }, 217 } 218 ) 219 220 // Create and start our channel acceptor. 221 testCtx := newChanAcceptorCtx(t, len(queries), responses) 222 testCtx.start() 223 224 // Dispatch three queries and assert that we get our expected response. 225 // for each. 226 testCtx.queryAndAssert(queries) 227 228 // Shutdown our acceptor. 229 testCtx.stop() 230 } 231 232 // TestInvalidResponse tests the case where our remote channel acceptor sends us 233 // an invalid response, so the channel acceptor stream terminates. 234 func TestInvalidResponse(t *testing.T) { 235 var ( 236 chan1 = [32]byte{1} 237 238 // We make a single query, and expect it to fail with our 239 // generic error because our response is invalid. 240 queries = map[*lnwire.OpenChannel]*ChannelAcceptResponse{ 241 { 242 PendingChannelID: chan1, 243 }: NewChannelAcceptResponse( 244 false, errChannelRejected, nil, 0, 0, 245 0, 0, 0, 0, 246 ), 247 } 248 249 // Create a single response which is invalid because it accepts 250 // the channel but also contains an error message. 251 responses = map[[32]byte]*lnrpc.ChannelAcceptResponse{ 252 chan1: { 253 PendingChanId: chan1[:], 254 Accept: true, 255 Error: "has an error as well", 256 }, 257 } 258 ) 259 260 // Create and start our channel acceptor. 261 testCtx := newChanAcceptorCtx(t, len(queries), responses) 262 testCtx.start() 263 264 testCtx.queryAndAssert(queries) 265 266 // We do not expect our channel acceptor to exit because of one invalid 267 // response, so we shutdown and assert here. 268 testCtx.stop() 269 } 270 271 // TestInvalidReserve tests validation of the channel reserve proposed by the 272 // acceptor against the dust limit that was proposed by the remote peer. 273 func TestInvalidReserve(t *testing.T) { 274 var ( 275 chan1 = [32]byte{1} 276 277 dustLimit = dcrutil.Amount(1000) 278 reserve = dustLimit / 2 279 280 // We make a single query, and expect it to fail with our 281 // generic error because channel reserve is too low. 282 queries = map[*lnwire.OpenChannel]*ChannelAcceptResponse{ 283 { 284 PendingChannelID: chan1, 285 DustLimit: dustLimit, 286 }: NewChannelAcceptResponse( 287 false, errChannelRejected, nil, 0, 0, 288 0, reserve, 0, 0, 289 ), 290 } 291 292 // Create a single response which is invalid because the 293 // proposed reserve is below our dust limit. 294 responses = map[[32]byte]*lnrpc.ChannelAcceptResponse{ 295 chan1: { 296 PendingChanId: chan1[:], 297 Accept: true, 298 ReserveAtoms: uint64(reserve), 299 }, 300 } 301 ) 302 303 // Create and start our channel acceptor. 304 testCtx := newChanAcceptorCtx(t, len(queries), responses) 305 testCtx.start() 306 307 testCtx.queryAndAssert(queries) 308 309 // We do not expect our channel acceptor to exit because of one invalid 310 // response, so we shutdown and assert here. 311 testCtx.stop() 312 }