@@ -2,6 +2,7 @@ package sphinx
2
2
3
3
import (
4
4
"bytes"
5
+ "crypto/rand"
5
6
"encoding/hex"
6
7
"encoding/json"
7
8
"fmt"
@@ -105,6 +106,184 @@ func newTestRoute(numHops int) ([]*Router, *PaymentPath, *[]HopData, *OnionPacke
105
106
return nodes , & route , & hopsData , fwdMsg , nil
106
107
}
107
108
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
+
108
287
func TestBolt4Packet (t * testing.T ) {
109
288
var (
110
289
route PaymentPath
@@ -669,6 +848,18 @@ func mustNewLegacyHopPayload(hopData *HopData) HopPayload {
669
848
return payload
670
849
}
671
850
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
+
672
863
// TestSphinxHopVariableSizedPayloads tests that we're able to fully decode an
673
864
// EOB payload that was targeted at the final hop in a route, and also when
674
865
// intermediate nodes have EOB data encoded as well. Additionally, we test that
0 commit comments