diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index 4d9ee95b0..aca562b03 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -188,53 +188,20 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, RouteRole, error) { func extractTLVPayload(r *sphinxHopIterator) (*Payload, RouteRole, error) { isFinal := r.processedPacket.Action == sphinx.ExitNode - // Extract TLVs from the packet constructor (the sender). - payload, parsed, err := ParseTLVPayload( - bytes.NewReader(r.processedPacket.Payload.Payload), + // Initial payload parsing and validation + payload, routeRole, recipientData, err := parseAndValidateSenderPayload( + r.processedPacket.Payload.Payload, isFinal, + r.blindingKit.UpdateAddBlinding.IsSome(), ) if err != nil { - // If we couldn't even parse our payload then we do a - // best-effort of determining our role in a blinded route, - // accepting that we can't know whether we were the introduction - // node (as the payload is not parseable). - routeRole := RouteRoleCleartext - if r.blindingKit.UpdateAddBlinding.IsSome() { - routeRole = RouteRoleRelaying - } - return nil, routeRole, err } - // Now that we've parsed our payload we can determine which role we're - // playing in the route. - _, payloadBlinding := parsed[record.BlindingPointOnionType] - routeRole := NewRouteRole( - r.blindingKit.UpdateAddBlinding.IsSome(), payloadBlinding, - ) - - // Validate the presence of the various payload fields we received from - // the sender. - if err := ValidateTLVPayload( - parsed, isFinal, r.blindingKit.UpdateAddBlinding.IsSome(), - ); err != nil { - return nil, routeRole, err - } - - // If there is no encrypted data from the receiver then return the - // payload as is since the forwarding info would have been received - // from the sender. - if payload.encryptedData != nil { + // If the payload contained no recipient data, then we can exit now. + if !recipientData { return payload, routeRole, nil } - // Validate the presence of various fields in the sender payload given - // that we now know that this is a hop with instructions from the - // recipient. - err = ValidatePayloadWithBlinded(isFinal, parsed) - if err != nil { - return payload, routeRole, err - } - // If we had an encrypted data payload present, pull out our forwarding // info from the blob. fwdInfo, err := r.blindingKit.DecryptAndValidateFwdInfo( @@ -249,6 +216,61 @@ func extractTLVPayload(r *sphinxHopIterator) (*Payload, RouteRole, error) { return payload, routeRole, nil } +// parseAndValidateSenderPayload parses the payload bytes received from the +// onion constructor (the sender) and validates that various fields have been +// set. It also uses the presence of a blinding key in either the +// update_add_htlc message or in the payload to determine the RouteRole. +// The RouteRole is returned even if an error is returned. The boolean return +// value indicates that the sender payload includes encrypted data from the +// recipient that should be parsed. +func parseAndValidateSenderPayload(payloadBytes []byte, isFinalHop, + updateAddBlindingSet bool) (*Payload, RouteRole, bool, error) { + + // Extract TLVs from the packet constructor (the sender). + payload, parsed, err := ParseTLVPayload(bytes.NewReader(payloadBytes)) + if err != nil { + // If we couldn't even parse our payload then we do a + // best-effort of determining our role in a blinded route, + // accepting that we can't know whether we were the introduction + // node (as the payload is not parseable). + routeRole := RouteRoleCleartext + if updateAddBlindingSet { + routeRole = RouteRoleRelaying + } + + return nil, routeRole, false, err + } + + // Now that we've parsed our payload we can determine which role we're + // playing in the route. + _, payloadBlinding := parsed[record.BlindingPointOnionType] + routeRole := NewRouteRole(updateAddBlindingSet, payloadBlinding) + + // Validate the presence of the various payload fields we received from + // the sender. + err = ValidateTLVPayload(parsed, isFinalHop, updateAddBlindingSet) + if err != nil { + return nil, routeRole, false, err + } + + // If there is no encrypted data from the receiver then return the + // payload as is since the forwarding info would have been received + // from the sender. + if payload.encryptedData == nil { + return payload, routeRole, false, nil + } + + // Validate the presence of various fields in the sender payload given + // that we now know that this is a hop with instructions from the + // recipient. + err = ValidatePayloadWithBlinded(isFinalHop, parsed) + if err != nil { + return payload, routeRole, true, err + } + + return payload, routeRole, true, nil +} + // ExtractErrorEncrypter decodes and returns the ErrorEncrypter for this hop, // along with a failure code to signal if the decoding was successful. The // ErrorEncrypter is used to encrypt errors back to the sender in the event that