diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index e121e061..a2840995 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -145,6 +145,10 @@ type ChannelLink interface { // will use this function in forwarding decisions accordingly. EligibleToForward() bool + // MayAddOutgoingHtlc returns an error if we may not add an outgoing + // htlc to the channel. + MayAddOutgoingHtlc() error + // AttachMailBox delivers an active MailBox to the link. The MailBox may // have buffered messages. AttachMailBox(MailBox) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 2718ae63..c133c46e 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2156,6 +2156,12 @@ func (l *channelLink) Bandwidth() lnwire.MilliSatoshi { return l.channel.AvailableBalance() } +// MayAddOutgoingHtlc indicates whether we may add any more outgoing htlcs to +// this channel. +func (l *channelLink) MayAddOutgoingHtlc() error { + return l.channel.MayAddOutgoingHtlc() +} + // AttachMailBox updates the current mailbox used by this link, and hooks up // the mailbox's message and packet outboxes to the link's upstream and // downstream chans, respectively. diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 13872a45..45fda210 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -756,6 +756,7 @@ func (f *mockChannelLink) Peer() lnpeer.Peer { return func (f *mockChannelLink) ChannelPoint() *wire.OutPoint { return &wire.OutPoint{} } func (f *mockChannelLink) Stop() {} func (f *mockChannelLink) EligibleToForward() bool { return f.eligible } +func (f *mockChannelLink) MayAddOutgoingHtlc() error { return nil } func (f *mockChannelLink) setLiveShortChanID(sid lnwire.ShortChannelID) { f.shortChanID = sid } func (f *mockChannelLink) UpdateShortChanID() (lnwire.ShortChannelID, error) { f.eligible = true diff --git a/lnwallet/channel.go b/lnwallet/channel.go index b9394f49..94c01e94 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -4934,6 +4934,33 @@ func (lc *LightningChannel) AddHTLC(htlc *lnwire.UpdateAddHTLC, return pd.HtlcIndex, nil } +// MayAddOutgoingHtlc validates whether we can add an outgoing htlc to this +// channel. We don't have a value or circuit for this htlc, because we just +// want to test that we have slots for a potential htlc so we use a "mock" +// htlc to validate a potential commitment state with one more outgoing htlc. +func (lc *LightningChannel) MayAddOutgoingHtlc() error { + lc.Lock() + defer lc.Unlock() + + // Create a "mock" outgoing htlc, using the smallest amount we can add + // to the commitment so that we validate commitment slots rather than + // available balance, since our actual htlc amount is unknown at this + // stage. + pd := lc.htlcAddDescriptor( + &lnwire.UpdateAddHTLC{ + Amount: lc.channelState.LocalChanCfg.MinHTLC, + }, + &channeldb.CircuitKey{}, + ) + + if err := lc.validateAddHtlc(pd); err != nil { + lc.log.Debugf("May add outgoing htlc rejected: %v", err) + return err + } + + return nil +} + // htlcAddDescriptor returns a payment descriptor for the htlc and open key // provided to add to our local update log. func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC,