Skip to content

Commit 2fc79d8

Browse files
committed
htlcswitch: handle forwarding settle and fail seperately
This commit adds two methods, `handlePacketFail` and `handlePacketSettle` to handle the settle and fail packets differently.
1 parent 6cb374a commit 2fc79d8

File tree

1 file changed

+169
-103
lines changed

1 file changed

+169
-103
lines changed

htlcswitch/switch.go

Lines changed: 169 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,118 +1099,26 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
10991099
// updates back. This behaviour is achieved by creation of payment circuits.
11001100
func (s *Switch) handlePacketForward(packet *htlcPacket) error {
11011101
switch htlc := packet.htlc.(type) {
1102-
11031102
// Channel link forwarded us a new htlc, therefore we initiate the
11041103
// payment circuit within our internal state so we can properly forward
11051104
// the ultimate settle message back latter.
11061105
case *lnwire.UpdateAddHTLC:
11071106
return s.handlePacketAdd(packet, htlc)
11081107

1109-
case *lnwire.UpdateFailHTLC, *lnwire.UpdateFulfillHTLC:
1110-
// If the source of this packet has not been set, use the
1111-
// circuit map to lookup the origin.
1112-
circuit, err := s.closeCircuit(packet)
1113-
if err != nil {
1114-
return err
1115-
}
1116-
1117-
// closeCircuit returns a nil circuit when a settle packet returns an
1118-
// ErrUnknownCircuit error upon the inner call to CloseCircuit.
1119-
if circuit == nil {
1120-
return nil
1121-
}
1122-
1123-
fail, isFail := htlc.(*lnwire.UpdateFailHTLC)
1124-
if isFail && !packet.hasSource {
1125-
// HTLC resolutions and messages restored from disk
1126-
// don't have the obfuscator set from the original htlc
1127-
// add packet - set it here for use in blinded errors.
1128-
packet.obfuscator = circuit.ErrorEncrypter
1129-
1130-
switch {
1131-
// No message to encrypt, locally sourced payment.
1132-
case circuit.ErrorEncrypter == nil:
1133-
1134-
// If this is a resolution message, then we'll need to
1135-
// encrypt it as it's actually internally sourced.
1136-
case packet.isResolution:
1137-
var err error
1138-
// TODO(roasbeef): don't need to pass actually?
1139-
failure := &lnwire.FailPermanentChannelFailure{}
1140-
fail.Reason, err = circuit.ErrorEncrypter.EncryptFirstHop(
1141-
failure,
1142-
)
1143-
if err != nil {
1144-
err = fmt.Errorf("unable to obfuscate "+
1145-
"error: %v", err)
1146-
log.Error(err)
1147-
}
1148-
1149-
// Alternatively, if the remote party send us an
1150-
// UpdateFailMalformedHTLC, then we'll need to convert
1151-
// this into a proper well formatted onion error as
1152-
// there's no HMAC currently.
1153-
case packet.convertedError:
1154-
log.Infof("Converting malformed HTLC error "+
1155-
"for circuit for Circuit(%x: "+
1156-
"(%s, %d) <-> (%s, %d))", packet.circuit.PaymentHash,
1157-
packet.incomingChanID, packet.incomingHTLCID,
1158-
packet.outgoingChanID, packet.outgoingHTLCID)
1159-
1160-
fail.Reason = circuit.ErrorEncrypter.EncryptMalformedError(
1161-
fail.Reason,
1162-
)
1163-
1164-
default:
1165-
// Otherwise, it's a forwarded error, so we'll perform a
1166-
// wrapper encryption as normal.
1167-
fail.Reason = circuit.ErrorEncrypter.IntermediateEncrypt(
1168-
fail.Reason,
1169-
)
1170-
}
1171-
} else if !isFail && circuit.Outgoing != nil {
1172-
// If this is an HTLC settle, and it wasn't from a
1173-
// locally initiated HTLC, then we'll log a forwarding
1174-
// event so we can flush it to disk later.
1175-
//
1176-
// TODO(roasbeef): only do this once link actually
1177-
// fully settles?
1178-
localHTLC := packet.incomingChanID == hop.Source
1179-
if !localHTLC {
1180-
log.Infof("Forwarded HTLC(%x) of %v (fee: %v) "+
1181-
"from IncomingChanID(%v) to OutgoingChanID(%v)",
1182-
circuit.PaymentHash[:], circuit.OutgoingAmount,
1183-
circuit.IncomingAmount-circuit.OutgoingAmount,
1184-
circuit.Incoming.ChanID, circuit.Outgoing.ChanID)
1185-
s.fwdEventMtx.Lock()
1186-
s.pendingFwdingEvents = append(
1187-
s.pendingFwdingEvents,
1188-
channeldb.ForwardingEvent{
1189-
Timestamp: time.Now(),
1190-
IncomingChanID: circuit.Incoming.ChanID,
1191-
OutgoingChanID: circuit.Outgoing.ChanID,
1192-
AmtIn: circuit.IncomingAmount,
1193-
AmtOut: circuit.OutgoingAmount,
1194-
},
1195-
)
1196-
s.fwdEventMtx.Unlock()
1197-
}
1198-
}
1199-
1200-
// A blank IncomingChanID in a circuit indicates that it is a pending
1201-
// user-initiated payment.
1202-
if packet.incomingChanID == hop.Source {
1203-
s.wg.Add(1)
1204-
go s.handleLocalResponse(packet)
1205-
return nil
1206-
}
1108+
case *lnwire.UpdateFulfillHTLC:
1109+
return s.handlePacketSettle(packet)
12071110

1208-
// Check to see that the source link is online before removing
1209-
// the circuit.
1210-
return s.mailOrchestrator.Deliver(packet.incomingChanID, packet)
1111+
// Channel link forwarded us an update_fail_htlc message.
1112+
//
1113+
// NOTE: when the channel link receives an update_fail_malformed_htlc
1114+
// from upstream, it will convert the message into update_fail_htlc and
1115+
// forward it. Thus there's no need to catch `UpdateFailMalformedHTLC`
1116+
// here.
1117+
case *lnwire.UpdateFailHTLC:
1118+
return s.handlePacketFail(packet, htlc)
12111119

12121120
default:
1213-
return errors.New("wrong update type")
1121+
return fmt.Errorf("wrong update type: %T", htlc)
12141122
}
12151123
}
12161124

@@ -3051,3 +2959,161 @@ func (s *Switch) handlePacketAdd(packet *htlcPacket,
30512959

30522960
return destination.handleSwitchPacket(packet)
30532961
}
2962+
2963+
// handlePacketSettle handles forwarding a settle packet.
2964+
func (s *Switch) handlePacketSettle(packet *htlcPacket) error {
2965+
// If the source of this packet has not been set, use the circuit map
2966+
// to lookup the origin.
2967+
circuit, err := s.closeCircuit(packet)
2968+
if err != nil {
2969+
return err
2970+
}
2971+
2972+
// closeCircuit returns a nil circuit when a settle packet returns an
2973+
// ErrUnknownCircuit error upon the inner call to CloseCircuit.
2974+
//
2975+
// NOTE: We can only get a nil circuit when it has already been deleted
2976+
// and when `UpdateFulfillHTLC` is received. After which `RevokeAndAck`
2977+
// is received, which invokes `processRemoteSettleFails` in its link.
2978+
if circuit == nil {
2979+
log.Debugf("Found nil circuit: packet=%v", spew.Sdump(packet))
2980+
return nil
2981+
}
2982+
2983+
localHTLC := packet.incomingChanID == hop.Source
2984+
2985+
// If this is a locally initiated HTLC, we need to handle the packet by
2986+
// storing the network result.
2987+
//
2988+
// A blank IncomingChanID in a circuit indicates that it is a pending
2989+
// user-initiated payment.
2990+
//
2991+
// NOTE: `closeCircuit` modifies the state of `packet`.
2992+
if localHTLC {
2993+
// TODO(yy): remove the goroutine and send back the error here.
2994+
s.wg.Add(1)
2995+
go s.handleLocalResponse(packet)
2996+
2997+
// If this is a locally initiated HTLC, there's no need to
2998+
// forward it so we exit.
2999+
return nil
3000+
}
3001+
3002+
// If this is an HTLC settle, and it wasn't from a locally initiated
3003+
// HTLC, then we'll log a forwarding event so we can flush it to disk
3004+
// later.
3005+
if circuit.Outgoing != nil {
3006+
log.Infof("Forwarded HTLC(%x) of %v (fee: %v) "+
3007+
"from IncomingChanID(%v) to OutgoingChanID(%v)",
3008+
circuit.PaymentHash[:], circuit.OutgoingAmount,
3009+
circuit.IncomingAmount-circuit.OutgoingAmount,
3010+
circuit.Incoming.ChanID, circuit.Outgoing.ChanID)
3011+
3012+
s.fwdEventMtx.Lock()
3013+
s.pendingFwdingEvents = append(
3014+
s.pendingFwdingEvents,
3015+
channeldb.ForwardingEvent{
3016+
Timestamp: time.Now(),
3017+
IncomingChanID: circuit.Incoming.ChanID,
3018+
OutgoingChanID: circuit.Outgoing.ChanID,
3019+
AmtIn: circuit.IncomingAmount,
3020+
AmtOut: circuit.OutgoingAmount,
3021+
},
3022+
)
3023+
s.fwdEventMtx.Unlock()
3024+
}
3025+
3026+
// Deliver this packet.
3027+
return s.mailOrchestrator.Deliver(packet.incomingChanID, packet)
3028+
}
3029+
3030+
// handlePacketFail handles forwarding a fail packet.
3031+
func (s *Switch) handlePacketFail(packet *htlcPacket,
3032+
htlc *lnwire.UpdateFailHTLC) error {
3033+
3034+
// If the source of this packet has not been set, use the circuit map
3035+
// to lookup the origin.
3036+
circuit, err := s.closeCircuit(packet)
3037+
if err != nil {
3038+
return err
3039+
}
3040+
3041+
// If this is a locally initiated HTLC, we need to handle the packet by
3042+
// storing the network result.
3043+
//
3044+
// A blank IncomingChanID in a circuit indicates that it is a pending
3045+
// user-initiated payment.
3046+
//
3047+
// NOTE: `closeCircuit` modifies the state of `packet`.
3048+
if packet.incomingChanID == hop.Source {
3049+
// TODO(yy): remove the goroutine and send back the error here.
3050+
s.wg.Add(1)
3051+
go s.handleLocalResponse(packet)
3052+
3053+
// If this is a locally initiated HTLC, there's no need to
3054+
// forward it so we exit.
3055+
return nil
3056+
}
3057+
3058+
// Exit early if this hasSource is true. This flag is only set via
3059+
// mailbox's `FailAdd`. This method has two callsites,
3060+
// - the packet has timed out after `MailboxDeliveryTimeout`, defaults
3061+
// to 1 min.
3062+
// - the HTLC fails the validation in `channel.AddHTLC`.
3063+
// In either case, the `Reason` field is populated. Thus there's no
3064+
// need to proceed and extract the failure reason below.
3065+
if packet.hasSource {
3066+
// Deliver this packet.
3067+
return s.mailOrchestrator.Deliver(packet.incomingChanID, packet)
3068+
}
3069+
3070+
// HTLC resolutions and messages restored from disk don't have the
3071+
// obfuscator set from the original htlc add packet - set it here for
3072+
// use in blinded errors.
3073+
packet.obfuscator = circuit.ErrorEncrypter
3074+
3075+
switch {
3076+
// No message to encrypt, locally sourced payment.
3077+
case circuit.ErrorEncrypter == nil:
3078+
// TODO(yy) further check this case as we shouldn't end up here
3079+
// as `isLocal` is already false.
3080+
3081+
// If this is a resolution message, then we'll need to encrypt it as
3082+
// it's actually internally sourced.
3083+
case packet.isResolution:
3084+
var err error
3085+
// TODO(roasbeef): don't need to pass actually?
3086+
failure := &lnwire.FailPermanentChannelFailure{}
3087+
htlc.Reason, err = circuit.ErrorEncrypter.EncryptFirstHop(
3088+
failure,
3089+
)
3090+
if err != nil {
3091+
err = fmt.Errorf("unable to obfuscate error: %w", err)
3092+
log.Error(err)
3093+
}
3094+
3095+
// Alternatively, if the remote party sends us an
3096+
// UpdateFailMalformedHTLC, then we'll need to convert this into a
3097+
// proper well formatted onion error as there's no HMAC currently.
3098+
case packet.convertedError:
3099+
log.Infof("Converting malformed HTLC error for circuit for "+
3100+
"Circuit(%x: (%s, %d) <-> (%s, %d))",
3101+
packet.circuit.PaymentHash,
3102+
packet.incomingChanID, packet.incomingHTLCID,
3103+
packet.outgoingChanID, packet.outgoingHTLCID)
3104+
3105+
htlc.Reason = circuit.ErrorEncrypter.EncryptMalformedError(
3106+
htlc.Reason,
3107+
)
3108+
3109+
default:
3110+
// Otherwise, it's a forwarded error, so we'll perform a
3111+
// wrapper encryption as normal.
3112+
htlc.Reason = circuit.ErrorEncrypter.IntermediateEncrypt(
3113+
htlc.Reason,
3114+
)
3115+
}
3116+
3117+
// Deliver this packet.
3118+
return s.mailOrchestrator.Deliver(packet.incomingChanID, packet)
3119+
}

0 commit comments

Comments
 (0)