Skip to content

Commit 4c8ea29

Browse files
committed
lnwire: allow longer failure messages
This fixes an incompatibility where lnd enforces a strict 256 byte failure message, where as the spec sets this only as the recommended length.
1 parent 5ff5838 commit 4c8ea29

File tree

8 files changed

+111
-8
lines changed

8 files changed

+111
-8
lines changed

channeldb/mp_payment.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"errors"
66
"io"
7+
"math"
78
"time"
89

910
"github.com/btcsuite/btcd/btcec/v2"
@@ -298,7 +299,7 @@ func deserializeHTLCFailInfo(r io.Reader) (*HTLCFailInfo, error) {
298299

299300
// Read failure.
300301
failureBytes, err := wire.ReadVarBytes(
301-
r, 0, lnwire.FailureMessageLength, "failure",
302+
r, 0, math.MaxUint16, "failure",
302303
)
303304
if err != nil {
304305
return nil, err

docs/release-notes/release-notes-0.16.0.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
* Warning messages from peers are now recognized and
66
[logged](https://github.com/lightningnetwork/lnd/pull/6546) by lnd.
77

8+
* Decrypt onion failure messages with a [length greater than 256
9+
bytes](https://github.com/lightningnetwork/lnd/pull/6913). This moves LND
10+
closer to being spec compliant.
11+
812
## RPC
913

1014
* The `RegisterConfirmationsNtfn` call of the `chainnotifier` RPC sub-server

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ require (
3131
github.com/kkdai/bstream v1.0.0
3232
github.com/lightninglabs/neutrino v0.14.2
3333
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display
34-
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5
34+
github.com/lightningnetwork/lightning-onion v1.2.1-0.20221202012345-ca23184850a1
3535
github.com/lightningnetwork/lnd/cert v1.1.1
3636
github.com/lightningnetwork/lnd/clock v1.1.0
3737
github.com/lightningnetwork/lnd/healthcheck v1.2.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,8 +440,8 @@ github.com/lightninglabs/neutrino v0.14.2 h1:yrnZUCYMZ5ECtXhgDrzqPq2oX8awoAN2D/c
440440
github.com/lightninglabs/neutrino v0.14.2/go.mod h1:OICUeTCn+4Tu27YRJIpWvvqySxx4oH4vgdP33Sw9RDc=
441441
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display h1:RZJ8H4ueU/aQ9pFtx5wqsuD3B/DezrewJeVwDKKYY8E=
442442
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display/go.mod h1:2oKOBU042GKFHrdbgGiKax4xVrFiZu51lhacUZQ9MnE=
443-
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5 h1:TkKwqFcQTGYoI+VEqyxA8rxpCin8qDaYX0AfVRinT3k=
444-
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5/go.mod h1:7dDx73ApjEZA0kcknI799m2O5kkpfg4/gr7N092ojNo=
443+
github.com/lightningnetwork/lightning-onion v1.2.1-0.20221202012345-ca23184850a1 h1:Wm0g70gkcAu2pGpNZwfWPSVOY21j8IyYsNewwK4OkT4=
444+
github.com/lightningnetwork/lightning-onion v1.2.1-0.20221202012345-ca23184850a1/go.mod h1:7dDx73ApjEZA0kcknI799m2O5kkpfg4/gr7N092ojNo=
445445
github.com/lightningnetwork/lnd/cert v1.1.1 h1:Nsav0RlIDRbOnzz2Yu69SQlK939IKya3Q2S0mDviIN8=
446446
github.com/lightningnetwork/lnd/cert v1.1.1/go.mod h1:1P46svkkd73oSoeI4zjkVKgZNwGq8bkGuPR8z+5vQUs=
447447
github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=

htlcswitch/failure_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package htlcswitch
2+
3+
import (
4+
"encoding/hex"
5+
"encoding/json"
6+
"os"
7+
"testing"
8+
9+
"github.com/btcsuite/btcd/btcec/v2"
10+
sphinx "github.com/lightningnetwork/lightning-onion"
11+
"github.com/lightningnetwork/lnd/lnwire"
12+
"github.com/lightningnetwork/lnd/tlv"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
// TestLongFailureMessage tests that longer failure messages can be interpreted
17+
// correctly.
18+
func TestLongFailureMessage(t *testing.T) {
19+
t.Parallel()
20+
21+
var testData struct {
22+
SessionKey string
23+
Path []string
24+
Reason string
25+
}
26+
27+
// Use long 1024-byte test vector from BOLT 04.
28+
testDataBytes, err := os.ReadFile("testdata/long_failure_msg.json")
29+
require.NoError(t, err)
30+
require.NoError(t, json.Unmarshal(testDataBytes, &testData))
31+
32+
sessionKeyBytes, _ := hex.DecodeString(testData.SessionKey)
33+
34+
reason, _ := hex.DecodeString(testData.Reason)
35+
36+
sphinxPath := make([]*btcec.PublicKey, len(testData.Path))
37+
for i, sKey := range testData.Path {
38+
bKey, err := hex.DecodeString(sKey)
39+
require.NoError(t, err)
40+
41+
key, err := btcec.ParsePubKey(bKey)
42+
require.NoError(t, err)
43+
44+
sphinxPath[i] = key
45+
}
46+
47+
sessionKey, _ := btcec.PrivKeyFromBytes(sessionKeyBytes)
48+
49+
circuit := &sphinx.Circuit{
50+
SessionKey: sessionKey,
51+
PaymentPath: sphinxPath,
52+
}
53+
54+
errorDecryptor := &SphinxErrorDecrypter{
55+
OnionErrorDecrypter: sphinx.NewOnionErrorDecrypter(circuit),
56+
}
57+
58+
// Assert that the failure message can still be extracted.
59+
failure, err := errorDecryptor.DecryptError(reason)
60+
require.NoError(t, err)
61+
62+
incorrectDetails, ok := failure.msg.(*lnwire.FailIncorrectDetails)
63+
require.True(t, ok)
64+
65+
var value varBytesRecordProducer
66+
67+
extraData := incorrectDetails.ExtraOpaqueData()
68+
typeMap, err := extraData.ExtractRecords(&value)
69+
require.NoError(t, err)
70+
require.Len(t, typeMap, 1)
71+
72+
expectedValue := make([]byte, 300)
73+
for i := range expectedValue {
74+
expectedValue[i] = 128
75+
}
76+
77+
require.Equal(t, expectedValue, value.data)
78+
}
79+
80+
type varBytesRecordProducer struct {
81+
data []byte
82+
}
83+
84+
func (v *varBytesRecordProducer) Record() tlv.Record {
85+
return tlv.MakePrimitiveRecord(34001, &v.data)
86+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"sessionKey": "4141414141414141414141414141414141414141414141414141414141414141",
3+
"path": [
4+
"02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619",
5+
"0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c",
6+
"027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007",
7+
"032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991",
8+
"02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145"
9+
],
10+
"reason": "2dd2f49c1f5af0fcad371d96e8cddbdcd5096dc309c1d4e110f955926506b3c03b44c192896f45610741c85ed4074212537e0c118d472ff3a559ae244acd9d783c65977765c5d4e00b723d00f12475aafaafff7b31c1be5a589e6e25f8da2959107206dd42bbcb43438129ce6cce2b6b4ae63edc76b876136ca5ea6cd1c6a04ca86eca143d15e53ccdc9e23953e49dc2f87bb11e5238cd6536e57387225b8fff3bf5f3e686fd08458ffe0211b87d64770db9353500af9b122828a006da754cf979738b4374e146ea79dd93656170b89c98c5f2299d6e9c0410c826c721950c780486cd6d5b7130380d7eaff994a8503a8fef3270ce94889fe996da66ed121741987010f785494415ca991b2e8b39ef2df6bde98efd2aec7d251b2772485194c8368451ad49c2354f9d30d95367bde316fec6cbdddc7dc0d25e99d3075e13d3de0822669861dafcd29de74eac48b64411987285491f98d78584d0c2a163b7221ea796f9e8671b2bb91e38ef5e18aaf32c6c02f2fb690358872a1ed28166172631a82c2568d23238017188ebbd48944a147f6cdb3690d5f88e51371cb70adf1fa02afe4ed8b581afc8bcc5104922843a55d52acde09bc9d2b71a663e178788280f3c3eae127d21b0b95777976b3eb17be40a702c244d0e5f833ff49dae6403ff44b131e66df8b88e33ab0a58e379f2c34bf5113c66b9ea8241fc7aa2b1fa53cf4ed3cdd91d407730c66fb039ef3a36d4050dde37d34e80bcfe02a48a6b14ae28227b1627b5ad07608a7763a531f2ffc96dff850e8c583461831b19feffc783bc1beab6301f647e9617d14c92c4b1d63f5147ccda56a35df8ca4806b8884c4aa3c3cc6a174fdc2232404822569c01aba686c1df5eecc059ba97e9688c8b16b70f0d24eacfdba15db1c71f72af1b2af85bd168f0b0800483f115eeccd9b02adf03bdd4a88eab03e43ce342877af2b61f9d3d85497cd1c6b96674f3d4f07f635bb26add1e36835e321d70263b1c04234e222124dad30ffb9f2a138e3ef453442df1af7e566890aedee568093aa922dd62db188aa8361c55503f8e2c2e6ba93de744b55c15260f15ec8e69bb01048ca1fa7bbbd26975bde80930a5b95054688a0ea73af0353cc84b997626a987cc06a517e18f91e02908829d4f4efc011b9867bd9bfe04c5f94e4b9261d30cc39982eb7b250f12aee2a4cce0484ff34eebba89bc6e35bd48d3968e4ca2d77527212017e202141900152f2fd8af0ac3aa456aae13276a13b9b9492a9a636e18244654b3245f07b20eb76b8e1cea8c55e5427f08a63a16b0a633af67c8e48ef8e53519041c9138176eb14b8782c6c2ee76146b8490b97978ee73cd0104e12f483be5a4af414404618e9f6633c55dda6f22252cb793d3d16fae4f0e1431434e7acc8fa2c009d4f6e345ade172313d558a4e61b4377e31b8ed4e28f7cd13a7fe3f72a409bc3bdabfe0ba47a6d861e21f64d2fac706dab18b3e546df4"
11+
}

lnwire/onion_error.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,9 +1264,9 @@ func DecodeFailure(r io.Reader, pver uint32) (FailureMessage, error) {
12641264

12651265
// Check the total length. Convert to 32 bits to prevent overflow.
12661266
totalLength := uint32(padLength) + uint32(failureLength)
1267-
if totalLength != FailureMessageLength {
1268-
return nil, fmt.Errorf("failure message length is "+
1269-
"incorrect: msg=%v, pad=%v, total=%v",
1267+
if totalLength < FailureMessageLength {
1268+
return nil, fmt.Errorf("failure message too short: "+
1269+
"msg=%v, pad=%v, total=%v",
12701270
failureLength, padLength, totalLength)
12711271
}
12721272

routing/missioncontrol_store.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"container/list"
66
"encoding/binary"
77
"fmt"
8+
"math"
89
"sync"
910
"time"
1011

@@ -245,7 +246,7 @@ func deserializeResult(k, v []byte) (*paymentResult, error) {
245246

246247
// Read failure.
247248
failureBytes, err := wire.ReadVarBytes(
248-
r, 0, lnwire.FailureMessageLength, "failure",
249+
r, 0, math.MaxUint16, "failure",
249250
)
250251
if err != nil {
251252
return nil, err

0 commit comments

Comments
 (0)