Skip to content

Avoid premature channel exhaustion for custom channels #8804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions lnwallet/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -6355,6 +6355,53 @@ func (lc *LightningChannel) addHTLC(htlc *lnwire.UpdateAddHTLC,
return 0, err
}

// Let's check if this is a custom channel HTLC.
if lc.isCustomChannel() && htlc.CustomRecords != nil {

// If the htlc amount is below dust we will override it and set
// it to be the dust limit.
if btcutil.Amount(htlc.Amount/1000) <
lc.channelState.LocalChanCfg.DustLimit {

pd.Amount = lnwire.MilliSatoshi(
lc.channelState.LocalChanCfg.DustLimit * 1000,
)
}

// Construct the payment descriptor that will be used to append
// to the remote log.
remotePd := &PaymentDescriptor{
Copy link
Collaborator Author

@GeorgeTsagk GeorgeTsagk Jun 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this should also be inside the if body above?

I suppose the compensation only makes sense for dust, because if the sender defined an above-dust amount then maybe they actually want to forward that amount to the network.

EntryType: Add,
RHash: PaymentHash(htlc.PaymentHash),
Timeout: htlc.Expiry,
// Use the pd amount in case it was overridden.
Amount: pd.Amount,
LogIndex: lc.remoteUpdateLog.logIndex,
HtlcIndex: lc.remoteUpdateLog.htlcCounter,
OnionBlob: htlc.OnionBlob[:],
BlindingPoint: htlc.BlindingPoint,
// NOTE: we don't set the custom records field, as this
// htlc is only used to compensate for the amount of
// sats that we're sending over to the remote side.
// Setting the custom records here could have undesired
// side effects.
}

localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex

// Perform the commitment sanity check.
err := lc.validateCommitmentSanity(
lc.remoteUpdateLog.logIndex, localACKedIndex,
false, NoBuffer, nil, pd,
)
if err != nil {
return 0, err
}

// Finally, append the HTLC to the remote log.
lc.remoteUpdateLog.appendHtlc(remotePd)
}

lc.localUpdateLog.appendHtlc(pd)

return pd.HtlcIndex, nil
Expand Down Expand Up @@ -6568,6 +6615,32 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64,
return 0, err
}

// Let's check if this is a custom channel HTLC.
if lc.isCustomChannel() && htlc.CustomRecords != nil {
localPd := &PaymentDescriptor{
EntryType: Add,
RHash: pd.RHash,
Timeout: pd.Timeout,
Amount: pd.Amount,
LogIndex: lc.localUpdateLog.logIndex,
HtlcIndex: lc.localUpdateLog.htlcCounter,
OnionBlob: pd.OnionBlob,
// TODO(george): what shoudl be used here?
OpenCircuitKey: &models.CircuitKey{},
BlindingPoint: pd.BlindingPoint,
// NOTE: we don't set the custom records field, as this
// htlc is only used to compensate for the amount of
// sats that were sent to us from the remote side.
}

if err := lc.validateAddHtlc(pd, FeeBuffer); err != nil {
return 0, err
}

// Finally, append the HTLC to the local log.
lc.localUpdateLog.appendHtlc(localPd)
}

lc.remoteUpdateLog.appendHtlc(pd)

return pd.HtlcIndex, nil
Expand Down Expand Up @@ -6987,6 +7060,15 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
return commitTx, nil
}

// isCustomChannel is a boolean check that asserts whether this channel is a custom
// channel.
func (lc *LightningChannel) isCustomChannel() bool {
// TODO(george): is this sufficient to check for a custom channel? do
// we also need feature bit?
return lc.channelState.CustomBlob.IsSome() &&
lc.auxSigner.IsSome() && lc.opts.leafStore.IsSome()
}

// CommitOutputResolution carries the necessary information required to allow
// us to sweep our commitment output in the case that either party goes to
// chain.
Expand Down