diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 3f59b89b..f0b60c18 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -681,9 +681,14 @@ func (l *channelLink) resolveFwdPkg(fwdPkg *channeldb.FwdPkg) (bool, error) { // If the package is fully acked but not completed, it must still have // settles and fails to propagate. if !fwdPkg.SettleFailFilter.IsFull() { - settleFails := lnwallet.PayDescsFromRemoteLogUpdates( + settleFails, err := lnwallet.PayDescsFromRemoteLogUpdates( fwdPkg.Source, fwdPkg.Height, fwdPkg.SettleFails, ) + if err != nil { + l.errorf("Unable to process remote log updates: %v", + err) + return false, err + } l.processRemoteSettleFails(fwdPkg, settleFails) } @@ -693,9 +698,14 @@ func (l *channelLink) resolveFwdPkg(fwdPkg *channeldb.FwdPkg) (bool, error) { // batch of adds presented to the sphinx router does not ever change. var needUpdate bool if !fwdPkg.AckFilter.IsFull() { - adds := lnwallet.PayDescsFromRemoteLogUpdates( + adds, err := lnwallet.PayDescsFromRemoteLogUpdates( fwdPkg.Source, fwdPkg.Height, fwdPkg.Adds, ) + if err != nil { + l.errorf("Unable to process remote log updates: %v", + err) + return false, err + } needUpdate = l.processRemoteAdds(fwdPkg, adds) // If the link failed during processing the adds, we must diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index ce2bfc58..5e5578b5 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -1781,9 +1781,14 @@ func (s *Switch) loadChannelFwdPkgs(source lnwire.ShortChannelID) ([]*channeldb. // NOTE: This should mimic the behavior processRemoteSettleFails. func (s *Switch) reforwardSettleFails(fwdPkgs []*channeldb.FwdPkg) { for _, fwdPkg := range fwdPkgs { - settleFails := lnwallet.PayDescsFromRemoteLogUpdates( + settleFails, err := lnwallet.PayDescsFromRemoteLogUpdates( fwdPkg.Source, fwdPkg.Height, fwdPkg.SettleFails, ) + if err != nil { + log.Errorf("Unable to process remote log updates: %v", + err) + continue + } switchPackets := make([]*htlcPacket, 0, len(settleFails)) for i, pd := range settleFails { diff --git a/lnwallet/channel.go b/lnwallet/channel.go index bf2fe526..7911d4ac 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -351,7 +351,7 @@ type PaymentDescriptor struct { // NOTE: The provided `logUpdates` MUST corresponding exactly to either the Adds // or SettleFails in this channel's forwarding package at `height`. func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, - logUpdates []channeldb.LogUpdate) []*PaymentDescriptor { + logUpdates []channeldb.LogUpdate) ([]*PaymentDescriptor, error) { // Allocate enough space to hold all of the payment descriptors we will // reconstruct, and also the list of pointers that will be returned to @@ -423,13 +423,18 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, Index: uint16(i), }, } + + // NOTE: UpdateFee is not expected since they are not forwarded. + case *lnwire.UpdateFee: + return nil, fmt.Errorf("unexpected update fee") + } payDescs = append(payDescs, pd) payDescPtrs = append(payDescPtrs, &payDescs[i]) } - return payDescPtrs + return payDescPtrs, nil } // commitment represents a commitment to a new state within an active channel. @@ -1568,6 +1573,23 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, ShaOnionBlob: wireMsg.ShaOnionBlob, removeCommitHeightRemote: commitHeight, } + + // For fee updates we'll create a FeeUpdate type to add to the log. We + // reuse the amount field to hold the fee rate. Since the amount field + // is denominated in msat we won't lose precision when storing the + // sat/kw denominated feerate. Note that we set both the add and remove + // height to the same value, as we consider the fee update locked in by + // adding and removing it at the same height. + case *lnwire.UpdateFee: + pd = &PaymentDescriptor{ + LogIndex: logUpdate.LogIndex, + Amount: lnwire.NewMSatFromSatoshis( + btcutil.Amount(wireMsg.FeePerKw), + ), + EntryType: FeeUpdate, + addCommitHeightRemote: commitHeight, + removeCommitHeightRemote: commitHeight, + } } return pd, nil @@ -2883,6 +2905,15 @@ func (lc *LightningChannel) createCommitDiff( ShaOnionBlob: pd.ShaOnionBlob, FailureCode: pd.FailCode, } + + case FeeUpdate: + // The Amount field holds the feerate denominated in + // msat. Since feerates are only denominated in sat/kw, + // we can convert it without loss of precision. + logUpdate.UpdateMsg = &lnwire.UpdateFee{ + ChanID: chanID, + FeePerKw: uint32(pd.Amount.ToSatoshis()), + } } // Gather the fwd pkg references from any settle or fail @@ -4254,6 +4285,12 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { pd := e.Value.(*PaymentDescriptor) + // Fee updates are local to this particular channel, and should + // never be forwarded. + if pd.EntryType == FeeUpdate { + continue + } + if pd.isForwarded { continue }