Skip to content

Commit 58458e8

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 11b2f79 commit 58458e8

File tree

1 file changed

+123
-4
lines changed

1 file changed

+123
-4
lines changed

path_test.go

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,121 @@ func TestOnionRouteBlinding(t *testing.T) {
375375
}
376376
}
377377

378+
// TestOnionMessageRouteBlinding tests that an onion message packet can
379+
// correctly be processed by a node in a blinded route.
380+
func TestOnionMessageRouteBlinding(t *testing.T) {
381+
t.Parallel()
382+
383+
// First, we'll read out the raw Json file at the target location.
384+
jsonBytes, err := os.ReadFile(blindedOnionMessageOnionTestFileName)
385+
require.NoError(t, err)
386+
387+
// Once we have the raw file, we'll unpack it into our
388+
// onionMessageJsonTestCase struct defined above.
389+
testCase := &onionMessageJsonTestCase{}
390+
require.NoError(t, json.Unmarshal(jsonBytes, testCase))
391+
392+
// Extract the original onion message packet to be processed.
393+
onion, err := hex.DecodeString(testCase.OnionMessage.OnionMessagePacket)
394+
require.NoError(t, err)
395+
396+
onionBytes := bytes.NewReader(onion)
397+
onionPacket := &OnionPacket{}
398+
require.NoError(t, onionPacket.Decode(onionBytes))
399+
400+
// peelOnion is a helper closure that can be used to set up a Router
401+
// and use it to process the given onion packet.
402+
peelOnion := func(key *btcec.PrivateKey,
403+
blindingPoint *btcec.PublicKey,
404+
onionPacket *OnionPacket) *ProcessedPacket {
405+
406+
r := NewRouter(&PrivKeyECDH{PrivKey: key}, NewMemoryReplayLog())
407+
408+
require.NoError(t, r.Start())
409+
defer r.Stop()
410+
411+
res, err := r.ProcessOnionPacket(
412+
onionPacket, nil, 10,
413+
WithBlindingPoint(blindingPoint),
414+
)
415+
require.NoError(t, err)
416+
417+
return res
418+
}
419+
420+
hops := testCase.Generate.Hops
421+
422+
// There are some things that the processor of the onion packet will
423+
// only be able to determine from the actual contents of the encrypted
424+
// data it receives. These things include the next_blinding_point for
425+
// the introduction point and the next_blinding_override. The decryption
426+
// of this data is dependent on the encoding chosen by higher layers.
427+
// The test uses TLVs. Since the extraction of this data is dependent
428+
// on layers outside the scope of this library, we provide handle these
429+
// cases manually for the sake of the test.
430+
var (
431+
firstBlinding = pubKeyFromString(testCase.Route.FirstPathKey)
432+
concatIndex = 1
433+
blindingOverride = pubKeyFromString(
434+
hops[0].EncodedOnionMessageTLVs.NextPathKeyOverride,
435+
)
436+
)
437+
438+
// Onion message routes are always entirely blinded, so
439+
// the first hop will always use the first blinding
440+
// point.
441+
blindingPoint := firstBlinding
442+
currentOnionPacket := onionPacket
443+
for i, hop := range testCase.Decrypt.Hops {
444+
// We encode the onion message packet to a buffer at each hop to
445+
// compare it to the onion message packet in the test vector.
446+
buff := bytes.NewBuffer(nil)
447+
require.NoError(t, currentOnionPacket.Encode(buff))
448+
449+
// hop.OnionMessage contains the onion_message hex string. This
450+
// contains the type 513 (two bytes), the path_key (33 bytes)
451+
// and the length of the onion_message_packet (two bytes). We
452+
// are only interested in the onion_message_packet so we only
453+
// check that part. 2 + 33 + 2 = 37 bytes, so we skip the first
454+
// 37 bytes, which equals 74 hex characters.
455+
const onionMessageHexHeaderLen = 74
456+
457+
require.Equal(
458+
t, hop.OnionMessage[onionMessageHexHeaderLen:],
459+
hex.EncodeToString(buff.Bytes()),
460+
)
461+
462+
priv := privKeyFromString(hop.PrivKey)
463+
464+
if i == concatIndex {
465+
blindingPoint = blindingOverride
466+
}
467+
468+
// With peelOnion we call into ProcessOnionPacket (with the
469+
// functional option WithBlindingPoint) and we expect that the
470+
// onion message packet for this hop is processed without error,
471+
// otherwise peelOnion fails the test.
472+
processedPkt := peelOnion(
473+
priv, blindingPoint, currentOnionPacket,
474+
)
475+
476+
// We derive the next blinding point from the current blinding
477+
// point and the private key of the current hop. The new
478+
// blindingPoint will be used to peel the next hop's onion
479+
// unless it is overridden by a blinding override.
480+
blindingPoint, err = NextEphemeral(
481+
&PrivKeyECDH{priv}, blindingPoint,
482+
)
483+
require.NoError(t, err)
484+
485+
// We set the current onion packet to the next packet in the
486+
// processed packet. This is the packet that the next hop will
487+
// process. During the next iteration we will run all the above
488+
// checks on this packet.
489+
currentOnionPacket = processedPkt.NextPacket
490+
}
491+
}
492+
378493
type onionBlindingJsonTestCase struct {
379494
Generate generateOnionData `json:"generate"`
380495
Decrypt decryptData `json:"decrypt"`
@@ -413,10 +528,10 @@ type blindingJsonTestCase struct {
413528
}
414529

415530
type onionMessageJsonTestCase struct {
416-
Generate generateOnionMessageData `json:"generate"`
417-
Route routeOnionMessageData `json:"route"`
418-
// OnionMessage onionMessageData `json:"onionmessage"`
419-
Decrypt decryptOnionMessageData `json:"decrypt"`
531+
Generate generateOnionMessageData `json:"generate"`
532+
Route routeOnionMessageData `json:"route"`
533+
OnionMessage onionMessageData `json:"onionmessage"`
534+
Decrypt decryptOnionMessageData `json:"decrypt"`
420535
}
421536

422537
type routeData struct {
@@ -431,6 +546,10 @@ type routeOnionMessageData struct {
431546
Hops []blindedOnionMessageHop `json:"hops"`
432547
}
433548

549+
type onionMessageData struct {
550+
OnionMessagePacket string `json:"onion_message_packet"`
551+
}
552+
434553
type unblindData struct {
435554
Hops []unblindedHop `json:"hops"`
436555
}

0 commit comments

Comments
 (0)