Skip to content

Commit dd68d45

Browse files
committed
sphinx_test: test jumbo size onion message packets
This commit adds a helper function to create onion messages of a specified length. This helper is then used to test the handling of packets larger than 1300 bytes specifically for onion messages.
1 parent b4f3edb commit dd68d45

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

sphinx_test.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sphinx
22

33
import (
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+
108287
func 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

Comments
 (0)