Skip to content

Add heuristic fee engine and related services#533

Open
markettes wants to merge 20 commits into
mainfrom
feat/heuristic-fee-engine-phase1
Open

Add heuristic fee engine and related services#533
markettes wants to merge 20 commits into
mainfrom
feat/heuristic-fee-engine-phase1

Conversation

@markettes

Copy link
Copy Markdown
Contributor

Introduce foundational components for a heuristic routing engine, including new methods for LightningClientService, migrations for routing engine tables, and repositories for channel fee and routing states. Implement helper classes for block height and channel ownership, along with tests to ensure functionality. This update enhances dynamic fee management and automated rebalancing capabilities.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Introduces the Phase 1 “foundation + signal” components for a heuristic routing engine, adding persisted routing/fee state models, a scheduled job to compute per-channel signals from forwarding history + live channel data, and UI/config surfaces to stage nodes for future Phase 2/3 actuation.

Changes:

  • Add ChannelRoutingState / ChannelFeeState entities + repositories and EF migration to persist routing-engine state.
  • Add TargetRatioReevaluationJob plus PeerCategorizationService (hysteresis categorization + EWMA smoothing) to compute targets and peer flow categories.
  • Extend services/UI/config: GetInfo/block height retrieval, routing-engine constants, and node settings UI for dynamic fees/rebalancing.

Reviewed changes

Copilot reviewed 31 out of 32 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/NodeGuard.Tests/Services/PeerCategorizationServiceTests.cs Unit tests for categorization hysteresis + target/EMA smoothing math.
test/NodeGuard.Tests/Helpers/ChannelOwnershipHelperTests.cs Tests ownership dedup logic for managed↔managed channels.
test/NodeGuard.Tests/Helpers/BlockHeightHelperTests.cs Tests SCID funding-height/age parsing and alias/zero handling.
test/NodeGuard.Tests/Data/Repositories/RebalanceRepositoryRoutingEngineTests.cs Tests new rebalance in-flight count and budget-consumption calculations.
test/NodeGuard.Tests/Data/Repositories/ChannelRoutingStateRepositoryTests.cs Tests routing-state upsert semantics and filtering by node pubkey.
test/NodeGuard.Tests/Data/Repositories/ChannelFlowAnalyticsRepositoryTests.cs Tests settled-only, windowed per-channel forwarding aggregations.
src/Services/PeerCategorizationService.cs Adds categorization decision record + core categorization/target smoothing logic.
src/Services/LightningService.cs Adds GetBlockHeight to expose chain tip via LND GetInfo.
src/Services/LightningClientService.cs Adds GetInfo RPC wrapper with error handling/logging.
src/Program.cs Registers new repositories/service and schedules the new Quartz job/trigger.
src/Pages/Nodes.razor Adds routing-engine configuration fields (dynamic fees/rebalance budgets/dry-run) to node UI.
src/Migrations/ApplicationDbContextModelSnapshot.cs Updates EF snapshot with routing-engine entities and new columns/defaults.
src/Migrations/20260707155251_AddRoutingEngineFoundation.Designer.cs Migration designer for routing-engine foundation schema changes.
src/Migrations/20260707155251_AddRoutingEngineFoundation.cs Adds new node/channel columns and creates ChannelRoutingStates / ChannelFeeStates tables.
src/Jobs/TargetRatioReevaluationJob.cs Implements Phase 1 job to compute/store routing signals and target ratios per channel.
src/Helpers/Constants.cs Adds routing-engine constants and env var parsing.
src/Helpers/ChannelOwnershipHelper.cs Adds helper to decide managed-node channel “ownership” for dedup.
src/Helpers/BlockHeightHelper.cs Adds helper to parse funding height and channel age from SCID.
src/Data/Repositories/RebalanceRepository.cs Adds in-flight count and consumed-fees calculations for routing-engine budgeting.
src/Data/Repositories/Interfaces/IRebalanceRepository.cs Declares new rebalance query methods for Phase 3 budgeting/caps.
src/Data/Repositories/Interfaces/IChannelRoutingStateRepository.cs New repository interface for routing-state persistence/upsert.
src/Data/Repositories/Interfaces/IChannelFlowAnalyticsRepository.cs New interface for settled-only forwarding flow aggregations.
src/Data/Repositories/Interfaces/IChannelFeeStateRepository.cs New interface for fee-state persistence/upsert (Phase 2).
src/Data/Repositories/ChannelRoutingStateRepository.cs Implements routing-state get/upsert logic keyed by ChannelId.
src/Data/Repositories/ChannelFlowAnalyticsRepository.cs Implements settled-only forwarding aggregations over ForwardingHtlcEvents.
src/Data/Repositories/ChannelFeeStateRepository.cs Implements fee-state get/upsert logic keyed by ChannelId.
src/Data/Models/Rebalance.cs Adds ReservedFeeSats for in-flight budget accounting.
src/Data/Models/Node.cs Adds routing-engine node-level feature flags and budget/cap settings.
src/Data/Models/ChannelRoutingState.cs Adds routing-state entity + PeerFlowCategory enum.
src/Data/Models/ChannelFeeState.cs Adds fee-state entity for future fee engine (baseline/last-applied/circuit breaker).
src/Data/Models/Channel.cs Adds per-channel opt-out flag IsDynamicFeeEnabled (default true).
src/Data/ApplicationDbContext.cs Wires up new DbSets, 1:1 mappings, and default values for new flags.
Files not reviewed (1)
  • src/Migrations/20260707155251_AddRoutingEngineFoundation.Designer.cs: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Data/Repositories/RebalanceRepository.cs
Comment thread src/Helpers/Constants.cs Outdated
Comment thread src/Data/Repositories/ChannelFlowAnalyticsRepository.cs Outdated
Comment thread src/Jobs/TargetRatioReevaluationJob.cs
@markettes markettes requested a review from Jossec101 July 8, 2026 12:25
Comment thread src/Data/Models/Rebalance.cs Outdated
/// Fee reserved for this in-flight rebalance so its spend counts against the node budget
/// before <see cref="FeePaidSats"/> settles (Phase 3 in-flight budget accounting).
/// </summary>
public long? ReservedFeeSats { get; set; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dont fully get this field, if the rebalance is in pending already you know the amount it's counted into the budget?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Comment thread src/Data/Repositories/ChannelFlowAnalyticsRepository.cs Outdated
Comment thread src/Data/Models/ChannelFeeState.cs Outdated
Comment on lines +23 to +24
/// Per-channel fee-engine state (1:1 with <see cref="Channel"/>). Declared in the Phase 1
/// migration so Phase 2 needs no schema change; rows are not created or populated until the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dont like this "phase 1" comments coming from the LLM, They should be todo or pure comments outside the summary as this can get obsolete if not changed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, will remove all this

Comment thread src/Data/Models/ChannelFeeState.cs Outdated
Comment on lines +47 to +52

/// <summary>
/// Circuit-breaker counter: incremented on each consecutive failure to apply a fee update,
/// reset to 0 on success.
/// </summary>
public int ConsecutiveFailures { get; set; } = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

not really, just a feature, will remove it

Comment thread src/Data/Models/Node.cs Outdated
Comment thread src/Data/Models/Channel.cs Outdated
Comment thread src/Helpers/Constants.cs Outdated
Comment thread src/Helpers/Constants.cs
Comment on lines +252 to +256
/// <summary>
/// Proportional gain mapping net-flow to target drift: target_goal = 0.5 + clamp(K·netFlowRatio, ±maxDrift).
/// Default 0.70. Env: ROUTING_ENGINE_TARGET_K.
/// </summary>
public static double ROUTING_ENGINE_TARGET_K = 0.70;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you simplify what's this field about?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Comment thread src/Helpers/Constants.cs
Comment on lines +257 to +280

/// <summary>
/// Maximum drift of target_goal away from 0.5 (reached at |netFlowRatio| = 0.5). Default 0.35,
/// giving a target_goal range of [0.15, 0.85]. Env: ROUTING_ENGINE_TARGET_MAX_DRIFT.
/// </summary>
public static double ROUTING_ENGINE_TARGET_MAX_DRIFT = 0.35;

/// <summary>
/// EWMA smoothing factor folding target_goal into the stored TargetLocalRatio. Default 0.10
/// (~10 cycles to converge). Env: ROUTING_ENGINE_TARGET_ALPHA.
/// </summary>
public static double ROUTING_ENGINE_TARGET_ALPHA = 0.10;

/// <summary>
/// EWMA smoothing factor for EmaLocalRatio (~24h effective window at 30-min sampling).
/// Default 0.04. Env: ROUTING_ENGINE_FEE_EMA_ALPHA.
/// </summary>
public static double ROUTING_ENGINE_FEE_EMA_ALPHA = 0.04;

/// <summary>
/// Consecutive divergent cycles required before a category flip commits (anti-flap hysteresis).
/// Default 3. Env: ROUTING_ENGINE_CATEGORY_FLIP_HYSTERESIS_CYCLES.
/// </summary>
public static int ROUTING_ENGINE_CATEGORY_FLIP_HYSTERESIS_CYCLES = 3;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same, clearer summaries help and examples also

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have added clarifying formulas to the alpha factors for the target and simpler descriptions

Comment on lines +86 to +87
if (channel.Status == Channel.ChannelStatus.Open && channel.ChanId != 0)
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also check if the channel is active/enabled? Check Availability on channels

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added here:

// Act only on channels that are active.
if (!lndChannel.Active)
{
continue;
}

Comment thread src/Pages/Nodes.razor
Comment on lines +468 to +475
<Field>
<FieldLabel>Restore Baseline on Disable</FieldLabel>
<FieldHelp>When dynamic fees are turned off, restore each channel's captured fee baseline in one final write. When off, last-set fees are frozen in place.</FieldHelp>
<Check TValue="bool" Checked="@_selectedNodeForLiquidity.RestoreFeeBaselineOnDisable"
CheckedChanged="@((value) => _selectedNodeForLiquidity.RestoreFeeBaselineOnDisable = value)">
Restore fee baseline on disable
</Check>
</Field>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dont think this has any benefit tbh

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Okay, I thought this was a good idea. If a node is disabled, you will have to change fees for every single channel, and this solves it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I mean what's the issue leaving old fees as they are?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we would leave them mid strategy instead in a market avg fee. No strong in this opinion though

Comment thread src/Program.cs Outdated
@markettes markettes requested a review from Jossec101 July 15, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants