From bf071c1985d016cf8e981c88174c8c2eb127a900 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 12 Sep 2017 22:04:52 +0200 Subject: [PATCH] htlcswitch: properly verify OutgoingCTLV+Timeout when final hop in link This commit fixes an existing bug in the way we perform validation of the timelock information as the final hop in the route. Previously, we would assert that the outgoing time lock in the per-hop payload would exactly match our time lock delta. Instead, we should be asserting two things: 1. That the time lock in the payload is >= the expected time lock 2. That timeout on the HTLC is exactly equal to the payload --- htlcswitch/link.go | 69 +++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index c6629e5c..3c253f91 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -865,6 +865,7 @@ func (l *channelLink) Bandwidth() lnwire.MilliSatoshi { // NOTE: Should be used inside main goroutine only, otherwise the result might // not be accurate. func (l *channelLink) getBandwidth() lnwire.MilliSatoshi { + // TODO(roasbeef): factor in reserve, just grab mutex return l.channel.LocalAvailableBalance() - l.overflowQueue.pendingAmount() } @@ -1026,15 +1027,19 @@ func (l *channelLink) processLockedInHtlcs( // to produce initial obfuscation of the onion // failureCode. onionReader := bytes.NewReader(onionBlob[:]) - obfuscator, failureCode := l.cfg.DecodeOnionObfuscator(onionReader) + obfuscator, failureCode := l.cfg.DecodeOnionObfuscator( + onionReader, + ) if failureCode != lnwire.CodeNone { - // If we unable to process the onion blob than - // we should send the malformed htlc error to - // payment sender. - l.sendMalformedHTLCError(pd.RHash, failureCode, onionBlob[:]) + // If we're unable to process the onion blob + // than we should send the malformed htlc error + // to payment sender. + l.sendMalformedHTLCError(pd.RHash, failureCode, + onionBlob[:]) needUpdate = true - log.Error("unable to decode onion obfuscator") + log.Errorf("unable to decode onion "+ + "obfuscator: %v", failureCode) continue } @@ -1054,13 +1059,15 @@ func (l *channelLink) processLockedInHtlcs( onionReader, pd.RHash[:], ) if failureCode != lnwire.CodeNone { - // If we unable to process the onion blob than - // we should send the malformed htlc error to - // payment sender. - l.sendMalformedHTLCError(pd.RHash, failureCode, onionBlob[:]) + // If we're unable to process the onion blob + // than we should send the malformed htlc error + // to payment sender. + l.sendMalformedHTLCError(pd.RHash, failureCode, + onionBlob[:]) needUpdate = true - log.Error("unable to decode onion hop iterator") + log.Errorf("unable to decode onion hop "+ + "iterator: %v", failureCode) continue } @@ -1122,19 +1129,35 @@ func (l *channelLink) processLockedInHtlcs( // We'll also ensure that our time-lock value // has been computed correctly. - if !l.cfg.DebugHTLC && - fwdInfo.OutgoingCTLV != l.cfg.FwrdingPolicy.TimeLockDelta { + expectedHeight := heightNow + l.cfg.FwrdingPolicy.TimeLockDelta + if !l.cfg.DebugHTLC { + switch { + case fwdInfo.OutgoingCTLV < expectedHeight: + log.Errorf("Onion payload of incoming "+ + "htlc(%x) has incorrect time-lock: "+ + "expected %v, got %v", + pd.RHash[:], expectedHeight, + fwdInfo.OutgoingCTLV) - log.Errorf("Onion payload of incoming "+ - "htlc(%x) has incorrect time-lock: "+ - "expected %v, got %v", - pd.RHash[:], l.cfg.FwrdingPolicy.TimeLockDelta, - fwdInfo.OutgoingCTLV) + failure := lnwire.NewFinalIncorrectCltvExpiry( + fwdInfo.OutgoingCTLV, + ) + l.sendHTLCError(pd.RHash, failure, obfuscator) + needUpdate = true + continue + case pd.Timeout != fwdInfo.OutgoingCTLV: + log.Errorf("HTLC(%x) has incorrect "+ + "time-lock: expected %v, got %v", + pd.RHash[:], pd.Timeout, + fwdInfo.OutgoingCTLV) - failure := lnwire.NewFinalIncorrectCltvExpiry(fwdInfo.OutgoingCTLV) - l.sendHTLCError(pd.RHash, failure, obfuscator) - needUpdate = true - continue + failure := lnwire.NewFinalIncorrectCltvExpiry( + fwdInfo.OutgoingCTLV, + ) + l.sendHTLCError(pd.RHash, failure, obfuscator) + needUpdate = true + continue + } } // If we're not currently in debug mode, and @@ -1307,6 +1330,8 @@ func (l *channelLink) processLockedInHtlcs( continue } + // TODO(roasbeef): also add max timeout value + // With all our forwarding constraints met, // we'll create the outgoing HTLC using the // parameters as specified in the forwarding