Conversation
The previous parametrization of the log normal distribution used for random payment amounts (mu = 2ln(amt) - ln(limit), sigma^2 = 2(ln(limit) - ln(amt))) technically had the expected payment amount as its mean, but with a pathologically heavy tail for realistic channel sizes: the median payment was amt^2 / limit (sub-satoshi dust for large channels) and exactly half of the distribution's expected volume sat above the payment limit itself, carried by astronomically rare payments that could never succeed. Sample means therefore never converged on the expected payment amount in practice, so nodes sent far less volume than their capacity and the simulation's activity multiplier implied. Re-parametrize with mu = ln(amt) - sigma^2/2, which has mean amt for any sigma, and choose sigma as large as possible subject to keeping 95% of samples below the payment limit and capping the coefficient of variation at one so that sample averages converge quickly (standard error of the mean is at most amt/sqrt(n) after n payments). The seeded end-to-end test's hard-coded payment ordering is regenerated because payment amount sampling shares the seeded rng stream with event timing; the test now asserts on a prefix of payments so that it does not depend on how many payments squeeze in at the wall-clock cutoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a primitive that resets a simulated channel's settled balance to an even split when either side's available liquidity has drawn down beneath a trigger fraction of capacity. This models the out-of-band actions that node operators use to restore usable liquidity (circular rebalances, swaps, splices) by their effect rather than their mechanism, so that long-running simulations can opt into sustained liquidity rather than permanent depletion. Balance locked in in-flight HTLCs is intentionally left untouched: only the settled portion of the channel is redistributed, which keeps the channel's capacity accounting invariant intact and lets in-flight HTLCs settle normally after a rebalance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No functional change. Ordering is needed so that tasks scanning the set of simulated channels can iterate in a stable order, keeping simulations run with a fixed seed deterministic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds SimGraph::start_rebalancer, which spawns a task that periodically scans the simulated network's channels and teleport-rebalances any channel whose available liquidity has drawn down beneath a configured fraction of its capacity. Without any restoring force, random payment activity progressively drains simulated channels to one side and payment success rates decay over long simulations. Real operators counteract this with out-of-band actions (circular rebalances, swaps, splices) that a payment-only simulator cannot express, so this task models their effect instead. It is opt-in: leaving it off remains the honest default for studying depletion behavior, while enabling it supports long-running load tests that need sustained liquidity. Each rebalance is logged with the amount moved, so the total intervention volume that a topology and traffic pattern require is itself observable output. The task introduces no new source of randomness to simulations run with a fixed seed: scans run at a fixed interval on the simulation clock and visit channels in short channel ID order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds an optional rebalance section to the simulation file that enables
periodic teleport-rebalancing of depleted channels on simulated
networks:
"rebalance": {
"trigger_below": 0.1,
"interval_secs": 3600
}
Both fields are optional and default to the values shown. The section
is rejected when running against real nodes, since the simulator has
no way to move liquidity in channels it does not control.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pathfinding previously set no limit on total routing fees, so payments would route through channels advertising absurd fees when they offered the only (or best-scored) path. Some real-world graph exports use maxed-out fee rates to mark channel directions as unusable; with no budget, the simulator routes straight through them and the sender bleeds channel balance in fees. Because fees are not recorded in payment results, this drain is invisible in the simulation's output, surfacing only as mysterious liquidity depletion. Cap the fee budget at 5% of the payment amount, which is generous by real-node standards but prevents routing at any cost. Test payment amounts are increased so that their route fees fit within the new budget on the test networks' fee gradients. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Expose the maximum routing fee that simulated nodes will pay as a top-level max_route_fee_pct field in the simulation file, expressed as a percentage of the payment amount and defaulting to the previous hard-coded 5%. Experiments studying fee behavior may legitimately want a tighter or looser sender fee tolerance, so the budget becomes a knob rather than a constant. The percentage is threaded through ln_node_from_graph to each SimNode (validated to be positive), and find_payment_route now receives an explicit per-payment fee budget. As with rebalancing, the field is rejected for configurations that run against real nodes, which apply their own fee limits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds an exclusion list to RebalanceConfig so that individual nodes can opt out of the rebalancing task. A channel is skipped by scans if either of its peers is excluded, regardless of which side has drawn down, since a real channel cannot be rebalanced without the participation of the node that holds the depleted balance. This models operators that do not maintain their liquidity, and lets their channels drain naturally under the simulation's payment flows while the rest of the network is kept liquid. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds an exclude list to the rebalance section of the simulation file,
containing the pubkeys of nodes that should not participate in
rebalancing:
"rebalance": {
"trigger_below": 0.1,
"interval_secs": 3600,
"exclude": ["02abc..."]
}
Exclusions that reference nodes that are not part of the simulated
network are rejected at startup, because they would otherwise be
silently ignored (most likely hiding a typo in the pubkey).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Went through this with Clara and it seems to output far more reasonable values: - sample output from test below: |
only1dreamgene
left a comment
There was a problem hiding this comment.
Skimmed through this, mostly looking at the payment-amount distribution logic and the new rebalancer. The log-normal / sigma-cap approach for payment_amount looks solid and the math checks out (including the sigma=0 degenerate case). Left two inline comments on things worth a look before this comes out of draft.
| _ = clock.sleep(cfg.interval) => {}, | ||
| } | ||
|
|
||
| let mut channels_lock = channels.lock().await; |
There was a problem hiding this comment.
This holds channels_lock for the entire scan of scids (lines 1239-1253), not just per-channel. propagate_payment needs the same channels mutex to add/remove HTLCs per hop, so every in-flight payment forward will stall until the whole rebalance scan finishes. With a large network and a short interval_secs, this could show up as periodic simulation-wide latency spikes that scale with network size. Might be worth releasing/reacquiring the lock per-channel (or per-batch) instead of holding it across the full loop.
| "The expected order of payments is not correct: {:?} vs {:?}", | ||
| payments_list.lock().unwrap(), | ||
| expected_payment_list, | ||
| let actual_payments: Vec<PublicKey> = payments_list |
There was a problem hiding this comment.
This changed from an exact equality check (payments_list.lock().unwrap().as_ref() == expected_payment_list) to a .take(expected_payment_list.len()) prefix comparison. That means the test no longer catches cases where the simulation emits fewer payments than expected, or extra/reordered payments beyond the first N — it'll pass as long as the prefix matches. Given this PR is changing the payment-amount and fee-limit logic that feeds this exact test, it'd be good to double check this loosening was intentional and not just masking a timing flake.
|
Likewise here if you'd be interested in picking this up @only1dreamgene it's certainly up for grabs! @elnosh maybe you'd be interested in grabbing this one if you're running sims concerned with routability? I reckon it'll give us better payment amounts to think about. |
We know that the way that we generate traffic isn't great - see #301.
The former attempt at fixing this was abandoned because it caused simulations to un-balance, which was problematic for the long simulations we were running. A solution here is to allow an "arbitrary auto rebalance" in the network so that payments can keep flowing.
This is useful for the channel jamming case, because we just care about there being some activity that is reproducible across networks.
TBD: