From a10ed36e8fa8cfc9c16188d32351a72b2a526390 Mon Sep 17 00:00:00 2001 From: Andrey Samokhvalov Date: Mon, 14 Aug 2017 14:21:57 +0300 Subject: [PATCH] htlcswitch+lnwallet: add malformed payment descriptor --- htlcswitch/link.go | 6 ++-- lnwallet/channel.go | 68 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 2b28700e1..b81f3d3fe 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1513,7 +1513,9 @@ func (l *channelLink) sendHTLCError(rHash [32]byte, failure lnwire.FailureMessag // to the payment sender. func (l *channelLink) sendMalformedHTLCError(rHash [32]byte, code lnwire.FailCode, onionBlob []byte) { - index, err := l.channel.FailHTLC(rHash) + + shaOnionBlob := sha256.Sum256(onionBlob) + index, err := l.channel.MalformedFailHTLC(rHash, code, shaOnionBlob) if err != nil { log.Errorf("unable cancel htlc: %v", err) return @@ -1522,7 +1524,7 @@ func (l *channelLink) sendMalformedHTLCError(rHash [32]byte, code lnwire.FailCod l.cfg.Peer.SendMessage(&lnwire.UpdateFailMalformedHTLC{ ChanID: l.ChanID(), ID: index, - ShaOnionBlob: sha256.Sum256(onionBlob), + ShaOnionBlob: shaOnionBlob, FailureCode: code, }) } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 3e2c9f610..81c390af2 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -107,6 +107,13 @@ const ( // which contains the Fail entry. Fail + // MalformedFail is an update type which removes a prior HTLC entry from the + // log. Adding a MalformedFail entry to ones log will modify the _remote_ + // parties update log once a new commitment view has been evaluated which + // contains the MalformedFail entry. The difference from Fail type lie in + // the different data we have to store. + MalformedFail + // Settle is an update type which settles a prior HTLC crediting the // balance of the receiving node. Adding a Settle entry to a log will // result in the settle entry being removed on the log as well as the @@ -123,6 +130,8 @@ func (u updateType) String() string { return "Add" case Fail: return "Fail" + case MalformedFail: + return "MalformedFail" case Settle: return "Settle" default: @@ -214,11 +223,21 @@ type PaymentDescriptor struct { // NOTE: Populated only on add payment descriptor entry types. OnionBlob []byte + // ShaOnionBlob is a sha of the onion blob. + // + // NOTE: Populated only in payment descriptor with MalfromedFail type. + ShaOnionBlob [sha256.Size]byte + // FailReason stores the reason why a particular payment was cancelled. // // NOTE: Populate only in fail payment descriptor entry types. FailReason []byte + // FailCode stores the code why a particular payment was cancelled. + // + // NOTE: Populated only in payment descriptor with MalfromedFail type. + FailCode lnwire.FailCode + // [our|their|]PkScript are the raw public key scripts that encodes the // redemption rules for this particular HTLC. These fields will only be // populated iff the EntryType of this PaymentDescriptor is Add. @@ -2153,7 +2172,7 @@ func processRemoveEntry(htlc *PaymentDescriptor, ourBalance, // Otherwise, this HTLC is being failed out, therefore the value of the // HTLC should return to the remote party. - case isIncoming && htlc.EntryType == Fail: + case isIncoming && (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): *theirBalance += htlc.Amount // If an outgoing HTLC is being settled, then this means that the @@ -2165,7 +2184,7 @@ func processRemoveEntry(htlc *PaymentDescriptor, ourBalance, // Otherwise, one of our outgoing HTLC's has timed out, so the value of // the HTLC should be returned to our settled balance. - case !isIncoming && htlc.EntryType == Fail: + case !isIncoming && (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): *ourBalance += htlc.Amount } @@ -2510,6 +2529,13 @@ func (lc *LightningChannel) ReceiveReestablish(msg *lnwire.ChannelReestablish) ( ID: htlc.Index, Reason: lnwire.OpaqueReason([]byte{}), }) + case MalformedFail: + updates = append(updates, &lnwire.UpdateFailMalformedHTLC{ + ChanID: chanID, + ID: htlc.Index, + ShaOnionBlob: htlc.ShaOnionBlob, + FailureCode: htlc.FailCode, + }) case Settle: updates = append(updates, &lnwire.UpdateFufillHTLC{ ChanID: chanID, @@ -3253,7 +3279,43 @@ func (lc *LightningChannel) FailHTLC(rHash [32]byte, reason []byte) (uint64, err return addEntry.HtlcIndex, nil } -// ReceiveFailHTLC attempts to cancel a targeted HTLC by its log htlc, +// MalformedFailHTLC attempts to fail a targeted HTLC by its payment hash, +// inserting an entry which will remove the target log entry within the next +// commitment update. This method is intended to be called in order to cancel +// in _incoming_ HTLC. +func (lc *LightningChannel) MalformedFailHTLC(rHash [32]byte, + failCode lnwire.FailCode, shaOnionBlob [sha256.Size]byte) (uint64, error) { + lc.Lock() + defer lc.Unlock() + + addEntries, ok := lc.rHashMap[rHash] + if !ok { + return 0, fmt.Errorf("unable to find HTLC to fail") + } + addEntry := addEntries[0] + + pd := &PaymentDescriptor{ + Amount: addEntry.Amount, + RHash: addEntry.RHash, + ParentIndex: addEntry.HtlcIndex, + LogIndex: lc.localUpdateLog.logIndex, + EntryType: MalformedFail, + FailCode: failCode, + ShaOnionBlob: shaOnionBlob, + } + + lc.localUpdateLog.appendUpdate(pd) + + lc.rHashMap[rHash][0] = nil + lc.rHashMap[rHash] = lc.rHashMap[rHash][1:] + if len(lc.rHashMap[rHash]) == 0 { + delete(lc.rHashMap, rHash) + } + + return addEntry.HtlcIndex, nil +} + +// ReceiveFailHTLC attempts to cancel a targeted HTLC by its log index, // inserting an entry which will remove the target log entry within the next // commitment update. This method should be called in response to the upstream // party cancelling an outgoing HTLC. The value of the failed HTLC is returned