Skip to content

[1/?] Local reputation: subsystem core, read only#10919

Open
GeorgeTsagk wants to merge 6 commits into
lightningnetwork:masterfrom
GeorgeTsagk:local-reputation-subsystem
Open

[1/?] Local reputation: subsystem core, read only#10919
GeorgeTsagk wants to merge 6 commits into
lightningnetwork:masterfrom
GeorgeTsagk:local-reputation-subsystem

Conversation

@GeorgeTsagk

@GeorgeTsagk GeorgeTsagk commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds a subsystem that implements local reputation as proposed here.

You can read more about channel jamming mitigationa here.

The current goal is to only record and calculate revenue/reputation averages in a log-only mode, meaning that:

  • we record HTLC add/settle/fail times
  • we don't affect HTLC forwarding at all: an HTLC rejection due to insufficient reputation is a no-op
  • we log individual HTLC "mock" decision, "would this HTLC make it into protected slots?"

This PR aims to be non-invasive to existing HTLC forwarding code paths. A reviewer treating the reputation subsystem as a black-box should be confident that by recording HTLC events via the reputation subsystem we're not interrupting any other operation.

Checklist for undrafting

  • Break up commits into smaller self-explanatory ones
  • Add better itest coverage (probably a debug API to verify reputation numbers as well)
  • [ ] (?) Handle cold start (historical traffic read) for 2nd part
  • [x] (?) Properly handle in-flight HTLCs when restarting for 2nd part

@GeorgeTsagk GeorgeTsagk self-assigned this Jun 23, 2026
@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label Jun 23, 2026
@github-actions

Copy link
Copy Markdown

PR Severity: CRITICAL

Automated classification | 21 non-test files | ~3,427 lines changed (excluding tests/generated)

CRITICAL (4 files)
  • htlcswitch/interfaces.go - htlcswitch package; HTLC forwarding/payment routing state machine
  • htlcswitch/reputation_guard.go - htlcswitch package; new reputation gating for HTLC forwarding
  • htlcswitch/switch.go - htlcswitch package; core switch integration
  • server.go - Core server coordination
MEDIUM (16 files)
  • lncfg/routing.go - lncfg/* routing config
  • log.go - top-level logger registration
  • reputation_adapter.go - adapter wiring reputation manager into server
  • reputation/buckets.go - new reputation package (uncategorized)
  • reputation/channel.go - new reputation package
  • reputation/channels.go - new reputation package
  • reputation/clock.go - new reputation package
  • reputation/config.go - new reputation package
  • reputation/decaying_average.go - new reputation package
  • reputation/decision.go - new reputation package
  • reputation/htlc.go - new reputation package
  • reputation/log.go - new reputation package
  • reputation/manager.go - new reputation package
  • reputation/manager_startup.go - new reputation package
  • reputation/revenue.go - new reputation package
  • reputation/store.go - new reputation package
LOW (13 files -- excluded from counts)
  • htlcswitch/reputation_hooks_test.go, reputation/*_test.go -- test files
  • itest/list_on_test.go, itest/lnd_reputation_test.go -- integration tests
  • lntest/harness_assertion.go -- test harness
  • reputation/DESIGN.md -- documentation

Analysis

This PR introduces a new channel reputation system for HTLC jamming mitigation. The critical classification is driven by direct modifications to htlcswitch -- one of lnd's most sensitive packages governing HTLC forwarding and the payment routing state machine -- and to server.go (core server coordination).

Key concerns warranting careful review:

  • htlcswitch/switch.go: Integration of reputation gating into the HTLC forwarding path. Any regression could cause incorrect HTLC accept/reject decisions, affecting payment reliability.
  • htlcswitch/reputation_guard.go (new file, 82 lines): New guard logic sitting in the critical forwarding path.
  • htlcswitch/interfaces.go: Interface additions that all implementors must satisfy; watch for subtle behavioral changes.
  • reputation/manager.go (650 lines): Large new manager with in-memory state, startup logic, and a backing store -- persistence correctness and concurrency safety should be verified.

Both severity-bump thresholds are exceeded (21 non-test files, ~3,427 non-test lines), but the base severity was already CRITICAL.


To override, add a severity-override-{critical,high,medium,low} label.
<!-- pr-severity-bot -->

@GeorgeTsagk
GeorgeTsagk force-pushed the local-reputation-subsystem branch 2 times, most recently from 1be208a to 5bdb907 Compare June 24, 2026 12:46
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels Jun 24, 2026
@GeorgeTsagk
GeorgeTsagk force-pushed the local-reputation-subsystem branch 3 times, most recently from 615d701 to 516c694 Compare June 25, 2026 13:12
@GeorgeTsagk
GeorgeTsagk force-pushed the local-reputation-subsystem branch from 516c694 to 8813fe2 Compare July 2, 2026 13:55
@carlaKC

carlaKC commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Chatted to @GeorgeTsagk about strategies to break up this PR up and lighten review burden on the LND team!

PR Breakdown

I was talking to claude about this, and produced this plan, but zero promises because I haven't even read it - just an artifact from this discussion!

(commits marked with * are dead code for the sake of incremental steps, could be squashed if that's not okay)

1. Implement reputation tracking*
  • Decaying averages
  • Tracking peers reputation
  • Ability to add/remove HTLCs to this system
2. Connect to switch
  • Report HTLCs to reputation manager (including on restart)
  • Set experimental field based on accountable and reputation signal
  • Log reputation decision for HTLCS
  • No persistence, no loading historical forwards
  • Minimal tracking of current HTLC set (if required to update reputation)
  • Log HTLC reputation data
3. Restarts and in-flight
  • Persist revenue and reputation for peers
  • Perform "best effort" load from historical forwards if DB values are missing

Once we get to this point, we get a very rudimentary "would this HTLC in isolation be able to enter the protected bucket (if needed)" sanity check. It doesn't take into account that there may be other HTLCs in flight, or whether we'll actually need to use protected resources, but this is a very valuable sanity check that we can't otherwise obtain with the data that's currently surfaced in LND (because we don't have historical failed forwards).

4. Implement bucketing logic*
  • General bucket slot tracking
  • Bucket state management
  • Benchmarks for performance (this is a place we've identified we need to be careful!)
5. Utilize buckets
  • Add decision making for bucket + reputation
  • Persistence of bucket data for in-flight HTLCs
  • Connect manager to bucket system

Other RPCs/snapshots can be added after that, but if the majority of folks aren't running LND with dev server then I think the value of surfacing this information in separate APIs is minimal. Perhaps could think about adding to more mainstream APIs (like listchannels), but that decision doesn't need to happen now IMO.

Review

@elnosh and I are happy to review here! We'll be able to provide strong reviews on the jamming work, since it's our focus. I should be able to provide reasonable review on the switch interactions, though my view of this system is of course a few years stale!

@GeorgeTsagk

GeorgeTsagk commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @carlaKC for writing the summary.

So I believe the next step here is to strip some things away from this PR and only keep 1 & 2:

  • Decaying averages & reputation
  • HTLC switch read-only hooks (feeding HTLCs to the system)
  • Individual HTLC mock-decision log (no buckets) i.e "if this HTLC was being forwarded in isolation, could it be protected"

This should leave us with a more minimal & lean diff, leaving out any noisy parts related to restarts/persistence and cold start.

Another comment on this strategy: if we ever deploy 1&2, then reputation systems in the wild will already start recording values from forwarding, at that point I don't think it would make sense to ship historical-read as a follow-up update to this system, we are practically doing a slow-bootstrap already.

@carlaKC

carlaKC commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

So I believe the next step here is to strip some things away from this PR and only keep 1 & 2

Yeah SGTM! If we're okay with a bit of temporarily dead code, I think it makes sense to do 1 / 2 as separate PRs for the sake of small incremental steps. That's a question of project preferences, so depends on how LND prefers to do things nowadays.

then reputation systems in the wild will already start recording values from forwarding, at that point I don't think it would make sense to ship historical-read as a follow-up update to this system, we are practically doing a slow-bootstrap already.

Indeed! We do need 6 months data to get realreal values, so perhaps for (3) we could just focus on persistence, because we won't get far if we lose all our data every time we restart. Just 2x fields per channel, so not too bad!


@erickcestari also agreed to help out with review ❣️

@GeorgeTsagk
GeorgeTsagk force-pushed the local-reputation-subsystem branch from 8813fe2 to d061d4a Compare July 14, 2026 18:35
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels Jul 14, 2026
@GeorgeTsagk
GeorgeTsagk force-pushed the local-reputation-subsystem branch 2 times, most recently from cbfa4d6 to 304508e Compare July 15, 2026 17:46
Add the log-only local reputation subsystem: per-channel decaying-average
outgoing reputation and aggregated-window incoming revenue, in-flight HTLC
risk tracking, and the per-HTLC isolation decision. A single worker goroutine
owns the channel-state maps lock-free, draining a bounded event queue fed by
the non-blocking OnForward/OnSettle/OnFail hooks.

Also add Manager.Snapshot(), a read-only view of the computed per-channel
state (scid, outgoing reputation, incoming revenue, in-flight risk, pending
count and the at-rest sufficiency verdict). Because the maps are owned solely
by the worker, Snapshot dispatches a snapshot request through the worker's
event queue and blocks on a reply channel rather than reading the maps
directly, keeping the single-owner, lock-free invariant race-free. Decayed
values are computed via non-mutating peekAt helpers so a snapshot never
advances the live state.
Unit tests for the reputation subsystem: the decaying-average and
aggregated-window arithmetic, config validation, the in-flight risk and
effective-fee matrix, the forward/settle/fail lifecycle on the worker, and the
read-only Snapshot() path (including its worker-request race-safety and
non-mutating peekAt helpers).
Wire the log-only reputation subsystem into the forwarding path. The switch
grows read-only OnForward/OnSettle/OnFail hooks (guarded so a hook panic can
never take down forwarding), the server constructs and starts the manager
behind the experimental --routing.reputation flag, and an adapter feeds the
best block height into the in-flight risk calculation off the hot path.

The subsystem is enabled by default; it can be disabled with the opt-out
routing.no-reputation flag.
Expose the local reputation subsystem's computed state through a new unary,
read-only FetchReputation method on the devrpc (dev/debug) sub-server. The
handler calls Manager.Snapshot() and marshals the per-channel state into the
new ChannelReputation proto message.

The reputation manager is wired into the devrpc Config through
PopulateDependencies. It is nil unless the experimental --routing.reputation
flag is set; the handler is nil-safe and returns a clear "reputation
subsystem disabled" error in that case. The method requires a read-only
offchain macaroon permission, and an lncli "fetchreputation" command and a
harness RPC wrapper are added to drive it.

Generated proto stubs regenerated via 'make rpc' (docker-based gen_protos).
Add seam tests for the switch reputation hooks and the panic guard, and an
end-to-end integration test that runs a forwarding node with
--routing.reputation. The itest asserts the log-only invariant (a successful
forward, a failed forward and a restart never affect routing) and reads the
exact computed state back over the new devrpc FetchReputation RPC: on a fresh
channel a single fast settle earns the outgoing channel exactly the advertised
forwarding fee as reputation and moves the incoming channel's revenue positive.
After a restart the snapshot is empty (no persistence) and re-accrues from live
traffic, confirming the documented self-bootstrapping reset.
Document the experimental log-only local reputation subsystem and its
read-only FetchReputation devrpc introspection RPC (and lncli command).
@GeorgeTsagk
GeorgeTsagk force-pushed the local-reputation-subsystem branch from 304508e to 81315c4 Compare July 16, 2026 11:38
@GeorgeTsagk GeorgeTsagk changed the title Add local reputation subsystem (read-only) [1/?] Local reputation: subsystem core, read only Jul 16, 2026
@GeorgeTsagk

Copy link
Copy Markdown
Collaborator Author

Ok marking this as ready for review, it now adds:

  • reputation subsystem and related math (+tests)
  • hooks into htlcswitch -> feeding HTLC traffic data into the system
  • devRPC methods to help expose internal values
  • basic e2e itest

@GeorgeTsagk
GeorgeTsagk marked this pull request as ready for review July 16, 2026 12:05
@GeorgeTsagk GeorgeTsagk added routing channel jamming Issues related to channel jamming mitigation logging Related to the logging / debug output functionality labels Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel jamming Issues related to channel jamming mitigation logging Related to the logging / debug output functionality routing severity-critical Requires expert review - security/consensus critical

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants