Skip to content

Conversation

@Lagrang3
Copy link
Collaborator

@Lagrang3 Lagrang3 commented Oct 15, 2025

Several changes to askrene, mainly to introduce penalties (biases) on nodes and channels that
can be used across payments.
It addresses issue #8600.

  • add timestamps to channel biases,
    - [ ] add expiration on biases,
    - [ ] add expiration on knowledge data,
  • add node biases,
  • penalize nodes on xpay that have failing channels,
  • tests,
  • fix docs,

@Lagrang3 Lagrang3 force-pushed the xpay-bad-nodes branch 8 times, most recently from 7f51753 to 9585fe2 Compare October 23, 2025 16:10
@Lagrang3 Lagrang3 marked this pull request as ready for review October 23, 2025 16:11
@Lagrang3 Lagrang3 requested a review from cdecker as a code owner October 23, 2025 16:11
@Lagrang3 Lagrang3 added this to the v25.12 milestone Oct 23, 2025
@Lagrang3 Lagrang3 changed the title [WIP] Xpay bad nodes Xpay bad nodes Oct 23, 2025
We add one more field to biases: "timestamp".
With the timestamp variable old biases can be removed with the
askrene-age command.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
Changelog-Added: askrene-bias-node: an RPC command to set a bias on node's outgoing or incoming channels.

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
Copy link
Contributor

@rustyrussell rustyrussell left a comment

Choose a reason for hiding this comment

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

Minor API changes, and I need to be convinced of the xpay change.

Comment on lines +972 to +980
for (bias = bias_hash_first(layer->biases, &biasit); bias;
bias = bias_hash_next(layer->biases, &biasit)) {
if (bias->timestamp < cutoff) {
bias_hash_delval(layer->biases, &biasit);
tal_free(bias);
num_removed++;
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

This is cool. but

  1. askrene-age needs to document this new behaviour!
  2. Changelog-None should be Changelog-Added: Plugins: askrene channel biases now have an associated timestamp, and are timed out by askrene-age.

Comment on lines +49 to +55
"out_direction": {
"type": "boolean",
"default": true,
"description": [
"If true the bias applies to outgoing channels otherwise it applies to incoming channels."
]
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer this be called "direction", a string "in" or "out", and a required field.

I think this makes it more explicit.

Comment on lines +1053 to +1055
// FIXME: is this the correct direction?
biases[idx] += node_bias->out_bias;
biases[idx ^ 1] += node_bias->in_bias;
Copy link
Contributor

Choose a reason for hiding this comment

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

To answer your FIXME: yes, it's correct.

The direction for a node pair corresponds to the outgoing channel: that's because the node controls fees only on outgoing. So, biases[index] is indeed this node's outgoing.

Comment on lines +856 to 882

/* We add a negative bias to this channel to penalize it for other
* payments.
* Biases are nice way to penalize or encourage the use of
* channels. But it has some limitations. For example the bias would
* have no effect if the channel already provides 0 cost routing. */
req = payment_ignored_req(aux_cmd, attempt, "askrene-bias-channel");
json_add_string(req->js, "layer", XPAY_GLOBAL_LAYER);
json_add_short_channel_id_dir(req->js, "short_channel_id_dir",
attempt->hops[index].scidd);
json_add_s32(req->js, "bias", -5);
json_add_bool(req->js, "relative", true);
send_payment_req(aux_cmd, attempt->payment, req);

/* We add a negative bias to the node that owns this half channel. */
if (index) {
req =
payment_ignored_req(aux_cmd, attempt, "askrene-bias-node");
json_add_string(req->js, "layer", XPAY_GLOBAL_LAYER);
json_add_pubkey(req->js, "node",
&attempt->hops[index - 1].next_node);
json_add_s32(req->js, "bias", -1);
json_add_bool(req->js, "relative", true);
json_add_bool(req->js, "out_direction", true);
send_payment_req(aux_cmd, attempt->payment, req);
}
goto check_previous_success;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have evidence to support this approach? We've effectively biased it already by lowering the capacity, after all.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think so. We add a bias to the channel that returned a weird error here, eg. WIRE_FEE_INSUFFICIENT or WIRE_PERMANENT_CHANNEL_FAILURE. We reduce the known liquidity only when we get a WIRE_TEMPORARY_CHANNEL_FAILURE.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We disable those failing channels for the current payment session but other payments instances can pick them up again.

@Lagrang3
Copy link
Collaborator Author

@rustyrussell: regarding the direction of the channels, either "in" or "out", it is true that it gives us more control but I can't think of a practical use case for making that distinction. If a node's out channels are heavily penalized that means we will eventually not going to route through it's in-channels anyways.

@cdecker
Copy link
Member

cdecker commented Nov 10, 2025

I don't quite see a case where we'd ever set out_direction = false or direction = in. Ultimately we want to bias a node that is failing our payments when forwarding, not payments leading into a given node.

This is similar to the Boltz situation, since they keep their channels purposefully unbalanced, with most capacity on their peer's side. We temporarily fixed that by blocklisting all outgoing channels: this way we'd never end up at that node, unless it is the destination, because incoming edges are still available. And I think it can be generalized this way.

I'd vote for removing the direction param, and just always using out as the interpretation. If we ever find a reason to penalize payments into a node, then we can still re-add the direction param.

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