diff --git a/htlcInterceptor.go b/htlcInterceptor.go index f8e88dc..0857b6f 100644 --- a/htlcInterceptor.go +++ b/htlcInterceptor.go @@ -66,6 +66,8 @@ func (app *App) interceptHtlcEvents(ctx context.Context) error { aliasFrom = trimPubKey([]byte(pubkeyFrom)) log.Errorf("[forward] Error getting alias for node %s", aliasFrom) } + + // we need to figure out which side of the channel is the other end channelEdgeTo, err := app.lnd.getPubKeyFromChannel(ctx, event.OutgoingRequestedChanId) if err != nil { log.Errorf("[forward] Error getting pubkey for channel %s", parse_channelID(event.OutgoingRequestedChanId)) @@ -81,6 +83,7 @@ func (app *App) interceptHtlcEvents(ctx context.Context) error { log.Errorf("[forward] Error getting alias for node %s", aliasTo) } + log.Debugf("[forward] HTLC event (%d->%d)", event.IncomingCircuitKey.ChanId, event.OutgoingRequestedChanId) forward_info_string := fmt.Sprintf( "from %s to %s (%d sat, chan_id:%s->%s, htlc_id:%d)", aliasFrom, @@ -143,9 +146,10 @@ func (app *App) htlcInterceptDecision(ctx context.Context, event *routerrpc.Forw // check if entry is a pair of from->to split := strings.Split(forward_list_entry, "->") from_channel_id, to_channel_id := split[0], split[1] - if parse_channelID(event.IncomingCircuitKey.ChanId) == from_channel_id && - parse_channelID(event.OutgoingRequestedChanId) == to_channel_id { + if (parse_channelID(event.IncomingCircuitKey.ChanId) == from_channel_id || from_channel_id == "*") && + (parse_channelID(event.OutgoingRequestedChanId) == to_channel_id || to_channel_id == "*") { accept = !accept + log.Tracef("[test] Incoming: %s <-> %s, Outgoing: %s <-> %s", parse_channelID(event.IncomingCircuitKey.ChanId), from_channel_id, parse_channelID(event.OutgoingRequestedChanId), to_channel_id) break } } else { diff --git a/main_test.go b/main_test.go index 91e79ba..cd58110 100644 --- a/main_test.go +++ b/main_test.go @@ -2,179 +2,15 @@ package main import ( "context" - "errors" "testing" "time" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" + log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" ) -type lndclientMock struct { - htlcEvents chan *routerrpc.HtlcEvent - htlcInterceptorRequests chan *routerrpc.ForwardHtlcInterceptRequest - htlcInterceptorResponses chan *routerrpc.ForwardHtlcInterceptResponse - - channelEvents chan *lnrpc.ChannelEventUpdate - channelAcceptorRequests chan *lnrpc.ChannelAcceptRequest - channelAcceptorResponses chan *lnrpc.ChannelAcceptResponse -} - -func newLndclientMock() *lndclientMock { - return &lndclientMock{ - htlcEvents: make(chan *routerrpc.HtlcEvent), - htlcInterceptorRequests: make(chan *routerrpc.ForwardHtlcInterceptRequest), - htlcInterceptorResponses: make(chan *routerrpc.ForwardHtlcInterceptResponse), - channelAcceptorRequests: make(chan *lnrpc.ChannelAcceptRequest), - channelAcceptorResponses: make(chan *lnrpc.ChannelAcceptResponse), - } -} - -// --------------- Channel events mocks --------------- - -type channelAcceptorMock struct { - lnrpc.Lightning_ChannelAcceptorClient - - channelAcceptorRequests chan *lnrpc.ChannelAcceptRequest - channelAcceptorResponses chan *lnrpc.ChannelAcceptResponse -} - -func (lnd *lndclientMock) channelAcceptor(ctx context.Context) ( - lnrpc.Lightning_ChannelAcceptorClient, error) { - - return &channelAcceptorMock{ - channelAcceptorRequests: lnd.channelAcceptorRequests, - channelAcceptorResponses: lnd.channelAcceptorResponses, - }, nil - -} - -func (c *channelAcceptorMock) RecvMsg(m interface{}) error { - req := <-c.channelAcceptorRequests - *m.(*lnrpc.ChannelAcceptRequest) = *req - return nil -} - -func (c *channelAcceptorMock) Send(m *lnrpc.ChannelAcceptResponse) error { - c.channelAcceptorResponses <- m - return nil -} - -type channelEventsMock struct { - lnrpc.Lightning_SubscribeChannelEventsClient - - channelEvents chan *lnrpc.ChannelEventUpdate -} - -func (h *channelEventsMock) Recv() (*lnrpc.ChannelEventUpdate, error) { - event := <-h.channelEvents - return event, nil -} - -func (l *lndclientMock) subscribeChannelEvents(ctx context.Context, - in *lnrpc.ChannelEventSubscription) ( - lnrpc.Lightning_SubscribeChannelEventsClient, error) { - - return &channelEventsMock{ - channelEvents: l.channelEvents, - }, nil -} - -// --------------- Node info mocks --------------- - -// getNodeInfo returns the information of a node given a pubKey -func (lnd *lndclientMock) getNodeInfo(ctx context.Context, pubkey string) ( - nodeInfo *lnrpc.NodeInfo, err error) { - info := &lnrpc.NodeInfo{ - Node: &lnrpc.LightningNode{ - Alias: "alias-" + trimPubKey([]byte(pubkey)), - }, - NumChannels: 2, - TotalCapacity: 1234, - } - return info, nil -} - -// getNodeAlias returns the alias of a node pubkey -func (lnd *lndclientMock) getNodeAlias(ctx context.Context, pubkey string) ( - string, error) { - info, err := lnd.getNodeInfo(ctx, pubkey) - if err != nil { - return "", err - } - - if info.Node == nil { - return "", errors.New("node info not available") - } - return info.Node.Alias, nil -} - -func (lnd *lndclientMock) getMyInfo(ctx context.Context) ( - *lnrpc.GetInfoResponse, error) { - info := &lnrpc.GetInfoResponse{ - IdentityPubkey: "my-pubkey-is-very-long-for-trimming-pubkey", - Alias: "my-alias", - } - return info, nil -} - -func (lnd *lndclientMock) getPubKeyFromChannel(ctx context.Context, chan_id uint64) ( - *lnrpc.ChannelEdge, error) { - return &lnrpc.ChannelEdge{ - Node1Pub: "my-pubkey-is-very-long-for-trimming-pubkey", - Node2Pub: "other-pubkey-is-very-long-for-trimming-pubkey", - }, nil -} - -// --------------- HTLC events mock --------------- - -type htlcEventsMock struct { - routerrpc.Router_SubscribeHtlcEventsClient - - htlcEvents chan *routerrpc.HtlcEvent -} - -func (h *htlcEventsMock) Recv() (*routerrpc.HtlcEvent, error) { - event := <-h.htlcEvents - return event, nil -} - -type htlcInterceptorMock struct { - routerrpc.Router_HtlcInterceptorClient - - htlcInterceptorRequests chan *routerrpc.ForwardHtlcInterceptRequest - htlcInterceptorResponses chan *routerrpc.ForwardHtlcInterceptResponse -} - -func (h *htlcInterceptorMock) Send(resp *routerrpc.ForwardHtlcInterceptResponse) error { - h.htlcInterceptorResponses <- resp - return nil -} - -func (h *htlcInterceptorMock) Recv() (*routerrpc.ForwardHtlcInterceptRequest, error) { - event := <-h.htlcInterceptorRequests - return event, nil -} - -func (l *lndclientMock) subscribeHtlcEvents(ctx context.Context, - in *routerrpc.SubscribeHtlcEventsRequest) ( - routerrpc.Router_SubscribeHtlcEventsClient, error) { - - return &htlcEventsMock{ - htlcEvents: l.htlcEvents, - }, nil -} - -func (l *lndclientMock) htlcInterceptor(ctx context.Context) ( - routerrpc.Router_HtlcInterceptorClient, error) { - - return &htlcInterceptorMock{ - htlcInterceptorRequests: l.htlcInterceptorRequests, - htlcInterceptorResponses: l.htlcInterceptorResponses, - }, nil -} - func TestApp(t *testing.T) { client := newLndclientMock() ctx, cancel := context.WithCancel(context.Background()) @@ -189,6 +25,8 @@ func TestApp(t *testing.T) { cancel() } +// --------------- HTLC Forward tests --------------- + func TestHTLCDenylist(t *testing.T) { client := newLndclientMock() ctx, cancel := context.WithCancel(context.Background()) @@ -202,7 +40,7 @@ func TestHTLCDenylist(t *testing.T) { app.DispatchHTLCAcceptor(ctx) time.Sleep(1 * time.Second) - // should be denied + // both keys match: should be denied key := &routerrpc.CircuitKey{ ChanId: 770495967390531585, @@ -223,7 +61,7 @@ func TestHTLCDenylist(t *testing.T) { Event: &routerrpc.HtlcEvent_SettleEvent{}, } - // should be allowed + // both keys no match: should be allowed key = &routerrpc.CircuitKey{ ChanId: 123456789876543210, HtlcId: 1337, @@ -242,6 +80,52 @@ func TestHTLCDenylist(t *testing.T) { IncomingHtlcId: key.HtlcId, Event: &routerrpc.HtlcEvent_SettleEvent{}, } + + // wildcard out, both match: should be denied + + Configuration.ForwardDenylist = []string{"700762x1327x1->*"} + + key = &routerrpc.CircuitKey{ + ChanId: 770495967390531585, + HtlcId: 1337, + } + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 759495353533530113, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_FAIL, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } + + // wildcard out, first key doesn't match: should be allowed + + Configuration.ForwardDenylist = []string{"700762x1327x1->*"} + + key = &routerrpc.CircuitKey{ + ChanId: 759495353533530113, + HtlcId: 1337, + } + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 759495353533530113, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_RESUME, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } } func TestHTLCAllowlist(t *testing.T) { @@ -251,12 +135,14 @@ func TestHTLCAllowlist(t *testing.T) { app := NewApp(ctx, client) - Configuration.ForwardMode = "allowlist" - Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"} app.DispatchHTLCAcceptor(ctx) time.Sleep(1 * time.Second) - // should be allowed + Configuration.ForwardMode = "allowlist" + + // both keys correct: should be allowed + Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 770495967390531585, @@ -277,7 +163,9 @@ func TestHTLCAllowlist(t *testing.T) { Event: &routerrpc.HtlcEvent_SettleEvent{}, } - // should be denied + // both keys wrong: should be denied + Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) key = &routerrpc.CircuitKey{ ChanId: 123456789876543210, @@ -297,8 +185,151 @@ func TestHTLCAllowlist(t *testing.T) { IncomingHtlcId: key.HtlcId, Event: &routerrpc.HtlcEvent_SettleEvent{}, } + + // wildcard: should be allowed + + Configuration.ForwardAllowlist = []string{"*"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 9876543210123456543, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_RESUME, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } + + // wildcard in: should be allowed + + Configuration.ForwardAllowlist = []string{"*->690757x1005x1"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + + key = &routerrpc.CircuitKey{ + ChanId: 123456789876543210, + HtlcId: 1337, + } + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 759495353533530113, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_RESUME, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } + + // wildcard out: should be allowed + + Configuration.ForwardAllowlist = []string{"700762x1327x1->*"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + + key = &routerrpc.CircuitKey{ + ChanId: 770495967390531585, + HtlcId: 1337, + } + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 123456789876543210, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_RESUME, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } + + // wildcard out but wrong in key: should be denied + + Configuration.ForwardAllowlist = []string{"700762x1327x1->*"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + + key = &routerrpc.CircuitKey{ + ChanId: 123456789876543210, + HtlcId: 1337, + } + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 123456789876543210, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_FAIL, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } + + // wildcard in but wrong out key: should be denied + + Configuration.ForwardAllowlist = []string{"*->700762x1327x1"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + + key = &routerrpc.CircuitKey{ + ChanId: 123456789876543210, + HtlcId: 1337, + } + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 123456789876543210, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_FAIL, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } + + // wildcard both: should be allowed + + Configuration.ForwardAllowlist = []string{"*->*"} + log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + + key = &routerrpc.CircuitKey{ + ChanId: 123456789876543210, + HtlcId: 1337, + } + client.htlcInterceptorRequests <- &routerrpc.ForwardHtlcInterceptRequest{ + IncomingCircuitKey: key, + OutgoingRequestedChanId: 123456789876543210, + } + + resp = <-client.htlcInterceptorResponses + require.Equal(t, routerrpc.ResolveHoldForwardAction_RESUME, resp.Action) + + client.htlcEvents <- &routerrpc.HtlcEvent{ + EventType: routerrpc.HtlcEvent_FORWARD, + IncomingChannelId: key.ChanId, + IncomingHtlcId: key.HtlcId, + Event: &routerrpc.HtlcEvent_SettleEvent{}, + } + } +// --------------- Channel accept tests --------------- + func TestChannelAllowlist(t *testing.T) { client := newLndclientMock() ctx, cancel := context.WithCancel(context.Background()) @@ -312,7 +343,7 @@ func TestChannelAllowlist(t *testing.T) { app.DispatchChannelAcceptor(ctx) time.Sleep(1 * time.Second) - // should be allowed + // correct key: should be allowed client.channelAcceptorRequests <- &lnrpc.ChannelAcceptRequest{ NodePubkey: []byte("my-pubkey-is-very-long-for-trimming-pubkey"), @@ -323,7 +354,7 @@ func TestChannelAllowlist(t *testing.T) { resp := <-client.channelAcceptorResponses require.Equal(t, resp.Accept, true) - // should be denied + // wrong key: should be denied client.channelAcceptorRequests <- &lnrpc.ChannelAcceptRequest{ NodePubkey: []byte("WRONG PUBKEY"), @@ -333,6 +364,19 @@ func TestChannelAllowlist(t *testing.T) { resp = <-client.channelAcceptorResponses require.Equal(t, resp.Accept, false) + + // wildcard: should be allowed + + Configuration.ChannelAllowlist = []string{"*"} + + client.channelAcceptorRequests <- &lnrpc.ChannelAcceptRequest{ + NodePubkey: []byte("WRONG PUBKEY"), + FundingAmt: 1337, + PendingChanId: []byte("759495353533530113"), + } + + resp = <-client.channelAcceptorResponses + require.Equal(t, resp.Accept, true) } func TestChannelDenylist(t *testing.T) { @@ -369,4 +413,17 @@ func TestChannelDenylist(t *testing.T) { resp = <-client.channelAcceptorResponses require.Equal(t, resp.Accept, true) + + // wildcard: should be denied + + Configuration.ChannelDenylist = []string{"*"} + + client.channelAcceptorRequests <- &lnrpc.ChannelAcceptRequest{ + NodePubkey: []byte("WRONG PUBKEY"), + FundingAmt: 1337, + PendingChanId: []byte("759495353533530113"), + } + + resp = <-client.channelAcceptorResponses + require.Equal(t, resp.Accept, false) }