@@ -12,6 +12,7 @@ import (
1212
1313 "github.com/btcsuite/btcd/btcec/v2"
1414 "github.com/davecgh/go-spew/spew"
15+ "github.com/lightningnetwork/lnd/tlv"
1516 "github.com/stretchr/testify/require"
1617)
1718
@@ -162,6 +163,97 @@ func TestBolt4Packet(t *testing.T) {
162163 }
163164}
164165
166+ // TestTLVPayloadMessagePacket tests the creation and encoding of an onion
167+ // message packet that uses a TLV payload for each hop in the route. This test
168+ // uses the test vectors defined in the BOLT 4 specification. The test reads a
169+ // JSON file containing a predefined route, session key, and the expected final
170+ // onion packet. It then constructs the route hop-by-hop, manually creating the
171+ // TLV payload for each, before creating a new onion packet with NewOnionPacket.
172+ // The test concludes by asserting that the newly encoded packet is identical to
173+ // the one specified in the test vector.
174+ func TestTLVPayloadMessagePacket (t * testing.T ) {
175+ t .Parallel ()
176+
177+ // First, we'll read out the raw JSON file at the target location.
178+ jsonBytes , err := os .ReadFile (testOnionMessageFileName )
179+ require .NoError (t , err )
180+
181+ // Once we have the raw file, we'll unpack it into our jsonTestCase
182+ // struct defined above.
183+ testCase := & onionMessageJsonTestCase {}
184+ require .NoError (t , json .Unmarshal (jsonBytes , testCase ))
185+
186+ // Next, we'll populate a new OnionHop using the information included
187+ // in this test case.
188+ var route PaymentPath
189+ for i , hop := range testCase .Route .Hops {
190+ pubKeyBytes , err := hex .DecodeString (hop .BlindedNodeID )
191+ require .NoError (t , err )
192+
193+ pubKey , err := btcec .ParsePubKey (pubKeyBytes )
194+ require .NoError (t , err )
195+
196+ encryptedRecipientData , err := hex .DecodeString (
197+ hop .EncryptedRecipientData ,
198+ )
199+ require .NoError (t , err )
200+
201+ // Manually encode our onion payload
202+ records := []tlv.Record {}
203+
204+ if i == len (testCase .Route .Hops )- 1 {
205+ helloBytes := []byte ("hello" )
206+ records = append (records , tlv .MakePrimitiveRecord (
207+ 1 , & helloBytes ,
208+ ))
209+ }
210+
211+ records = append (records , tlv .MakePrimitiveRecord (
212+ 4 , & encryptedRecipientData ,
213+ ))
214+
215+ stream , err := tlv .NewStream (records ... )
216+ require .NoError (t , err , "new stream" )
217+
218+ b := new (bytes.Buffer )
219+ require .NoError (t , stream .Encode (b ), "encode" )
220+
221+ route [i ] = OnionHop {
222+ NodePub : * pubKey ,
223+ HopPayload : HopPayload {
224+ Type : PayloadTLV ,
225+ Payload : b .Bytes (),
226+ },
227+ }
228+ }
229+
230+ finalPacket , err := hex .DecodeString (
231+ testCase .OnionMessage .OnionMessagePacket ,
232+ )
233+ require .NoError (t , err )
234+
235+ sessionKeyBytes , err := hex .DecodeString (testCase .Generate .SessionKey )
236+
237+ require .NoError (t , err )
238+
239+ // With all the required data assembled, we'll craft a new packet.
240+ sessionKey , _ := btcec .PrivKeyFromBytes (sessionKeyBytes )
241+
242+ pkt , err := NewOnionPacket (
243+ & route , sessionKey , nil , DeterministicPacketFiller ,
244+ )
245+ require .NoError (t , err )
246+
247+ var b bytes.Buffer
248+ require .NoError (t , pkt .Encode (& b ))
249+
250+ // Finally, we expect that our packet matches the packet included in
251+ // the spec's test vectors.
252+ require .Equalf (t , finalPacket , b .Bytes (), "final packet does not " +
253+ "match expected BOLT 4 packet, want: %s, got %s" ,
254+ hex .EncodeToString (finalPacket ), hex .EncodeToString (b .Bytes ()))
255+ }
256+
165257func TestSphinxCorrectness (t * testing.T ) {
166258 nodes , _ , hopDatas , fwdMsg , err := newTestRoute (testLegacyRouteNumHops )
167259 if err != nil {
@@ -755,6 +847,9 @@ const (
755847
756848 // testTLVFileName is the name of the tlv-payload-only onion test file.
757849 testTLVFileName = "testdata/onion-test.json"
850+
851+ // testOnionMessageFileName is the name of the onion message test file.
852+ testOnionMessageFileName = "testdata/blinded-onion-message-onion-test.json"
758853)
759854
760855type jsonHop struct {
0 commit comments