Skip to content

Commit 5e26d46

Browse files
committed
sphinx_test: add test for blinded route processing
We add TestOnionMessageRouteBlinding which verifies that the onion message packet from the test vector can be processed correctly by the nodes in a blinded route.
1 parent 75af2ed commit 5e26d46

File tree

1 file changed

+131
-4
lines changed

1 file changed

+131
-4
lines changed

path_test.go

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,129 @@ func TestOnionRouteBlinding(t *testing.T) {
393393
}
394394
}
395395

396+
// TestOnionMessageRouteBlinding tests that an onion message packet can
397+
// correctly be processed by a node in a blinded route.
398+
func TestOnionMessageRouteBlinding(t *testing.T) {
399+
t.Parallel()
400+
401+
// First, we'll read out the raw Json file at the target location.
402+
jsonBytes, err := os.ReadFile(blindedOnionMessageOnionTestFileName)
403+
require.NoError(t, err)
404+
405+
// Once we have the raw file, we'll unpack it into our
406+
// onionMessageJsonTestCase struct defined below.
407+
testCase := &onionMessageJsonTestCase{}
408+
require.NoError(t, json.Unmarshal(jsonBytes, testCase))
409+
410+
// Extract the original onion message packet to be processed.
411+
onion, err := hex.DecodeString(testCase.OnionMessage.OnionMessagePacket)
412+
require.NoError(t, err)
413+
414+
onionBytes := bytes.NewReader(onion)
415+
onionPacket := &OnionPacket{}
416+
require.NoError(t, onionPacket.Decode(onionBytes))
417+
418+
// peelOnion is a helper closure that can be used to set up a Router
419+
// and use it to process the given onion packet.
420+
peelOnion := func(key *btcec.PrivateKey,
421+
blindingPoint *btcec.PublicKey,
422+
onionPacket *OnionPacket) *ProcessedPacket {
423+
424+
r := NewRouter(&PrivKeyECDH{PrivKey: key}, NewMemoryReplayLog())
425+
426+
require.NoError(t, r.Start())
427+
defer r.Stop()
428+
429+
// Onion messages don't use associated data, so we pass in nil
430+
// here. Also, we set a random value for incomingCLTV as it's
431+
// only used as an accompanying purposefully general type in the
432+
// ReplayLog.
433+
res, err := r.ProcessOnionPacket(
434+
onionPacket, nil, 10,
435+
WithBlindingPoint(blindingPoint),
436+
)
437+
require.NoError(t, err)
438+
439+
return res
440+
}
441+
442+
hops := testCase.Generate.Hops
443+
444+
// There are some things that the processor of the onion will only be
445+
// able to determine from the actual contents of the onion_message and
446+
// the encrypted_recipient_date it receives. These things include the
447+
// first_path_key for the introduction point and the
448+
// next_path_key_override. This test doesn't decode the onion_message
449+
// and the decryption of the encrypted_recipient_data so it doesn't
450+
// extract these values. Instead we provide them manually. It also needs
451+
// to know where the next_path_key_override is located in the route,
452+
// hence it needs the concatIndex, where the part of the blinded route
453+
// constructed by Dave starts.
454+
var (
455+
firstPathKey = pubKeyFromString(
456+
testCase.Route.FirstPathKey,
457+
)
458+
concatIndex = 1
459+
nextPathKeyOverride = pubKeyFromString(
460+
hops[0].EncodedOnionMessageTLVs.NextPathKeyOverride,
461+
)
462+
)
463+
464+
// Onion message routes are always entirely blinded, so the first hop
465+
// will always use the first path key.
466+
pathKey := firstPathKey
467+
468+
currentOnionPacket := onionPacket
469+
for i, hop := range testCase.Decrypt.Hops {
470+
// We encode the onion message packet to a buffer at each hop to
471+
// compare it to the onion message packet in the test vector.
472+
buff := bytes.NewBuffer(nil)
473+
require.NoError(t, currentOnionPacket.Encode(buff))
474+
475+
// hop.OnionMessage contains the onion_message hex string. This
476+
// contains the type 513 (two bytes), the path_key (33 bytes)
477+
// and the length of the onion_message_packet (two bytes). We
478+
// are only interested in the onion_message_packet so we only
479+
// check that part. 2 + 33 + 2 = 37 bytes, so we skip the first
480+
// 37 bytes, which equals 74 hex characters.
481+
const onionMessageHexHeaderLen = 74
482+
483+
require.Equal(
484+
t, hop.OnionMessage[onionMessageHexHeaderLen:],
485+
hex.EncodeToString(buff.Bytes()),
486+
)
487+
488+
priv := privKeyFromString(hop.PrivKey)
489+
490+
if i == concatIndex {
491+
pathKey = nextPathKeyOverride
492+
}
493+
494+
// With peelOnion we call into ProcessOnionPacket (with the
495+
// functional option WithBlindingPoint) and we expect that the
496+
// onion message packet for this hop is processed without error,
497+
// otherwise peelOnion fails the test.
498+
processedPkt := peelOnion(
499+
priv, pathKey, currentOnionPacket,
500+
)
501+
502+
// We derive the next path key from the current path key and the
503+
// private key of the current hop. The new path key will be used
504+
// to peel the next hop's onion unless it is overridden by a
505+
// path key override.
506+
pathKey, err = NextEphemeral(
507+
&PrivKeyECDH{priv}, pathKey,
508+
)
509+
require.NoError(t, err)
510+
511+
// We set the current onion packet to the next packet in the
512+
// processed packet. This is the packet that the next hop will
513+
// process. During the next iteration we will run all the above
514+
// checks on this packet.
515+
currentOnionPacket = processedPkt.NextPacket
516+
}
517+
}
518+
396519
type onionBlindingJsonTestCase struct {
397520
Generate generateOnionData `json:"generate"`
398521
Decrypt decryptData `json:"decrypt"`
@@ -432,10 +555,10 @@ type blindingJsonTestCase struct {
432555
}
433556

434557
type onionMessageJsonTestCase struct {
435-
Generate generateOnionMessageData `json:"generate"`
436-
Route routeOnionMessageData `json:"route"`
437-
// OnionMessage onionMessageData `json:"onionmessage"`
438-
Decrypt decryptOnionMessageData `json:"decrypt"`
558+
Generate generateOnionMessageData `json:"generate"`
559+
Route routeOnionMessageData `json:"route"`
560+
OnionMessage onionMessageData `json:"onionmessage"`
561+
Decrypt decryptOnionMessageData `json:"decrypt"`
439562
}
440563

441564
type routeData struct {
@@ -450,6 +573,10 @@ type routeOnionMessageData struct {
450573
Hops []blindedOnionMessageHop `json:"hops"`
451574
}
452575

576+
type onionMessageData struct {
577+
OnionMessagePacket string `json:"onion_message_packet"`
578+
}
579+
453580
type unblindData struct {
454581
Hops []unblindedHop `json:"hops"`
455582
}

0 commit comments

Comments
 (0)