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  }