multi: explicitly signal final hop in pack hop payload

Previously, we'd use the value of nextChanID to infer whether a payload
was for the final hop in a route. This commit updates our packing logic
to explicitly signal to account for blinded routes, which allow zero
value nextChanID in intermediate hops. This is a preparatory commit
that allows us to more thoroughly validate payloads.
This commit is contained in:
Carla Kirk-Cohen committed 2023-12-18 11:27:52 -05:00
1 parent b5afd905d1
commit 585f28c5f5
3 files changed
+20 -15

No files matched your search

+1 -1
View File
@@ -121,7 +121,7 @@ func fuzzPayload(f *testing.F, finalPayload bool) {
var b bytes.Buffer var b bytes.Buffer
hop, nextChanID := hopFromPayload(payload1) hop, nextChanID := hopFromPayload(payload1)
err = hop.PackHopPayload(&b, nextChanID) err = hop.PackHopPayload(&b, nextChanID, finalPayload)
if errors.Is(err, route.ErrAMPMissingMPP) { if errors.Is(err, route.ErrAMPMissingMPP) {
// PackHopPayload refuses to encode an AMP record // PackHopPayload refuses to encode an AMP record
// without an MPP record. However, NewPayloadFromReader // without an MPP record. However, NewPayloadFromReader
+13 -8
View File
@@ -172,11 +172,15 @@ func (h *Hop) Copy() *Hop {
// PackHopPayload writes to the passed io.Writer, the series of byes that can // PackHopPayload writes to the passed io.Writer, the series of byes that can
// be placed directly into the per-hop payload (EOB) for this hop. This will // be placed directly into the per-hop payload (EOB) for this hop. This will
// include the required routing fields, as well as serializing any of the // include the required routing fields, as well as serializing any of the
// passed optional TLVRecords. nextChanID is the unique channel ID that // passed optional TLVRecords. nextChanID is the unique channel ID that
// references the _outgoing_ channel ID that follows this hop. This field // references the _outgoing_ channel ID that follows this hop. The lastHop bool
// follows the same semantics as the NextAddress field in the onion: it should // is used to signal whether this hop is the final hop in a route. Previously,
// be set to zero to indicate the terminal hop. // a zero nextChanID would be used for this purpose, but with the addition of
func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error { // blinded routes which allow zero nextChanID values for intermediate hops we
// add an explicit signal.
func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64,
finalHop bool) error {
// If this is a legacy payload, then we'll exit here as this method // If this is a legacy payload, then we'll exit here as this method
// shouldn't be called. // shouldn't be called.
if h.LegacyPayload == true { if h.LegacyPayload == true {
@@ -218,7 +222,7 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
// attach it to the final hop. Otherwise the route was constructed // attach it to the final hop. Otherwise the route was constructed
// incorrectly. // incorrectly.
if h.MPP != nil { if h.MPP != nil {
if nextChanID == 0 { if finalHop {
records = append(records, h.MPP.Record()) records = append(records, h.MPP.Record())
} else { } else {
return ErrIntermediateMPPHop return ErrIntermediateMPPHop
@@ -559,7 +563,8 @@ func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
// If we aren't on the last hop, then we set the "next address" // If we aren't on the last hop, then we set the "next address"
// field to be the channel that directly follows it. // field to be the channel that directly follows it.
if i != len(r.Hops)-1 { finalHop := i == len(r.Hops)-1
if !finalHop {
nextHop = r.Hops[i+1].ChannelID nextHop = r.Hops[i+1].ChannelID
} }
@@ -591,7 +596,7 @@ func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
// channel should be forwarded to so we can construct a // channel should be forwarded to so we can construct a
// valid payload. // valid payload.
var b bytes.Buffer var b bytes.Buffer
err := hop.PackHopPayload(&b, nextHop) err := hop.PackHopPayload(&b, nextHop, finalHop)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+6 -6
View File
@@ -105,7 +105,7 @@ func TestMPPHop(t *testing.T) {
// Encoding an MPP record to an intermediate hop should result in a // Encoding an MPP record to an intermediate hop should result in a
// failure. // failure.
var b bytes.Buffer var b bytes.Buffer
err := hop.PackHopPayload(&b, 2) err := hop.PackHopPayload(&b, 2, false)
if err != ErrIntermediateMPPHop { if err != ErrIntermediateMPPHop {
t.Fatalf("expected err: %v, got: %v", t.Fatalf("expected err: %v, got: %v",
ErrIntermediateMPPHop, err) ErrIntermediateMPPHop, err)
@@ -113,7 +113,7 @@ func TestMPPHop(t *testing.T) {
// Encoding an MPP record to a final hop should be successful. // Encoding an MPP record to a final hop should be successful.
b.Reset() b.Reset()
err = hop.PackHopPayload(&b, 0) err = hop.PackHopPayload(&b, 0, true)
if err != nil { if err != nil {
t.Fatalf("expected err: %v, got: %v", nil, err) t.Fatalf("expected err: %v, got: %v", nil, err)
} }
@@ -135,7 +135,7 @@ func TestAMPHop(t *testing.T) {
// Encoding an AMP record to an intermediate hop w/o an MPP record // Encoding an AMP record to an intermediate hop w/o an MPP record
// should result in a failure. // should result in a failure.
var b bytes.Buffer var b bytes.Buffer
err := hop.PackHopPayload(&b, 2) err := hop.PackHopPayload(&b, 2, false)
if err != ErrAMPMissingMPP { if err != ErrAMPMissingMPP {
t.Fatalf("expected err: %v, got: %v", t.Fatalf("expected err: %v, got: %v",
ErrAMPMissingMPP, err) ErrAMPMissingMPP, err)
@@ -144,7 +144,7 @@ func TestAMPHop(t *testing.T) {
// Encoding an AMP record to a final hop w/o an MPP record should result // Encoding an AMP record to a final hop w/o an MPP record should result
// in a failure. // in a failure.
b.Reset() b.Reset()
err = hop.PackHopPayload(&b, 0) err = hop.PackHopPayload(&b, 0, true)
if err != ErrAMPMissingMPP { if err != ErrAMPMissingMPP {
t.Fatalf("expected err: %v, got: %v", t.Fatalf("expected err: %v, got: %v",
ErrAMPMissingMPP, err) ErrAMPMissingMPP, err)
@@ -154,7 +154,7 @@ func TestAMPHop(t *testing.T) {
// successful. // successful.
hop.MPP = record.NewMPP(testAmt, testAddr) hop.MPP = record.NewMPP(testAmt, testAddr)
b.Reset() b.Reset()
err = hop.PackHopPayload(&b, 0) err = hop.PackHopPayload(&b, 0, true)
if err != nil { if err != nil {
t.Fatalf("expected err: %v, got: %v", nil, err) t.Fatalf("expected err: %v, got: %v", nil, err)
} }
@@ -170,7 +170,7 @@ func TestNoForwardingParams(t *testing.T) {
} }
var b bytes.Buffer var b bytes.Buffer
err := hop.PackHopPayload(&b, 2) err := hop.PackHopPayload(&b, 2, false)
require.NoError(t, err) require.NoError(t, err)
} }