@@ -2,6 +2,7 @@ package sphinx
22
33import (
44 "bytes"
5+ "crypto/rand"
56 "encoding/hex"
67 "encoding/json"
78 "fmt"
@@ -105,6 +106,184 @@ func newTestRoute(numHops int) ([]*Router, *PaymentPath, *[]HopData, *OnionPacke
105106 return nodes , & route , & hopsData , fwdMsg , nil
106107}
107108
109+ func newOnionMessageRoute (numHops int ) (* OnionPacket , * PaymentPath , []* Router ,
110+ error ) {
111+
112+ if numHops < 2 {
113+ return nil , nil , nil , fmt .Errorf ("at least 2 hops are " +
114+ "required to create an onion message route" )
115+ }
116+
117+ // Create routers for each hop.
118+ nodes := make ([]* Router , numHops )
119+ for i := 0 ; i < numHops ; i ++ {
120+ privKey , err := btcec .NewPrivateKey ()
121+ if err != nil {
122+ return nil , nil , nil , fmt .Errorf ("unable to generate " +
123+ "random key for sphinx node: %v" , err )
124+ }
125+ nodes [i ] = NewRouter (
126+ & PrivKeyECDH {PrivKey : privKey }, NewMemoryReplayLog (),
127+ )
128+ }
129+
130+ // Split the nodes into two parts for creating two blinded paths.
131+ mid := numHops / 2
132+ firstPathNodes := nodes [:mid ]
133+ secondPathNodes := nodes [mid :]
134+
135+ // Create the sessions keys for the two blinded paths.
136+ firstSessionKey , _ := btcec .NewPrivateKey ()
137+ secondSessionKey , _ := btcec .NewPrivateKey ()
138+
139+ // Create the first blinded path, adding a next_path_key_override TLV
140+ // at the last node.
141+ firstPathInfos := make ([]* HopInfo , len (firstPathNodes ))
142+ for i , node := range firstPathNodes {
143+ nextNodeID := node .onionKey .PubKey ().SerializeCompressed ()
144+ var b bytes.Buffer
145+ var tlvStream * tlv.Stream
146+ var err error
147+ if i == len (firstPathNodes )- 1 {
148+ secondsSessPub := secondSessionKey .PubKey ()
149+ pathKeyOverride := secondsSessPub .SerializeCompressed ()
150+ tlvStream , err = tlv .NewStream (
151+ tlv .MakePrimitiveRecord (4 , & nextNodeID ),
152+ tlv .MakePrimitiveRecord (8 , & pathKeyOverride ),
153+ )
154+ } else {
155+ tlvStream , err = tlv .NewStream (
156+ tlv .MakePrimitiveRecord (4 , & nextNodeID ),
157+ )
158+ }
159+ if err != nil {
160+ return nil , nil , nil , fmt .Errorf ("unable to create " +
161+ "TLV stream: %v" , err )
162+ }
163+ if err := tlvStream .Encode (& b ); err != nil {
164+ return nil , nil , nil , fmt .Errorf ("unable to encode " +
165+ "TLV stream: %v" , err )
166+ }
167+ firstPathInfos [i ] = & HopInfo {
168+ NodePub : node .onionKey .PubKey (),
169+ PlainText : b .Bytes (),
170+ }
171+ }
172+ firstBlindedPath , err := BuildBlindedPath (
173+ firstSessionKey , firstPathInfos ,
174+ )
175+ if err != nil {
176+ return nil , nil , nil , fmt .Errorf ("error generating first " +
177+ "blinded path: %v" , err )
178+ }
179+
180+ // Create the second blinded path, omitting the next_node_id TLV for the
181+ // last node.
182+ secondPathInfos := make ([]* HopInfo , len (secondPathNodes ))
183+ for i , node := range secondPathNodes {
184+ nextNodeID := node .onionKey .PubKey ().SerializeCompressed ()
185+ var tlvStream * tlv.Stream
186+ var err error
187+ if i == len (secondPathNodes )- 1 {
188+ pathID := make ([]byte , 20 )
189+ if _ , err := rand .Read (pathID ); err != nil {
190+ return nil , nil , nil , fmt .Errorf ("unable to " +
191+ "generate random path ID: %v" , err )
192+ }
193+ tlvStream , err = tlv .NewStream (
194+ tlv .MakePrimitiveRecord (6 , & pathID ),
195+ )
196+ } else {
197+ tlvStream , err = tlv .NewStream (
198+ tlv .MakePrimitiveRecord (4 , & nextNodeID ),
199+ )
200+ }
201+ if err != nil {
202+ return nil , nil , nil , fmt .Errorf ("unable to create " +
203+ "TLV stream: %v" , err )
204+ }
205+ var b bytes.Buffer
206+ if err := tlvStream .Encode (& b ); err != nil {
207+ return nil , nil , nil , fmt .Errorf ("unable to encode " +
208+ "TLV stream: %v" , err )
209+ }
210+
211+ secondPathInfos [i ] = & HopInfo {
212+ NodePub : node .onionKey .PubKey (),
213+ PlainText : b .Bytes (),
214+ }
215+ }
216+ secondBlindedPath , err := BuildBlindedPath (
217+ secondSessionKey , secondPathInfos ,
218+ )
219+ if err != nil {
220+ return nil , nil , nil , fmt .Errorf ("error generating second " +
221+ "blinded path: %v" , err )
222+ }
223+
224+ blindedPath := & BlindedPath {
225+ IntroductionPoint : firstBlindedPath .Path .IntroductionPoint ,
226+ BlindingPoint : firstBlindedPath .Path .BlindingPoint ,
227+ BlindedHops : append (
228+ firstBlindedPath .Path .BlindedHops ,
229+ secondBlindedPath .Path .BlindedHops ... ,
230+ ),
231+ }
232+
233+ // Create the route from the blinded path, always adding the
234+ // hop.CipherText as a TLV field type 4.
235+ var route PaymentPath
236+ for i , hop := range blindedPath .BlindedHops {
237+ var payload []byte
238+ var b bytes.Buffer
239+ var tlvStream * tlv.Stream
240+ var err error
241+
242+ if i == len (blindedPath .BlindedHops )- 1 {
243+ hello := []byte ("hello" )
244+ tlvStream , err = tlv .NewStream (
245+ tlv .MakePrimitiveRecord (4 , & hop .CipherText ),
246+ tlv .MakePrimitiveRecord (65 , & hello ),
247+ )
248+ } else {
249+ tlvStream , err = tlv .NewStream (
250+ tlv .MakePrimitiveRecord (4 , & hop .CipherText ),
251+ )
252+ }
253+
254+ if err != nil {
255+ return nil , nil , nil , fmt .Errorf ("unable to create " +
256+ "TLV stream: %v" , err )
257+ }
258+
259+ if err := tlvStream .Encode (& b ); err != nil {
260+ return nil , nil , nil , fmt .Errorf ("unable to encode " +
261+ "TLV stream: %v" , err )
262+ }
263+ payload = b .Bytes ()
264+ route [i ] = OnionHop {
265+ NodePub : * hop .BlindedNodePub ,
266+ HopPayload : HopPayload {
267+ Type : PayloadTLV ,
268+ Payload : payload ,
269+ },
270+ }
271+ }
272+
273+ // Generate the onion packet.
274+ sessionKey , _ := btcec .NewPrivateKey ()
275+ onionPacket , err := NewOnionPacket (
276+ & route , sessionKey , nil , DeterministicPacketFiller ,
277+ WithOnionMessage (),
278+ )
279+ if err != nil {
280+ return nil , nil , nil , fmt .Errorf ("unable to create onion " +
281+ "packet: %v" , err )
282+ }
283+
284+ return onionPacket , & route , nodes , nil
285+ }
286+
108287func TestBolt4Packet (t * testing.T ) {
109288 var (
110289 route PaymentPath
@@ -669,6 +848,18 @@ func mustNewLegacyHopPayload(hopData *HopData) HopPayload {
669848 return payload
670849}
671850
851+ // TestPaymentPathTotalPayloadSizeExceeds1300 tests that a PaymentPath can have
852+ // a TotalPayloadSize greater than 1300 bytes.
853+ func TestPaymentPathTotalPayloadSizeExceeds1300 (t * testing.T ) {
854+ _ , route , _ , err := newOnionMessageRoute (15 )
855+ require .NoError (t , err , "newOnionMessageRoute should not return an " +
856+ "error" )
857+
858+ totalSize := route .TotalPayloadSize ()
859+ require .Greater (t , totalSize , 1300 , "TotalPayloadSize should be " +
860+ "greater than 1300" )
861+ }
862+
672863// TestSphinxHopVariableSizedPayloads tests that we're able to fully decode an
673864// EOB payload that was targeted at the final hop in a route, and also when
674865// intermediate nodes have EOB data encoded as well. Additionally, we test that
0 commit comments