Skip to content
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

draft: HTLC Endorsement to Mitigate Channel Jamming #1071

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
24 changes: 23 additions & 1 deletion 02-peer-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -1978,7 +1978,10 @@ is destined, is described in [BOLT #4](04-onion-routing.md).
1. type: 0 (`blinded_path`)
2. data:
* [`point`:`path_key`]

1. type: 1 (`endorsed`)
carlaKC marked this conversation as resolved.
Show resolved Hide resolved

Choose a reason for hiding this comment

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

Suggested change
1. type: 1 (`endorsed`)
1. type: 3 (`endorsed`)

Can we move this to a new optional/required pair (type 2/3)?

2. data:
* [`byte`:`endorsed`]

#### Requirements

A sending node:
Expand Down Expand Up @@ -2016,6 +2019,21 @@ A sending node:
- MUST increase the value of `id` by 1 for each successive offer.
- if it is relaying a payment inside a blinded route:
- MUST set `path_key` (see [Route Blinding](04-onion-routing.md#route-blinding))
- if it is the original source of the HTLC:
- if it does not expect immediate fulfillment upon receipt by the
final destination:
- SHOULD set `endorsed` to `0`.
- otherwise:
- SHOULD set `endorsed` to `1`.
carlaKC marked this conversation as resolved.
Show resolved Hide resolved
- MAY choose to set `endorsed` to `0` to mimic endorsement patterns of
HTLCs it has forwarded.
carlaKC marked this conversation as resolved.
Show resolved Hide resolved
- otherwise:
- if `endorsed` is present and non-zero for the corresponding incoming HTLC
AND the incoming peer is considered to have sufficient local reputation
(see [Local Reputation](recommendations/local-resource-conservation.md#local-reputation)):
- SHOULD set `endorsed` to `1`
- otherwise:
- SHOULD set `endorsed` to `0`.
carlaKC marked this conversation as resolved.
Show resolved Hide resolved

`id` MUST NOT be reset to 0 after the update is complete (i.e. after `revoke_and_ack` has
been received). It MUST continue incrementing instead.
Expand Down Expand Up @@ -2047,6 +2065,10 @@ A receiving node:
- MUST respond with an error as detailed in [Failure Messages](04-onion-routing.md#failure-messages)
- Otherwise:
- MUST follow the requirements for the reader of `payload` in [Payload Format](04-onion-routing.md#payload-format)
- if `endorsed` is not provided OR `endorsed` is zero:
- MAY choose to limit the liquidity and slots available to forward the
corresponding outgoing HTLC in `onion_routing_packet`, if any
(see [Resource Bucketing](recommendations/local-resource-conservation.md#resource-bucketing)):

The `onion_routing_packet` contains an obfuscated list of hops and instructions for each hop along the path.
It commits to the HTLC by setting the `payment_hash` as associated data, i.e. includes the `payment_hash` in the computation of HMACs.
Expand Down
334 changes: 334 additions & 0 deletions recommendations/local-resource-conservation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
# Local Resource Conservation

## Table of Contents
* [Introduction](#introduction)
* [Overview](#overview)
* [Local Reputation](#local-reputation)
* [Recommendations for Reputation Scoring](#recommendations-for-reputation-scoring)
* [Effective HTLC Fees](#effective-htlc-fees)
* [Outgoing Channel Revenue](#outgoing-channel-revenue)
* [Incoming Channel Revenue](#incoming-channel-revenue)
* [In Flight HTLC Risk](#in-flight-htlc-risk)
* [Resource Bucketing](#resource-bucketing)
* [Implementation Notes](#implementation-notes)
* [Decaying Average](#decaying-average)
* [Multiple Channels](#multiple-channels)

## Introduction
Channel jamming is a known denial-of-service attack against the Lightning
Network. An attacker that controls both ends of a payment route can disrupt
usage of a channel by sending payments that are destined to fail through a
target route. Fees are currently only paid to routing nodes on successful
completion of a payment, so this attack is virtually free - the attacker pays
only for the costs of running a node, funding channels on-chain and the
opportunity cost of capital committed to the attack.

A channel can be fully jammed by depleting its available HTLC slots by holding
`max_accepted_htlcs` or its available liquidity by holding the
`max_htlc_value_in_flight`. An economically rational attacker will pursue the
attack method with the lowest cost and highest utility. An attack can exhaust
the resources along the target route in two ways, though the difference
between the two behaviors is not rigorously defined:
* Quick Jamming: sending a continuous stream of payments through a route that
are quickly resolved, but block all of the liquidity or HTLC slots along the
route.
* Slow Jamming: sending a slow stream of payments through a route and only
failing them at the latest possible timeout, blocking all of the liquidity
or HTLC slots along the route for the duration.

This document outlines recommendations for implementing local resource
conservation to mitigate slow jamming attacks. It is part of a hybrid solution
to mitigating channel jamming, intended to be followed by the introduction of
unconditional fees to mitigate fast jamming.

## Overview
Local resource conservation combines the following elements to provide nodes
with a mechanism to protect themselves against slow-jamming attacks:

* [HTLC Endorsement](#../02-peer-protocol.md#adding-an-htlc-update_add_htlc):
propagation of a signal along the payment route that indicates whether the
node sending `update_add_htlc` recommends that the HTLC should be granted
access to downstream resources (and that it will stake its reputation on the
fast resolution of the HTLC).
* [Local Reputation](#local-reputation): local tracking of the historical
forwarding behavior of channel peers, used to decide whether to grant
incoming HTLCs full access to local resources and whether to propagate
endorsement downstream.
* [Resource Bucketing](#resource-bucketing): reservation of a portion of
"protected" liquidity and slots for endorsed HTLCs that have been forwarded
by high reputation nodes.

Sequence:
* The `update_add_htlc` is sent by an upstream peer.

Choose a reason for hiding this comment

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

The update_add_htlc is sent by an upstream peer.

Minor - If the receiving peer is also the HTLC recipient, the reputation
algorithm could halt here. Unless the HTLC preimage is unknown to the recipient ?
Normally there is only keysend payments, as such types of HTLCs.

I don't know if there should be a mention in the "Local Reputation" subsection,
that the reputation algorithm MAY NOT be run if the payment is final and there is
no slow jamming risk. It could be still worthy to score up the reputation of the
sending peer.

* If the sending peer has sufficient local reputation (per the receiving
peer's view) AND the incoming HTLC was sent with a non-zero `endorsed` TLV:
* The HTLC will be allowed access to the "protected" bucket, so will have
full access to the channel's liquidity and slots,
* The corresponding outgoing HTLC (if present) will be forwarded with
`endorsed` set to `1`.
* Otherwise:
* The HTLC will be limited to the remaining "general" slots and liquidity,

Choose a reason for hiding this comment

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

"* The HTLC will be limited to the remaining "general" slots and liquidity,
and will be failed if there are no resources remaining in this bucket."

While immediate failure is an option for the local routing node, an economically
rational one can rather halt the processing and wait for the local ressources
allocated to downstream channels to free up, and then process up the non-endorsed
HTLC.

There is no interactivity needed with the upstream peer after the commitment_signed
have been exchanged. The "halting time" per the local routing node measurement can
be subtracted from the CLTV delta difference between the outgoing_cltv_value and
the current chain height.

In a world where there is economical competition among the routing node, why
the receiving peer would reject a HTLC for free ? It can be more rational to
bet and pockets in the fee_base_msat and fee_proportional_millionths. You
might offer back to the other routing peer that can fulfill this HTLC forwarding
request, high quality economic traffic.

Especially, if the peer does not meet sufficient local reputation, while not
all the protected_slot_count are occupied, there is no economic sense to mark
an endorsed or non-endorsed HTLC as "general".

One suggestion could be rather to document that a receiving peer can support some
clawback where a HTLC can be upgraded from "general" to protected_slot_count as
some implementation policy. After reading "Resource Bucketing" subsection, and lrc's
addHTLC, I don't see that mentioned or implemented, if it's a worthy concern.

and will be failed if there are no resources remaining in this bucket.
* The corresponding outgoing HTLC (if present) will be forwarded with
`endorsed` set to `0`.

In the steady state when Lightning is being used as expected, there should be
no need for use of protected resources as channels are not saturated during
regular operation. Should the network come under attack, honest nodes that
have built up reputation over time will still be able to utilize protected
resources to process payments in the network.

Choose a reason for hiding this comment

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

Should the network come under attack, honest nodes that
have built up reputation over time will still be able to utilize protected
resources to process payments in the network.

Minor - There could be a succinct mention of how the local resource conservation
system behave for honest routing nodes boostrapping their HTLC forwarding in the
network. Such nodes might not have accumulated enough reputation during a steady
state to leverage it in face of slow jamming attack. It can be worthy to be sure
the system works smoothly for marginal peers added to the network topology.

Especially, if there are some negative events happening deeper in the stack of
the routing nodes (e.g a cloud center being taking down by a tsunami). Some
segments of the channels topology could have to be substituted on a short period
of time. The whole graph seen with by concatenating node_announcement and
channel_announcement by not be a bijection with the infrastructure.


## Local Reputation

### Recommendations for Reputation Scoring
Local reputation can be used by forwarding nodes to decide whether to allocate
resources to and signal endorsement of a HTLC on the outgoing channel. Nodes MAY
use any metric of their choosing to classify a peer as having sufficient
reputation, though a poor choice of reputation scoring metric may affect their
reputation with their downstream peers.

Choose a reason for hiding this comment

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

Nodes MAY use any metric of their choosing to classify a peer as having sufficient
reputation, though a poor choice of reputation scoring metric may affect their
reputation with their downstream peers.

For the implementators, maybe few examples of scoring metrics could be given:

  • reliability: the uptime of the sending peer in the channel
  • success rate: success rates versus number of failures
  • profit / volume: based on earned fees and total amount moved through the channel
  • utility: that one is blurred...opportuntiy cost ? e.g if off-chain fees have been paid to open the chan

See that presentation about "Lightning network topology, its creation and maintenance
from which the above metrics are inspired from.

Maybe it could be added in this document or in a blip. I know there is the idea of what
is "observable within the protocol" described latter in this subsection, yet even for
the forwarding fees, this is a hard problem. The upstream peer has no visibility on
the local routing node's difference between amount_msat and amt_to_forward, if
onion encryption holds.


The goal of this reputation metric is to ensure that the cost to obtain
sufficient reputation for a HTLC to be endorsed on the outgoing channel is
greater than the damage that a node can inflict by abusing the access that it
is granted. This algorithm uses forwarding fees to measure damage, as this value
is observable within the protocol. It is reasonable to expect an adversary to
"about turn" - to behave perfectly to build up reputation, then alter their
behavior to abuse it. For this reason, in-flight HTLCs have a temporary

Choose a reason for hiding this comment

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

For this reason, in-flight HTLCs have a temporary negative impact on reputation until
they are resolved.

Minor - This sentence, or the idea behind it, I don't get it.

Let's consider a simple slow jamming, where an attacker forwards a bunch of spammy
HTLCs, which are never resolved successfully and a blank state where no reputation
have been accumulated in the reputationTracker.

At the moment of reception of the update_add_htlc, there is no informational state
by the receiving peer on how the HTLC is resolved, either by a success or a failure.
Marking that in-flight HTLC as having a negative impact on the channel or peer reputation
could lead to reject another concurrent in-flight HTLC, and a fees gain. The final state
of those 2 in-flight HTLCs could be a success. However at the time of processing among
upstream and downstream, the target node has no medium to predict in a deterministic
fashion the HTLC resolution.

Unless it is suggested that a target node should limit the max number of in-flight HTLC
originating from a single upstream peer ? I don't see that idea mentioned more neither
in the "Resource Bucketing" subsection or "Local Reputation" subsection. It could be
interesting as some kind of implementation policy to limit worst-case damage from a
single peer. E.g one that would have build up a high IncomingReputation, and
then suddenly engage in a slow jamming on the target node.

Maybe, this could be described in a blip or another document as an implementation policy.

negative impact on reputation until they are resolved.

If granted full access to a node's liquidity and slots, the maximum amount of
damage that can be inflicted on the targeted node is bounded by the largest
cltv delta from the current block height that it will allow a HTLC to set
before failing it with [expiry_too_far](../04-onion-routing.md#failure-messages).
This value is typically 2016 blocks (~2 weeks) at the time of writing.

Upon receipt of `update_add_htlc`, the local node considers the following to
determine whether the sender is classified as having sufficient reputation:

Choose a reason for hiding this comment

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

Upon receipt of update_add_htlc, the local node considers the following to
determine whether the sender is classified as having sufficient reputation:

Sounds it is the forwarding fees (or profit / volume criteria) that is described
above. I don't know if it would be worthy to introduce a versioning of the forwarding
fees algorithm. Such forwarding fees algorithms as defined by the triade of "Outgoing
Channel Revenue", "Incoming Channel Revenue" and "In-flight HTLCs".

It could be interesting to have versioning, if in the future the others criteras on
which the forwarding evaluations is done are updated, e.g the 2016 blocks or the 60
second MPP timeout.

* Outgoing channel revenue: the total routing fees over the maximum allowed HTLC
hold period (~2 weeks) that the outgoing channel has earned the local node,
considering both incoming and outgoing HTLCs that have used the channel.
* Incoming channel revenue: the total routing fees that the incoming channel has
earned the local node, considering only incoming HTLCs on the channel.
* In-flight HTLCs risk: any endorsed HTLC that the incoming channel has
in-flight on the requested outgoing channel negatively affect reputation,
based on the assumption that they will be held until just before their
expiry height.

On a per-HTLC basis, the local node will classify the sending node's

Choose a reason for hiding this comment

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

On a per-HTLC basis, the local node will classify the sending node's
reputation for the offered HTLC as follows:

  • if incoming_channel_revenue - in_flight_risk >= outgoing_channel_revenue:
    • the sending node is considered to have sufficient reputation for the
      offered HTLC.
    • otherwise, the sending node is considered to have insufficient reputation
      for the offered HTLC.

Maybe, it could be worthy to say that the local node reputation evaluated on a
per-HTLC basis is attached to the channel id and id as put in update_add_htlc
(bolt2).

Normally, if this bolt is correctly implemented that should mean that the node's
reputation is evaluated on a monotonic counter, which is good if there is a need
to rollback the state.

ResourceManager builds channelReputation on ShortChannelId, which is good
as it's an implicit commitment to the chain height and transaction output. As far
as I can see, there is no order check about InconingIndex in reputation_tracker.go,
though I might miss something.

reputation for the offered HTLC as follows:
* if `incoming_channel_revenue - in_flight_risk >= outgoing_channel_revenue`:
* the sending node is considered to have *sufficient* reputation for the
offered HTLC.
* otherwise, the sending node is considered to have *insufficient* reputation
for the offered HTLC.

The sections that follow provide details of how to calculate the components of
the inequality.

#### Effective HTLC Fees
The contribution that the fees paid by a HTLC make to reputation standings is
adjusted by the amount of time that a HTLC took to resolve. By accounting for
the "opportunity cost" of HTLCs that are held for longer periods of time, the
reputation score rewards fast resolving, successful payments and penalizes
slow resolving payments (regardless of successful resolution).

Choose a reason for hiding this comment

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

By accounting for the "opportunity cost" of HTLCs that are held for longer periods of
time, the reputation score rewards fast resolving, successful payments and penalizes
slow resolving payments (regardless of successful resolution).

Ouch...Does it mean that if you have A <-> B <-> C <-> D as peer topology, and A
forwards an endorsed HTLC 123 to D over B and C, A + D can collusion to hold the HTLC
resolution ? Assume both B and C have their own's local resolution_period so
respectively B_resolution_period and C_resolution_period, with as default 90
seconds.

At time T evaluated within B_resolution_period, B adds HTLC 123 on B's commitment
transaction. This HTLC 123 is evaluated to have been added on C's commitment transaction
at time L within C_resolution_period. In a world of non-reliable clocks among lightning
peers, there is no certainty of time T being equivalent to time L.

Under the max value of 2016 blocks for expiry_too_far, A can forward the HTLC 123
to D, and hold the resolution to inflate the resolution_time as evaluated by B about
the "good" behavior of C. If onion encryption holds, there is no way for B to guess that
C is not the final HTLC recipient.

It could be that A and D can downgrade C's reputation as evaluated by B's ResourceManager.
This reputation downgrade happening while neither A and D are owning a key in the funding
utxo backing the B and C chan.

I don't know if it's a correct concern or if there is not some misunderstanding ? Though
if correct, it is hard for B and C to coordinate themselves on spotting that A and D are
acting in collusion. Both B and C, as economically rational forwarding nodes can have a
competing interest to accept traffic from A and D, and attributate the blame on resolultion
to their counterparty.

I could eventually suggest to simplify to remove that idea of resolution_time, in
that version of this document...Or use the chain time to evaluate the resolution_time
within a target node's resolution_period ? Hmmm...maybe too complicated...


We define the following parameters:
* `resolution_period`: the amount of time a HTLC is allowed to resolve in that
classifies as "good" behavior, expressed in seconds. The recommended default
is 90 seconds (given that the protocol allows for a 60 second MPP timeout).

Choose a reason for hiding this comment

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

I think it would be useful to have an additional indication on the definition of the clock used to tick the resolution_period second and from which all the opportunity_cost computation are scaled on.

E.g, the Epoch which is defined on unix systems as "1970-01-01 00:00:00 +0000 (UTC), it's not a perfect clock though it's better than nothing.

Choose a reason for hiding this comment

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

Comment to be re-evaluated in light of finding here: #1071 (comment)

Choose a reason for hiding this comment

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

The recommended default is 90 seconds (given that the protocol allows for a 60 second MPP timeout).

Well...I believe there might be a variant of the selective reputation attack pointed
out in the precedent comment. Where you would have 2 MPP shard M and N flowing over to
parallel routing path 1st and 2nd and the 2 shards are identified by the same hash. A peer included
on the payment path 2nd would drop on the floor its shard to get the final recipient failing
the MPP resolution, and that way downgrade reputation for the peers along the routing path 1st.

In a world without ptlcs, this sounds it could be a realistic concern...I don't have
good idea on how to solve that, without suggesting heuristics that could negatively affect
MPP payment privacy.

* `resolution_time`: the amount of time elapsed in seconds between a HTLC being
added to and removed from the local node's commitment transaction.
* `fees`: the fees that are offered to the local node to forward the HTLC,
expressed in milli-satoshis.

We define the `opportunity_cost` of the time a HTLC takes to resolve:
`opportunity_cost`: `ceil ( (resolution_time - resolution_period) / resolution_period) * fees`

Choose a reason for hiding this comment

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

We define the opportunity_cost of the time a HTLC takes to resolve:
opportunity_cost: ceil ( (resolution_time - resolution_period) / resolution_period) * fees

Minor - It could be that the equation is modified to add the channel overall liquidity available,
both "protected" and "general". That could represent better the available liquidity that has been
sacrificed by the target node to accept this HTLC, and not another HTLC (i.e the "cost").

As it's about "Effective Fees", that other HTLC could have come from either the same upstream peer,
or an alternative upstream peer. Here comparing an endorsed HTLC of the same outgoing_cltv_value
and both peers with "sufficient" reputation.

opportunityCost is already used in reputation_tracker.go, though one suggestion could be
to detail this in another document, or a blip as more an implementation policy.


Given that `resolution_time` will be > 0 in practice, `opportunity_cost` is 0
for HTLCs that resolve within `resolution_period`, and charges the `fees` that
the HTLC would have earned per period it is held thereafter. This cost accounts
for the slot and liquidity that could have otherwise been paid for by
successful, fast resolving HTLCs during the `resolution_time` the HTLC was
locked in the channel.

Each HTLC's contribution to reputation is expressed by its `effective_fee`
which is determined by its endorsement, resolution time and outcome:
* if `endorsed` is non-zero in `update_add_htlc`:
* if successfully resolved with `update_fulfill_htlc`:
* `effective_fees` = `fees` - `opportunity_cost`
* otherwise:
* `effective_fees` = -`opportunity_cost`

Choose a reason for hiding this comment

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

Each HTLC's contribution to reputation is expressed by its effective_fee
which is determined by its endorsement, resolution time and outcome:

  • if endorsed is non-zero in update_add_htlc:
    • if successfully resolved with update_fulfill_htlc:
      • effective_fees = fees - opportunity_cost
    • otherwise:
      • effective_fees = -opportunity_cost

Minor - Minding the comment about on-the-fly HTLC upgrade from "general" to protected_liquidity_portion, if all the protected slots are not occupied, one could
suggest that the HTLC's contribution to the reputation is not purely determined
by the endorsement.

Zooming out, opportunityCost is better evaluated at the node level for HTLCs in competition
among all the available liqudity / slots ressources. If I'm understanding correctly, lrc's
reputationTracker do no evaluate the opportunityCost of a HTLC channel for all the target
node channels (minus the outgoing one).

I don't know if this part wouldn't be better to be documented in another document or a blip
as an implementation policy. Like if you have endorsement value oscillating among 0 and
the experimental endorsement value of endorsed=7.

* otherwise:
* if successfully resolved AND `resolution_time` <= `resolution_period`
* `effective_fees` = `fees`
* otherwise:
* `effective_fees` = 0

Choose a reason for hiding this comment

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

  • otherwise:
  • effective_fees = 0

Minor - It could be that independently of the clock picked up to evaluate the resolution_period,
assigning a score of 0 to the effective_fees can have a too severe impact on the forwarding node
reputation.

As explained in the rational, the forwading node make no promise about their behavior, though at
the same time they can make little promise on the HTLC final recipient, which can influence
the resolution_time, even all under natural condition.

One could rather suggested a function of n^4 where n is the difference between resolution_period
and resolution_time. I don't know about the exact exponent value, this could be something experimented
by implementations as a policy, informing some "natural HTLC failure rate" tolerance.


##### Rationale
Reputation is negatively affected by slow-resolving HTLCs (regardless of whether
they are settled or failed) to ensure that reputation scoring reacts to bad
behavior. HTLCs that resolve within a period of time that is considered
reasonable do not decrease reputation, as some rate of failure is natural in
the network.

The resolution of HTLCs with `endorsed` set to `0` do not have a negative
impact on reputation because the forwarding node made no promises about their
behavior. They are allowed to build reputation when they resolve successfully
within the specified `resolution_period` to allow new nodes in the network to
bootstrap reputation.

#### Outgoing Channel Revenue
Outgoing channel revenue measures the damage to the local node (ie, the loss in
forwarding fees) that a slow jamming attack can incur. For an individual
channel, this is equal to the total revenue forwarded in both directions over
the maximum allowed HTLC hold time.

Choose a reason for hiding this comment

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

For an individual channel, this is equal to the total revenue forwarded in both directions over
the maximum allowed HTLC hold time.

Minor - Let's consider the equation incoming_channel_revenue - in_flight_risk >= outgoing_channel_revenue to evaluate a node's "sufficient" reputation. From what I'm understanding, the incoming fees is accounted on the outgoing channel somehow to protect the target node reputation from the view of the downstream peer, if the HTLC would come to fail.

Is this way of measurement creating an exploitable by colluding peers ? Hmmmm...if you have A <-> B <-> C, maybe eventually A and C could collusion to have all their "endorsed" HTLCs forwarded through B always solving. Those resolutions could have a success rate above the natural rate of failure of HTLCs from
the remaining "honest" upstream peers.

One result could be for A to build cheap reputation in the view of the target node B. Reputation that
can be consumed to launch slow jamming attacks in the future provoking a higher damage to node B,
that what has been paid previously as fee_base_msat and fee_proportional_millionths.

One suggestion could be to clarify the rational, especially on the observation that even without
accruance for many HTLCs fees over a unified measure period, the reputation accumulated on the
outgoing channel in view of the downstream peer could be worthy to protect.


We define the following parameters:
* `outgoing_revenue_window`: the largest cltv delta from the current block
height that a node will allow a HTLC to set before failing it with
[expiry_too_far](../04-onion-routing.md#failure-messages), expressed in
seconds (assuming 10 minute blocks).

Choose a reason for hiding this comment

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

  • outgoing_revenue_window: the largest cltv delta from the current block
    height that a node will allow a HTLC to set before failing it with
    expiry_too_far, expressed in
    seconds (assuming 10 minute blocks).

Minor minor - Given block intervals of 10 minute is a poisson distribution, it could be better
to document the outoing_revenue_window in block height or block time, rather than seconds.


We define the `outgoing_channel_revenue`:
* for each `update_add_htlc` processed on the outgoing channel over the rolling
window [`now` - `outgoing_revenue_window`; `now`]:
* if the HTLC has been resolved:
* `outgoing_channel_revenue` += `fee`

##### Rationale
We consider bi-directional HTLC forwards on the outgoing channel to properly
account for the potential loss of a jamming attack - even when fees don't
accrue on the channel, it is pairwise valuable to the local node. In flight
HTLCs have no impact on outgoing channel revenue, as their fee contribution is
unknown.

#### Incoming Channel Revenue
Incoming channel revenue measures the unforgeable history that the incoming
channel has built with the local node via payment of forwarding fees. As this
value aims to capture the contribution of the channel (rather than its value
to the local node), only incoming HTLCs are considered.

Choose a reason for hiding this comment

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

Incoming channel revenue measures the unforgeable history that the incoming
channel has built with the local node via payment of forwarding fees. As this
value aims to capture the contribution of the channel (rather than its value
to the local node), only incoming HTLCs are considered.

Minor - One could observe that "unforgeable history" could far from being true given
the fees are expressed in milli-satoshis. A peer could accumulate a high balance of
milli-satoshis under the threshold of a satoshi, and yet this channel revenue is never
paid is the channel is closed before the balance has materialized over a satoshi.

Hmmm...I don't believe it that serious, unless an upstream peer can boost its reputation
by forwarding a lot of milli-sat satoshis with a high rate of success...Could that pending
balance of milli-satoshis be "siphoned" back by the upstream peer by abusing negative routing
fees ? I.e fee_base_msat where more money is given to the routed HTLCs to re-equilibrate liquidity.

I would say it's interesting to think more about that...


We define the following parameters:
* `incoming_revenue_multiplier`: a multiplier applied to
`outgoing_revenue_window` to determine the rolling window over which the
incoming channel's forwarding history is considered (default 10).

We define the `incoming_channel_revenue`:
* for each incoming `update_add_htlc` processed on the incoming channel over
the rolling window [`now` - `outgoing_channel_window` *
`incoming_channel_multiplier`; `now`]:

Choose a reason for hiding this comment

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

We define the incoming_channel_revenue:

  • for each incoming update_add_htlc processed on the incoming channel over
    the rolling window [now - outgoing_channel_window *
    incoming_channel_multiplier; now]:

Minor - Is there a rational somewhere about why picking up a rolling window rather
than evaluating incoming_channel_revenue since channel opening ? Like there could
be performance issue if one has to go through all the HTLCs since years and reevaluate
them to build reputation.

On the other hand, picking up a rolling window opens some windows of opportunities for
a slow jamming attacker. If you can guess the periodicity of the traffic of the neighboring
upstream peers, and this periodicity is low, the attacker can make previsions on when
the target node ressources will be more cheaper to preempt them and build reputation.

Hmmmm...I don't think there is a perfect period value to consider, it could be more
interesting to have this documented as implementation policy, or in another document like
blip. Especially testing what different periods yields, at least to avoid building
periodicity over the whole network of forwarding nodes.

* if the HTLC has been resolved:
* `incoming_channel_revenue` += `effective_fee(HTLC)`

##### Rationale
For the incoming channel, only HTLCs that they have forwarded to the local
node count torwards their unforegable contribution to building reputation, as
they push fees to the local node. While the incoming channel may be valuable
as a sink (or predominantly outgoing) channel, the HTLCs that the local node
has forwarded outwards do not represent fees paid by a potential attacker.

#### In Flight HTLC Risk
Whenever a HTLC is forwarded, we assume that it will resolve with the worst
case off-chain resolution time and dock reputation accordingly. This decrease
will be temporary in the case of fast resolution, and preemptively slash
reputation in the case where the HTLC is used as part of a slow jamming attack.

Choose a reason for hiding this comment

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

This decrease will be temporary in the case of fast resolution, and preemptively slash
reputation in the case where the HTLC is used as part of a slow jamming attack.

Minor - See the comment above about concurrent "honest" HTLCs, preemptively slashing
the reputation of the HTLC could yield a loss of fees gains for economically rational
forwarding nodes.

Sounds it could be interesting to introduce a sub-versioning of the risk calculation
itself (where the reputation algorithm would be an encompassing version), to let many
versions of a risk algorithm being experimented with or deployed.


We define the following parameters:
* `height_added`: the block height at which the HTLC was irrevocably committed
to by the local node.

We define the `outstanding_risk` of in-flight HTLCs:
* `outstanding_risk` = `fees` * ((`cltv_expiry` - `height_added`) * 10 * 60) / `resolution_period`

We define the `in_flight_htlc_risk` for an incoming channel sending
`update_add_htlc`:
* `in_flight_htlc_risk` = `outstanding_risk(proposed update_add_htlc)`
* for each HTLC originating from the incoming channel:
* if `endorsed` is non-zero:
* `in_flight_htlc_risk` += `outstanding_risk(HTLC)`

Choose a reason for hiding this comment

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

  • for each HTLC originating from the incoming channel:
  • if endorsed is non-zero:
    • in_flight_htlc_risk += outstanding_risk(HTLC)

Let's consider again the equation incoming_channel_revenue - in_flight_risk >= outgoing_channel_revenue. E.g if the incoming channel revenue is 10_000 sats and the
outgoing channel revenue is 5_000 sats, if I'm understanding correctly this means that
the sum of the endorsed HTLCs is subtracted from the incoming channel revenue.

If the result is inferior to the outgoing channel revenue, the upstream peer is
considered as not having a sufficient reputation in the view of the target node.

Isn't that weird that each endorsed HTLC is making more likely for a peer to not be
classified as with sufficient reputation ? Like one could think it should be each
non-endorsed HTLC that is making that outcome more probable. There is something
I don't get here, and the rational is not very clear on that.


##### Rationale
In flight HTLC are included in reputation scoring to account for sudden changes
in a peer's behavior. Even when sufficient reputation is obtained, each HTLC
choosing to take advantage of that reputation is treated as if it will be used
to inflict maximum damage. The expiry height of each incoming in flight HTLC is
considered so that risk is directly related to the amount of time the HTLC
could be held in the channel. Ten minute blocks are assumed for simplicity.

## Resource Bucketing
When making the decision to forward a HTLC on its outgoing channel, a node
MAY choose to limit its exposure to HTLCs that put it at risk of a denial of
service attack.

Choose a reason for hiding this comment

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

When making the decision to forward a HTLC on its outgoing channel, a node
MAY choose to limit its exposure to HTLCs that put it at risk of a denial of
service attack.

Minor - The ressource bucketing algorithm could be versioned, e.g if the distribution
between the protected_slot_count from max_accepted_htlcs is made with another
ratio than 0.5. Good too for implementation to experiment their bucket policy with
many numbers.


We define the following parameters:
* `protected_slot_count`: defines the number of HTLC slots that are reserved
for endorsed HTLCs from peers with sufficient reputation (default: 0.5 *
remote peer's `max_accepted_htlcs`).
* `protected_liquidity_portion`: defines the portion of liquidity that is
reserved for endorsed HTLCs from peers with sufficient reputation (default:
0.5).

Choose a reason for hiding this comment

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

  • protected_liquidity_portion: defines the portion of liquidity that is
    reserved for endorsed HTLCs from peers with sufficient reputation (default: 0.5).

Minor minor - The base of the 0.5 could be precised if it's the funding utxo
amount, from the htlc_minimum_msat, with or without the channel reserve.


A node implementing resource bucketing limits exposure on its outgoing channel:
* MUST choose `protected_slot_count` <= the remote channel peer's
`max_accepted_htlcs`.
* MUST choose `protected_liquidity_portion` in [0;1].

For each `update_add_htlc` proposed by an incoming channel:
* If `endorsed` is non-zero AND the incoming channel has sufficient local
reputation for the HTLC (see [Recommendations for Reputation Scoring](#recommendations-for-reputation-scoring)):
* SHOULD forward the HTLC as usual.
* SHOULD set `endorsed` to 1 in the outgoing `update_add_htlc`.
* Otherwise:
* SHOULD reduce the remote peer's `max_accepted_htlcs` by
`protected_slot_count` for the purposes of the proposed HTLC.
* SHOULD reduce the `max_htlc_value_in_flight` by
`protected_liquidity_portion` * `max_htlc_value_in_flight`.

Choose a reason for hiding this comment

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

  • SHOULD reduce the max_htlc_value_in_flight by
    protected_liquidity_portion * max_htlc_value_in_flight.

See comment above HTLC on-the-fly upgrade from "general" to "protected". This
could be the kind of situation where a high-value HTLC paying good routing fees
is rejected from forward on outgoing channel, because the HTLC outgoing_htlc_value
is falling just above protected_liquidity_portion * max_htlc_value_in_flight.

Of course, all depend if there is high volume of traffic going through
the target node, and that traffic probabilistically should soon occupied the
protected_slot_liquidity, or if it's more economically interesting to take
the risk of making an exemption.

One could suggest this part could be better left to be described in another
document, or a blip and have implementation experimenting with that. This
could be too "rigid" for low-volume forwarding nodes and the parameters too
"flexible" for high-volume, topologically well-connected forwarding nodes.

* SHOULD set `endorsed` to `0` in the outgoing `update_add_htlc`.

## Implementation Notes

### Decaying Average
Rolling windows specified in this write up may be implemented as a decaying
average to minimize the amount of data that needs to be stored per-channel. In
flight HTLCs can be accounted for separately to this calculation, as the node
will already have data for these HTLCs available.

Choose a reason for hiding this comment

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

Rolling windows specified in this write up may be implemented as a decaying
average to minimize the amount of data that needs to be stored per-channel. In
flight HTLCs can be accounted for separately to this calculation, as the node
will already have data for these HTLCs available.

Minor - I believe the overall "Local Resource Conversartion" proposal would gain to
have the implementation notes dried up in its own document or blip, including
some magic values that are referenced in other subsections (e.g the 10 for the
incoming_channel_multiplier defining the rolling window).

Without thoughts really on the decaying average, there could be implementation
alternative such as taking all the HTLCs points since the channel opening and
periodically re-evaluating their score according to the on-chain fees, as one
can see in the blocks, or the total HTLC forwarding traffic that has been through
the target node. Just ideas, more to note the range of rolling windows algorithm
that could be experimented with.


Track the following values for each rolling window:
* `last_update`: stores the timestamp of the last update to the decaying
average, expressed in seconds.
* `decaying_average`: stores the value of the decaying average.
* `decay_rate`: a constant rate of decay based on the rolling window chosen,
calculated as: `((1/2)^(2/window_length_seconds))`.

To update the `decaying_average` at time `t`:
* `last_update_diff` = `now` - `last_update`.
* `decaying_average` = `decaying_average` * `decay_rate` ^ `last_update_diff`.
* `last_update` = `t`.

When assessing whether a peer has sufficient reputation for a HTLC:
* MUST update `decaying_average` as described above.

When updating the `decaying_average` to add the `effective_fee` of a newly
resolved HTLC at timestamp `t`:
* MUST update `decaying_average` as described above.
* `decaying_average` = `decaying_average` + `effective_fee`

### Bootstrapping Outgoing Channel Revenue
New channels with no revenue history:
* MAY choose not to endorse any HTLCs in their first two weeks of operation
to establish baseline revenue.

Choose a reason for hiding this comment

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

New channels with no revenue history:

  • MAY choose not to endorse any HTLCs in their first two weeks of operation
    to establish baseline revenue.

Minor - It would deserve its own blip, especially if some nodes tries altnerative
bootstrapping ideas, e.g modulating the no endorsment period in function of
the peers's number of channel_announcements.

* MAY choose to use `outgoing_channel_revenue` for a similar channel as a
default starting point for scoring reputation.

### Multiple Channels
If the local node has multiple channels open with the incoming and outgoing
peers:
* MAY consider `incoming_channel_revenue` across all channels with the peer
when assessing reputation.
* MAY consider `outgoing_channel_revenue` for all channels with the outgoing
peer, but SHOULD take care to [bootstrap](#bootstrapping-outgoing-channel-revenue)
new channels so they do not lower the reputation threshold for existing ones.

Choose a reason for hiding this comment

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

  • MAY consider incoming_channel_revenue across all channels with the peer
    when assessing reputation.
  • MAY consider outgoing_channel_revenue for all channels with the outgoing
    peer, but SHOULD take care to bootstrap

Minor - ....Hmmmmmm, it could be interesting to adopt the communalized reputation assesment
over many channels from a upstream peer on the HTLC timing. Given their lower and
upper bounded by the height_added and resolution_window, a set of slow-jamming
HTLCs might have to fit within the same window.

Especially, it could be useful to prevent sudden spikes of slow-jamming HTLCs to
occupy liquidity / slots, when those slow-jamming are triggered with few hops of
depths in the graph. While not downgrading the forwarding of the upstream peers
the rest of the time. It could be a thing.

* SHOULD calculate `outgoing_channel_revenue` for the channel that is selected
for [non-strict-forwarding](../04-onion-routing.md#non-strict-forwarding)
forwarding.