From e78c102dff7d1848788e1971df4a269606b16b9f Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 23 Sep 2024 10:18:27 +0100 Subject: [PATCH 01/30] Add json API for struct route_exclusion To be able to write a route_exclusion to a json stream. Changelog-None. Signed-off-by: Lagrang3 --- common/json_stream.c | 12 ++++++++++++ common/json_stream.h | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/common/json_stream.c b/common/json_stream.c index 31a5367b15e4..81ff9ff6c33c 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -494,6 +495,17 @@ void json_add_short_channel_id_dir(struct json_stream *response, short_channel_id_outnum(scidd.scid), scidd.dir); } +void json_add_route_exclusion(struct json_stream *response, + const char *fieldname, + const struct route_exclusion *ex) +{ + if (ex->type == EXCLUDE_NODE) + json_add_node_id(response, fieldname, &ex->u.node_id); + else + json_add_short_channel_id_dir(response, fieldname, + ex->u.chan_id); +} + static void json_add_address_fields(struct json_stream *response, const struct wireaddr *addr, const char *typefield) diff --git a/common/json_stream.h b/common/json_stream.h index eeca272c3e09..1962de0c4634 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -36,6 +36,7 @@ struct wally_psbt; struct lease_rates; struct wireaddr; struct wireaddr_internal; +struct route_exclusion; struct json_stream { struct json_out *jout; @@ -301,6 +302,12 @@ void json_add_channel_id(struct json_stream *response, const char *fieldname, const struct channel_id *cid); +/* '"fieldname" : ' or "" if fieldname is NULL, where is + * either a node_id or a short_channel_id_dir. */ +void json_add_route_exclusion(struct json_stream *response, + const char *fieldname, + const struct route_exclusion *ex); + /* '"fieldname" : ' or "" if fieldname is NULL */ void json_add_txid(struct json_stream *result, const char *fieldname, const struct bitcoin_txid *txid); From 6f087dd30f5fe89170f16d794c3d2417f337e27a Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 23 Sep 2024 10:24:23 +0100 Subject: [PATCH 02/30] askrene: add route_exclution array Disabled nodes and channels are now saved into a tal_arr of type strut route_exclution. Signed-off-by: Lagrang3 --- plugins/askrene/layer.c | 65 +++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/plugins/askrene/layer.c b/plugins/askrene/layer.c index 3991716dc988..3636e79b9eb8 100644 --- a/plugins/askrene/layer.c +++ b/plugins/askrene/layer.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -81,8 +82,8 @@ struct layer { /* Additional info, indexed by scid+dir */ struct constraint_hash *constraints; - /* Nodes to completely disable (tal_arr) */ - struct node_id *disabled_nodes; + /* Channels and nodes to disable (tal_arr). */ + struct route_exclusion *disabled; }; struct layer *new_temp_layer(const tal_t *ctx, const char *name) @@ -94,7 +95,7 @@ struct layer *new_temp_layer(const tal_t *ctx, const char *name) local_channel_hash_init(l->local_channels); l->constraints = tal(l, struct constraint_hash); constraint_hash_init(l->constraints); - l->disabled_nodes = tal_arr(l, struct node_id, 0); + l->disabled = tal_arr(l, struct route_exclusion, 0); return l; } @@ -299,7 +300,10 @@ size_t layer_trim_constraints(struct layer *layer, u64 cutoff) void layer_add_disabled_node(struct layer *layer, const struct node_id *node) { - tal_arr_expand(&layer->disabled_nodes, *node); + struct route_exclusion ex; + ex.type = EXCLUDE_NODE; + ex.u.node_id = *node; + tal_arr_expand(&layer->disabled, ex); } void layer_add_localmods(const struct layer *layer, @@ -312,29 +316,32 @@ void layer_add_localmods(const struct layer *layer, /* First, disable all channels into blocked nodes (local updates * can add new ones)! */ - for (size_t i = 0; i < tal_count(layer->disabled_nodes); i++) { - const struct gossmap_node *node; + for (size_t i = 0; i < tal_count(layer->disabled); i++) { + struct route_exclusion ex = layer->disabled[i]; - node = gossmap_find_node(gossmap, &layer->disabled_nodes[i]); - if (!node) - continue; - for (size_t n = 0; n < node->num_chans; n++) { - struct short_channel_id scid; - struct gossmap_chan *c; - int dir; - c = gossmap_nth_chan(gossmap, node, n, &dir); - scid = gossmap_chan_scid(gossmap, c); - - /* Disabled zero-capacity on incoming */ - gossmap_local_updatechan(localmods, - scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(0), - 0, - 0, - 0, - false, - !dir); + /* Disable all channels of excluded nodes. */ + if (ex.type == EXCLUDE_NODE) { + const struct gossmap_node *node; + + node = gossmap_find_node(gossmap, &ex.u.node_id); + if (!node) + continue; + for (size_t n = 0; n < node->num_chans; n++) { + struct short_channel_id scid; + struct gossmap_chan *c; + int dir; + c = gossmap_nth_chan(gossmap, node, n, &dir); + scid = gossmap_chan_scid(gossmap, c); + + /* Disabled zero-capacity on incoming */ + gossmap_local_updatechan( + localmods, scid, AMOUNT_MSAT(0), + AMOUNT_MSAT(0), 0, 0, 0, false, !dir); + } + } else { + gossmap_local_updatechan( + localmods, ex.u.chan_id.scid, AMOUNT_MSAT(0), + AMOUNT_MSAT(0), 0, 0, 0, false, ex.u.chan_id.dir); } } @@ -422,9 +429,9 @@ static void json_add_layer(struct json_stream *js, json_object_start(js, fieldname); json_add_string(js, "layer", layer->name); - json_array_start(js, "disabled_nodes"); - for (size_t i = 0; i < tal_count(layer->disabled_nodes); i++) - json_add_node_id(js, NULL, &layer->disabled_nodes[i]); + json_array_start(js, "disabled"); + for (size_t i = 0; i < tal_count(layer->disabled); i++) + json_add_route_exclusion(js, NULL, &layer->disabled[i]); json_array_end(js); json_array_start(js, "created_channels"); for (lc = local_channel_hash_first(layer->local_channels, &lcit); From 1c0132e61755bfb029b8cd1327e918c3733cced3 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 23 Sep 2024 10:27:47 +0100 Subject: [PATCH 03/30] add askrene-disable-channel Changelog-EXPERIMENTAL: askrene: add askrene-disable-channel RPC Signed-off-by: Lagrang3 --- contrib/msggen/msggen/schema.json | 76 +++++++++++++++++-- doc/Makefile | 1 + doc/index.rst | 1 + .../lightning-askrene-disable-channel.json | 56 ++++++++++++++ doc/schemas/lightning-askrene-listlayers.json | 20 +++-- plugins/askrene/askrene.c | 42 ++++++++++ plugins/askrene/layer.c | 9 +++ plugins/askrene/layer.h | 4 + tests/test_askrene.py | 19 ++++- 9 files changed, 216 insertions(+), 12 deletions(-) create mode 100644 doc/schemas/lightning-askrene-disable-channel.json diff --git a/contrib/msggen/msggen/schema.json b/contrib/msggen/msggen/schema.json index a9b6d1572ac4..f660f3ef43ed 100644 --- a/contrib/msggen/msggen/schema.json +++ b/contrib/msggen/msggen/schema.json @@ -361,6 +361,62 @@ "Main web site: " ] }, + "lightning-askrene-disable-channel.json": { + "$schema": "../rpc-schema-draft.json", + "type": "object", + "additionalProperties": false, + "rpc": "askrene-disable-channel", + "title": "Command to disable a channel in a layer (EXPERIMENTAL)", + "description": [ + "WARNING: experimental, so API may change.", + "", + "The **askrene-disable-channel** RPC command tells askrene to disable a channel whenever the given layer is used. This is mainly useful to force the use of alternate paths." + ], + "request": { + "required": [ + "layer", + "short_channel_id" + ], + "properties": { + "layer": { + "type": "string", + "description": [ + "The name of the layer to apply this change to." + ] + }, + "short_channel_id": { + "type": "short_channel_id", + "description": [ + "The channel to disable." + ] + }, + "direction": { + "type": "u32", + "description": [ + "The direction of the channel. If the direction is not specified then both directions are disabled." + ] + } + } + }, + "response": { + "required": [], + "properties": {} + }, + "see_also": [ + "lightning-getroutes(7)", + "lightning-askrene-create-channel(7)", + "lightning-askrene-inform-channel(7)", + "lightning-askrene-disable-node(7)", + "lightning-askrene-listlayers(7)", + "lightning-askrene-age(7)" + ], + "author": [ + "Rusty Russell <> is mainly responsible." + ], + "resources": [ + "Main web site: " + ] + }, "lightning-askrene-disable-node.json": { "$schema": "../rpc-schema-draft.json", "type": "object", @@ -555,7 +611,7 @@ "additionalProperties": false, "required": [ "layer", - "disabled_nodes", + "disabled", "created_channels", "constraints" ], @@ -566,12 +622,22 @@ "The name of the layer." ] }, - "disabled_nodes": { + "disabled": { "type": "array", "items": { - "type": "pubkey", - "description": [ - "The id of the disabled node." + "oneOf": [ + { + "type": "pubkey", + "description": [ + "The id of the disabled node." + ] + }, + { + "type": "short_channel_id_dir", + "description": [ + "The short channel id of the disabled channel." + ] + } ] } }, diff --git a/doc/Makefile b/doc/Makefile index fd59c4182646..719b0527f2f7 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -8,6 +8,7 @@ GENERATE_MARKDOWN := doc/lightning-addgossip.7 \ doc/lightning-addpsbtoutput.7 \ doc/lightning-askrene-create-channel.7 \ doc/lightning-askrene-disable-node.7 \ + doc/lightning-askrene-disable-channel.7 \ doc/lightning-askrene-inform-channel.7 \ doc/lightning-askrene-listlayers.7 \ doc/lightning-askrene-reserve.7 \ diff --git a/doc/index.rst b/doc/index.rst index 5515229db141..75e3d863f6d1 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -15,6 +15,7 @@ Core Lightning Documentation lightning-addgossip lightning-addpsbtoutput lightning-askrene-create-channel + lightning-askrene-disable-channel lightning-askrene-disable-node lightning-askrene-inform-channel lightning-askrene-listlayers diff --git a/doc/schemas/lightning-askrene-disable-channel.json b/doc/schemas/lightning-askrene-disable-channel.json new file mode 100644 index 000000000000..84f3f43042f0 --- /dev/null +++ b/doc/schemas/lightning-askrene-disable-channel.json @@ -0,0 +1,56 @@ +{ + "$schema": "../rpc-schema-draft.json", + "type": "object", + "additionalProperties": false, + "rpc": "askrene-disable-channel", + "title": "Command to disable a channel in a layer (EXPERIMENTAL)", + "description": [ + "WARNING: experimental, so API may change.", + "", + "The **askrene-disable-channel** RPC command tells askrene to disable a channel whenever the given layer is used. This is mainly useful to force the use of alternate paths." + ], + "request": { + "required": [ + "layer", + "short_channel_id" + ], + "properties": { + "layer": { + "type": "string", + "description": [ + "The name of the layer to apply this change to." + ] + }, + "short_channel_id": { + "type": "short_channel_id", + "description": [ + "The channel to disable." + ] + }, + "direction": { + "type": "u32", + "description": [ + "The direction of the channel. If the direction is not specified then both directions are disabled." + ] + } + } + }, + "response": { + "required": [], + "properties": {} + }, + "see_also": [ + "lightning-getroutes(7)", + "lightning-askrene-create-channel(7)", + "lightning-askrene-inform-channel(7)", + "lightning-askrene-disable-node(7)", + "lightning-askrene-listlayers(7)", + "lightning-askrene-age(7)" + ], + "author": [ + "Rusty Russell <> is mainly responsible." + ], + "resources": [ + "Main web site: " + ] +} diff --git a/doc/schemas/lightning-askrene-listlayers.json b/doc/schemas/lightning-askrene-listlayers.json index a0a5cba70511..9f996f8a54de 100644 --- a/doc/schemas/lightning-askrene-listlayers.json +++ b/doc/schemas/lightning-askrene-listlayers.json @@ -32,7 +32,7 @@ "additionalProperties": false, "required": [ "layer", - "disabled_nodes", + "disabled", "created_channels", "constraints" ], @@ -43,12 +43,22 @@ "The name of the layer." ] }, - "disabled_nodes": { + "disabled": { "type": "array", "items": { - "type": "pubkey", - "description": [ - "The id of the disabled node." + "oneOf": [ + { + "type": "pubkey", + "description": [ + "The id of the disabled node." + ] + }, + { + "type": "short_channel_id_dir", + "description": [ + "The short channel id of the disabled channel." + ] + } ] } }, diff --git a/plugins/askrene/askrene.c b/plugins/askrene/askrene.c index 751b687416ef..9cf17dd49e64 100644 --- a/plugins/askrene/askrene.c +++ b/plugins/askrene/askrene.c @@ -881,6 +881,44 @@ static struct command_result *json_askrene_inform_channel(struct command *cmd, return command_finished(cmd, response); } +static struct command_result *json_askrene_disable_channel(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct short_channel_id *scid; + int *direction; + const char *layername; + struct layer *layer; + struct json_stream *response; + struct askrene *askrene = get_askrene(cmd->plugin); + + if (!param(cmd, buffer, params, + p_req("layer", param_layername, &layername), + p_req("short_channel_id", param_short_channel_id, &scid), + p_opt("direction", param_zero_or_one, &direction), + NULL)) + return command_param_failed(); + + layer = find_layer(askrene, layername); + if (!layer) + layer = new_layer(askrene, layername); + + struct short_channel_id_dir scidd = {.scid = *scid}; + if (direction) { + scidd.dir = *direction; + layer_add_disabled_channel(layer, &scidd); + } else { + /* If no direction is provided we disable both. */ + scidd.dir = 0; + layer_add_disabled_channel(layer, &scidd); + scidd.dir = 1; + layer_add_disabled_channel(layer, &scidd); + } + + response = jsonrpc_stream_success(cmd); + return command_finished(cmd, response); +} + static struct command_result *json_askrene_disable_node(struct command *cmd, const char *buffer, const jsmntok_t *params) @@ -983,6 +1021,10 @@ static const struct plugin_command commands[] = { "askrene-age", json_askrene_age, }, + { + "askrene-disable-channel", + json_askrene_disable_channel, + }, }; static void askrene_markmem(struct plugin *plugin, struct htable *memtable) diff --git a/plugins/askrene/layer.c b/plugins/askrene/layer.c index 3636e79b9eb8..ae37b971c159 100644 --- a/plugins/askrene/layer.c +++ b/plugins/askrene/layer.c @@ -306,6 +306,15 @@ void layer_add_disabled_node(struct layer *layer, const struct node_id *node) tal_arr_expand(&layer->disabled, ex); } +void layer_add_disabled_channel(struct layer *layer, + const struct short_channel_id_dir *scidd) +{ + struct route_exclusion ex; + ex.type = EXCLUDE_CHANNEL; + ex.u.chan_id = *scidd; + tal_arr_expand(&layer->disabled, ex); +} + void layer_add_localmods(const struct layer *layer, const struct gossmap *gossmap, bool zero_cost, diff --git a/plugins/askrene/layer.h b/plugins/askrene/layer.h index 7acea220c072..5503644ae0c7 100644 --- a/plugins/askrene/layer.h +++ b/plugins/askrene/layer.h @@ -102,6 +102,10 @@ size_t layer_trim_constraints(struct layer *layer, u64 cutoff); /* Add a disabled node to a layer. */ void layer_add_disabled_node(struct layer *layer, const struct node_id *node); +/* Add a disabled channel to a layer. */ +void layer_add_disabled_channel(struct layer *layer, + const struct short_channel_id_dir *scidd); + /* Print out a json object per layer, or all if layer is NULL */ void json_add_layers(struct json_stream *js, struct askrene *askrene, diff --git a/tests/test_askrene.py b/tests/test_askrene.py index a87ae3c6ec53..503a5e99f453 100644 --- a/tests/test_askrene.py +++ b/tests/test_askrene.py @@ -16,15 +16,19 @@ def test_layers(node_factory): assert l2.rpc.askrene_listlayers('test_layers') == {'layers': []} expect = {'layer': 'test_layers', - 'disabled_nodes': [], + 'disabled': [], 'created_channels': [], 'constraints': []} l2.rpc.askrene_disable_node('test_layers', l1.info['id']) - expect['disabled_nodes'].append(l1.info['id']) + expect['disabled'].append(l1.info['id']) assert l2.rpc.askrene_listlayers('test_layers') == {'layers': [expect]} assert l2.rpc.askrene_listlayers() == {'layers': [expect]} assert l2.rpc.askrene_listlayers('test_layers2') == {'layers': []} + l2.rpc.askrene_disable_channel('test_layers', "1x2x3", 0) + expect['disabled'].append("1x2x3/0") + assert l2.rpc.askrene_listlayers('test_layers') == {'layers': [expect]} + # Tell it l3 connects to l1! l2.rpc.askrene_create_channel('test_layers', l3.info['id'], @@ -164,6 +168,17 @@ def test_getroutes(node_factory): # Set up l1 with this as the gossip_store l1 = node_factory.get_node(gossip_store_file=gsfile.name) + # Disabling channels makes getroutes fail + l1.rpc.askrene_disable_channel("chans_disabled", "0x1x0") + l1.rpc.askrene_disable_channel("chans_disabled", "0x2x1") + l1.rpc.askrene_disable_channel("chans_disabled", "0x2x3") + with pytest.raises(RpcError, match="Could not find route"): + l1.rpc.getroutes(source=nodemap[0], + destination=nodemap[1], + amount_msat=1000, + layers=["chans_disabled"], + maxfee_msat=1000, + final_cltv=99) # Start easy assert l1.rpc.getroutes(source=nodemap[0], destination=nodemap[1], From f9bc499e906b45caf9971cd5f2620725e97333f0 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Wed, 25 Sep 2024 08:45:57 +0100 Subject: [PATCH 04/30] askrene: refactor disabled channels/nodes Use a bitmap to mark disabled channels instead of tweaking values in localmods. Signed-off-by: Lagrang3 --- plugins/askrene/askrene.c | 6 +++ plugins/askrene/askrene.h | 5 +++ plugins/askrene/layer.c | 84 ++++++++++++++++++++++++--------------- plugins/askrene/layer.h | 5 +++ plugins/askrene/mcf.c | 13 +++++- tests/test_askrene.py | 2 + 6 files changed, 83 insertions(+), 32 deletions(-) diff --git a/plugins/askrene/askrene.c b/plugins/askrene/askrene.c index 9cf17dd49e64..6aa47e90e5d8 100644 --- a/plugins/askrene/askrene.c +++ b/plugins/askrene/askrene.c @@ -337,6 +337,12 @@ static const char *get_routes(const tal_t *ctx, gossmap_apply_localmods(askrene->gossmap, localmods); + rq->disabled = tal_get_disabled_bitmap(rq, rq); + if (!rq->disabled) { + ret = tal_fmt(ctx, "Failed creation of disabled bitmap."); + goto out; + } + srcnode = gossmap_find_node(askrene->gossmap, source); if (!srcnode) { ret = tal_fmt(ctx, "Unknown source node %s", fmt_node_id(tmpctx, source)); diff --git a/plugins/askrene/askrene.h b/plugins/askrene/askrene.h index 7fb432838c8f..892d18204e80 100644 --- a/plugins/askrene/askrene.h +++ b/plugins/askrene/askrene.h @@ -2,6 +2,7 @@ #define LIGHTNING_PLUGINS_ASKRENE_ASKRENE_H #include "config.h" #include +#include #include #include #include @@ -41,6 +42,10 @@ struct route_query { /* This is *not* updated during a query! Has all layers applied. */ const struct gossmap *gossmap; + /* Bit is set for [idx*2+dir] if that channel is disabled or if it + * belongs to a disabled node.. */ + bitmap *disabled; + /* We need to take in-flight payments into account */ const struct reserve_htable *reserved; diff --git a/plugins/askrene/layer.c b/plugins/askrene/layer.c index ae37b971c159..ac37485e8ed2 100644 --- a/plugins/askrene/layer.c +++ b/plugins/askrene/layer.c @@ -323,37 +323,6 @@ void layer_add_localmods(const struct layer *layer, const struct local_channel *lc; struct local_channel_hash_iter lcit; - /* First, disable all channels into blocked nodes (local updates - * can add new ones)! */ - for (size_t i = 0; i < tal_count(layer->disabled); i++) { - struct route_exclusion ex = layer->disabled[i]; - - /* Disable all channels of excluded nodes. */ - if (ex.type == EXCLUDE_NODE) { - const struct gossmap_node *node; - - node = gossmap_find_node(gossmap, &ex.u.node_id); - if (!node) - continue; - for (size_t n = 0; n < node->num_chans; n++) { - struct short_channel_id scid; - struct gossmap_chan *c; - int dir; - c = gossmap_nth_chan(gossmap, node, n, &dir); - scid = gossmap_chan_scid(gossmap, c); - - /* Disabled zero-capacity on incoming */ - gossmap_local_updatechan( - localmods, scid, AMOUNT_MSAT(0), - AMOUNT_MSAT(0), 0, 0, 0, false, !dir); - } - } else { - gossmap_local_updatechan( - localmods, ex.u.chan_id.scid, AMOUNT_MSAT(0), - AMOUNT_MSAT(0), 0, 0, 0, false, ex.u.chan_id.dir); - } - } - for (lc = local_channel_hash_first(layer->local_channels, &lcit); lc; lc = local_channel_hash_next(layer->local_channels, &lcit)) { @@ -489,3 +458,56 @@ void layer_memleak_mark(struct askrene *askrene, struct htable *memtable) memleak_scan_htable(memtable, &l->local_channels->raw); } } + +static void set_channel_bit(bitmap *bm, const struct gossmap *gossmap, + const struct short_channel_id_dir *scidd) +{ + const struct gossmap_chan *chan = + gossmap_find_chan(gossmap, &scidd->scid); + + if (!chan) + return; + + bitmap_set_bit(bm, gossmap_chan_idx(gossmap, chan) * 2 + scidd->dir); +} + +static void set_node_channels_bit(bitmap *bm, const struct gossmap *gossmap, + const struct node_id *node_id) +{ + const struct gossmap_node *node = gossmap_find_node(gossmap, node_id); + for (size_t k = 0; k < node->num_chans; k++) { + int half; + const struct gossmap_chan *chan = + gossmap_nth_chan(gossmap, node, k, &half); + + bitmap_set_bit(bm, gossmap_chan_idx(gossmap, chan) * 2 + half); + bitmap_set_bit(bm, gossmap_chan_idx(gossmap, chan) * 2 + !half); + } +} + +bitmap *tal_get_disabled_bitmap(const tal_t *ctx, struct route_query *rq) +{ + + bitmap *disabled = tal_arrz( + ctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(rq->gossmap))); + + if (!disabled) + return NULL; + + /* Disable every channel in the list of disabled scids. */ + for (size_t i = 0; i < tal_count(rq->layers); i++) { + const struct layer *l = rq->layers[i]; + + for (size_t j = 0; j < tal_count(l->disabled); j++) { + const struct route_exclusion *ex = &l->disabled[j]; + + if (ex->type == EXCLUDE_CHANNEL) + set_channel_bit(disabled, rq->gossmap, + &ex->u.chan_id); + else + set_node_channels_bit(disabled, rq->gossmap, + &ex->u.node_id); + } + } + return disabled; +} diff --git a/plugins/askrene/layer.h b/plugins/askrene/layer.h index 5503644ae0c7..8ab655817090 100644 --- a/plugins/askrene/layer.h +++ b/plugins/askrene/layer.h @@ -120,4 +120,9 @@ void json_add_constraint(struct json_stream *js, /* Scan for memleaks */ void layer_memleak_mark(struct askrene *askrene, struct htable *memtable); + +/* Creates a bitmap of disabled channels. It loops over all layers in the + * query searching for disabled channels and disabled nodes. All channels + * linked to a disabled node will be disabled. */ +bitmap *tal_get_disabled_bitmap(const tal_t *ctx, struct route_query *rq); #endif /* LIGHTNING_PLUGINS_ASKRENE_LAYER_H */ diff --git a/plugins/askrene/mcf.c b/plugins/askrene/mcf.c index 18b62ab7570e..ee85c3753ecf 100644 --- a/plugins/askrene/mcf.c +++ b/plugins/askrene/mcf.c @@ -583,11 +583,22 @@ static s64 linear_fee_cost( return pfee + bfee* base_fee_penalty+ delay*delay_feefactor; } +static bool channel_is_available(const struct gossmap_chan *c, int dir, + const struct gossmap *gossmap, + const bitmap *disabled) +{ + if (!gossmap_chan_set(c, dir)) + return false; + const u32 chan_idx = gossmap_chan_idx(gossmap, c); + return !bitmap_test_bit(disabled, chan_idx * 2 + dir); +} + static struct linear_network * init_linear_network(const tal_t *ctx, const struct pay_parameters *params) { struct linear_network * linear_network = tal(ctx, struct linear_network); const struct gossmap *gossmap = params->rq->gossmap; + const bitmap *disabled = params->rq->disabled; const size_t max_num_chans = gossmap_max_chan_idx(gossmap); const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL; @@ -630,7 +641,7 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params) const struct gossmap_chan *c = gossmap_nth_chan(gossmap, node, j, &half); - if (!gossmap_chan_set(c, half)) + if (!channel_is_available(c, half, gossmap, disabled)) continue; const u32 chan_id = gossmap_chan_idx(gossmap, c); diff --git a/tests/test_askrene.py b/tests/test_askrene.py index 503a5e99f453..ab6b2044ab2d 100644 --- a/tests/test_askrene.py +++ b/tests/test_askrene.py @@ -172,6 +172,8 @@ def test_getroutes(node_factory): l1.rpc.askrene_disable_channel("chans_disabled", "0x1x0") l1.rpc.askrene_disable_channel("chans_disabled", "0x2x1") l1.rpc.askrene_disable_channel("chans_disabled", "0x2x3") + # we can also disable by mistake channels that do not exists + l1.rpc.askrene_disable_channel("chans_disabled", "111x222x333") with pytest.raises(RpcError, match="Could not find route"): l1.rpc.getroutes(source=nodemap[0], destination=nodemap[1], From 3071fba47af38190521c1f1ab250e7909e8187c5 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 2 Sep 2024 12:24:55 +0100 Subject: [PATCH 05/30] renepay: call getroutes RPC instead of computing Changelog-EXPERIMENTAL: Renepay uses getroutes rpc to obtain payment routes. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 155 +++++++++++++++++++++++++++++------------ 1 file changed, 111 insertions(+), 44 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 49b420f66e9b..56ed9e0c38cc 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -632,9 +632,52 @@ REGISTER_PAYMENT_MODIFIER(routehints, routehints_cb); * Compute the payment routes. */ -static struct command_result *compute_routes_cb(struct payment *payment) +static bool json_to_myroute(const char *buf, const jsmntok_t *tok, + struct route *route) +{ + u64 probability_ppm; + const char *err = + json_scan(tmpctx, buf, tok, "{probability_ppm:%,amount_msat:%}", + JSON_SCAN(json_to_u64, &probability_ppm), + JSON_SCAN(json_to_msat, &route->amount)); + if (err) + return false; + route->success_prob = probability_ppm * 1e-6; + const jsmntok_t *path_tok = json_get_member(buf, tok, "path"); + if (!path_tok || path_tok->type != JSMN_ARRAY) + return false; + + route->hops = tal_arr(route, struct route_hop, path_tok->size); + if (!route->hops) + return false; + + size_t i; + const jsmntok_t *hop_tok; + json_for_each_arr(i, hop_tok, path_tok) + { + struct route_hop *hop = &route->hops[i]; + err = json_scan(tmpctx, buf, hop_tok, + "{short_channel_id:%,direction:%,next_node_id:%" + ",amount_msat:%,delay:%}", + JSON_SCAN(json_to_short_channel_id, &hop->scid), + JSON_SCAN(json_to_int, &hop->direction), + JSON_SCAN(json_to_node_id, &hop->node_id), + JSON_SCAN(json_to_msat, &hop->amount), + JSON_SCAN(json_to_u32, &hop->delay)); + if (err) { + route->hops = tal_free(route->hops); + return false; + } + } + route->amount_sent = route->hops[0].amount; + return true; +} + +static struct command_result *getroutes_done(struct command *cmd UNUSED, + const char *buf, + const jsmntok_t *result, + struct payment *payment) { - assert(payment->status == PAYMENT_PENDING); struct routetracker *routetracker = payment->routetracker; assert(routetracker); @@ -643,7 +686,53 @@ static struct command_result *compute_routes_cb(struct payment *payment) plugin_err(pay_plugin->plugin, "%s: no previously computed routes expected.", __func__); + routetracker->computed_routes = tal_free(routetracker->computed_routes); + + const jsmntok_t *routes_tok = json_get_member(buf, result, "routes"); + assert(routes_tok && routes_tok->type == JSMN_ARRAY); + + routetracker->computed_routes = + tal_arr(routetracker, struct route *, 0); + + size_t i; + const jsmntok_t *r; + json_for_each_arr(i, r, routes_tok) + { + struct route *route = new_route( + routetracker->computed_routes, payment->groupid, + payment->next_partid++, payment->payment_info.payment_hash, + AMOUNT_MSAT(0), AMOUNT_MSAT(0)); + assert(route); + tal_arr_expand(&routetracker->computed_routes, route); + bool success = json_to_myroute(buf, r, route); + if (!success) + plugin_err(pay_plugin->plugin, + "%s: failed to parse route from json: %.*s", + __func__, json_tok_full_len(r), + json_tok_full(buf, r)); + const size_t pathlen = tal_count(route->hops); + if (!amount_msat_eq(route->amount, + route->hops[pathlen - 1].amount)) + plugin_log(pay_plugin->plugin, LOG_UNUSUAL, + "%s: route partid=%" PRIu64 + " delivers %s which is different from what " + "it claims to " + "deliver %s", + __func__, route->key.partid, + fmt_amount_msat( + tmpctx, route->hops[pathlen - 1].amount), + fmt_amount_msat(tmpctx, route->amount)); + /* FIXME: it seems that the route we get in response claims to + * deliver an amount which is different to the amount in the + * last hop. */ + route->amount = route->hops[pathlen - 1].amount; + } + return payment_continue(payment); +} +static struct command_result *compute_routes_cb(struct payment *payment) +{ + assert(payment->status == PAYMENT_PENDING); struct amount_msat feebudget, fees_spent, remaining; /* Total feebudget */ @@ -674,48 +763,26 @@ static struct command_result *compute_routes_cb(struct payment *payment) return payment_continue(payment); } - enum jsonrpc_errcode errcode; - const char *err_msg = NULL; - - gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods); - - /* get_routes returns the answer, we assign it to the computed_routes, - * that's why we need to tal_free the older array. Maybe it would be - * better to pass computed_routes as a reference? */ - routetracker->computed_routes = tal_free(routetracker->computed_routes); - - // TODO: add an algorithm selector here - /* We let this return an unlikely path, as it's better to try once than - * simply refuse. Plus, models are not truth! */ - routetracker->computed_routes = get_routes( - routetracker, - &payment->payment_info, - &pay_plugin->my_id, - &payment->payment_info.destination, - pay_plugin->gossmap, - pay_plugin->uncertainty, - payment->disabledmap, - remaining, - feebudget, - &payment->next_partid, - payment->groupid, - &errcode, - &err_msg); - /* Otherwise the error message remains a child of the routetracker. */ - err_msg = tal_steal(tmpctx, err_msg); - - gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods); - - /* Couldn't feasible route, we stop. */ - if (!routetracker->computed_routes || - tal_count(routetracker->computed_routes) == 0) { - if (err_msg == NULL) - err_msg = tal_fmt( - tmpctx, "get_routes returned NULL error message"); - return payment_fail(payment, errcode, "%s", err_msg); - } - - return payment_continue(payment); + struct command *cmd = payment_command(payment); + assert(cmd); + struct out_req *req = + jsonrpc_request_start(cmd->plugin, cmd, "getroutes", getroutes_done, + payment_rpc_failure, payment); + // FIXME: when multi-destination is needed to fully support blinded path + // FIXME: we could have more than one algorithm to compute routes: + // Minimum Cost Flows or the Max-Expected-Value (recently discussed with + // Rene) or Dijkstra + json_add_node_id(req->js, "source", &pay_plugin->my_id); + json_add_node_id(req->js, "destination", + &payment->payment_info.destination); + json_add_amount_msat(req->js, "amount_msat", remaining); + json_array_start(req->js, "layers"); + // FIXME: put here the layers that we use + json_add_string(req->js, NULL, "auto.localchans"); + json_array_end(req->js); + json_add_amount_msat(req->js, "maxfee_msat", feebudget); + json_add_u32(req->js, "final_cltv", payment->payment_info.final_cltv); + return send_outreq(cmd->plugin, req); } REGISTER_PAYMENT_MODIFIER(compute_routes, compute_routes_cb); From c3bbe072e94b0d8fd84989daafc4f2a7d6d70799 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Thu, 12 Sep 2024 13:17:31 +0100 Subject: [PATCH 06/30] renepay: add default knowledge layer Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 2 ++ plugins/renepay/renepayconfig.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 56ed9e0c38cc..3d76d20ada3f 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -778,7 +778,9 @@ static struct command_result *compute_routes_cb(struct payment *payment) json_add_amount_msat(req->js, "amount_msat", remaining); json_array_start(req->js, "layers"); // FIXME: put here the layers that we use + json_add_string(req->js, NULL, "auto.sourcefree"); json_add_string(req->js, NULL, "auto.localchans"); + json_add_string(req->js, NULL, RENEPAY_LAYER); json_array_end(req->js); json_add_amount_msat(req->js, "maxfee_msat", feebudget); json_add_u32(req->js, "final_cltv", payment->payment_info.final_cltv); diff --git a/plugins/renepay/renepayconfig.h b/plugins/renepay/renepayconfig.h index d1ade031618b..968d82198ea7 100644 --- a/plugins/renepay/renepayconfig.h +++ b/plugins/renepay/renepayconfig.h @@ -2,6 +2,8 @@ #define LIGHTNING_PLUGINS_RENEPAY_RENEPAYCONFIG_H #include "config.h" +#define RENEPAY_LAYER "renepay" + #define MAX_NUM_ATTEMPTS 10 /* Knowledge is proportionally decreased with time up to TIMER_FORGET_SEC when From b059dc77d7a2424a5c884414fb11b32ad5083c69 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Thu, 12 Sep 2024 13:41:36 +0100 Subject: [PATCH 07/30] renepay: use askrene-age to relax knowledge Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 3d76d20ada3f..c67ef4bb09fa 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -1146,19 +1146,34 @@ REGISTER_PAYMENT_MODIFIER(pendingsendpays, pendingsendpays_cb); * Reduce the knowledge of the network as time goes by. */ -static struct command_result *knowledgerelax_cb(struct payment *payment) +static struct command_result *askreneage_success(struct command *cmd UNUSED, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct payment *payment) { - const u64 now_sec = time_now().ts.tv_sec; - enum renepay_errorcode err = uncertainty_relax( - pay_plugin->uncertainty, now_sec - pay_plugin->last_time); - if (err) - plugin_err(pay_plugin->plugin, - "uncertainty_relax failed with error %s", - renepay_errorcode_name(err)); - pay_plugin->last_time = now_sec; return payment_continue(payment); } +/* FIXME: we lack an RPC call to request askrene to relax constraints by a + * smooth amount, instead we will need to forget all knowledge that exceeds a + * certain date. */ +static struct command_result *knowledgerelax_cb(struct payment *payment) +{ + /* Remove all knowledge older than TIMER_FORGET_SEC (number of seconds + * in the past). */ + const u64 cutoff = time_now().ts.tv_sec - TIMER_FORGET_SEC; + struct command *cmd = payment_command(payment); + assert(cmd); + + struct out_req *req = jsonrpc_request_start( + cmd->plugin, cmd, "askrene-age", askreneage_success, + payment_rpc_failure, payment); + + json_add_string(req->js, "layer", RENEPAY_LAYER); + json_add_u64(req->js, "cutoff", cutoff); + return send_outreq(cmd->plugin, req); +} + REGISTER_PAYMENT_MODIFIER(knowledgerelax, knowledgerelax_cb); /***************************************************************************** From 679c5f2b82cb17b0a7ed239c0183a0734ce03114 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Thu, 12 Sep 2024 14:02:35 +0100 Subject: [PATCH 08/30] renepay: remove unused payment modifier Remove getmychannels payment modifier, the task to evaluate local channels is performed by askrene. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 168 ++++------------------------------------- 1 file changed, 14 insertions(+), 154 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index c67ef4bb09fa..16d0770febd5 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -321,145 +321,6 @@ static struct command_result *selfpay_cb(struct payment *payment) REGISTER_PAYMENT_MODIFIER(selfpay, selfpay_cb); -/***************************************************************************** - * getmychannels - * - * Calls listpeerchannels to get and updated state of the local channels. - */ - -static void -uncertainty_update_from_listpeerchannels(struct uncertainty *uncertainty, - const struct short_channel_id_dir *scidd, - struct amount_msat max, bool enabled, - const char *buf, const jsmntok_t *chantok) -{ - if (!enabled) - return; - - struct amount_msat capacity, min, gap; - const char *errmsg = json_scan(tmpctx, buf, chantok, "{total_msat:%}", - JSON_SCAN(json_to_msat, &capacity)); - if (errmsg) - goto error; - - if (!uncertainty_add_channel(pay_plugin->uncertainty, scidd->scid, - capacity)) { - errmsg = tal_fmt( - tmpctx, - "Unable to find/add scid=%s in the uncertainty network", - fmt_short_channel_id(tmpctx, scidd->scid)); - goto error; - } - - if (!amount_msat_scale(&gap, capacity, 0.1) || - !amount_msat_sub(&min, max, gap)) - min = AMOUNT_MSAT(0); - - // FIXME this does not include pending HTLC of ongoing payments! - /* Allow a gap between min and max so that we don't use up all of our - * channels' spendable sats and avoid our local error: - * WIRE_TEMPORARY_CHANNEL_FAILURE: Capacity exceeded - HTLC fee: Xsat - * - * */ - if (!uncertainty_set_liquidity(pay_plugin->uncertainty, scidd, min, - max)) { - errmsg = tal_fmt( - tmpctx, - "Unable to set liquidity to channel scidd=%s in the " - "uncertainty network.", - fmt_short_channel_id_dir(tmpctx, scidd)); - goto error; - } - return; - -error: - plugin_log( - pay_plugin->plugin, LOG_UNUSUAL, - "Failed to update local channel %s from listpeerchannels rpc: %s", - fmt_short_channel_id(tmpctx, scidd->scid), - errmsg); -} - -static void gossmod_cb(struct gossmap_localmods *mods, - const struct node_id *self, - const struct node_id *peer, - const struct short_channel_id_dir *scidd, - struct amount_msat htlcmin, - struct amount_msat htlcmax, - struct amount_msat spendable, - struct amount_msat fee_base, - u32 fee_proportional, - u32 cltv_delta, - bool enabled, - const char *buf, - const jsmntok_t *chantok, - struct payment *payment) -{ - struct amount_msat min, max; - - if (scidd->dir == node_id_idx(self, peer)) { - /* local channels can send up to what's spendable */ - min = AMOUNT_MSAT(0); - max = spendable; - } else { - /* remote channels can send up no more than spendable */ - min = htlcmin; - max = amount_msat_min(spendable, htlcmax); - } - - /* FIXME: features? */ - gossmap_local_addchan(mods, self, peer, scidd->scid, NULL); - - gossmap_local_updatechan(mods, scidd->scid, min, max, - fee_base.millisatoshis, /* Raw: gossmap */ - fee_proportional, - cltv_delta, - enabled, - scidd->dir); - - /* Is it disabled? */ - if (!enabled) - payment_disable_chan(payment, *scidd, LOG_DBG, - "listpeerchannels says not enabled"); - - /* Also update the uncertainty network by fixing the liquidity of the - * outgoing channel. If we try to set the liquidity of the incoming - * channel as well we would have conflicting information because our - * knowledge model does not take into account channel reserves. */ - if (scidd->dir == node_id_idx(self, peer)) - uncertainty_update_from_listpeerchannels( - pay_plugin->uncertainty, scidd, max, enabled, buf, chantok); -} - -static struct command_result *getmychannels_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct payment *payment) -{ - // FIXME: should local gossmods be global (ie. member of pay_plugin) or - // local (ie. member of payment)? - payment->local_gossmods = gossmods_from_listpeerchannels( - payment, &pay_plugin->my_id, buf, result, /* zero_rates = */ true, - gossmod_cb, payment); - - return payment_continue(payment); -} - -static struct command_result *getmychannels_cb(struct payment *payment) -{ - struct command *cmd = payment_command(payment); - if (!cmd) - plugin_err(pay_plugin->plugin, - "getmychannels_pay_mod: cannot get a valid cmd."); - - struct out_req *req = jsonrpc_request_start( - cmd->plugin, cmd, "listpeerchannels", getmychannels_done, - payment_rpc_failure, payment); - return send_outreq(cmd->plugin, req); -} - -REGISTER_PAYMENT_MODIFIER(getmychannels, getmychannels_cb); - /***************************************************************************** * refreshgossmap * @@ -1296,23 +1157,22 @@ void *payment_virtual_program[] = { /*0*/ OP_CALL, &previoussuccess_pay_mod, /*2*/ OP_CALL, &selfpay_pay_mod, /*4*/ OP_CALL, &knowledgerelax_pay_mod, - /*6*/ OP_CALL, &getmychannels_pay_mod, - /*8*/ OP_CALL, &refreshgossmap_pay_mod, - /*10*/ OP_CALL, &routehints_pay_mod, - /*12*/OP_CALL, &channelfilter_pay_mod, + /*6*/ OP_CALL, &refreshgossmap_pay_mod, + /*8*/ OP_CALL, &routehints_pay_mod, + /*10*/OP_CALL, &channelfilter_pay_mod, // TODO shadow_additions /* do */ - /*14*/ OP_CALL, &pendingsendpays_pay_mod, - /*16*/ OP_CALL, &checktimeout_pay_mod, - /*18*/ OP_CALL, &refreshgossmap_pay_mod, - /*20*/ OP_CALL, &compute_routes_pay_mod, - /*22*/ OP_CALL, &send_routes_pay_mod, + /*12*/ OP_CALL, &pendingsendpays_pay_mod, + /*14*/ OP_CALL, &checktimeout_pay_mod, + /*16*/ OP_CALL, &refreshgossmap_pay_mod, + /*18*/ OP_CALL, &compute_routes_pay_mod, + /*20*/ OP_CALL, &send_routes_pay_mod, /*do*/ - /*24*/ OP_CALL, &sleep_pay_mod, - /*26*/ OP_CALL, &collect_results_pay_mod, + /*22*/ OP_CALL, &sleep_pay_mod, + /*24*/ OP_CALL, &collect_results_pay_mod, /*while*/ - /*28*/ OP_IF, ¬haveresults_pay_cond, (void *)24, + /*26*/ OP_IF, ¬haveresults_pay_cond, (void *)22, /* while */ - /*31*/ OP_IF, &retry_pay_cond, (void *)14, - /*34*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ - /*36*/ NULL}; + /*29*/ OP_IF, &retry_pay_cond, (void *)12, + /*32*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ + /*34*/ NULL}; From bf53f94ca7357b59952d1fa60e203a5161e73960 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 16 Sep 2024 16:54:28 +0100 Subject: [PATCH 09/30] renepay: pass routehints to askrene For every route hint (bolt11) create local channel with askrene-create-channel RPC. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 180 +++++++++++++++----------------- plugins/renepay/payment.c | 12 +++ plugins/renepay/payment.h | 4 + plugins/renepay/renepayconfig.h | 9 ++ plugins/renepay/uncertainty.h | 9 -- 5 files changed, 110 insertions(+), 104 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 16d0770febd5..1803dd9fc193 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -367,122 +367,111 @@ REGISTER_PAYMENT_MODIFIER(refreshgossmap, refreshgossmap_cb); * Use route hints from the invoice to update the local gossmods and uncertainty * network. */ -// TODO check how this is done in pay.c -static void add_hintchan(struct payment *payment, const struct node_id *src, - const struct node_id *dst, u16 cltv_expiry_delta, - const struct short_channel_id scid, u32 fee_base_msat, - u32 fee_proportional_millionths) +struct routehints_batch { + size_t num_elements; + struct payment *payment; +}; + +static struct command_result *add_one_hint_done(struct command *cmd, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct routehints_batch *batch) { - assert(payment); - assert(payment->local_gossmods); + assert(batch->num_elements); + assert(batch->payment); + batch->num_elements--; - int dir = node_id_idx(src, dst); - - const char *errmsg; - const struct chan_extra *ce = - uncertainty_find_channel(pay_plugin->uncertainty, scid); - - if (!ce) { - /* This channel is not public, we don't know his capacity - One possible solution is set the capacity to - MAX_CAP and the state to [0,MAX_CAP]. Alternatively we could - the capacity to amount and state to [amount,amount], but that - wouldn't work if the recepient provides more than one hints - telling us to partition the payment in multiple routes. */ - ce = uncertainty_add_channel(pay_plugin->uncertainty, scid, - MAX_CAPACITY); - if (!ce) { - errmsg = tal_fmt(tmpctx, - "Unable to find/add scid=%s in the " - "local uncertainty network", - fmt_short_channel_id(tmpctx, scid)); - goto function_error; - } - /* FIXME: features? */ - if (!gossmap_local_addchan(payment->local_gossmods, src, dst, - scid, NULL) || - !gossmap_local_updatechan( - payment->local_gossmods, scid, - /* We assume any HTLC is allowed */ - AMOUNT_MSAT(0), MAX_CAPACITY, fee_base_msat, - fee_proportional_millionths, cltv_expiry_delta, true, - dir)) { - errmsg = tal_fmt( - tmpctx, - "Failed to update scid=%s in the local_gossmods.", - fmt_short_channel_id(tmpctx, scid)); - goto function_error; - } - } else { - /* The channel is pubic and we already keep track of it in the - * gossmap and uncertainty network. It would be wrong to assume - * that this channel has sufficient capacity to forward the - * entire payment! Doing so leads to knowledge updates in which - * the known min liquidity is greater than the channel's - * capacity. */ - } + if (!batch->num_elements) + return payment_continue(batch->payment); - return; + return command_still_pending(cmd); +} -function_error: - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "Failed to update hint channel %s: %s", - fmt_short_channel_id(tmpctx, scid), - errmsg); +static struct command_result * +add_one_hint_failed(struct command *cmd, const char *buf, + const jsmntok_t *result, struct routehints_batch *batch) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, + "failed to create channel hint: %.*s", + json_tok_full_len(result), json_tok_full(buf, result)); + return add_one_hint_done(cmd, buf, result, batch); } -static struct command_result *routehints_done(struct command *cmd UNUSED, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct payment *payment) +static struct command_result *routehints_cb(struct payment *payment) { - // FIXME are there route hints for B12? - assert(payment); - assert(payment->local_gossmods); + struct command *cmd = payment_command(payment); + assert(cmd); + + /* For each channel hint, if not in gossmap make a request to + * askrene-create-channel + * */ + size_t num_channel_updates_rejected = 0; + bool gossmap_changed = + gossmap_refresh(pay_plugin->gossmap, &num_channel_updates_rejected); + if (gossmap_changed && num_channel_updates_rejected) + plugin_log(pay_plugin->plugin, LOG_DBG, + "gossmap ignored %zu channel updates", + num_channel_updates_rejected); - const struct node_id *destination = &payment->payment_info.destination; const struct route_info **routehints = payment->payment_info.routehints; - assert(routehints); + if (!routehints) + return payment_continue(payment); + + const struct node_id *destination = &payment->payment_info.destination; const size_t nhints = tal_count(routehints); - /* Hints are added to the local_gossmods. */ + struct routehints_batch *batch = tal(cmd, struct routehints_batch); + batch->num_elements = 0; + batch->payment = payment; + for (size_t i = 0; i < nhints; i++) { /* Each one, presumably, leads to the destination */ const struct route_info *r = routehints[i]; const struct node_id *end = destination; for (int j = tal_count(r) - 1; j >= 0; j--) { - add_hintchan(payment, &r[j].pubkey, end, - r[j].cltv_expiry_delta, - r[j].short_channel_id, r[j].fee_base_msat, + struct gossmap_chan *chan = gossmap_find_chan( + pay_plugin->gossmap, &r[j].short_channel_id); + + if (chan) + /* this channel is public, don't add a hint */ + continue; + + /* FIXME: what if the channel is local? can askrene + * handle trying to add the same channel twice? */ + + struct out_req *req = jsonrpc_request_start( + cmd->plugin, cmd, "askrene-create-channel", + add_one_hint_done, add_one_hint_failed, batch); + + // FIXME: initialize a name for private_layer + json_add_string(req->js, "layer", + payment->private_layer); + json_add_node_id(req->js, "source", &r[j].pubkey); + json_add_node_id(req->js, "destination", end); + json_add_short_channel_id(req->js, "short_channel_id", + r[j].short_channel_id); + json_add_u32(req->js, "fee_base_msat", + r[j].fee_base_msat); + json_add_u32(req->js, "fee_proportional_millionths", r[j].fee_proportional_millionths); - end = &r[j].pubkey; - } - } + json_add_u32(req->js, "delay", r[j].cltv_expiry_delta); - /* Add hints to the uncertainty network. */ - gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods); - int skipped_count = - uncertainty_update(pay_plugin->uncertainty, pay_plugin->gossmap); - gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods); - if (skipped_count) - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "%s: uncertainty was updated but %d channels have " - "been ignored.", - __func__, skipped_count); + /* we don't have this information, we try to guess */ + json_add_amount_msat(req->js, "htlc_minimum_msat", + AMOUNT_MSAT(0)); + json_add_amount_msat(req->js, "htlc_maximum_msat", + MAX_CAPACITY); + json_add_amount_msat(req->js, "capacity_msat", + MAX_CAPACITY); - return payment_continue(payment); -} + send_outreq(cmd->plugin, req); -static struct command_result *routehints_cb(struct payment *payment) -{ - struct command *cmd = payment_command(payment); - assert(cmd); - struct out_req *req = jsonrpc_request_start( - cmd->plugin, cmd, "waitblockheight", routehints_done, - payment_rpc_failure, payment); - json_add_num(req->js, "blockheight", 0); - return send_outreq(cmd->plugin, req); + batch->num_elements++; + end = &r[j].pubkey; + } + } + return command_still_pending(cmd); } REGISTER_PAYMENT_MODIFIER(routehints, routehints_cb); @@ -642,6 +631,7 @@ static struct command_result *compute_routes_cb(struct payment *payment) json_add_string(req->js, NULL, "auto.sourcefree"); json_add_string(req->js, NULL, "auto.localchans"); json_add_string(req->js, NULL, RENEPAY_LAYER); + json_add_string(req->js, NULL, payment->private_layer); json_array_end(req->js); json_add_amount_msat(req->js, "maxfee_msat", feebudget); json_add_u32(req->js, "final_cltv", payment->payment_info.final_cltv); diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c index 58c18f670a2e..55c2454347df 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -117,6 +118,17 @@ struct payment *payment_new( p->waitresult_timer = NULL; p->routetracker = new_routetracker(p, p); + + /* The name of our private layer is just the payment hash in hex. */ + /* FIXME: we need a way to tell askrene to remove obsolete layers */ + const size_t bytelen = sizeof(pinfo->payment_hash); + const size_t hexlen = hex_str_size(bytelen); + p->private_layer = tal_arr(p, char, hexlen); + + if (!hex_encode(&pinfo->payment_hash, bytelen, p->private_layer, + hexlen)) + return tal_free(p); + return p; } diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index 37e104127279..2d1350514523 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -69,6 +69,10 @@ struct payment { struct plugin_timer *waitresult_timer; struct routetracker *routetracker; + + /* The name of the layer where we put information regarding this + * payment. */ + char *private_layer; }; static inline const struct sha256 payment_hash(const struct payment *p) diff --git a/plugins/renepay/renepayconfig.h b/plugins/renepay/renepayconfig.h index 968d82198ea7..605725bb5739 100644 --- a/plugins/renepay/renepayconfig.h +++ b/plugins/renepay/renepayconfig.h @@ -13,4 +13,13 @@ /* Time lapse used to wait for failed sendpays. */ #define COLLECTOR_TIME_WINDOW_MSEC 50 +/* FIXME a hard coded constant to indicate a limit on any channel + capacity. Channels for which the capacity is unknown (because they are not + announced) use this value. It makes sense, because if we don't even know the + channel capacity the liquidity could be anything but it will never be greater + than the global number of msats. + It remains to be checked if this value does not lead to overflow somewhere in + the code. */ +#define MAX_CAPACITY (AMOUNT_MSAT(1000000 * MSAT_PER_BTC)) + #endif /* LIGHTNING_PLUGINS_RENEPAY_RENEPAYCONFIG_H */ diff --git a/plugins/renepay/uncertainty.h b/plugins/renepay/uncertainty.h index 894a95732b49..efde2e3c3fad 100644 --- a/plugins/renepay/uncertainty.h +++ b/plugins/renepay/uncertainty.h @@ -6,15 +6,6 @@ #include #include -/* FIXME a hard coded constant to indicate a limit on any channel - capacity. Channels for which the capacity is unknown (because they are not - announced) use this value. It makes sense, because if we don't even know the - channel capacity the liquidity could be anything but it will never be greater - than the global number of msats. - It remains to be checked if this value does not lead to overflow somewhere in - the code. */ -#define MAX_CAPACITY (AMOUNT_MSAT(21000000 * MSAT_PER_BTC)) - struct uncertainty { struct chan_extra_map *chan_extra_map; }; From fdfa65df2966698c9204b82d05da98ab0c470638 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Tue, 17 Sep 2024 08:44:29 +0100 Subject: [PATCH 10/30] renepay: remove unused parts of gossmap refresh Remove unused update of the uncertainty network. The uncertainty network is now handled by askrene. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 1803dd9fc193..ee469a0e27b6 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -342,20 +342,6 @@ static struct command_result *refreshgossmap_cb(struct payment *payment) "gossmap ignored %zu channel updates", num_channel_updates_rejected); - if (gossmap_changed) { - gossmap_apply_localmods(pay_plugin->gossmap, - payment->local_gossmods); - int skipped_count = uncertainty_update(pay_plugin->uncertainty, - pay_plugin->gossmap); - gossmap_remove_localmods(pay_plugin->gossmap, - payment->local_gossmods); - if (skipped_count) - plugin_log( - pay_plugin->plugin, LOG_UNUSUAL, - "%s: uncertainty was updated but %d channels have " - "been ignored.", - __func__, skipped_count); - } return payment_continue(payment); } From f070ddfe2b915c615de747b5f0ce696f804236d1 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Tue, 17 Sep 2024 15:04:57 +0100 Subject: [PATCH 11/30] renepay: apply channel filters through askrene Uses askrene-inform-channel to disable channels that meet renepay filter criteria. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 69 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index ee469a0e27b6..afd6417cb2a6 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -430,7 +430,6 @@ static struct command_result *routehints_cb(struct payment *payment) cmd->plugin, cmd, "askrene-create-channel", add_one_hint_done, add_one_hint_failed, batch); - // FIXME: initialize a name for private_layer json_add_string(req->js, "layer", payment->private_layer); json_add_node_id(req->js, "source", &r[j].pubkey); @@ -1031,24 +1030,56 @@ REGISTER_PAYMENT_MODIFIER(knowledgerelax, knowledgerelax_cb); * FIXME: shall we set these threshold parameters as plugin options? */ +struct channelfilter_batch { + size_t num_requests; + struct payment *payment; +}; + +static struct command_result * +one_channelfilter_done(struct command *cmd, const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct channelfilter_batch *batch) +{ + assert(batch->num_requests); + assert(batch->payment); + batch->num_requests--; + + if (!batch->num_requests) + return payment_continue(batch->payment); + + return command_still_pending(cmd); +} + +static struct command_result * +one_channelfilter_failed(struct command *cmd, const char *buf, + const jsmntok_t *result, + struct channelfilter_batch *batch) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, "failed to disable channel: %.*s", + json_tok_full_len(result), json_tok_full(buf, result)); + return one_channelfilter_done(cmd, buf, result, batch); +} + static struct command_result *channelfilter_cb(struct payment *payment) { assert(payment); + struct command *cmd = payment_command(payment); + assert(cmd); assert(pay_plugin->gossmap); const double HTLC_MAX_FRACTION = 0.01; // 1% const u64 HTLC_MAX_STOP_MSAT = 1000000000; // 1M sats - u64 disabled_count = 0; - - u64 htlc_max_threshold = HTLC_MAX_FRACTION * payment->payment_info .amount.millisatoshis; /* Raw: a fraction of this amount. */ /* Don't exclude channels with htlc_max above HTLC_MAX_STOP_MSAT even if * that represents a fraction of the payment smaller than * HTLC_MAX_FRACTION. */ htlc_max_threshold = MIN(htlc_max_threshold, HTLC_MAX_STOP_MSAT); + + struct channelfilter_batch *batch = tal(cmd, struct channelfilter_batch); + batch->num_requests = 0; + batch->payment = payment; - gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods); for (const struct gossmap_node *node = gossmap_first_node(pay_plugin->gossmap); node; node = gossmap_next_node(pay_plugin->gossmap, node)) { @@ -1063,18 +1094,36 @@ static struct command_result *channelfilter_cb(struct payment *payment) .scid = gossmap_chan_scid( pay_plugin->gossmap, chan), .dir = dir}; - disabledmap_add_channel(payment->disabledmap, - scidd); - disabled_count++; + + /* FIXME: there is no askrene-disable-channel, + * we will fake its disabling by setting its + * liquidity to 0 */ + struct out_req *req = jsonrpc_request_start( + cmd->plugin, cmd, "askrene-inform-channel", + one_channelfilter_done, + one_channelfilter_failed, batch); + + /* This constraint only applies to this payment + */ + json_add_string(req->js, "layer", + payment->private_layer); + json_add_short_channel_id( + req->js, "short_channel_id", scidd.scid); + json_add_num(req->js, "direction", scidd.dir); + json_add_amount_msat(req->js, "maximum_msat", + AMOUNT_MSAT(0)); + send_outreq(cmd->plugin, req); + + batch->num_requests++; } } } - gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods); + // FIXME: prune the network over other parameters, eg. capacity, // fees, ... plugin_log(pay_plugin->plugin, LOG_DBG, "channelfilter: disabling %" PRIu64 " channels.", - disabled_count); + batch->num_requests); return payment_continue(payment); } From 9e46388c24156f33573140310312862c3478720b Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Tue, 17 Sep 2024 15:18:28 +0100 Subject: [PATCH 12/30] renepay: minor fixes Prefer the provided API to work with amount_msat and fp16. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index afd6417cb2a6..7892e4702c87 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -1067,16 +1067,24 @@ static struct command_result *channelfilter_cb(struct payment *payment) assert(cmd); assert(pay_plugin->gossmap); const double HTLC_MAX_FRACTION = 0.01; // 1% - const u64 HTLC_MAX_STOP_MSAT = 1000000000; // 1M sats + const struct amount_msat HTLC_MAX_STOP_MSAT = + AMOUNT_MSAT(1000000000); // 1M sats + struct amount_msat htlc_max_threshold; + + if (!amount_msat_scale(&htlc_max_threshold, + payment->payment_info.amount, HTLC_MAX_FRACTION)) + plugin_err(cmd->plugin, "%s: error scaling amount_msat", + __func__); - u64 htlc_max_threshold = HTLC_MAX_FRACTION * payment->payment_info - .amount.millisatoshis; /* Raw: a fraction of this amount. */ /* Don't exclude channels with htlc_max above HTLC_MAX_STOP_MSAT even if * that represents a fraction of the payment smaller than * HTLC_MAX_FRACTION. */ - htlc_max_threshold = MIN(htlc_max_threshold, HTLC_MAX_STOP_MSAT); - - struct channelfilter_batch *batch = tal(cmd, struct channelfilter_batch); + htlc_max_threshold = + amount_msat_min(htlc_max_threshold, HTLC_MAX_STOP_MSAT); + + struct channelfilter_batch *batch = + tal(cmd, struct channelfilter_batch); + assert(batch); batch->num_requests = 0; batch->payment = payment; @@ -1087,9 +1095,8 @@ static struct command_result *channelfilter_cb(struct payment *payment) int dir; const struct gossmap_chan *chan = gossmap_nth_chan( pay_plugin->gossmap, node, i, &dir); - const u64 htlc_max = - fp16_to_u64(chan->half[dir].htlc_max); - if (htlc_max < htlc_max_threshold) { + if (amount_msat_greater_fp16( + htlc_max_threshold, chan->half[dir].htlc_max)) { struct short_channel_id_dir scidd = { .scid = gossmap_chan_scid( pay_plugin->gossmap, chan), From 58920e5c25c0339a4a4c2b381f172888af3a2912 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Tue, 17 Sep 2024 15:32:20 +0100 Subject: [PATCH 13/30] renepay: remove extra call to refreshgossmap Preceding the introduction of askrene we needed to make a gossmap refresh at every payment cycle to ensure to pick up the udpates in the gossip store due to "addgossip" RPCs are called from the messages of failed onions. With askrene this is no longer necessary. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 7892e4702c87..9fffca6e52ca 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -1196,15 +1196,14 @@ void *payment_virtual_program[] = { /* do */ /*12*/ OP_CALL, &pendingsendpays_pay_mod, /*14*/ OP_CALL, &checktimeout_pay_mod, - /*16*/ OP_CALL, &refreshgossmap_pay_mod, - /*18*/ OP_CALL, &compute_routes_pay_mod, - /*20*/ OP_CALL, &send_routes_pay_mod, + /*16*/ OP_CALL, &compute_routes_pay_mod, + /*18*/ OP_CALL, &send_routes_pay_mod, /*do*/ - /*22*/ OP_CALL, &sleep_pay_mod, - /*24*/ OP_CALL, &collect_results_pay_mod, + /*20*/ OP_CALL, &sleep_pay_mod, + /*22*/ OP_CALL, &collect_results_pay_mod, /*while*/ - /*26*/ OP_IF, ¬haveresults_pay_cond, (void *)22, + /*24*/ OP_IF, ¬haveresults_pay_cond, (void *)20, /* while */ - /*29*/ OP_IF, &retry_pay_cond, (void *)12, - /*32*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ - /*34*/ NULL}; + /*27*/ OP_IF, &retry_pay_cond, (void *)12, + /*30*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ + /*32*/ NULL}; From 94d4611d6d7ba103ad38fde2a0c40230f85acfed Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Wed, 18 Sep 2024 07:17:07 +0100 Subject: [PATCH 14/30] renepay: add json helper for route hops Signed-off-by: Lagrang3 --- plugins/renepay/json.c | 35 +++++++++++++++++++---------------- plugins/renepay/json.h | 3 +++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/plugins/renepay/json.c b/plugins/renepay/json.c index 4db97bd93a47..b98496338137 100644 --- a/plugins/renepay/json.c +++ b/plugins/renepay/json.c @@ -262,6 +262,24 @@ void json_add_payment(struct json_stream *s, const struct payment *payment) // - number of parts? } +void json_add_route_hops(struct json_stream *js, const char *fieldname, + const struct route_hop *hops) +{ + const size_t pathlen = tal_count(hops); + json_array_start(js, fieldname); + for (size_t j = 0; j < pathlen; j++) { + const struct route_hop *h = &hops[j]; + json_object_start(js, NULL); + json_add_node_id(js, "id", &h->node_id); + json_add_short_channel_id(js, "channel", h->scid); + json_add_amount_msat(js, "amount_msat", h->amount); + json_add_num(js, "direction", h->direction); + json_add_u32(js, "delay", h->delay); + json_object_end(js); + } + json_array_end(js); +} + void json_add_route(struct json_stream *js, const struct route *route, const struct payment *payment) { @@ -273,22 +291,7 @@ void json_add_route(struct json_stream *js, const struct route *route, assert(route->hops); const size_t pathlen = tal_count(route->hops); - - json_array_start(js, "route"); - /* An empty route means a payment to oneself, pathlen=0 */ - for (size_t j = 0; j < pathlen; j++) { - const struct route_hop *hop = &route->hops[j]; - - json_object_start(js, NULL); - json_add_node_id(js, "id", &hop->node_id); - json_add_short_channel_id(js, "channel", hop->scid); - json_add_amount_msat(js, "amount_msat", hop->amount); - json_add_num(js, "direction", hop->direction); - json_add_u32(js, "delay", hop->delay); - json_add_string(js, "style", "tlv"); - json_object_end(js); - } - json_array_end(js); + json_add_route_hops(js, "route", route->hops); json_add_sha256(js, "payment_hash", &pinfo->payment_hash); if (pinfo->payment_secret) diff --git a/plugins/renepay/json.h b/plugins/renepay/json.h index d08749c2c0f5..4d3fadb1b4fe 100644 --- a/plugins/renepay/json.h +++ b/plugins/renepay/json.h @@ -17,6 +17,9 @@ struct payment_result *tal_sendpay_result_from_json(const tal_t *ctx, void json_add_payment(struct json_stream *s, const struct payment *payment); +void json_add_route_hops(struct json_stream *js, const char *fieldname, + const struct route_hop *hops); + void json_add_route(struct json_stream *s, const struct route *route, const struct payment *payment); From 50bc78268606d0e82214fe4cba17ba012cd780d2 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Tue, 17 Sep 2024 15:53:38 +0100 Subject: [PATCH 15/30] renepay: resolve multiple RPC Resolve multiple RPC calls before moving to the next payment state. Signed-off-by: Lagrang3 --- plugins/renepay/main.c | 1 + plugins/renepay/mods.c | 104 ++++++++++++++++---------------------- plugins/renepay/payment.c | 3 ++ plugins/renepay/payment.h | 3 ++ 4 files changed, 51 insertions(+), 60 deletions(-) diff --git a/plugins/renepay/main.c b/plugins/renepay/main.c index f2bee7af98d1..f159d5d285fb 100644 --- a/plugins/renepay/main.c +++ b/plugins/renepay/main.c @@ -157,6 +157,7 @@ static struct command_result * payment_start(struct payment *p) p->status = PAYMENT_PENDING; plugin_log(pay_plugin->plugin, LOG_DBG, "Starting renepay"); p->exec_state = 0; + p->pending_rpcs = 0; return payment_continue(p); } diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 9fffca6e52ca..52bcb5b67345 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -25,6 +25,14 @@ void *payment_virtual_program[]; /* Advance the payment virtual machine */ struct command_result *payment_continue(struct payment *payment) { + struct command *cmd = payment_command(payment); + + /* Do not advance until we complete those pending RPC calls. + * If each RPC call was an execution thread this line here would wait + * for all threads to arrive to this point. */ + if (payment->pending_rpcs > 0) + return command_still_pending(cmd); + assert(payment->exec_state != INVALID_STATE); void *op = payment_virtual_program[payment->exec_state++]; @@ -354,34 +362,26 @@ REGISTER_PAYMENT_MODIFIER(refreshgossmap, refreshgossmap_cb); * network. */ -struct routehints_batch { - size_t num_elements; - struct payment *payment; -}; - -static struct command_result *add_one_hint_done(struct command *cmd, +static struct command_result *askrene_hint_done(struct command *cmd, const char *buf UNUSED, const jsmntok_t *result UNUSED, - struct routehints_batch *batch) + struct payment *payment) { - assert(batch->num_elements); - assert(batch->payment); - batch->num_elements--; - - if (!batch->num_elements) - return payment_continue(batch->payment); - - return command_still_pending(cmd); + assert(payment->pending_rpcs > 0); + payment->pending_rpcs--; + return payment_continue(payment); } -static struct command_result * -add_one_hint_failed(struct command *cmd, const char *buf, - const jsmntok_t *result, struct routehints_batch *batch) +static struct command_result *askrene_hint_fail(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct payment *payment) { - plugin_log(cmd->plugin, LOG_UNUSUAL, - "failed to create channel hint: %.*s", - json_tok_full_len(result), json_tok_full(buf, result)); - return add_one_hint_done(cmd, buf, result, batch); + plugin_log( + cmd->plugin, LOG_UNUSUAL, + "failed to add channel hint with askrene-create-channel: %.*s", + json_tok_full_len(result), json_tok_full(buf, result)); + return askrene_hint_done(cmd, buf, result, payment); } static struct command_result *routehints_cb(struct payment *payment) @@ -406,9 +406,6 @@ static struct command_result *routehints_cb(struct payment *payment) const struct node_id *destination = &payment->payment_info.destination; const size_t nhints = tal_count(routehints); - struct routehints_batch *batch = tal(cmd, struct routehints_batch); - batch->num_elements = 0; - batch->payment = payment; for (size_t i = 0; i < nhints; i++) { /* Each one, presumably, leads to the destination */ @@ -428,7 +425,7 @@ static struct command_result *routehints_cb(struct payment *payment) struct out_req *req = jsonrpc_request_start( cmd->plugin, cmd, "askrene-create-channel", - add_one_hint_done, add_one_hint_failed, batch); + askrene_hint_done, askrene_hint_fail, payment); json_add_string(req->js, "layer", payment->private_layer); @@ -451,8 +448,8 @@ static struct command_result *routehints_cb(struct payment *payment) MAX_CAPACITY); send_outreq(cmd->plugin, req); + payment->pending_rpcs++; - batch->num_elements++; end = &r[j].pubkey; } } @@ -464,7 +461,7 @@ REGISTER_PAYMENT_MODIFIER(routehints, routehints_cb); /***************************************************************************** * compute_routes * - * Compute the payment routes. + * Get the payment routes calling (askrene)getroutes. */ static bool json_to_myroute(const char *buf, const jsmntok_t *tok, @@ -1001,6 +998,7 @@ static struct command_result *knowledgerelax_cb(struct payment *payment) struct command *cmd = payment_command(payment); assert(cmd); + // FIXME: should relax other layers too? struct out_req *req = jsonrpc_request_start( cmd->plugin, cmd, "askrene-age", askreneage_success, payment_rpc_failure, payment); @@ -1030,34 +1028,24 @@ REGISTER_PAYMENT_MODIFIER(knowledgerelax, knowledgerelax_cb); * FIXME: shall we set these threshold parameters as plugin options? */ -struct channelfilter_batch { - size_t num_requests; - struct payment *payment; -}; - static struct command_result * -one_channelfilter_done(struct command *cmd, const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct channelfilter_batch *batch) +askrene_filter_done(struct command *cmd, const char *buf UNUSED, + const jsmntok_t *result UNUSED, struct payment *payment) { - assert(batch->num_requests); - assert(batch->payment); - batch->num_requests--; - - if (!batch->num_requests) - return payment_continue(batch->payment); - - return command_still_pending(cmd); + assert(payment->pending_rpcs > 0); + payment->pending_rpcs--; + return payment_continue(payment); } -static struct command_result * -one_channelfilter_failed(struct command *cmd, const char *buf, - const jsmntok_t *result, - struct channelfilter_batch *batch) +static struct command_result *askrene_filter_fail(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct payment *payment) { - plugin_log(cmd->plugin, LOG_UNUSUAL, "failed to disable channel: %.*s", + plugin_log(cmd->plugin, LOG_UNUSUAL, + "failed to filter channel with askrene-inform-channel: %.*s", json_tok_full_len(result), json_tok_full(buf, result)); - return one_channelfilter_done(cmd, buf, result, batch); + return askrene_filter_done(cmd, buf, result, payment); } static struct command_result *channelfilter_cb(struct payment *payment) @@ -1082,12 +1070,7 @@ static struct command_result *channelfilter_cb(struct payment *payment) htlc_max_threshold = amount_msat_min(htlc_max_threshold, HTLC_MAX_STOP_MSAT); - struct channelfilter_batch *batch = - tal(cmd, struct channelfilter_batch); - assert(batch); - batch->num_requests = 0; - batch->payment = payment; - + u64 num_disabled = 0; for (const struct gossmap_node *node = gossmap_first_node(pay_plugin->gossmap); node; node = gossmap_next_node(pay_plugin->gossmap, node)) { @@ -1107,8 +1090,8 @@ static struct command_result *channelfilter_cb(struct payment *payment) * liquidity to 0 */ struct out_req *req = jsonrpc_request_start( cmd->plugin, cmd, "askrene-inform-channel", - one_channelfilter_done, - one_channelfilter_failed, batch); + askrene_filter_done, askrene_filter_fail, + payment); /* This constraint only applies to this payment */ @@ -1121,7 +1104,8 @@ static struct command_result *channelfilter_cb(struct payment *payment) AMOUNT_MSAT(0)); send_outreq(cmd->plugin, req); - batch->num_requests++; + payment->pending_rpcs++; + num_disabled++; } } } @@ -1130,7 +1114,7 @@ static struct command_result *channelfilter_cb(struct payment *payment) // fees, ... plugin_log(pay_plugin->plugin, LOG_DBG, "channelfilter: disabling %" PRIu64 " channels.", - batch->num_requests); + num_disabled); return payment_continue(payment); } diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c index 55c2454347df..70d959961f05 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -100,6 +100,7 @@ struct payment *payment_new( /* === Hidden State === */ p->exec_state = INVALID_STATE; + p->pending_rpcs = 0; p->next_partid = 1; p->cmd_array = tal_arr(p, struct command *, 0); p->local_gossmods = NULL; @@ -136,6 +137,7 @@ struct payment *payment_new( static void payment_cleanup(struct payment *p) { p->exec_state = INVALID_STATE; + p->pending_rpcs = 0; tal_resize(&p->cmd_array, 0); p->local_gossmods = tal_free(p->local_gossmods); @@ -206,6 +208,7 @@ bool payment_update( /* === Hidden State === */ p->exec_state = INVALID_STATE; + p->pending_rpcs = 0; p->next_partid = 1; /* I shouldn't be calling a payment_update on a payment that has pending diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index 2d1350514523..7be915dc159c 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -47,6 +47,9 @@ struct payment { /* Position in the payment virtual machine */ u64 exec_state; + /* Number of pending RPCs responses before we move to the next state. */ + u32 pending_rpcs; + /* Used in get_payflows to set ids to each pay_flow. */ u64 next_partid; From 8468b543349bf87576028eea8ee2eb46c2ecf795 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Thu, 19 Sep 2024 09:28:15 +0100 Subject: [PATCH 16/30] renepay: refactor send routes We do not move to the next payment step until all sendpay RPCs are finished. We flag routes that fail the sendpay call. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 104 ++++++++++++++++++++++++++++++++- plugins/renepay/route.c | 3 +- plugins/renepay/routetracker.c | 85 +-------------------------- plugins/renepay/routetracker.h | 6 +- 4 files changed, 108 insertions(+), 90 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 52bcb5b67345..c1f8ff6d953c 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -629,6 +629,57 @@ REGISTER_PAYMENT_MODIFIER(compute_routes, compute_routes_cb); * request calling sendpay. */ +struct sendpay_request { + struct payment *payment; + struct route *route; +}; + +static struct command_result *sendpay_done(struct command *cmd, const char *buf, + const jsmntok_t *result, + struct sendpay_request *data) +{ + struct payment *payment = data->payment; + tal_free(data); + + assert(payment->pending_rpcs > 0); + payment->pending_rpcs--; + return payment_continue(payment); +} + +/* sendpay really only fails immediately in two ways: + * 1. We screwed up and misused the API. + * 2. The first peer is disconnected. + */ +static struct command_result *sendpay_fail(struct command *cmd, const char *buf, + const jsmntok_t *result, + struct sendpay_request *data) +{ + struct payment *payment = data->payment; + struct route *route = data->route; + const char *err = + json_scan(tmpctx, buf, result, "{code:%,message:%}", + JSON_SCAN(json_to_jsonrpc_errcode, &route->final_error), + JSON_SCAN_TAL(route, json_strdup, &route->final_msg)); + if (err) + plugin_err(cmd->plugin, + "Unable to parse sendpay error: %s, json: %.*s", err, + json_tok_full_len(result), + json_tok_full(buf, result)); + + payment_note(payment, LOG_INFORM, + "Sendpay failed: partid=%" PRIu64 + " errorcode:%d message=%s", + route->key.partid, route->final_error, route->final_msg); + + if (route->final_error != PAY_TRY_OTHER_ROUTE) + plugin_log(cmd->plugin, LOG_UNUSUAL, + "Strange error from sendpay: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + return sendpay_done(cmd, buf, result, data); +} + static struct command_result *send_routes_cb(struct payment *payment) { assert(payment); @@ -646,7 +697,57 @@ static struct command_result *send_routes_cb(struct payment *payment) for (size_t i = 0; i < tal_count(routetracker->computed_routes); i++) { struct route *route = routetracker->computed_routes[i]; - route_sendpay_request(cmd, take(route), payment); + const struct payment_info *pinfo = &payment->payment_info; + + struct sendpay_request *data = tal(cmd, struct sendpay_request); + data->payment = payment; + data->route = route; + + struct out_req *req = + jsonrpc_request_start(cmd->plugin, cmd, "sendpay", + sendpay_done, sendpay_fail, data); + + json_add_route_hops(req->js, "route", route->hops); + json_add_sha256(req->js, "payment_hash", &pinfo->payment_hash); + json_add_u64(req->js, "partid", route->key.partid); + json_add_u64(req->js, "groupid", route->key.groupid); + + /* FIXME: sendpay has a check that we don't total more than + * the exact amount, if we're setting partid (i.e. MPP). + * However, we always set partid, and we add a shadow amount if + * we've only have one part, so we have to use that amount + * here. + * + * The spec was loosened so you are actually allowed + * to overpay, so this check is now overzealous. */ + const size_t pathlen = tal_count(route->hops); + if (pathlen > 0 && + amount_msat_greater(route_delivers(route), pinfo->amount)) + json_add_amount_msat(req->js, "amount_msat", + route_delivers(route)); + else + json_add_amount_msat(req->js, "amount_msat", + pinfo->amount); + + if (pinfo->payment_secret) + json_add_secret(req->js, "payment_secret", + pinfo->payment_secret); + + /* FIXME: some of these fields might not be required for all + * payment parts. */ + json_add_string(req->js, "bolt11", pinfo->invstr); + + if (pinfo->payment_metadata) + json_add_hex_talarr(req->js, "payment_metadata", + pinfo->payment_metadata); + if (pinfo->label) + json_add_string(req->js, "label", pinfo->label); + if (pinfo->description) + json_add_string(req->js, "description", + pinfo->description); + + send_outreq(cmd->plugin, req); + payment->pending_rpcs++; payment_note(payment, LOG_INFORM, "Sent route request: partid=%" PRIu64 @@ -657,7 +758,6 @@ static struct command_result *send_routes_cb(struct payment *payment) fmt_amount_msat(tmpctx, route_fees(route)), route_delay(route), fmt_route_path(tmpctx, route)); } - tal_resize(&routetracker->computed_routes, 0); return payment_continue(payment); } diff --git a/plugins/renepay/route.c b/plugins/renepay/route.c index 06ac2526fa03..3be6f756384d 100644 --- a/plugins/renepay/route.c +++ b/plugins/renepay/route.c @@ -11,7 +11,8 @@ struct route *new_route(const tal_t *ctx, u32 groupid, route->key.groupid = groupid; route->key.payment_hash = payment_hash; - route->final_error = LIGHTNINGD; + // FIXME: assuming jsonrpc_errcode == 0 means no error + route->final_error = 0; route->final_msg = NULL; route->hops = NULL; route->success_prob = 0.0; diff --git a/plugins/renepay/routetracker.c b/plugins/renepay/routetracker.c index f08f8becbd78..fdbf6cb34d0b 100644 --- a/plugins/renepay/routetracker.c +++ b/plugins/renepay/routetracker.c @@ -149,8 +149,8 @@ static void remove_route(struct route *route, struct route_map *map) * - after a sendpay is accepted, * - or after listsendpays reveals some pending route that we didn't * previously know about. */ -static void route_pending_register(struct routetracker *routetracker, - struct route *route) +void route_pending_register(struct routetracker *routetracker, + struct route *route) { assert(route); assert(routetracker); @@ -192,71 +192,6 @@ static void route_pending_register(struct routetracker *routetracker, } } -/* Callback function for sendpay request success. */ -static struct command_result *sendpay_done(struct command *cmd, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct route *route) -{ - assert(route); - struct payment *payment = route_get_payment_verify(route); - route_pending_register(payment->routetracker, route); - return command_still_pending(cmd); -} - -/* sendpay really only fails immediately in two ways: - * 1. We screwed up and misused the API. - * 2. The first peer is disconnected. - */ -static struct command_result *sendpay_failed(struct command *cmd, - const char *buf, - const jsmntok_t *tok, - struct route *route) -{ - assert(route); - struct payment *payment = route_get_payment_verify(route); - struct routetracker *routetracker = payment->routetracker; - assert(routetracker); - - enum jsonrpc_errcode errcode; - const char *msg; - const char *err; - - err = json_scan(tmpctx, buf, tok, "{code:%,message:%}", - JSON_SCAN(json_to_jsonrpc_errcode, &errcode), - JSON_SCAN_TAL(tmpctx, json_strdup, &msg)); - if (err) - plugin_err(pay_plugin->plugin, - "Unable to parse sendpay error: %s, json: %.*s", err, - json_tok_full_len(tok), json_tok_full(buf, tok)); - - payment_note(payment, LOG_INFORM, - "Sendpay failed: partid=%" PRIu64 - " errorcode:%d message=%s", - route->key.partid, errcode, msg); - - if (errcode != PAY_TRY_OTHER_ROUTE) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "Strange error from sendpay: %.*s", - json_tok_full_len(tok), json_tok_full(buf, tok)); - } - - /* There is no new knowledge from this kind of failure. - * We just disable this scid. */ - struct short_channel_id_dir scidd_disable = { - .scid = route->hops[0].scid, .dir = route->hops[0].direction}; - payment_disable_chan(payment, scidd_disable, LOG_INFORM, - "sendpay didn't like first hop: %s", msg); - - if (!route_map_del(routetracker->sent_routes, route)) - plugin_err(pay_plugin->plugin, - "%s: route (%s) is not marked as sent", - __func__, - fmt_routekey(tmpctx, &route->key)); - tal_free(route); - return command_still_pending(cmd); -} - void payment_collect_results(struct payment *payment, struct preimage **payment_preimage, enum jsonrpc_errcode *final_error, @@ -324,22 +259,6 @@ void payment_collect_results(struct payment *payment, tal_resize(&routetracker->finalized_routes, 0); } -struct command_result *route_sendpay_request(struct command *cmd, - struct route *route TAKES, - struct payment *payment) -{ - struct out_req *req = - jsonrpc_request_start(pay_plugin->plugin, cmd, "sendpay", - sendpay_done, sendpay_failed, route); - - json_add_route(req->js, route, payment); - - route_map_add(payment->routetracker->sent_routes, route); - if(taken(route)) - tal_steal(payment->routetracker->sent_routes, route); - return send_outreq(pay_plugin->plugin, req); -} - struct command_result *notification_sendpay_failure(struct command *cmd, const char *buf, const jsmntok_t *params) diff --git a/plugins/renepay/routetracker.h b/plugins/renepay/routetracker.h index 2e4c440a2339..2065922ea5ae 100644 --- a/plugins/renepay/routetracker.h +++ b/plugins/renepay/routetracker.h @@ -34,10 +34,8 @@ void payment_collect_results(struct payment *payment, enum jsonrpc_errcode *final_error, const char **final_msg); -/* Sends a sendpay request for this route. */ -struct command_result *route_sendpay_request(struct command *cmd, - struct route *route TAKES, - struct payment *payment); +void route_pending_register(struct routetracker *routetracker, + struct route *route); struct command_result *notification_sendpay_failure(struct command *cmd, const char *buf, From 316c34faba641a2f80ba8d240136e33ed8cf348a Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Fri, 20 Sep 2024 10:29:47 +0100 Subject: [PATCH 17/30] renepay: reserve liquidity in askrene For every route we send we make a call to askrene-reserve. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 161 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 17 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index c1f8ff6d953c..3c5d7ac65751 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -763,6 +763,131 @@ static struct command_result *send_routes_cb(struct payment *payment) REGISTER_PAYMENT_MODIFIER(send_routes, send_routes_cb); +/***************************************************************************** + * reserve_routes + * + * Reserves liquidity in the uncertainty network corresponding to the routes + * that we send by calling askrene-reserve. + */ + +static struct command_result * +askrene_disable_channel_done(struct command *cmd, const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct payment *payment); + +static struct command_result * +askrene_disable_channel_fail(struct command *cmd, const char *buf, + const jsmntok_t *result, struct payment *payment); + +static struct command_result * +askrene_reserve_done(struct command *cmd, const char *buf UNUSED, + const jsmntok_t *result UNUSED, struct payment *payment) +{ + assert(payment->pending_rpcs > 0); + payment->pending_rpcs--; + return payment_continue(payment); +} + +static struct command_result * +askrene_reserve_fail(struct command *cmd, const char *buf, + const jsmntok_t *result, struct payment *payment) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, + "failed to reserve liquidity with askrene-reserve: %.*s", + json_tok_full_len(result), json_tok_full(buf, result)); + return askrene_reserve_done(cmd, buf, result, payment); +} + +static struct command_result *reserve_routes_cb(struct payment *payment) +{ + assert(payment); + struct routetracker *routetracker = payment->routetracker; + assert(routetracker); + if (!routetracker->computed_routes || + tal_count(routetracker->computed_routes) == 0) { + plugin_log(pay_plugin->plugin, LOG_UNUSUAL, + "%s: there are no new routes, skipping.", __func__); + return payment_continue(payment); + } + struct command *cmd = payment_command(payment); + assert(cmd); + for (size_t i = 0; i < tal_count(routetracker->computed_routes); i++) { + struct route *route = routetracker->computed_routes[i]; + assert(route->hops); + assert(tal_count(route->hops)); + + // FIXME: assuming jsonrpc_errcode == 0 means no error + if (route->final_error) { + + /* Disable the first channel in the route */ + const struct short_channel_id_dir scidd = { + .scid = route->hops[0].scid, + .dir = route->hops[0].direction}; + + plugin_log(cmd->plugin, LOG_DBG, + "route(partid=%" PRIu64 + ") failed at sendpay with error %s, " + "disabling the first " + "channel %s", + route->key.partid, route->final_msg, + fmt_short_channel_id_dir(tmpctx, &scidd)); + + /* FIXME: there is no askrene-disable-channel, + * we will fake its disabling by setting its + * liquidity to 0 */ + struct out_req *req = jsonrpc_request_start( + cmd->plugin, cmd, "askrene-inform-channel", + askrene_disable_channel_done, + askrene_disable_channel_fail, payment); + + /* This constraint only applies to this payment */ + json_add_string(req->js, "layer", + payment->private_layer); + json_add_short_channel_id(req->js, "short_channel_id", + scidd.scid); + json_add_num(req->js, "direction", scidd.dir); + json_add_amount_msat(req->js, "maximum_msat", + AMOUNT_MSAT(0)); + send_outreq(cmd->plugin, req); + + payment->pending_rpcs++; + tal_free(route); + } else { + // FIXME: don't forget to unreserve, maybe add a + // destructor to the route that calls unreserve + struct out_req *req = jsonrpc_request_start( + cmd->plugin, cmd, "askrene-reserve", + askrene_reserve_done, askrene_reserve_fail, + payment); + + json_array_start(req->js, "path"); + for (size_t j = 0; j < tal_count(route->hops); j++) { + json_object_start(req->js, NULL); + json_add_short_channel_id(req->js, + "short_channel_id", + route->hops[j].scid); + json_add_num(req->js, "direction", + route->hops[j].direction); + json_add_amount_msat(req->js, "amount_msat", + route->hops[j].amount); + json_object_end(req->js); + } + json_array_end(req->js); + + send_outreq(cmd->plugin, req); + + payment->pending_rpcs++; + // TODO: check this + route_pending_register(payment->routetracker, + take(route)); + } + } + tal_resize(&routetracker->computed_routes, 0); + return payment_continue(payment); +} + +REGISTER_PAYMENT_MODIFIER(reserve_routes, reserve_routes_cb); + /***************************************************************************** * sleep * @@ -1129,23 +1254,24 @@ REGISTER_PAYMENT_MODIFIER(knowledgerelax, knowledgerelax_cb); */ static struct command_result * -askrene_filter_done(struct command *cmd, const char *buf UNUSED, - const jsmntok_t *result UNUSED, struct payment *payment) +askrene_disable_channel_done(struct command *cmd, const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct payment *payment) { assert(payment->pending_rpcs > 0); payment->pending_rpcs--; return payment_continue(payment); } -static struct command_result *askrene_filter_fail(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct payment *payment) +static struct command_result * +askrene_disable_channel_fail(struct command *cmd, const char *buf, + const jsmntok_t *result, struct payment *payment) { - plugin_log(cmd->plugin, LOG_UNUSUAL, - "failed to filter channel with askrene-inform-channel: %.*s", - json_tok_full_len(result), json_tok_full(buf, result)); - return askrene_filter_done(cmd, buf, result, payment); + plugin_log( + cmd->plugin, LOG_UNUSUAL, + "failed to disable channel with askrene-inform-channel: %.*s", + json_tok_full_len(result), json_tok_full(buf, result)); + return askrene_disable_channel_done(cmd, buf, result, payment); } static struct command_result *channelfilter_cb(struct payment *payment) @@ -1190,8 +1316,8 @@ static struct command_result *channelfilter_cb(struct payment *payment) * liquidity to 0 */ struct out_req *req = jsonrpc_request_start( cmd->plugin, cmd, "askrene-inform-channel", - askrene_filter_done, askrene_filter_fail, - payment); + askrene_disable_channel_done, + askrene_disable_channel_fail, payment); /* This constraint only applies to this payment */ @@ -1282,12 +1408,13 @@ void *payment_virtual_program[] = { /*14*/ OP_CALL, &checktimeout_pay_mod, /*16*/ OP_CALL, &compute_routes_pay_mod, /*18*/ OP_CALL, &send_routes_pay_mod, + /*20*/ OP_CALL, &reserve_routes_pay_mod, /*do*/ - /*20*/ OP_CALL, &sleep_pay_mod, - /*22*/ OP_CALL, &collect_results_pay_mod, + /*22*/ OP_CALL, &sleep_pay_mod, + /*24*/ OP_CALL, &collect_results_pay_mod, /*while*/ - /*24*/ OP_IF, ¬haveresults_pay_cond, (void *)20, + /*26*/ OP_IF, ¬haveresults_pay_cond, (void *)22, /* while */ - /*27*/ OP_IF, &retry_pay_cond, (void *)12, - /*30*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ + /*29*/ OP_IF, &retry_pay_cond, (void *)12, + /*32*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ /*32*/ NULL}; From b790b7bb628800191b4de27bec0eef732a08cf2d Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Sat, 21 Sep 2024 07:09:52 +0100 Subject: [PATCH 18/30] renepay: remove unused get_routes func Signed-off-by: Lagrang3 --- plugins/renepay/Makefile | 6 - plugins/renepay/dijkstra.c | 186 -- plugins/renepay/dijkstra.h | 30 - plugins/renepay/mcf.c | 1860 -------------------- plugins/renepay/mcf.h | 67 - plugins/renepay/mods.c | 2 - plugins/renepay/routebuilder.c | 428 ----- plugins/renepay/routebuilder.h | 30 - plugins/renepay/test/Makefile | 1 - plugins/renepay/test/run-arc.c | 86 - plugins/renepay/test/run-bottleneck.c | 286 --- plugins/renepay/test/run-dijkstra.c | 100 -- plugins/renepay/test/run-mcf-diamond.c | 211 --- plugins/renepay/test/run-mcf.c | 607 ------- plugins/renepay/test/run-missingcapacity.c | 179 -- plugins/renepay/test/run-testflow.c | 1 - 16 files changed, 4080 deletions(-) delete mode 100644 plugins/renepay/dijkstra.c delete mode 100644 plugins/renepay/dijkstra.h delete mode 100644 plugins/renepay/mcf.c delete mode 100644 plugins/renepay/mcf.h delete mode 100644 plugins/renepay/routebuilder.c delete mode 100644 plugins/renepay/routebuilder.h delete mode 100644 plugins/renepay/test/run-arc.c delete mode 100644 plugins/renepay/test/run-bottleneck.c delete mode 100644 plugins/renepay/test/run-dijkstra.c delete mode 100644 plugins/renepay/test/run-mcf-diamond.c delete mode 100644 plugins/renepay/test/run-mcf.c delete mode 100644 plugins/renepay/test/run-missingcapacity.c diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index 501db8a51c57..48c3dba92364 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -1,13 +1,10 @@ PLUGIN_RENEPAY_SRC := \ plugins/renepay/main.c \ plugins/renepay/flow.c \ - plugins/renepay/mcf.c \ - plugins/renepay/dijkstra.c \ plugins/renepay/disabledmap.c \ plugins/renepay/payment.c \ plugins/renepay/chan_extra.c \ plugins/renepay/route.c \ - plugins/renepay/routebuilder.c \ plugins/renepay/routetracker.c \ plugins/renepay/routefail.c \ plugins/renepay/uncertainty.c \ @@ -18,15 +15,12 @@ PLUGIN_RENEPAY_SRC := \ PLUGIN_RENEPAY_HDRS := \ plugins/renepay/payplugin.h \ plugins/renepay/flow.h \ - plugins/renepay/mcf.h \ - plugins/renepay/dijkstra.h \ plugins/renepay/disabledmap.h \ plugins/renepay/payment.h \ plugins/renepay/payment_info.h \ plugins/renepay/chan_extra.h \ plugins/renepay/renepayconfig.h \ plugins/renepay/route.h \ - plugins/renepay/routebuilder.h \ plugins/renepay/routetracker.h \ plugins/renepay/routefail.h \ plugins/renepay/uncertainty.h \ diff --git a/plugins/renepay/dijkstra.c b/plugins/renepay/dijkstra.c deleted file mode 100644 index db41ebaf902d..000000000000 --- a/plugins/renepay/dijkstra.c +++ /dev/null @@ -1,186 +0,0 @@ -#define NDEBUG 1 -#include "config.h" -#include - -/* In the heap we keep node idx, but in this structure we keep the distance - * value associated to every node, and their position in the heap as a pointer - * so that we can update the nodes inside the heap when the distance label is - * changed. - * - * Therefore this is no longer a multipurpose heap, the node_idx must be an - * index between 0 and less than max_num_nodes. */ -struct dijkstra { - // - s64 *distance; - u32 *base; - u32 **heapptr; - size_t heapsize; - struct gheap_ctx gheap_ctx; -}; - -static const s64 INFINITE = INT64_MAX; - -/* Required a global dijkstra for gheap. */ -static struct dijkstra *global_dijkstra; - -/* The heap comparer for Dijkstra search. Since the top element must be the one - * with the smallest distance, we use the operator >, rather than <. */ -static int dijkstra_less_comparer( - const void *const ctx UNUSED, - const void *const a, - const void *const b) -{ - return global_dijkstra->distance[*(u32*)a] - > global_dijkstra->distance[*(u32*)b]; -} - -/* The heap move operator for Dijkstra search. */ -static void dijkstra_item_mover(void *const dst, const void *const src) -{ - u32 src_idx = *(u32*)src; - *(u32*)dst = src_idx; - - // we keep track of the pointer position of each element in the heap, - // for easy update. - global_dijkstra->heapptr[src_idx] = dst; -} - -/* Allocation of resources for the heap. */ -struct dijkstra *dijkstra_new(const tal_t *ctx, size_t max_num_nodes) -{ - struct dijkstra *dijkstra = tal(ctx, struct dijkstra); - - dijkstra->distance = tal_arr(dijkstra,s64,max_num_nodes); - dijkstra->base = tal_arr(dijkstra,u32,max_num_nodes); - dijkstra->heapptr = tal_arrz(dijkstra,u32*,max_num_nodes); - - dijkstra->heapsize=0; - - dijkstra->gheap_ctx.fanout=2; - dijkstra->gheap_ctx.page_chunks=1024; - dijkstra->gheap_ctx.item_size=sizeof(dijkstra->base[0]); - dijkstra->gheap_ctx.less_comparer=dijkstra_less_comparer; - dijkstra->gheap_ctx.less_comparer_ctx=NULL; - dijkstra->gheap_ctx.item_mover=dijkstra_item_mover; - - return dijkstra; -} - - -void dijkstra_init(struct dijkstra *dijkstra) -{ - const size_t max_num_nodes = tal_count(dijkstra->distance); - dijkstra->heapsize=0; - for(size_t i=0;idistance[i]=INFINITE; - dijkstra->heapptr[i] = NULL; - } -} -size_t dijkstra_size(const struct dijkstra *dijkstra) -{ - return dijkstra->heapsize; -} - -size_t dijkstra_maxsize(const struct dijkstra *dijkstra) -{ - return tal_count(dijkstra->distance); -} - -static void dijkstra_append(struct dijkstra *dijkstra, u32 node_idx, s64 distance) -{ - assert(dijkstra_size(dijkstra) < dijkstra_maxsize(dijkstra)); - assert(node_idx < dijkstra_maxsize(dijkstra)); - - const size_t pos = dijkstra->heapsize; - - dijkstra->base[pos]=node_idx; - dijkstra->distance[node_idx]=distance; - dijkstra->heapptr[node_idx] = &(dijkstra->base[pos]); - dijkstra->heapsize++; -} - -void dijkstra_update(struct dijkstra *dijkstra, u32 node_idx, s64 distance) -{ - assert(node_idx < dijkstra_maxsize(dijkstra)); - - if(!dijkstra->heapptr[node_idx]) - { - // not in the heap - dijkstra_append(dijkstra, node_idx,distance); - global_dijkstra = dijkstra; - gheap_restore_heap_after_item_increase( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize, - dijkstra->heapptr[node_idx] - - dijkstra->base); - global_dijkstra = NULL; - return; - } - - if(dijkstra->distance[node_idx] > distance) - { - // distance decrease - dijkstra->distance[node_idx] = distance; - - global_dijkstra = dijkstra; - gheap_restore_heap_after_item_increase( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize, - dijkstra->heapptr[node_idx] - - dijkstra->base); - global_dijkstra = NULL; - }else - { - // distance increase - dijkstra->distance[node_idx] = distance; - - global_dijkstra = dijkstra; - gheap_restore_heap_after_item_decrease( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize, - dijkstra->heapptr[node_idx] - - dijkstra->base); - global_dijkstra = NULL; - - } - // assert(gheap_is_heap(&dijkstra->gheap_ctx, - // dijkstra->base, - // dijkstra_size())); -} - -u32 dijkstra_top(const struct dijkstra *dijkstra) -{ - return dijkstra->base[0]; -} - -bool dijkstra_empty(const struct dijkstra *dijkstra) -{ - return dijkstra->heapsize==0; -} - -void dijkstra_pop(struct dijkstra *dijkstra) -{ - if(dijkstra->heapsize==0) - return; - - const u32 top = dijkstra_top(dijkstra); - assert(dijkstra->heapptr[top]==dijkstra->base); - - global_dijkstra = dijkstra; - gheap_pop_heap( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize--); - global_dijkstra = NULL; - - dijkstra->heapptr[top]=NULL; -} - -const s64* dijkstra_distance_data(const struct dijkstra *dijkstra) -{ - return dijkstra->distance; -} diff --git a/plugins/renepay/dijkstra.h b/plugins/renepay/dijkstra.h deleted file mode 100644 index bfff1d1dd288..000000000000 --- a/plugins/renepay/dijkstra.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H -#define LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H -#include "config.h" -#include -#include -#include - -/* Allocation of resources for the heap. */ -struct dijkstra *dijkstra_new(const tal_t *ctx, size_t max_num_nodes); - -/* Initialization of the heap for a new Dijkstra search. */ -void dijkstra_init(struct dijkstra *dijkstra); - -/* Inserts a new element in the heap. If node_idx was already in the heap then - * its distance value is updated. */ -void dijkstra_update(struct dijkstra *dijkstra, u32 node_idx, s64 distance); - -u32 dijkstra_top(const struct dijkstra *dijkstra); -bool dijkstra_empty(const struct dijkstra *dijkstra); -void dijkstra_pop(struct dijkstra *dijkstra); - -const s64* dijkstra_distance_data(const struct dijkstra *dijkstra); - -/* Number of elements on the heap. */ -size_t dijkstra_size(const struct dijkstra *dijkstra); - -/* Maximum number of elements the heap can host */ -size_t dijkstra_maxsize(const struct dijkstra *dijkstra); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H */ diff --git a/plugins/renepay/mcf.c b/plugins/renepay/mcf.c deleted file mode 100644 index 7d2f1490300a..000000000000 --- a/plugins/renepay/mcf.c +++ /dev/null @@ -1,1860 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* # Optimal payments - * - * In this module we reduce the routing optimization problem to a linear - * cost optimization problem and find a solution using MCF algorithms. - * The optimization of the routing itself doesn't need a precise numerical - * solution, since we can be happy near optimal results; e.g. paying 100 msat or - * 101 msat for fees doesn't make any difference if we wish to deliver 1M sats. - * On the other hand, we are now also considering Pickhard's - * [1] model to improve payment reliability, - * hence our optimization moves to a 2D space: either we like to maximize the - * probability of success of a payment or minimize the routing fees, or - * alternatively we construct a function of the two that gives a good compromise. - * - * Therefore from now own, the definition of optimal is a matter of choice. - * To simplify the API of this module, we think the best way to state the - * problem is: - * - * Find a routing solution that pays the least of fees while keeping - * the probability of success above a certain value `min_probability`. - * - * - * # Fee Cost - * - * Routing fees is non-linear function of the payment flow x, that's true even - * without the base fee: - * - * fee_msat = base_msat + floor(millionths*x_msat / 10^6) - * - * We approximate this fee into a linear function by computing a slope `c_fee` such - * that: - * - * fee_microsat = c_fee * x_sat - * - * Function `linear_fee_cost` computes `c_fee` based on the base and - * proportional fees of a channel. - * The final product if microsat because if only - * the proportional fee was considered we can have c_fee = millionths. - * Moving to costs based in msats means we have to either truncate payments - * below 1ksats or estimate as 0 cost for channels with less than 1000ppm. - * - * TODO(eduardo): shall we build a linear cost function in msats? - * - * # Probability cost - * - * The probability of success P of the payment is the product of the prob. of - * success of forwarding parts of the payment over all routing channels. This - * problem is separable if we log it, and since we would like to increase P, - * then we can seek to minimize -log(P), and that's our prob. cost function [1]. - * - * - log P = sum_{i} - log P_i - * - * The probability of success `P_i` of sending some flow `x` on a channel with - * liquidity l in the range a<=l a - * = 1. ; for x <= a - * - * Notice that unlike the similar formula in [1], the one we propose does not - * contain the quantization shot noise for counting states. The formula remains - * valid independently of the liquidity units (sats or msats). - * - * The cost associated to probability P is then -k log P, where k is some - * constant. For k=1 we get the following table: - * - * prob | cost - * ----------- - * 0.01 | 4.6 - * 0.02 | 3.9 - * 0.05 | 3.0 - * 0.10 | 2.3 - * 0.20 | 1.6 - * 0.50 | 0.69 - * 0.80 | 0.22 - * 0.90 | 0.10 - * 0.95 | 0.05 - * 0.98 | 0.02 - * 0.99 | 0.01 - * - * Clearly -log P(x) is non-linear; we try to linearize it piecewise: - * split the channel into 4 arcs representing 4 liquidity regions: - * - * arc_0 -> [0, a) - * arc_1 -> [a, a+(b-a)*f1) - * arc_2 -> [a+(b-a)*f1, a+(b-a)*f2) - * arc_3 -> [a+(b-a)*f2, a+(b-a)*f3) - * - * where f1 = 0.5, f2 = 0.8, f3 = 0.95; - * We fill arc_0's capacity with complete certainty P=1, then if more flow is - * needed we start filling the capacity in arc_1 until the total probability - * of success reaches P=0.5, then arc_2 until P=1-0.8=0.2, and finally arc_3 until - * P=1-0.95=0.05. We don't go further than 5% prob. of success per channel. - - * TODO(eduardo): this channel linearization is hard coded into - * `CHANNEL_PIVOTS`, maybe we can parametrize this to take values from the config file. - * - * With this choice, the slope of the linear cost function becomes: - * - * m_0 = 0 - * m_1 = 1.38 k /(b-a) - * m_2 = 3.05 k /(b-a) - * m_3 = 9.24 k /(b-a) - * - * Notice that one of the assumptions in [2] for the MCF problem is that flows - * and the slope of the costs functions are integer numbers. The only way we - * have at hand to make it so, is to choose a universal value of `k` that scales - * up the slopes so that floor(m_i) is not zero for every arc. - * - * # Combine fee and prob. costs - * - * We attempt to solve the original problem of finding the solution that - * pays the least fees while keeping the prob. of success above a certain value, - * by constructing a cost function which is a linear combination of fee and - * prob. costs. - * TODO(eduardo): investigate how this procedure is justified, - * possibly with the use of Lagrange optimization theory. - * - * At first, prob. and fee costs live in different dimensions, they cannot be - * summed, it's like comparing apples and oranges. - * However we propose to scale the prob. cost by a global factor k that - * translates into the monetization of prob. cost. - * - * k/1000, for instance, becomes the equivalent monetary cost - * of increasing the probability of success by 0.1% for P~100%. - * - * The input parameter `prob_cost_factor` in the function `minflow` is defined - * as the PPM from the delivery amount `T` we are *willing to pay* to increase the - * prob. of success by 0.1%: - * - * k_microsat = floor(1000*prob_cost_factor * T_sat) - * - * Is this enough to make integer prob. cost per unit flow? - * For `prob_cost_factor=10`; i.e. we pay 10ppm for increasing the prob. by - * 0.1%, we get that - * - * -> any arc with (b-a) > 10000 T, will have zero prob. cost, which is - * reasonable because even if all the flow passes through that arc, we get - * a 1.3 T/(b-a) ~ 0.01% prob. of failure at most. - * - * -> if (b-a) ~ 10000 T, then the arc will have unit cost, or just that we - * pay 1 microsat for every sat we send through this arc. - * - * -> it would be desirable to have a high proportional fee when (b-a)~T, - * because prob. of failure start to become very high. - * In this case we get to pay 10000 microsats for every sat. - * - * Once `k` is fixed then we can combine the linear prob. and fee costs, both - * are in monetary units. - * - * Note: with costs in microsats, because slopes represent ppm and flows are in - * sats, then our integer bounds with 64 bits are such that we can move as many - * as 10'000 BTC without overflow: - * - * 10^6 (max ppm) * 10^8 (sats per BTC) * 10^4 = 10^18 - * - * # References - * - * [1] Pickhardt and Richter, https://arxiv.org/abs/2107.05322 - * [2] R.K. Ahuja, T.L. Magnanti, and J.B. Orlin. Network Flows: - * Theory, Algorithms, and Applications. Prentice Hall, 1993. - * - * - * TODO(eduardo) it would be interesting to see: - * how much do we pay for reliability? - * Cost_fee(most reliable solution) - Cost_fee(cheapest solution) - * - * TODO(eduardo): it would be interesting to see: - * how likely is the most reliable path with respect to the cheapest? - * Prob(reliable)/Prob(cheapest) = Exp(Cost_prob(cheapest)-Cost_prob(reliable)) - * - * */ - -#define PARTS_BITS 2 -#define CHANNEL_PARTS (1 << PARTS_BITS) - -// These are the probability intervals we use to decompose a channel into linear -// cost function arcs. -static const double CHANNEL_PIVOTS[]={0,0.5,0.8,0.95}; - -static const s64 INFINITE = INT64_MAX; -static const u64 INFINITE_MSAT = UINT64_MAX; -static const u32 INVALID_INDEX = 0xffffffff; -static const s64 MU_MAX = 128; - -/* Let's try this encoding of arcs: - * Each channel `c` has two possible directions identified by a bit - * `half` or `!half`, and each one of them has to be - * decomposed into 4 liquidity parts in order to - * linearize the cost function, but also to solve MCF - * problem we need to keep track of flows in the - * residual network hence we need for each directed arc - * in the network there must be another arc in the - * opposite direction refered to as it's dual. In total - * 1+2+1 additional bits of information: - * - * (chan_idx)(half)(part)(dual) - * - * That means, for each channel we need to store the - * information of 16 arcs. If we implement a convex-cost - * solver then we can reduce that number to size(half)size(dual)=4. - * - * In the adjacency of a `node` we are going to store - * the outgoing arcs. If we ever need to loop over the - * incoming arcs then we will define a reverse adjacency - * API. - * Then for each outgoing channel `(c,half)` there will - * be 4 parts for the actual residual capacity, hence - * with the dual bit set to 0: - * - * (c,half,0,0) - * (c,half,1,0) - * (c,half,2,0) - * (c,half,3,0) - * - * and also we need to consider the dual arcs - * corresponding to the channel direction `(c,!half)` - * (the dual has reverse direction): - * - * (c,!half,0,1) - * (c,!half,1,1) - * (c,!half,2,1) - * (c,!half,3,1) - * - * These are the 8 outgoing arcs relative to `node` and - * associated with channel `c`. The incoming arcs will - * be: - * - * (c,!half,0,0) - * (c,!half,1,0) - * (c,!half,2,0) - * (c,!half,3,0) - * - * (c,half,0,1) - * (c,half,1,1) - * (c,half,2,1) - * (c,half,3,1) - * - * but they will be stored as outgoing arcs on the peer - * node `next`. - * - * I hope this will clarify my future self when I forget. - * - * */ - -/* - * We want to use the whole number here for convenience, but - * we can't us a union, since bit order is implementation-defined and - * we want chanidx on the highest bits: - * - * [ 0 1 2 3 4 5 6 ... 31 ] - * dual part chandir chanidx - */ -struct arc { - u32 idx; -}; - -#define ARC_DUAL_BITOFF (0) -#define ARC_PART_BITOFF (1) -#define ARC_CHANDIR_BITOFF (1 + PARTS_BITS) -#define ARC_CHANIDX_BITOFF (1 + PARTS_BITS + 1) -#define ARC_CHANIDX_BITS (32 - ARC_CHANIDX_BITOFF) - -/* How many arcs can we have for a single channel? - * linearization parts, both directions, and dual */ -#define ARCS_PER_CHANNEL ((size_t)1 << (PARTS_BITS + 1 + 1)) - -static inline void arc_to_parts(struct arc arc, - u32 *chanidx, - int *chandir, - u32 *part, - bool *dual) -{ - if (chanidx) - *chanidx = (arc.idx >> ARC_CHANIDX_BITOFF); - if (chandir) - *chandir = (arc.idx >> ARC_CHANDIR_BITOFF) & 1; - if (part) - *part = (arc.idx >> ARC_PART_BITOFF) & ((1 << PARTS_BITS)-1); - if (dual) - *dual = (arc.idx >> ARC_DUAL_BITOFF) & 1; -} - -static inline struct arc arc_from_parts(u32 chanidx, int chandir, u32 part, bool dual) -{ - struct arc arc; - - assert(part < CHANNEL_PARTS); - assert(chandir == 0 || chandir == 1); - assert(chanidx < (1U << ARC_CHANIDX_BITS)); - arc.idx = ((u32)dual << ARC_DUAL_BITOFF) - | (part << ARC_PART_BITOFF) - | ((u32)chandir << ARC_CHANDIR_BITOFF) - | (chanidx << ARC_CHANIDX_BITOFF); - return arc; -} - -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) - -struct pay_parameters { - /* The gossmap we are using */ - struct gossmap *gossmap; - const struct gossmap_node *source; - const struct gossmap_node *target; - - /* Extra information we intuited about the channels */ - struct chan_extra_map *chan_extra_map; - - /* Optional bitarray of disabled channels. */ - const bitmap *disabled; - - // how much we pay - struct amount_msat amount; - - // channel linearization parameters - double cap_fraction[CHANNEL_PARTS], - cost_fraction[CHANNEL_PARTS]; - - struct amount_msat max_fee; - double min_probability; - double base_probability; - double delay_feefactor; - double base_fee_penalty; - u32 prob_cost_factor; -}; - -/* Representation of the linear MCF network. - * This contains the topology of the extended network (after linearization and - * addition of arc duality). - * This contains also the arc probability and linear fee cost, as well as - * capacity; these quantities remain constant during MCF execution. */ -struct linear_network -{ - u32 *arc_tail_node; - // notice that a tail node is not needed, - // because the tail of arc is the head of dual(arc) - - struct arc *node_adjacency_next_arc; - struct arc *node_adjacency_first_arc; - - // probability and fee cost associated to an arc - s64 *arc_prob_cost, *arc_fee_cost; - s64 *capacity; - - size_t max_num_arcs,max_num_nodes; -}; - -/* This is the structure that keeps track of the network properties while we - * seek for a solution. */ -struct residual_network { - /* residual capacity on arcs */ - s64 *cap; - - /* some combination of prob. cost and fee cost on arcs */ - s64 *cost; - - /* potential function on nodes */ - s64 *potential; -}; - -/* Helper function. - * Given an arc idx, return the dual's idx in the residual network. */ -static struct arc arc_dual(struct arc arc) -{ - arc.idx ^= (1U << ARC_DUAL_BITOFF); - return arc; -} -/* Helper function. */ -static bool arc_is_dual(struct arc arc) -{ - bool dual; - arc_to_parts(arc, NULL, NULL, NULL, &dual); - return dual; -} - -/* Helper function. - * Given an arc of the network (not residual) give me the flow. */ -static s64 get_arc_flow( - const struct residual_network *network, - const struct arc arc) -{ - assert(!arc_is_dual(arc)); - assert(arc_dual(arc).idx < tal_count(network->cap)); - return network->cap[ arc_dual(arc).idx ]; -} - -/* Helper function. - * Given an arc idx, return the node from which this arc emanates in the residual network. */ -static u32 arc_tail(const struct linear_network *linear_network, - const struct arc arc) -{ - assert(arc.idx < tal_count(linear_network->arc_tail_node)); - return linear_network->arc_tail_node[ arc.idx ]; -} -/* Helper function. - * Given an arc idx, return the node that this arc is pointing to in the residual network. */ -static u32 arc_head(const struct linear_network *linear_network, - const struct arc arc) -{ - const struct arc dual = arc_dual(arc); - assert(dual.idx < tal_count(linear_network->arc_tail_node)); - return linear_network->arc_tail_node[dual.idx]; -} - -/* Helper function. - * Given node idx `node`, return the idx of the first arc whose tail is `node`. - * */ -static struct arc node_adjacency_begin( - const struct linear_network * linear_network, - const u32 node) -{ - assert(node < tal_count(linear_network->node_adjacency_first_arc)); - return linear_network->node_adjacency_first_arc[node]; -} - -/* Helper function. - * Is this the end of the adjacency list. */ -static bool node_adjacency_end(const struct arc arc) -{ - return arc.idx == INVALID_INDEX; -} - -/* Helper function. - * Given node idx `node` and `arc`, returns the idx of the next arc whose tail is `node`. */ -static struct arc node_adjacency_next( - const struct linear_network *linear_network, - const struct arc arc) -{ - assert(arc.idx < tal_count(linear_network->node_adjacency_next_arc)); - return linear_network->node_adjacency_next_arc[arc.idx]; -} - -static bool channel_is_available(const struct gossmap_chan *c, int dir, - const struct gossmap *gossmap, - const bitmap *disabled) -{ - if (!gossmap_chan_set(c, dir)) - return false; - - const u32 chan_idx = gossmap_chan_idx(gossmap, c); - return !bitmap_test_bit(disabled, chan_idx * 2 + dir); -} - -// TODO(eduardo): unit test this -/* Split a directed channel into parts with linear cost function. */ -static bool linearize_channel(const struct pay_parameters *params, - const struct gossmap_chan *c, const int dir, - s64 *capacity, s64 *cost) -{ - struct chan_extra_half *extra_half = get_chan_extra_half_by_chan( - params->gossmap, - params->chan_extra_map, - c, - dir); - - if (!extra_half) { - return false; - } - - /* FIXME: this assertion has been reported to be triggered in issue - * #7535. A quick and dirty solution is to comment it and work-around - * this case. But in principle if we do things the right way we should - * not have htlc_total>known_max. The problem is likely to be - * asynchronous way in which reserved htlcs are removed and known_max is - * updated. */ - // assert( - // amount_msat_less_eq(extra_half->htlc_total, extra_half->known_max)); - assert( - amount_msat_less_eq(extra_half->known_min, extra_half->known_max)); - - s64 h = (extra_half->htlc_total.millisatoshis+999)/1000; /* Raw: linearize_channel */ - s64 a = extra_half->known_min.millisatoshis/1000, /* Raw: linearize_channel */ - b = 1 + extra_half->known_max.millisatoshis/1000; /* Raw: linearize_channel */ - - /* If HTLCs add up to more than the known_max it means we have a - * completely wrong knowledge. */ - // assert(ha it doesn't mean automatically that our - * known_min should have been updated, because we reserve this HTLC - * after sendpay behind the scenes it might happen that sendpay failed - * because of insufficient funds we haven't noticed yet. */ - // assert(h<=a); - - /* We reduce this channel capacity because HTLC are reserving liquidity. */ - a -= h; - b -= h; - a = MAX(a,0); - b = MAX(a+1,b); - - /* An extra bound on capacity, here we use it to reduce the flow such - * that it does not exceed htlcmax. */ - s64 cap_on_capacity = - gossmap_chan_htlc_max(c, dir).millisatoshis/1000; /* Raw: linearize_channel */ - - capacity[0]=a; - cost[0]=0; - assert(params->base_probability > 5e-7); - assert(params->base_probability <= 1.0); - const double base_prob_factor = -log(params->base_probability); - - for(size_t i=1;icap_fraction[i]*(b-a), cap_on_capacity); - cap_on_capacity -= capacity[i]; - assert(cap_on_capacity>=0); - - cost[i] = (params->cost_fraction[i]*1.0/(b-a) + base_prob_factor) - *params->amount.millisatoshis /* Raw: linearize_channel */ - *params->prob_cost_factor; - } - return true; -} - -static struct residual_network * -alloc_residual_network(const tal_t *ctx, const size_t max_num_nodes, - const size_t max_num_arcs) -{ - struct residual_network *residual_network = - tal(ctx, struct residual_network); - if (!residual_network) - goto function_fail; - - residual_network->cap = tal_arrz(residual_network, s64, max_num_arcs); - residual_network->cost = tal_arrz(residual_network, s64, max_num_arcs); - residual_network->potential = - tal_arrz(residual_network, s64, max_num_nodes); - - if (!residual_network->cap || !residual_network->cost || - !residual_network->potential) { - goto function_fail; - } - return residual_network; - - function_fail: - return tal_free(residual_network); -} - -static void init_residual_network( - const struct linear_network * linear_network, - struct residual_network* residual_network) -{ - const size_t max_num_arcs = linear_network->max_num_arcs; - const size_t max_num_nodes = linear_network->max_num_nodes; - - for(struct arc arc = {0};arc.idx < max_num_arcs; ++arc.idx) - { - if(arc_is_dual(arc)) - continue; - - struct arc dual = arc_dual(arc); - residual_network->cap[arc.idx]=linear_network->capacity[arc.idx]; - residual_network->cap[dual.idx]=0; - - residual_network->cost[arc.idx]=residual_network->cost[dual.idx]=0; - } - for(u32 i=0;ipotential[i]=0; - } -} - -static void combine_cost_function( - const struct linear_network* linear_network, - struct residual_network *residual_network, - s64 mu) -{ - for(struct arc arc = {0};arc.idx < linear_network->max_num_arcs; ++arc.idx) - { - if(arc_tail(linear_network,arc)==INVALID_INDEX) - continue; - - const s64 pcost = linear_network->arc_prob_cost[arc.idx], - fcost = linear_network->arc_fee_cost[arc.idx]; - - const s64 combined = pcost==INFINITE || fcost==INFINITE ? INFINITE : - mu*fcost + (MU_MAX-1-mu)*pcost; - - residual_network->cost[arc.idx] - = mu==0 ? pcost : - (mu==(MU_MAX-1) ? fcost : combined); - } -} - -static void linear_network_add_adjacenct_arc( - struct linear_network *linear_network, - const u32 node_idx, - const struct arc arc) -{ - assert(arc.idx < tal_count(linear_network->arc_tail_node)); - linear_network->arc_tail_node[arc.idx] = node_idx; - - assert(node_idx < tal_count(linear_network->node_adjacency_first_arc)); - const struct arc first_arc = linear_network->node_adjacency_first_arc[node_idx]; - - assert(arc.idx < tal_count(linear_network->node_adjacency_next_arc)); - linear_network->node_adjacency_next_arc[arc.idx]=first_arc; - - assert(node_idx < tal_count(linear_network->node_adjacency_first_arc)); - linear_network->node_adjacency_first_arc[node_idx]=arc; -} - -/* Get the fee cost associated to this directed channel. - * Cost is expressed as PPM of the payment. - * - * Choose and integer `c_fee` to linearize the following fee function - * - * fee_msat = base_msat + floor(millionths*x_msat / 10^6) - * - * into - * - * fee_microsat = c_fee * x_sat - * - * use `base_fee_penalty` to weight the base fee and `delay_feefactor` to - * weight the CLTV delay. - * */ -static s64 linear_fee_cost( - const struct gossmap_chan *c, - const int dir, - double base_fee_penalty, - double delay_feefactor) -{ - assert(c); - assert(dir==0 || dir==1); - s64 pfee = c->half[dir].proportional_fee, - bfee = c->half[dir].base_fee, - delay = c->half[dir].delay; - - return pfee + bfee* base_fee_penalty+ delay*delay_feefactor; -} - -static struct linear_network * -init_linear_network(const tal_t *ctx, const struct pay_parameters *params, - char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - - struct linear_network * linear_network = tal(ctx, struct linear_network); - if (!linear_network) { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of linear_network"); - goto function_fail; - } - - const size_t max_num_chans = gossmap_max_chan_idx(params->gossmap); - const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL; - const size_t max_num_nodes = gossmap_max_node_idx(params->gossmap); - - linear_network->max_num_arcs = max_num_arcs; - linear_network->max_num_nodes = max_num_nodes; - - linear_network->arc_tail_node = tal_arr(linear_network,u32,max_num_arcs); - if(!linear_network->arc_tail_node) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of arc_tail_node"); - goto function_fail; - } - for(size_t i=0;iarc_tail_node);++i) - linear_network->arc_tail_node[i]=INVALID_INDEX; - - linear_network->node_adjacency_next_arc = tal_arr(linear_network,struct arc,max_num_arcs); - if(!linear_network->node_adjacency_next_arc) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of node_adjacency_next_arc"); - goto function_fail; - } - for(size_t i=0;inode_adjacency_next_arc);++i) - linear_network->node_adjacency_next_arc[i].idx=INVALID_INDEX; - - linear_network->node_adjacency_first_arc = tal_arr(linear_network,struct arc,max_num_nodes); - if(!linear_network->node_adjacency_first_arc) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of node_adjacency_first_arc"); - goto function_fail; - } - for(size_t i=0;inode_adjacency_first_arc);++i) - linear_network->node_adjacency_first_arc[i].idx=INVALID_INDEX; - - linear_network->arc_prob_cost = tal_arr(linear_network,s64,max_num_arcs); - if(!linear_network->arc_prob_cost) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of arc_prob_cost"); - goto function_fail; - } - for(size_t i=0;iarc_prob_cost);++i) - linear_network->arc_prob_cost[i]=INFINITE; - - linear_network->arc_fee_cost = tal_arr(linear_network,s64,max_num_arcs); - if(!linear_network->arc_fee_cost) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of arc_fee_cost"); - goto function_fail; - } - for(size_t i=0;iarc_fee_cost);++i) - linear_network->arc_fee_cost[i]=INFINITE; - - linear_network->capacity = tal_arrz(linear_network,s64,max_num_arcs); - if(!linear_network->capacity) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of capacity"); - goto function_fail; - } - - for(struct gossmap_node *node = gossmap_first_node(params->gossmap); - node; - node=gossmap_next_node(params->gossmap,node)) - { - const u32 node_id = gossmap_node_idx(params->gossmap,node); - - for(size_t j=0;jnum_chans;++j) - { - int half; - const struct gossmap_chan *c = gossmap_nth_chan(params->gossmap, - node, j, &half); - - if (!channel_is_available(c, half, params->gossmap, - params->disabled)) - continue; - - const u32 chan_id = gossmap_chan_idx(params->gossmap, c); - - const struct gossmap_node *next = gossmap_nth_node(params->gossmap, - c,!half); - - const u32 next_id = gossmap_node_idx(params->gossmap,next); - - if(node_id==next_id) - continue; - - // `cost` is the word normally used to denote cost per - // unit of flow in the context of MCF. - s64 prob_cost[CHANNEL_PARTS], capacity[CHANNEL_PARTS]; - - // split this channel direction to obtain the arcs - // that are outgoing to `node` - if (!linearize_channel(params, c, half, - capacity, prob_cost)) { - if(fail) - *fail = - tal_fmt(ctx, "linearize_channel failed"); - goto function_fail; - } - - const s64 fee_cost = linear_fee_cost(c,half, - params->base_fee_penalty, - params->delay_feefactor); - - // let's subscribe the 4 parts of the channel direction - // (c,half), the dual of these guys will be subscribed - // when the `i` hits the `next` node. - for(size_t k=0;kcapacity[arc.idx] = capacity[k]; - linear_network->arc_prob_cost[arc.idx] = prob_cost[k]; - - linear_network->arc_fee_cost[arc.idx] = fee_cost; - - // + the respective dual - struct arc dual = arc_dual(arc); - - linear_network_add_adjacenct_arc(linear_network,next_id,dual); - - linear_network->capacity[dual.idx] = 0; - linear_network->arc_prob_cost[dual.idx] = -prob_cost[k]; - - linear_network->arc_fee_cost[dual.idx] = -fee_cost; - } - } - } - - tal_free(this_ctx); - return linear_network; - - function_fail: - tal_free(this_ctx); - return tal_free(linear_network); -} - -/* Simple queue to traverse the network. */ -struct queue_data -{ - u32 idx; - struct lqueue_link ql; -}; - -// TODO(eduardo): unit test this -/* Finds an admissible path from source to target, traversing arcs in the - * residual network with capacity greater than 0. - * The path is encoded into prev, which contains the idx of the arcs that are - * traversed. */ -static bool -find_admissible_path(const tal_t *ctx, - const struct linear_network *linear_network, - const struct residual_network *residual_network, - const u32 source, const u32 target, struct arc *prev) -{ - tal_t *this_ctx = tal(ctx,tal_t); - - bool target_found = false; - - for(size_t i=0;iidx = source; - lqueue_enqueue(&myqueue,qdata); - - while(!lqueue_empty(&myqueue)) - { - qdata = lqueue_dequeue(&myqueue); - u32 cur = qdata->idx; - - tal_free(qdata); - - if(cur==target) - { - target_found = true; - break; - } - - for(struct arc arc = node_adjacency_begin(linear_network,cur); - !node_adjacency_end(arc); - arc = node_adjacency_next(linear_network,arc)) - { - // check if this arc is traversable - if(residual_network->cap[arc.idx] <= 0) - continue; - - u32 next = arc_head(linear_network,arc); - - assert(next < tal_count(prev)); - - // if that node has been seen previously - if(prev[next].idx!=INVALID_INDEX) - continue; - - prev[next] = arc; - - qdata = tal(this_ctx,struct queue_data); - qdata->idx = next; - lqueue_enqueue(&myqueue,qdata); - } - } - tal_free(this_ctx); - return target_found; -} - -/* Get the max amount of flow one can send from source to target along the path - * encoded in `prev`. */ -static s64 get_augmenting_flow( - const struct linear_network* linear_network, - const struct residual_network *residual_network, - const u32 source, - const u32 target, - const struct arc *prev) -{ - s64 flow = INFINITE; - - u32 cur = target; - while(cur!=source) - { - assert(curcap[arc.idx]); - - // we are traversing in the opposite direction to the flow, - // hence the next node is at the tail of the arc. - cur = arc_tail(linear_network,arc); - } - - assert(flow0); - return flow; -} - -/* Augment a `flow` amount along the path defined by `prev`.*/ -static void augment_flow( - const struct linear_network *linear_network, - struct residual_network *residual_network, - const u32 source, - const u32 target, - const struct arc *prev, - s64 flow) -{ - u32 cur = target; - - while(cur!=source) - { - assert(cur < tal_count(prev)); - const struct arc arc = prev[cur]; - const struct arc dual = arc_dual(arc); - - assert(arc.idx < tal_count(residual_network->cap)); - assert(dual.idx < tal_count(residual_network->cap)); - - residual_network->cap[arc.idx] -= flow; - residual_network->cap[dual.idx] += flow; - - assert(residual_network->cap[arc.idx] >=0 ); - - // we are traversing in the opposite direction to the flow, - // hence the next node is at the tail of the arc. - cur = arc_tail(linear_network,arc); - } -} - - -// TODO(eduardo): unit test this -/* Finds any flow that satisfy the capacity and balance constraints of the - * uncertainty network. For the balance function condition we have: - * balance(source) = - balance(target) = amount - * balance(node) = 0 , for every other node - * Returns an error code if no feasible flow is found. - * - * 13/04/2023 This implementation uses a simple augmenting path approach. - * */ -static bool find_feasible_flow(const tal_t *ctx, - const struct linear_network *linear_network, - struct residual_network *residual_network, - const u32 source, const u32 target, s64 amount, - char **fail) -{ - assert(amount>=0); - tal_t *this_ctx = tal(ctx,tal_t); - - /* path information - * prev: is the id of the arc that lead to the node. */ - struct arc *prev = tal_arr(this_ctx,struct arc,linear_network->max_num_nodes); - if(!prev) - { - if(fail) - *fail = tal_fmt(ctx, "bad allocation of prev"); - goto function_fail; - } - - while(amount>0) - { - // find a path from source to target - if (!find_admissible_path(this_ctx, linear_network, - residual_network, source, target, - prev)) - - { - if(fail) - *fail = tal_fmt(ctx, "find_admissible_path failed"); - goto function_fail; - } - - // traverse the path and see how much flow we can send - s64 delta = get_augmenting_flow(linear_network, - residual_network, - source,target,prev); - - // commit that flow to the path - delta = MIN(amount,delta); - assert(delta>0 && delta<=amount); - - augment_flow(linear_network,residual_network,source,target,prev,delta); - amount -= delta; - } - - tal_free(this_ctx); - return true; - - function_fail: - tal_free(this_ctx); - return false; -} - -// TODO(eduardo): unit test this -/* Similar to `find_admissible_path` but use Dijkstra to optimize the distance - * label. Stops when the target is hit. */ -static bool find_optimal_path(const tal_t *ctx, struct dijkstra *dijkstra, - const struct linear_network *linear_network, - const struct residual_network *residual_network, - const u32 source, const u32 target, - struct arc *prev, char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - bool target_found = false; - - bitmap *visited = tal_arrz(this_ctx, bitmap, - BITMAP_NWORDS(linear_network->max_num_nodes)); - - if(!visited) - { - if(fail) - *fail = tal_fmt(ctx, "bad allocation of visited"); - goto finish; - } - - - for(size_t i=0;icap[arc.idx] <= 0) - continue; - - u32 next = arc_head(linear_network,arc); - - s64 cij = residual_network->cost[arc.idx] - - residual_network->potential[cur] - + residual_network->potential[next]; - - // Dijkstra only works with non-negative weights - assert(cij>=0); - - if(distance[next]<=distance[cur]+cij) - continue; - - dijkstra_update(dijkstra,next,distance[cur]+cij); - prev[next]=arc; - } - } - - if (!target_found && fail) - *fail = tal_fmt(ctx, "no route to destination"); - - finish: - tal_free(this_ctx); - return target_found; -} - -/* Set zero flow in the residual network. */ -static void zero_flow( - const struct linear_network *linear_network, - struct residual_network *residual_network) -{ - for(u32 node=0;nodemax_num_nodes;++node) - { - residual_network->potential[node]=0; - for(struct arc arc=node_adjacency_begin(linear_network,node); - !node_adjacency_end(arc); - arc = node_adjacency_next(linear_network,arc)) - { - if(arc_is_dual(arc))continue; - - struct arc dual = arc_dual(arc); - - residual_network->cap[arc.idx] = linear_network->capacity[arc.idx]; - residual_network->cap[dual.idx] = 0; - } - } -} - -// TODO(eduardo): unit test this -/* Starting from a feasible flow (satisfies the balance and capacity - * constraints), find a solution that minimizes the network->cost function. - * - * TODO(eduardo) The MCF must be called several times until we get a good - * compromise between fees and probabilities. Instead of re-computing the MCF at - * each step, we might use the previous flow result, which is not optimal in the - * current iteration but I might be not too far from the truth. - * It comes to mind to use cycle cancelling. */ -static bool optimize_mcf(const tal_t *ctx, struct dijkstra *dijkstra, - const struct linear_network *linear_network, - struct residual_network *residual_network, - const u32 source, const u32 target, const s64 amount, - char **fail) -{ - assert(amount>=0); - tal_t *this_ctx = tal(ctx,tal_t); - char *errmsg; - - zero_flow(linear_network,residual_network); - struct arc *prev = tal_arr(this_ctx,struct arc,linear_network->max_num_nodes); - - const s64 *const distance = dijkstra_distance_data(dijkstra); - - s64 remaining_amount = amount; - - while(remaining_amount>0) - { - if (!find_optimal_path(this_ctx, dijkstra, linear_network, - residual_network, source, target, prev, - &errmsg)) { - if (fail) - *fail = - tal_fmt(ctx, "find_optimal_path failed: %s", - errmsg); - goto function_fail; - } - - // traverse the path and see how much flow we can send - s64 delta = get_augmenting_flow(linear_network,residual_network,source,target,prev); - - // commit that flow to the path - delta = MIN(remaining_amount,delta); - assert(delta>0 && delta<=remaining_amount); - - augment_flow(linear_network,residual_network,source,target,prev,delta); - remaining_amount -= delta; - - // update potentials - for(u32 n=0;nmax_num_nodes;++n) - { - // see page 323 of Ahuja-Magnanti-Orlin - residual_network->potential[n] -= MIN(distance[target],distance[n]); - - /* Notice: - * if node i is permanently labeled we have - * d_i<=d_t - * which implies - * MIN(d_i,d_t) = d_i - * if node i is temporarily labeled we have - * d_i>=d_t - * which implies - * MIN(d_i,d_t) = d_t - * */ - } - } - tal_free(this_ctx); - return true; - - function_fail: - - tal_free(this_ctx); - return false; -} - -// flow on directed channels -struct chan_flow -{ - s64 half[2]; -}; - -/* Search in the network a path of positive flow until we reach a node with - * positive balance. */ -static u32 find_positive_balance( - const struct gossmap *gossmap, - const bitmap *disabled, - const struct chan_flow *chan_flow, - const u32 start_idx, - const s64 *balance, - - const struct gossmap_chan **prev_chan, - int *prev_dir, - u32 *prev_idx) -{ - u32 final_idx = start_idx; - - /* TODO(eduardo) - * This is guaranteed to halt if there are no directed flow cycles. - * There souldn't be any. In fact if cost is strickly - * positive, then flow cycles do not exist at all in the - * MCF solution. But if cost is allowed to be zero for - * some arcs, then we might have flow cyles in the final - * solution. We must somehow ensure that the MCF - * algorithm does not come up with spurious flow cycles. */ - while(balance[final_idx]<=0) - { - // printf("%s: node = %d\n",__func__,final_idx); - u32 updated_idx=INVALID_INDEX; - struct gossmap_node *cur - = gossmap_node_byidx(gossmap,final_idx); - - for(size_t i=0;inum_chans;++i) - { - int dir; - const struct gossmap_chan *c - = gossmap_nth_chan(gossmap, - cur,i,&dir); - - if (!channel_is_available(c, dir, gossmap, disabled)) - continue; - - const u32 c_idx = gossmap_chan_idx(gossmap,c); - - // follow the flow - if(chan_flow[c_idx].half[dir]>0) - { - const struct gossmap_node *next - = gossmap_nth_node(gossmap,c,!dir); - u32 next_idx = gossmap_node_idx(gossmap,next); - - - prev_dir[next_idx] = dir; - prev_chan[next_idx] = c; - prev_idx[next_idx] = final_idx; - - updated_idx = next_idx; - break; - } - } - - assert(updated_idx!=INVALID_INDEX); - assert(updated_idx!=final_idx); - - final_idx = updated_idx; - } - return final_idx; -} - -struct list_data -{ - struct list_node list; - struct flow *flow_path; -}; - -static inline uint64_t pseudorand_interval(uint64_t a, uint64_t b) -{ - if (a == b) - return b; - assert(b > a); - return a + pseudorand(b - a); -} - -/* Given a flow in the residual network, build a set of payment flows in the - * gossmap that corresponds to this flow. */ -static struct flow ** -get_flow_paths(const tal_t *ctx, const struct gossmap *gossmap, - const bitmap *disabled, - - // chan_extra_map cannot be const because we use it to keep - // track of htlcs and in_flight sats. - struct chan_extra_map *chan_extra_map, - const struct linear_network *linear_network, - const struct residual_network *residual_network, - - // how many msats in excess we paid for not having msat accuracy - // in the MCF solver - struct amount_msat excess, - const double base_probability, - - // error message - char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - struct flow **flows = tal_arr(ctx,struct flow*,0); - - assert(amount_msat_less(excess, AMOUNT_MSAT(1000))); - - const size_t max_num_chans = gossmap_max_chan_idx(gossmap); - struct chan_flow *chan_flow = tal_arrz(this_ctx,struct chan_flow,max_num_chans); - - const size_t max_num_nodes = gossmap_max_node_idx(gossmap); - s64 *balance = tal_arrz(this_ctx,s64,max_num_nodes); - - const struct gossmap_chan **prev_chan - = tal_arr(this_ctx,const struct gossmap_chan *,max_num_nodes); - - - int *prev_dir = tal_arr(this_ctx,int,max_num_nodes); - u32 *prev_idx = tal_arr(this_ctx,u32,max_num_nodes); - - if (!chan_flow || !balance || !prev_chan || !prev_idx || !prev_dir) { - if (fail) - *fail = tal_fmt(ctx, "bad allocation"); - goto function_fail; - } - - // Convert the arc based residual network flow into a flow in the - // directed channel network. - // Compute balance on the nodes. - for(u32 n = 0;n htlc_max) { - /* htlc_min is too big or htlc_max is too small, - * we cannot send `delta` along this route. - * - * FIXME: We try anyways because failing - * channels will be blacklisted downstream. */ - htlc_min = 0; - } - - /* If we divide this route into different flows make it - * random to avoid routing nodes making correlations. */ - if (delta > htlc_max) { - // FIXME: choosing a number in the range - // [htlc_min, htlc_max] or - // [0.5 htlc_max, htlc_max] - // The choice of the fraction was completely - // arbitrary. - delta = pseudorand_interval( - MAX(htlc_min, (htlc_max * 50) / 100), - htlc_max); - } - - struct flow *fp = tal(this_ctx,struct flow); - fp->path = tal_arr(fp,const struct gossmap_chan *,length); - fp->dirs = tal_arr(fp,int,length); - - balance[node_idx] += delta; - balance[final_idx]-= delta; - - // walk backwards, substract flow - for(u32 cur_idx = final_idx; - cur_idx!=node_idx; - cur_idx=prev_idx[cur_idx]) - { - assert(cur_idx!=INVALID_INDEX); - - const int dir = prev_dir[cur_idx]; - const struct gossmap_chan *const c = prev_chan[cur_idx]; - const u32 c_idx = gossmap_chan_idx(gossmap,c); - - length--; - fp->path[length]=c; - fp->dirs[length]=dir; - // notice: fp->path and fp->dirs have the path - // in the correct order. - - chan_flow[c_idx].half[prev_dir[cur_idx]]-=delta; - } - - assert(delta>0); - - // substract the excess of msats for not having msat - // accuracy - struct amount_msat delivered = amount_msat(delta*1000); - if (!amount_msat_sub(&delivered, delivered, excess)) { - if (fail) - *fail = tal_fmt( - ctx, "unable to substract excess"); - goto function_fail; - } - excess = amount_msat(0); - fp->amount = delivered; - - fp->success_prob = - flow_probability(fp, gossmap, chan_extra_map, false) - * pow(base_probability, tal_count(fp->path)); - if (fp->success_prob < 0) { - if (fail) - *fail = - tal_fmt(ctx, "failed to compute " - "flow probability"); - goto function_fail; - } - - // add fp to flows - tal_arr_expand(&flows, fp); - } - } - - /* Establish ownership. */ - for(size_t i=0;i= min_probability; - bool B_prob_pass = B_prob >= min_probability; - - // all bounds are met - if(A_fee_pass && B_fee_pass && A_prob_pass && B_prob_pass) - { - // prefer lower fees - goto fees_or_prob; - } - - // prefer the solution that satisfies both bounds - if(!(A_fee_pass && A_prob_pass) && (B_fee_pass && B_prob_pass)) - { - return false; - } - // prefer the solution that satisfies both bounds - if((A_fee_pass && A_prob_pass) && !(B_fee_pass && B_prob_pass)) - { - return true; - } - - // no solution satisfies both bounds - - // bound on fee is met - if(A_fee_pass && B_fee_pass) - { - // pick the highest prob. - return A_prob > B_prob; - } - - // bound on prob. is met - if(A_prob_pass && B_prob_pass) - { - goto fees_or_prob; - } - - // prefer the solution that satisfies the bound on fees - if(A_fee_pass && !B_fee_pass) - { - return true; - } - if(B_fee_pass && !A_fee_pass) - { - return false; - } - - // none of them satisfy the fee bound - - // prefer the solution that satisfies the bound on prob. - if(A_prob_pass && !B_prob_pass) - { - return true; - } - if(B_prob_pass && !A_prob_pass) - { - return true; - } - - // no bound whatsoever is satisfied - - fees_or_prob: - - // fees are the same, wins the highest prob. - if(amount_msat_eq(A_fee,B_fee)) - { - return A_prob > B_prob; - } - - // go for fees - return amount_msat_less_eq(A_fee,B_fee); -} - -/* Channels that are not in the chan_extra_map should be disabled. */ -static bool check_disabled(const bitmap *disabled, - const struct gossmap *gossmap, - const struct chan_extra_map *chan_extra_map) -{ - assert(disabled); - assert(gossmap); - assert(chan_extra_map); - - if (tal_bytelen(disabled) != - 2 * bitmap_sizeof(gossmap_max_chan_idx(gossmap))) - return false; - - for (struct gossmap_chan *chan = gossmap_first_chan(gossmap); chan; - chan = gossmap_next_chan(gossmap, chan)) { - const u32 chan_idx = gossmap_chan_idx(gossmap, chan); - /* If both directions are disabled anyways, there is no need to - * fetch their information in chan_extra. */ - if (bitmap_test_bit(disabled, chan_idx * 2 + 0) && - bitmap_test_bit(disabled, chan_idx * 2 + 1)) - continue; - - struct short_channel_id scid = gossmap_chan_scid(gossmap, chan); - struct chan_extra *ce = - chan_extra_map_get(chan_extra_map, scid); - if (!ce) - return false; - } - return true; -} - -// TODO(eduardo): choose some default values for the minflow parameters -/* eduardo: I think it should be clear that this module deals with linear - * flows, ie. base fees are not considered. Hence a flow along a path is - * described with a sequence of directed channels and one amount. - * In the `pay_flow` module there are dedicated routes to compute the actual - * amount to be forward on each hop. - * - * TODO(eduardo): notice that we don't pay fees to forward payments with local - * channels and we can tell with absolute certainty the liquidity on them. - * Check that local channels have fee costs = 0 and bounds with certainty (min=max). */ -// TODO(eduardo): we should LOG_DBG the process of finding the MCF while -// adjusting the frugality factor. -struct flow **minflow(const tal_t *ctx, struct gossmap *gossmap, - const struct gossmap_node *source, - const struct gossmap_node *target, - struct chan_extra_map *chan_extra_map, - const bitmap *disabled, struct amount_msat amount, - struct amount_msat max_fee, double min_probability, - double base_probability, - double delay_feefactor, double base_fee_penalty, - u32 prob_cost_factor, char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - char *errmsg; - struct flow **best_flow_paths = NULL; - - struct pay_parameters *params = tal(this_ctx,struct pay_parameters); - struct dijkstra *dijkstra; - - params->gossmap = gossmap; - params->source = source; - params->target = target; - params->chan_extra_map = chan_extra_map; - - params->disabled = disabled; - - if (!check_disabled(disabled, gossmap, chan_extra_map)) { - if (fail) - *fail = tal_fmt(ctx, "Invalid disabled bitmap."); - goto function_fail; - } - - params->amount = amount; - - // template the channel partition into linear arcs - params->cap_fraction[0]=0; - params->cost_fraction[0]=0; - for(size_t i =1;icap_fraction[i]=CHANNEL_PIVOTS[i]-CHANNEL_PIVOTS[i-1]; - params->cost_fraction[i]= - log((1-CHANNEL_PIVOTS[i-1])/(1-CHANNEL_PIVOTS[i])) - /params->cap_fraction[i]; - - // printf("channel part: %ld, fraction: %lf, cost_fraction: %lf\n", - // i,params->cap_fraction[i],params->cost_fraction[i]); - } - - params->max_fee = max_fee; - params->min_probability = min_probability; - params->delay_feefactor = delay_feefactor; - params->base_fee_penalty = base_fee_penalty; - params->prob_cost_factor = prob_cost_factor; - params->base_probability = base_probability; - - // build the uncertainty network with linearization and residual arcs - struct linear_network *linear_network= init_linear_network(this_ctx, params, &errmsg); - if (!linear_network) { - if(fail) - *fail = tal_fmt(ctx, "init_linear_network failed: %s", - errmsg); - goto function_fail; - } - - struct residual_network *residual_network = - alloc_residual_network(this_ctx, linear_network->max_num_nodes, - linear_network->max_num_arcs); - if (!residual_network) { - if (fail) - *fail = tal_fmt( - ctx, "failed to allocate the residual network"); - goto function_fail; - } - - dijkstra = dijkstra_new(this_ctx, gossmap_max_node_idx(params->gossmap)); - - const u32 target_idx = gossmap_node_idx(params->gossmap,target); - const u32 source_idx = gossmap_node_idx(params->gossmap,source); - - init_residual_network(linear_network,residual_network); - - struct amount_msat best_fee; - double best_prob_success; - - /* TODO(eduardo): - * Some MCF algorithms' performance depend on the size of maxflow. If we - * were to work in units of msats we 1. risking overflow when computing - * costs and 2. we risk a performance overhead for no good reason. - * - * Working in units of sats was my first choice, but maybe working in - * units of 10, or 100 sats could be even better. - * - * IDEA: define the size of our precision as some parameter got at - * runtime that depends on the size of the payment and adjust the MCF - * accordingly. - * For example if we are trying to pay 1M sats our precision could be - * set to 1000sat, then channels that had capacity for 3M sats become 3k - * flow units. */ - const u64 pay_amount_msats = params->amount.millisatoshis % 1000; /* Raw: minflow */ - const u64 pay_amount_sats = params->amount.millisatoshis/1000 /* Raw: minflow */ - + (pay_amount_msats ? 1 : 0); - const struct amount_msat excess - = amount_msat(pay_amount_msats ? 1000 - pay_amount_msats : 0); - - if (!find_feasible_flow(this_ctx, linear_network, residual_network, - source_idx, target_idx, pay_amount_sats, - &errmsg)) { - // there is no flow that satisfy the constraints, we stop here - if(fail) - *fail = tal_fmt(ctx, "failed to find a feasible flow: %s", errmsg); - goto function_fail; - } - - // first flow found - best_flow_paths = get_flow_paths( - this_ctx, params->gossmap, params->disabled, params->chan_extra_map, - linear_network, residual_network, excess, params->base_probability, - &errmsg); - if (!best_flow_paths) { - if (fail) - *fail = - tal_fmt(ctx, "get_flow_paths failed: %s", errmsg); - goto function_fail; - } - best_flow_paths = tal_steal(ctx, best_flow_paths); - - best_prob_success = - flowset_probability(this_ctx, best_flow_paths, params->gossmap, - params->chan_extra_map, false, &errmsg) - * pow(params->base_probability, flowset_size(best_flow_paths)); - if (best_prob_success < 0) { - if (fail) - *fail = tal_fmt( - ctx, - "flowset_probability failed on MaxFlow phase: %s", - errmsg); - goto function_fail; - } - if (!flowset_fee(&best_fee, best_flow_paths)) { - if (fail) - *fail = - tal_fmt(ctx, "flowset_fee failed on MaxFlow phase"); - goto function_fail; - } - - // binary search for a value of `mu` that fits our fee and prob. - // constraints. - // mu=0 corresponds to only probabilities - // mu=MU_MAX-1 corresponds to only fee - s64 mu_left = 0, mu_right = MU_MAX; - while(mu_leftgossmap, params->disabled, - params->chan_extra_map, linear_network, - residual_network, excess, params->base_probability, - &errmsg); - if(!flow_paths) - { - // get_flow_paths doesn't fail unless there is a bug. - if (fail) - *fail = - tal_fmt(ctx, "get_flow_paths failed: %s", errmsg); - goto function_fail; - } - - double prob_success = - flowset_probability(this_ctx, flow_paths, params->gossmap, - params->chan_extra_map, false, &errmsg) - * pow(params->base_probability, flowset_size(flow_paths)); - if (prob_success < 0) { - // flowset_probability doesn't fail unless there is a bug. - if (fail) - *fail = - tal_fmt(ctx, "flowset_probability: %s", errmsg); - goto function_fail; - } - - struct amount_msat fee; - if (!flowset_fee(&fee, flow_paths)) { - // flowset_fee doesn't fail unless there is a bug. - if (fail) - *fail = - tal_fmt(ctx, "flowset_fee failed evaluating MinCostFlow candidate"); - goto function_fail; - } - - /* Is this better than the previous one? */ - if(!best_flow_paths || - is_better(params->max_fee,params->min_probability, - fee,prob_success, - best_fee, best_prob_success)) - { - - best_flow_paths = tal_free(best_flow_paths); - best_flow_paths = tal_steal(ctx,flow_paths); - - best_fee = fee; - best_prob_success=prob_success; - flow_paths = NULL; - } - /* I don't like this candidate. */ - else - tal_free(flow_paths); - - if(amount_msat_greater(fee,params->max_fee)) - { - // too expensive - mu_left = mu+1; - - }else if(prob_success < params->min_probability) - { - // too unlikely - mu_right = mu; - }else - { - // with mu constraints are satisfied, now let's optimize - // the fees - mu_left = mu+1; - } - } - - tal_free(this_ctx); - return best_flow_paths; - - function_fail: - tal_free(this_ctx); - return tal_free(best_flow_paths); -} - diff --git a/plugins/renepay/mcf.h b/plugins/renepay/mcf.h deleted file mode 100644 index 88f368ce80b7..000000000000 --- a/plugins/renepay/mcf.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_MCF_H -#define LIGHTNING_PLUGINS_RENEPAY_MCF_H -#include "config.h" -#include -#include -#include - -struct chan_extra_map; - -enum { - RENEPAY_ERR_OK, - // No feasible flow found, either there is not enough known liquidity (or capacity) - // in the channels to complete the payment - RENEPAY_ERR_NOFEASIBLEFLOW, - // There is at least one feasible flow, but the the cheapest solution that we - // found is too expensive, we return the result anyways. - RENEPAY_ERR_NOCHEAPFLOW -}; - - - -/** - * optimal_payment_flow - API for min cost flow function(s). - * @ctx: context to allocate returned flows from - * @gossmap: the gossip map - * @source: the source to start from - * @target: the target to pay - * @chan_extra_map: hashtable of extra per-channel information - * @disabled: NULL, or a bitmap by channel index of channels not to use. - * @amount: the amount we want to reach @target - * - * @max_fee: the maximum allowed in fees - * - * @min_probability: minimum probability accepted - * - * @delay_feefactor converts 1 block delay into msat, as if it were an additional - * fee. So if a CLTV delay on a node is 5 blocks, that's treated as if it - * were a fee of 5 * @delay_feefactor. - * - * @base_fee_penalty: factor to compute additional proportional cost from each - * unit of base fee. So #base_fee_penalty will be added to the effective - * proportional fee for each msat of base fee. - * - * effective_ppm = proportional_fee + base_fee_msat * base_fee_penalty - * - * @prob_cost_factor: factor used to monetize the probability cost. It is - * defined as the number of ppm (parts per million of the total payment) we - * are willing to pay to improve the probability of success by 0.1%. - * - * k_microsat = floor(1000*prob_cost_factor * payment_sat) - * - * this k is used to compute a prob. cost in units of microsats - * - * cost(payment) = - k_microsat * log Prob(payment) - * - * Return a series of subflows which deliver amount to target, or NULL. - */ -struct flow **minflow(const tal_t *ctx, struct gossmap *gossmap, - const struct gossmap_node *source, - const struct gossmap_node *target, - struct chan_extra_map *chan_extra_map, - const bitmap *disabled, struct amount_msat amount, - struct amount_msat max_fee, double min_probability, - double base_probability, - double delay_feefactor, double base_fee_penalty, - u32 prob_cost_factor, char **fail); -#endif /* LIGHTNING_PLUGINS_RENEPAY_MCF_H */ diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 3c5d7ac65751..852f2c65e7ba 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -5,12 +5,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include diff --git a/plugins/renepay/routebuilder.c b/plugins/renepay/routebuilder.c deleted file mode 100644 index e128a881d64b..000000000000 --- a/plugins/renepay/routebuilder.c +++ /dev/null @@ -1,428 +0,0 @@ -#include "config.h" -#include -#include -#include - -#include - -static void uncertainty_remove_routes(struct uncertainty *uncertainty, - struct route **routes) -{ - const size_t N = tal_count(routes); - for (size_t i = 0; i < N; i++) - uncertainty_remove_htlcs(uncertainty, routes[i]); -} - -/* Shave-off amounts that do not meet the liquidity constraints. Disable - * channels that produce an htlc_max bottleneck. */ -static enum renepay_errorcode -flow_adjust_htlcmax_constraints(struct flow *flow, struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - bitmap *disabled_bitmap) -{ - assert(flow); - assert(gossmap); - assert(chan_extra_map); - assert(disabled_bitmap); - assert(!amount_msat_is_zero(flow_delivers(flow))); - - enum renepay_errorcode errorcode; - - struct amount_msat max_deliverable; - const struct gossmap_chan *bad_channel; - - errorcode = flow_maximum_deliverable(&max_deliverable, flow, gossmap, - chan_extra_map, &bad_channel); - - if (!errorcode) { - assert(!amount_msat_is_zero(max_deliverable)); - - // no issues - flow->amount = - amount_msat_min(flow_delivers(flow), max_deliverable); - - return errorcode; - } - - if (errorcode == RENEPAY_BAD_CHANNEL) { - // this is a channel that we can disable - // FIXME: log this error? disabling both directions? - bitmap_set_bit(disabled_bitmap, - gossmap_chan_idx(gossmap, bad_channel) * 2 + 0); - bitmap_set_bit(disabled_bitmap, - gossmap_chan_idx(gossmap, bad_channel) * 2 + 1); - } - - // we had an unexpected error - return errorcode; -} - -static enum renepay_errorcode -route_check_constraints(struct route *route, struct gossmap *gossmap, - struct uncertainty *uncertainty, - bitmap *disabled_bitmap) -{ - assert(route); - assert(route->hops); - const size_t pathlen = tal_count(route->hops); - if (!amount_msat_eq(route->amount, route->hops[pathlen - 1].amount)) - return RENEPAY_PRECONDITION_ERROR; - if (!amount_msat_eq(route->amount_sent, route->hops[0].amount)) - return RENEPAY_PRECONDITION_ERROR; - - for (size_t i = 0; i < pathlen; i++) { - struct route_hop *hop = &route->hops[i]; - int dir = hop->direction; - struct gossmap_chan *chan = - gossmap_find_chan(gossmap, &hop->scid); - assert(chan); - struct chan_extra *ce = - uncertainty_find_channel(uncertainty, hop->scid); - - // check that we stay within the htlc max and min limits - if (amount_msat_greater(hop->amount, - gossmap_chan_htlc_max(chan, dir)) || - amount_msat_less(hop->amount, - gossmap_chan_htlc_min(chan, dir))) { - bitmap_set_bit(disabled_bitmap, - gossmap_chan_idx(gossmap, chan) * 2 + - dir); - return RENEPAY_BAD_CHANNEL; - } - - // check that the sum of all htlcs and this amount does not - // exceed the maximum known by our knowledge - struct amount_msat total_htlcs = ce->half[dir].htlc_total; - if (!amount_msat_add(&total_htlcs, total_htlcs, hop->amount)) - return RENEPAY_AMOUNT_OVERFLOW; - - if (amount_msat_greater(total_htlcs, ce->half[dir].known_max)) - return RENEPAY_UNEXPECTED; - } - return RENEPAY_NOERROR; -} - -static void tal_report_error(const tal_t *ctx, enum jsonrpc_errcode *ecode, - const char **fail, - enum jsonrpc_errcode error_value, const char *fmt, - ...) -{ - tal_t *this_ctx = tal(ctx, tal_t); - - va_list ap; - const char *str; - - va_start(ap, fmt); - str = tal_vfmt(this_ctx, fmt, ap); - va_end(ap); - - if (ecode) - *ecode = error_value; - - if (fail) - *fail = tal_fmt(ctx, "%s", str); - - this_ctx = tal_free(this_ctx); -} - -/* Routes are computed and saved in the payment for later use. */ -struct route **get_routes(const tal_t *ctx, - struct payment_info *payment_info, - - const struct node_id *source, - const struct node_id *destination, - struct gossmap *gossmap, - struct uncertainty *uncertainty, - struct disabledmap *disabledmap, - - struct amount_msat amount_to_deliver, - struct amount_msat feebudget, - - u64 *next_partid, - u64 groupid, - - enum jsonrpc_errcode *ecode, - const char **fail) -{ - assert(gossmap); - assert(uncertainty); - - const tal_t *this_ctx = tal(ctx, tal_t); - struct route **routes = tal_arr(ctx, struct route *, 0); - - double probability_budget = payment_info->min_prob_success; - const double base_probability = payment_info->base_prob_success; - double delay_feefactor = payment_info->delay_feefactor; - const double base_fee_penalty = payment_info->base_fee_penalty; - const double prob_cost_factor = payment_info->prob_cost_factor; - const unsigned int maxdelay = payment_info->maxdelay; - bool delay_feefactor_updated = true; - - bitmap *disabled_bitmap = - tal_disabledmap_get_bitmap(this_ctx, disabledmap, gossmap); - - if (!disabled_bitmap) { - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "Failed to build disabled_bitmap."); - goto function_fail; - } - if (amount_msat_is_zero(amount_to_deliver)) { - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "amount to deliver is zero"); - goto function_fail; - } - - /* Also disable every channel that we don't have in the chan_extra_map. - * We might have channels in the gossmap that are not usable for - * probability computations for example if we don't know their capacity. - * We can tell the solver to ignore those channels by disabling them - * here. - */ - for (struct gossmap_chan *chan = gossmap_first_chan(gossmap); chan; - chan = gossmap_next_chan(gossmap, chan)) { - const u32 chan_id = gossmap_chan_idx(gossmap, chan); - struct short_channel_id scid = gossmap_chan_scid(gossmap, chan); - struct chan_extra *ce = - chan_extra_map_get(uncertainty->chan_extra_map, scid); - if (!ce) { - bitmap_set_bit(disabled_bitmap, chan_id * 2 + 0); - bitmap_set_bit(disabled_bitmap, chan_id * 2 + 1); - } - } - - const struct gossmap_node *src, *dst; - src = gossmap_find_node(gossmap, source); - if (!src) { - tal_report_error(ctx, ecode, fail, PAY_ROUTE_NOT_FOUND, - "We don't have any channels."); - goto function_fail; - } - dst = gossmap_find_node(gossmap, destination); - if (!dst) { - tal_report_error( - ctx, ecode, fail, PAY_ROUTE_NOT_FOUND, - "Destination is unknown in the network gossip."); - goto function_fail; - } - - char *errmsg; - - while (!amount_msat_is_zero(amount_to_deliver)) { - - /* TODO: choose an algorithm, could be something like - * payment->algorithm, that we set up based on command line - * options and that can be changed according to some conditions - * met during the payment process, eg. add "select_solver" pay - * mod. */ - /* TODO: use uncertainty instead of chan_extra */ - - /* Min. Cost Flow algorithm to find optimal flows. */ - struct flow **flows = - minflow(this_ctx, gossmap, src, dst, - uncertainty_get_chan_extra_map(uncertainty), - disabled_bitmap, amount_to_deliver, feebudget, - probability_budget, - base_probability, - delay_feefactor, - base_fee_penalty, - prob_cost_factor, &errmsg); - delay_feefactor_updated = false; - - if (!flows) { - tal_report_error( - ctx, ecode, fail, PAY_ROUTE_NOT_FOUND, - "minflow couldn't find a feasible flow: %s", - errmsg); - goto function_fail; - } - - enum renepay_errorcode errorcode; - for (size_t i = 0; i < tal_count(flows); i++) { - - // do we overpay? - if (amount_msat_greater(flows[i]->amount, - amount_to_deliver)) { - // should not happen - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s: flow is delivering to destination " - "(%s) more than requested (%s)", - __func__, - fmt_amount_msat(this_ctx, flows[i]->amount), - fmt_amount_msat(this_ctx, - amount_to_deliver)); - goto function_fail; - } - - // fees considered, remove the least amount as to fit in - // with the htlcmax constraints - errorcode = flow_adjust_htlcmax_constraints( - flows[i], gossmap, - uncertainty_get_chan_extra_map(uncertainty), - disabled_bitmap); - if (errorcode == RENEPAY_BAD_CHANNEL) - // we handle a bad channel error by disabling - // it, infinite loops are avoided since we have - // everytime less and less channels - continue; - if (errorcode) { - // any other error is bad - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "flow_adjust_htlcmax_constraints returned " - "errorcode: %s", - renepay_errorcode_name(errorcode)); - goto function_fail; - } - - // a bound check, we shouldn't deliver a zero amount, it - // would mean a bug somewhere - if (amount_msat_is_zero(flows[i]->amount)) { - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "flow conveys a zero amount"); - goto function_fail; - } - - const double prob = flow_probability( - flows[i], gossmap, - uncertainty_get_chan_extra_map(uncertainty), true); - if (prob < 0) { - // should not happen - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "flow_probability failed"); - goto function_fail; - } - - // this flow seems good, build me a route - struct route *r = flow_to_route( - this_ctx, groupid, *next_partid, - payment_info->payment_hash, - payment_info->final_cltv, gossmap, flows[i]); - - if (!r) { - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s failed to build route from flow.", - __func__); - goto function_fail; - } - - const struct amount_msat fee = route_fees(r); - const struct amount_msat delivering = route_delivers(r); - - // are we still within the fee budget? - if (amount_msat_greater(fee, feebudget)) { - tal_report_error( - ctx, ecode, fail, PAY_ROUTE_TOO_EXPENSIVE, - "Fee exceeds our fee budget, fee=%s " - "(feebudget=%s)", - fmt_amount_msat(this_ctx, fee), - fmt_amount_msat(this_ctx, feebudget)); - goto function_fail; - } - - // check the CLTV delay does not exceed our settings - const unsigned int delay = route_delay(r); - if (delay > maxdelay) { - if (!delay_feefactor_updated) { - delay_feefactor *= 2; - delay_feefactor_updated = true; - } - - /* FIXME: What is a sane limit? */ - if (delay_feefactor > 1000) { - tal_report_error( - ctx, ecode, fail, - PAY_ROUTE_TOO_EXPENSIVE, - "CLTV delay exceeds our CLTV " - "budget, delay=%u (maxdelay=%u)", - delay, maxdelay); - goto function_fail; - } - continue; - } - - // check that the route satisfy all constraints - errorcode = route_check_constraints( - r, gossmap, uncertainty, disabled_bitmap); - - if (errorcode == RENEPAY_BAD_CHANNEL) - continue; - if (errorcode) { - // any other error is bad - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "route_check_constraints returned " - "errorcode: %s", - renepay_errorcode_name(errorcode)); - goto function_fail; - } - - // update the fee budget - if (!amount_msat_sub(&feebudget, feebudget, fee)) { - // should never happen - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s routing fees (%s) exceed fee " - "budget (%s).", - __func__, - fmt_amount_msat(this_ctx, fee), - fmt_amount_msat(this_ctx, feebudget)); - goto function_fail; - } - - // update the amount that we deliver - if (!amount_msat_sub(&amount_to_deliver, - amount_to_deliver, delivering)) { - // should never happen - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s: route delivering to destination (%s) " - "is more than requested (%s)", - __func__, - fmt_amount_msat(this_ctx, delivering), - fmt_amount_msat(this_ctx, - amount_to_deliver)); - goto function_fail; - } - - // update the probability target - if (prob < 1e-10) { - // probability is too small for division - probability_budget = 1.0; - } else { - /* prob here is a conditional probability, the - * next flow will have a conditional - * probability prob2 and we would like that - * prob*prob2 >= probability_budget hence - * probability_budget/prob becomes the next - * iteration's target. */ - probability_budget = - MIN(1.0, probability_budget / prob); - } - - // route added - (*next_partid)++; - uncertainty_commit_htlcs(uncertainty, r); - tal_arr_expand(&routes, r); - } - } - - /* remove the temporary routes from the uncertainty network */ - uncertainty_remove_routes(uncertainty, routes); - - /* ownership */ - for (size_t i = 0; i < tal_count(routes); i++) - routes[i] = tal_steal(routes, routes[i]); - - tal_free(this_ctx); - return routes; - -function_fail: - /* remove the temporary routes from the uncertainty network */ - uncertainty_remove_routes(uncertainty, routes); - - /* Discard any routes we have constructed here. */ - tal_free(this_ctx); - return tal_free(routes); -} diff --git a/plugins/renepay/routebuilder.h b/plugins/renepay/routebuilder.h deleted file mode 100644 index 2692d2a82adc..000000000000 --- a/plugins/renepay/routebuilder.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_ROUTEBUILDER_H -#define LIGHTNING_PLUGINS_RENEPAY_ROUTEBUILDER_H - -#include "config.h" -#include -#include -#include -#include -#include -#include - -struct route **get_routes(const tal_t *ctx, - struct payment_info *payment_info, - - const struct node_id *source, - const struct node_id *destination, - struct gossmap *gossmap, - struct uncertainty *uncertainty, - struct disabledmap *disabledmap, - - struct amount_msat amount_to_deliver, - struct amount_msat feebudget, - - u64 *next_partid, - u64 groupid, - - enum jsonrpc_errcode *ecode, - const char **fail); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_ROUTEBUILDER_H */ diff --git a/plugins/renepay/test/Makefile b/plugins/renepay/test/Makefile index 2cf525d60d8e..9e3087963457 100644 --- a/plugins/renepay/test/Makefile +++ b/plugins/renepay/test/Makefile @@ -9,7 +9,6 @@ ALL_TEST_PROGRAMS += $(PLUGIN_RENEPAY_TEST_PROGRAMS) $(PLUGIN_RENEPAY_TEST_OBJS): $(PLUGIN_RENEPAY_SRC) PLUGIN_RENEPAY_TEST_COMMON_OBJS := \ - plugins/renepay/dijkstra.o \ plugins/renepay/chan_extra.o \ bitcoin/chainparams.o \ common/gossmap.o \ diff --git a/plugins/renepay/test/run-arc.c b/plugins/renepay/test/run-arc.c deleted file mode 100644 index 793a0e70a0e2..000000000000 --- a/plugins/renepay/test/run-arc.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../flow.c" -#include "../mcf.c" - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -int main(int argc, char *argv[]) -{ - bool dual; - u32 part; - int chandir; - u32 chanidx; - - common_setup(argv[0]); - - for (int i = 0; i < 32; i++) { - for (int j = 0; j < 32; j++) { - for (int k = 0; k < 32; k++) { - struct arc a, a2; - - a.idx = (1U << i) | (1U << j) | (1U << k); - arc_to_parts(a, &chanidx, &chandir, &part, &dual); - a2 = arc_from_parts(chanidx, chandir, part, dual); - assert(a.idx == a2.idx); - } - } - } - - /* Test all chanidx */ - for (int i = 0; i < (1U << ARC_CHANIDX_BITS); i++) { - struct arc a = arc_from_parts(i, chandir, part, dual); - - arc_to_parts(a, &chanidx, NULL, NULL, NULL); - assert(chanidx == i); - } - - /* Test both chandir */ - for (int i = 0; i < 2; i++) { - struct arc a = arc_from_parts(chanidx, i, part, dual); - - arc_to_parts(a, NULL, &chandir, NULL, NULL); - assert(chandir == i); - } - - /* Test all parts */ - for (int i = 0; i < CHANNEL_PARTS; i++) { - struct arc a = arc_from_parts(chanidx, chandir, i, dual); - - arc_to_parts(a, NULL, NULL, &part, NULL); - assert(part == i); - } - - /* Test both dual */ - for (int i = 0; i < 2; i++) { - struct arc a = arc_from_parts(chanidx, chandir, part, i); - - arc_to_parts(a, NULL, NULL, NULL, &dual); - assert(dual == i); - - assert(arc_is_dual(a) == dual); - - a = arc_dual(a); - arc_to_parts(a, NULL, NULL, NULL, &dual); - assert(dual == !i); - assert(arc_is_dual(a) == dual); - } - - common_shutdown(); -} - diff --git a/plugins/renepay/test/run-bottleneck.c b/plugins/renepay/test/run-bottleneck.c deleted file mode 100644 index 510ddebde5e7..000000000000 --- a/plugins/renepay/test/run-bottleneck.c +++ /dev/null @@ -1,286 +0,0 @@ -/* Checks that get_route can handle bottleneck situations assigning values to - * routes that do not exceed the liquidity constraints. */ -#include "config.h" - -#include "../errorcodes.c" -#include "../flow.c" -#include "../mcf.c" -#include "../uncertainty.c" -#include "../disabledmap.c" -#include "../route.c" -#include "../routebuilder.c" -#include "common.h" - -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static u8 empty_map[] = {10}; - -static const char *print_flows(const tal_t *ctx, const char *desc, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - struct flow **flows) -{ - tal_t *this_ctx = tal(ctx, tal_t); - double tot_prob = - flowset_probability(tmpctx, flows, gossmap, chan_extra_map, false, NULL); - assert(tot_prob >= 0); - char *buff = tal_fmt(ctx, "%s: %zu subflows, prob %2lf\n", desc, - tal_count(flows), tot_prob); - for (size_t i = 0; i < tal_count(flows); i++) { - struct amount_msat fee, delivered; - tal_append_fmt(&buff, " "); - for (size_t j = 0; j < tal_count(flows[i]->path); j++) { - struct short_channel_id scid = - gossmap_chan_scid(gossmap, flows[i]->path[j]); - tal_append_fmt(&buff, "%s%s", j ? "->" : "", - fmt_short_channel_id(this_ctx, scid)); - } - delivered = flows[i]->amount; - if (!flow_fee(&fee, flows[i])) { - abort(); - } - tal_append_fmt(&buff, " prob %.2f, %s delivered with fee %s\n", - flows[i]->success_prob, - fmt_amount_msat(this_ctx, delivered), - fmt_amount_msat(this_ctx, fee)); - } - - tal_free(this_ctx); - return buff; -} - -#define NUM_NODES 8 - -static void remove_file(char *fname) { assert(!remove(fname)); } - -int main(int argc, char *argv[]) -{ - int fd; - char *gossfile; - struct gossmap *gossmap; - struct node_id nodes[NUM_NODES]; - - common_setup(argv[0]); - chainparams = chainparams_for_network("regtest"); - - fd = tmpdir_mkstemp(tmpctx, "run-bottleneck.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - assert(write(fd, empty_map, sizeof(empty_map)) == sizeof(empty_map)); - - gossmap = gossmap_load(tmpctx, gossfile, NULL); - assert(gossmap); - - for (size_t i = 0; i < NUM_NODES; i++) { - struct privkey tmp; - memset(&tmp, i+1, sizeof(tmp)); - node_id_from_privkey(&tmp, &nodes[i]); - } - - /* We will try a payment from 1 to 8, forcing a payment split between - * two routes 1->2->4->5->6->8 and 1->3->4->5->7->8. - * To force the split the total payment amount will be greater than the - * channel 1-2 and 1-3 capacities. Then channel 4--5 will be a common - * edge in the payment routes. - * - * MCF does not handle fees hence if the capacity of 4--5 is enough to - * let the entire payment pass, we expect that minflow computes two - * routes that are scaled down by get_route algorithm - * to fit for the fee constraints. - * - * +--2--+ +--6--+ - * | | | | - * 1 4---5 8 - * | | | | - * +--3--+ +--7--+ - * - * */ - struct short_channel_id scid; - - assert(mk_short_channel_id(&scid, 1, 2, 0)); - add_connection(fd, &nodes[0], &nodes[1], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(60 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(60 * 1000), - true); - - assert(mk_short_channel_id(&scid, 1, 3, 0)); - add_connection(fd, &nodes[0], &nodes[2], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(60 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(60 * 1000), - true); - - assert(mk_short_channel_id(&scid, 2, 4, 0)); - add_connection(fd, &nodes[1], &nodes[3], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - true); - - assert(mk_short_channel_id(&scid, 3, 4, 0)); - add_connection(fd, &nodes[2], &nodes[3], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - true); - - assert(mk_short_channel_id(&scid, 4, 5, 0)); - add_connection(fd, &nodes[3], &nodes[4], scid, - AMOUNT_MSAT(0), - /* MCF cuts off at 95% of the conditional capacity, for - * cap = 106k that means only 100.7k sats can be sent - * through this channel. */ - AMOUNT_MSAT(106 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(110 * 1000), - true); - - assert(mk_short_channel_id(&scid, 5, 6, 0)); - add_connection(fd, &nodes[4], &nodes[5], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 100 * 1000 /* 10% */, 5, - AMOUNT_SAT(1000 * 1000), - true); - - assert(mk_short_channel_id(&scid, 5, 7, 0)); - add_connection(fd, &nodes[4], &nodes[6], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 100 * 1000 /* 10% */, 5, - AMOUNT_SAT(1000 * 1000), - true); - - assert(mk_short_channel_id(&scid, 6, 8, 0)); - add_connection(fd, &nodes[5], &nodes[7], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - true); - - assert(mk_short_channel_id(&scid, 7, 8, 0)); - add_connection(fd, &nodes[6], &nodes[7], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - true); - - assert(gossmap_refresh(gossmap, NULL)); - struct uncertainty *uncertainty = uncertainty_new(tmpctx); - int skipped_count = - uncertainty_update(uncertainty, gossmap); - assert(skipped_count==0); - - bitmap *disabled = tal_arrz( - tmpctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - assert(disabled); - - char *errmsg; - struct flow **flows; - flows = - minflow(tmpctx, gossmap, gossmap_find_node(gossmap, &nodes[0]), - gossmap_find_node(gossmap, &nodes[7]), - uncertainty->chan_extra_map, disabled, - /* Half the capacity */ - AMOUNT_MSAT(100 * 1000 * 1000), - /* max_fee = */ AMOUNT_MSAT(20 * 1000 * 1000), - /* min probability = */ 0.9, - /* base probability = */ 1.0, - /* delay fee factor = */ 1e-6, - /* base fee penalty */ 10, - /* prob cost factor = */ 10, &errmsg); - - if (!flows) { - printf("Minflow has failed with: %s\n", errmsg); - assert(flows); - } - - if(flows) - printf("%s\n", print_flows(tmpctx, "Simple minflow", gossmap, - uncertainty->chan_extra_map, flows)); - - struct preimage preimage; - - struct amount_msat maxfee = AMOUNT_MSAT(20*1000*1000); - struct payment_info pinfo; - pinfo.invstr = NULL; - pinfo.label = NULL; - pinfo.description = NULL; - pinfo.payment_secret = NULL; - pinfo.payment_metadata = NULL; - pinfo.routehints = NULL; - pinfo.destination = nodes[7]; - pinfo.amount = AMOUNT_MSAT(100 * 1000 * 1000); - - assert(amount_msat_add(&pinfo.maxspend, maxfee, pinfo.amount)); - pinfo.maxdelay = 100; - pinfo.final_cltv = 5; - - pinfo.start_time = time_now(); - pinfo.stop_time = timeabs_add(pinfo.start_time, time_from_sec(10000)); - - pinfo.base_fee_penalty = 1e-5; - pinfo.prob_cost_factor = 1e-5; - pinfo.delay_feefactor = 1e-6; - pinfo.min_prob_success = 0.9; - pinfo.base_prob_success = 1.0; - pinfo.use_shadow = false; - - randombytes_buf(&preimage, sizeof(preimage)); - sha256(&pinfo.payment_hash, &preimage, sizeof(preimage)); - - // char hex_preimage[600], hex_sha256[600]; - // assert(hex_encode(preimage.r, sizeof(preimage.r), hex_preimage, sizeof(hex_preimage))); - // assert(hex_encode(pinfo.payment_hash.u.u8, sizeof(pinfo.payment_hash), hex_sha256, sizeof(hex_sha256))); - // printf("preimage: %s\npayment_hash: %s\n", hex_preimage, hex_sha256); - - struct disabledmap *disabledmap = disabledmap_new(tmpctx); - - enum jsonrpc_errcode errcode; - const char *err_msg; - - u64 groupid = 1; - u64 next_partid=1; - - struct route **routes = get_routes( - /* ctx */tmpctx, - /* payment */&pinfo, - /* source */&nodes[0], - /* destination */&nodes[7], - /* gossmap */gossmap, - /* uncertainty */uncertainty, - disabledmap, - /* amount */ pinfo.amount, - /* feebudget */maxfee, - &next_partid, - groupid, - &errcode, - &err_msg); - - if (!routes) { - printf("get_route failed with error %d: %s\n", errcode, err_msg); - assert(routes); - } - if(routes) - printf("get_routes: %s\n", print_routes(tmpctx, routes)); - - common_shutdown(); -} diff --git a/plugins/renepay/test/run-dijkstra.c b/plugins/renepay/test/run-dijkstra.c deleted file mode 100644 index 49e7a364f53b..000000000000 --- a/plugins/renepay/test/run-dijkstra.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static void insertion_in_increasing_distance(const tal_t *ctx) -{ - struct dijkstra *dijkstra = dijkstra_new(ctx,10); - - for(int i=0;i -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for disabledmap_add_channel */ -void disabledmap_add_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_add_channel called!\n"); abort(); } -/* Generated stub for disabledmap_add_node */ -void disabledmap_add_node(struct disabledmap *p UNNEEDED, struct node_id node UNNEEDED) -{ fprintf(stderr, "disabledmap_add_node called!\n"); abort(); } -/* Generated stub for disabledmap_channel_is_warned */ -bool disabledmap_channel_is_warned(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_channel_is_warned called!\n"); abort(); } -/* Generated stub for disabledmap_new */ -struct disabledmap *disabledmap_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "disabledmap_new called!\n"); abort(); } -/* Generated stub for disabledmap_reset */ -void disabledmap_reset(struct disabledmap *p UNNEEDED) -{ fprintf(stderr, "disabledmap_reset called!\n"); abort(); } -/* Generated stub for disabledmap_warn_channel */ -void disabledmap_warn_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_warn_channel called!\n"); abort(); } -/* Generated stub for json_add_payment */ -void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED) -{ fprintf(stderr, "json_add_payment called!\n"); abort(); } -/* Generated stub for new_routetracker */ -struct routetracker *new_routetracker(const tal_t *ctx UNNEEDED, struct payment *payment UNNEEDED) -{ fprintf(stderr, "new_routetracker called!\n"); abort(); } -/* Generated stub for pay_plugin */ -struct pay_plugin *pay_plugin; -/* Generated stub for routetracker_cleanup */ -void routetracker_cleanup(struct routetracker *routetracker UNNEEDED) -{ fprintf(stderr, "routetracker_cleanup called!\n"); abort(); } -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static u8 empty_map[] = { - 0 -}; - -static const char* print_flows( - const tal_t *ctx, - const char *desc, - const struct gossmap *gossmap, - struct chan_extra_map* chan_extra_map, - struct flow **flows) -{ - tal_t *this_ctx = tal(ctx,tal_t); - double tot_prob = - flowset_probability(tmpctx, flows, gossmap, chan_extra_map, false, NULL); - assert(tot_prob>=0); - char *buff = tal_fmt(ctx,"%s: %zu subflows, prob %2lf\n", desc, tal_count(flows),tot_prob); - for (size_t i = 0; i < tal_count(flows); i++) { - struct amount_msat fee, delivered; - tal_append_fmt(&buff," "); - for (size_t j = 0; j < tal_count(flows[i]->path); j++) { - struct short_channel_id scid - = gossmap_chan_scid(gossmap, - flows[i]->path[j]); - tal_append_fmt(&buff,"%s%s", j ? "->" : "", - fmt_short_channel_id(this_ctx, scid)); - } - delivered = flows[i]->amount; - if (!flow_fee(&fee, flows[i])) - { - abort(); - } - tal_append_fmt(&buff," prob %.2f, %s delivered with fee %s\n", - flows[i]->success_prob, - fmt_amount_msat(this_ctx, delivered), - fmt_amount_msat(this_ctx, fee)); - } - - tal_free(this_ctx); - return buff; -} - -static void remove_file(char *fname) { assert(!remove(fname)); } - -int main(int argc, char *argv[]) -{ - int fd; - char *gossfile; - struct gossmap *gossmap; - struct node_id l1, l2, l3, l4; - struct short_channel_id scid12, scid13, scid24, scid34; - struct gossmap_localmods *mods; - struct chan_extra_map *chan_extra_map; - - char *errmsg; - - common_setup(argv[0]); - - fd = tmpdir_mkstemp(tmpctx, "run-not_mcf-diamond.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - assert(write_all(fd, empty_map, sizeof(empty_map))); - - gossmap = gossmap_load(tmpctx, gossfile, NULL); - assert(gossmap); - - /* These are in ascending order, for easy direction setting */ - assert(node_id_from_hexstr("022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", 66, &l1)); - assert(node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", 66, &l2)); - assert(node_id_from_hexstr("035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", 66, &l3)); - assert(node_id_from_hexstr("0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199", 66, &l4)); - assert(short_channel_id_from_str("1x2x0", 5, &scid12)); - assert(short_channel_id_from_str("1x3x0", 5, &scid13)); - assert(short_channel_id_from_str("2x4x0", 5, &scid24)); - assert(short_channel_id_from_str("3x4x0", 5, &scid34)); - - mods = gossmap_localmods_new(tmpctx); - - /* 1->2->4 has capacity 10k sat, 1->3->4 has capacity 5k sat (lower fee!) */ - assert(gossmap_local_addchan(mods, &l1, &l2, scid12, NULL)); - assert(gossmap_local_updatechan(mods, scid12, - /*htlc_min=*/ AMOUNT_MSAT(0), - /*htlc_max=*/ AMOUNT_MSAT(10000000), - /*base_fee=*/ 0, - /*ppm_fee =*/ 1001, - /* delay =*/ 5, - /* enabled=*/ true, - /* dir =*/ 0)); - assert(gossmap_local_addchan(mods, &l2, &l4, scid24, NULL)); - assert(gossmap_local_updatechan(mods, scid24, - AMOUNT_MSAT(0), - AMOUNT_MSAT(10000000), - 0, 1002, 5, - true, - 0)); - assert(gossmap_local_addchan(mods, &l1, &l3, scid13, NULL)); - assert(gossmap_local_updatechan(mods, scid13, - AMOUNT_MSAT(0), - AMOUNT_MSAT(5000000), - 0, 503, 5, - true, - 0)); - assert(gossmap_local_addchan(mods, &l3, &l4, scid34, NULL)); - assert(gossmap_local_updatechan(mods, scid34, - AMOUNT_MSAT(0), - AMOUNT_MSAT(5000000), - 0, 504, 5, - true, - 0)); - - gossmap_apply_localmods(gossmap, mods); - chan_extra_map = tal(tmpctx, struct chan_extra_map); - chan_extra_map_init(chan_extra_map); - /* The local chans have no "capacity", so set them manually. */ - new_chan_extra(chan_extra_map, - scid12, - AMOUNT_MSAT(10000000)); - new_chan_extra(chan_extra_map, - scid24, - AMOUNT_MSAT(10000000)); - new_chan_extra(chan_extra_map, - scid13, - AMOUNT_MSAT(5000000)); - new_chan_extra(chan_extra_map, - scid34, - AMOUNT_MSAT(5000000)); - - bitmap *disabled = tal_arrz( - tmpctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - - struct flow **flows; - flows = minflow(tmpctx, gossmap, - gossmap_find_node(gossmap, &l1), - gossmap_find_node(gossmap, &l4), - chan_extra_map, - disabled, - /* Half the capacity */ - AMOUNT_MSAT(1000000), // 1000 sats - /* max_fee = */ AMOUNT_MSAT(10000), // 10 sats - /* min probability = */ 0.8, // 80% - /* base probability = */ 1.0, - /* delay fee factor = */ 0, - /* base fee penalty */ 0, - /* prob cost factor = */ 1, - &errmsg); - if (!flows) { - printf("Minflow has failed with: %s", errmsg); - assert(0 && "minflow failed"); - } - - printf("%s\n", - print_flows(tmpctx,"Simple minflow", gossmap,chan_extra_map, flows)); - - common_shutdown(); -} diff --git a/plugins/renepay/test/run-mcf.c b/plugins/renepay/test/run-mcf.c deleted file mode 100644 index 79c291fe92c4..000000000000 --- a/plugins/renepay/test/run-mcf.c +++ /dev/null @@ -1,607 +0,0 @@ -#include "config.h" - -#define RENEPAY_UNITTEST // logs are written in /tmp/debug.txt -#include "../payment.c" -#include "../flow.c" -#include "../route.c" -#include "../uncertainty.c" -#include "../mcf.c" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for disabledmap_add_channel */ -void disabledmap_add_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_add_channel called!\n"); abort(); } -/* Generated stub for disabledmap_add_node */ -void disabledmap_add_node(struct disabledmap *p UNNEEDED, struct node_id node UNNEEDED) -{ fprintf(stderr, "disabledmap_add_node called!\n"); abort(); } -/* Generated stub for disabledmap_channel_is_warned */ -bool disabledmap_channel_is_warned(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_channel_is_warned called!\n"); abort(); } -/* Generated stub for disabledmap_new */ -struct disabledmap *disabledmap_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "disabledmap_new called!\n"); abort(); } -/* Generated stub for disabledmap_reset */ -void disabledmap_reset(struct disabledmap *p UNNEEDED) -{ fprintf(stderr, "disabledmap_reset called!\n"); abort(); } -/* Generated stub for disabledmap_warn_channel */ -void disabledmap_warn_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_warn_channel called!\n"); abort(); } -/* Generated stub for json_add_payment */ -void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED) -{ fprintf(stderr, "json_add_payment called!\n"); abort(); } -/* Generated stub for new_routetracker */ -struct routetracker *new_routetracker(const tal_t *ctx UNNEEDED, struct payment *payment UNNEEDED) -{ fprintf(stderr, "new_routetracker called!\n"); abort(); } -/* Generated stub for pay_plugin */ -struct pay_plugin *pay_plugin; -/* Generated stub for routetracker_cleanup */ -void routetracker_cleanup(struct routetracker *routetracker UNNEEDED) -{ fprintf(stderr, "routetracker_cleanup called!\n"); abort(); } -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static void swap(int *a, int *b) -{ - int temp = *a; - *a = *b; - *b = temp; -} - -/* Canned gossmap, taken from tests/test_gossip.py, with channels mined. - * $> od -tx1 -Anone -v < /tmp/ltests-rtchpzh1/test_gossip_store_compact_noappend_1/lightning-2/regtest/gossip_store | sed 's/ / 0x/g'| cut -c2- | sed -e 's/ /,/g' -e 's/$/,/' - */ -static u8 canned_map[] = { - 0x0c,0x80,0x00,0x01,0xbc,0x09,0x8b,0x67,0xe6,0x00,0x00,0x00,0x00,0x10,0x08,0x00, - 0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x01,0xb0,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x22,0x6e, - 0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f, - 0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67, - 0x00,0x00,0x01,0x00,0x00,0x02,0x2d,0x22,0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7, - 0xac,0x44,0x7c,0x85,0xc4,0x6c,0x92,0x3d,0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1, - 0x1c,0x1e,0x3c,0xa3,0x1d,0x59,0x03,0x5d,0x2b,0x11,0x92,0xdf,0xba,0x13,0x4e,0x10, - 0xe5,0x40,0x87,0x5d,0x36,0x6e,0xbc,0x8b,0xc3,0x53,0xd5,0xaa,0x76,0x6b,0x80,0xc0, - 0x90,0xb3,0x9c,0x3a,0x5d,0x88,0x5d,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64,0x40, - 0x99,0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19,0xff, - 0x9c,0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64, - 0x40,0x99,0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19, - 0xff,0x9c,0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x00,0x00,0x01,0xb0,0x6e,0x30,0x94, - 0x60,0x65,0x55,0xb3,0x60,0x01,0x00,0x00,0x33,0x57,0x33,0xf5,0x94,0x2d,0xf5,0xd9, - 0x50,0xeb,0x87,0x66,0xde,0xe3,0xa9,0xd6,0x62,0x69,0x22,0x84,0x4e,0xd6,0xae,0x7a, - 0x11,0x0d,0xd7,0xe7,0xed,0xc3,0x2e,0x3f,0x6f,0x3d,0x9a,0xc5,0xcd,0xea,0x23,0xce, - 0x25,0xbb,0x8d,0xbf,0x76,0x1f,0xd3,0xd5,0xfc,0x56,0xc0,0x5b,0x68,0x56,0x31,0x6d, - 0x12,0xe9,0xd3,0x2c,0xa0,0xf0,0x8c,0x69,0xca,0x03,0x06,0xfe,0x71,0x6e,0x7b,0x51, - 0x51,0x31,0x7d,0x64,0x40,0xb7,0x37,0x3d,0x9f,0xbc,0x64,0x6e,0xad,0x48,0xf2,0x16, - 0x3f,0x2b,0x6d,0x51,0x1a,0xfe,0x6a,0x79,0xc7,0x55,0x51,0xc2,0x62,0x0f,0xc8,0x09, - 0x74,0xf2,0xf8,0x64,0x32,0x9d,0x97,0x78,0xa0,0x8c,0xdf,0xbc,0x9f,0x2c,0x9c,0x13, - 0x44,0xc4,0x32,0x70,0x2c,0x66,0x80,0x7c,0xfb,0x4d,0xb6,0x9b,0x80,0xfa,0xe8,0xc3, - 0x3c,0x70,0x14,0x3d,0x94,0x8b,0x36,0x61,0x4d,0x62,0x08,0x91,0xbe,0xe2,0xdf,0x99, - 0xc8,0x6b,0xc6,0x22,0x07,0xc1,0x7e,0x3b,0x91,0x86,0x21,0x4c,0x0c,0xcf,0xf2,0xde, - 0xd5,0x59,0x8a,0xcc,0xc9,0x0e,0xb1,0xd5,0xb2,0xf7,0xa8,0x3c,0xd7,0xf6,0x8d,0x71, - 0x2e,0xa0,0x47,0xd8,0x01,0x9f,0x34,0x30,0x63,0xb0,0xa2,0x36,0x35,0x6a,0x38,0x71, - 0x46,0xf5,0x8f,0xa8,0x32,0xdd,0xc1,0x3c,0x47,0x14,0x52,0x2c,0xbb,0x50,0x3f,0x5f, - 0x3c,0xa8,0xfc,0xec,0x66,0x02,0xbe,0x24,0x38,0xad,0x3f,0x98,0xfa,0x0c,0xee,0xd5, - 0x8f,0xe3,0xa0,0x66,0xd3,0x85,0xfc,0xac,0xd9,0x8c,0x70,0x4b,0x2a,0x8e,0x98,0xa3, - 0xe2,0x0b,0xf7,0x6b,0x35,0xb7,0x36,0x00,0x00,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00, - 0x00,0x02,0x2d,0x22,0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44,0x7c,0x85, - 0xc4,0x6c,0x92,0x3d,0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e,0x3c,0xa3, - 0x1d,0x59,0x03,0x5d,0x2b,0x11,0x92,0xdf,0xba,0x13,0x4e,0x10,0xe5,0x40,0x87,0x5d, - 0x36,0x6e,0xbc,0x8b,0xc3,0x53,0xd5,0xaa,0x76,0x6b,0x80,0xc0,0x90,0xb3,0x9c,0x3a, - 0x5d,0x88,0x5d,0x02,0x90,0x53,0x52,0x1d,0x6e,0xa7,0xa5,0x2c,0xdd,0x55,0xf7,0x33, - 0xd0,0xfb,0x2d,0x07,0x7c,0x03,0x73,0xb0,0x05,0x3b,0x5b,0x81,0x0d,0x92,0x72,0x44, - 0x06,0x1b,0x75,0x73,0x02,0xd6,0x06,0x3d,0x02,0x26,0x91,0xb2,0x49,0x0a,0xb4,0x54, - 0xde,0xe7,0x3a,0x57,0xc6,0xff,0x5d,0x30,0x83,0x52,0xb4,0x61,0xec,0xe6,0x9f,0x3c, - 0x28,0x4f,0x2c,0x24,0x12,0x00,0x00,0x00,0x0a,0x91,0x11,0x83,0xf6,0x00,0x00,0x00, - 0x00,0x10,0x05,0x00,0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x80,0x00,0x00,0x8a,0xc5, - 0x33,0xff,0x38,0x65,0x55,0xb3,0x60,0x01,0x02,0x2f,0xd9,0x23,0x60,0x1e,0x1c,0xa0, - 0xac,0xe5,0x06,0x8c,0xe4,0x8e,0x14,0xf3,0xcd,0x31,0x44,0x16,0xc4,0x0d,0x2e,0x14, - 0x8c,0xa1,0xc8,0x4f,0xa6,0xa8,0xe4,0x64,0x9b,0x45,0x79,0xd1,0xb5,0x2f,0x04,0x19, - 0x86,0xe5,0x5c,0x99,0x43,0xf1,0xd0,0xf3,0x6f,0x52,0xd6,0x88,0xf0,0x9b,0x9c,0x58, - 0x98,0x69,0x0d,0x4e,0x76,0x3e,0xbd,0x6e,0x95,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00, - 0x00,0x65,0x55,0xb3,0x60,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b,0x02,0x33, - 0x80,0x00,0x00,0x00,0x95,0x97,0xd7,0xa4,0x39,0x65,0x55,0xb3,0x65,0x01,0x01,0x36, - 0x59,0x20,0x77,0x0e,0xf4,0x73,0x10,0xd7,0xb6,0x59,0x5a,0x7c,0xbe,0xd0,0x56,0x51, - 0x3d,0x97,0xbe,0x84,0xb7,0x02,0xb5,0x89,0x72,0xbd,0xb3,0x19,0x2a,0x54,0x5f,0x57, - 0x52,0x09,0x1d,0xff,0x0f,0xe6,0x70,0x1e,0x71,0x23,0xaa,0x3f,0x98,0x89,0x86,0x35, - 0x87,0xc2,0x66,0xd9,0x99,0xbd,0xa2,0x7e,0x16,0x12,0xd9,0x6b,0xc4,0xd3,0x0c,0x00, - 0x07,0x88,0xa0,0x00,0x0a,0x0a,0x69,0xa2,0x65,0x55,0xb3,0x65,0x02,0x2d,0x22,0x36, - 0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44,0x7c,0x85,0xc4,0x6c,0x92,0x3d,0xa5, - 0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e,0x3c,0xa3,0x1d,0x59,0x02,0x2d,0x22, - 0x53,0x49,0x4c,0x45,0x4e,0x54,0x41,0x52,0x54,0x49,0x53,0x54,0x2d,0x31,0x2d,0x31, - 0x2d,0x67,0x63,0x63,0x65,0x34,0x66,0x36,0x38,0x2d,0x6d,0x6f,0x64,0x64,0x65,0x64, - 0x00,0x00,0x80,0x00,0x00,0x8a,0xd0,0xde,0x20,0xc6,0x65,0x55,0xb3,0x60,0x01,0x02, - 0x11,0xd6,0x4b,0xc5,0x88,0x33,0x4b,0x6c,0x1e,0x0c,0x4c,0x5c,0x65,0x69,0x35,0x66, - 0x45,0x80,0xbc,0x4b,0x56,0x3d,0x98,0x2c,0xb3,0x45,0xef,0x22,0x44,0x6e,0xca,0x6c, - 0x1f,0xe9,0x2a,0xf2,0x4a,0xe1,0x26,0xdb,0x15,0x6f,0x7e,0x3b,0xdd,0xd3,0x40,0x54, - 0xb2,0xc7,0x07,0xfe,0x67,0xb0,0xf3,0x35,0x56,0x25,0xc6,0x53,0xfd,0x54,0x56,0x5e, - 0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf, - 0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f, - 0x00,0x00,0x67,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x60,0x01,0x01,0x00,0x06, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a, - 0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x00,0x95,0x87,0xf8,0x92,0xf3, - 0x65,0x55,0xb3,0x65,0x01,0x01,0x32,0x6c,0xde,0x4a,0xc5,0xe8,0xd4,0xa0,0xed,0xbc, - 0x2c,0x13,0x5e,0xb9,0x1e,0xc3,0xc3,0x86,0xcb,0x75,0xeb,0x6f,0xce,0xc5,0xc5,0x57, - 0x01,0x1c,0x9c,0xb7,0x32,0x17,0x01,0x10,0x8c,0xdd,0x04,0x31,0x78,0xae,0xb4,0x88, - 0x8d,0xf8,0xe8,0x35,0x90,0x69,0x91,0x84,0xd0,0x16,0xd8,0x44,0xbc,0xde,0x37,0xe8, - 0x4e,0x1a,0x95,0xb6,0xa6,0x73,0x00,0x07,0x88,0xa0,0x00,0x0a,0x0a,0x69,0xa2,0x65, - 0x55,0xb3,0x65,0x03,0x5d,0x2b,0x11,0x92,0xdf,0xba,0x13,0x4e,0x10,0xe5,0x40,0x87, - 0x5d,0x36,0x6e,0xbc,0x8b,0xc3,0x53,0xd5,0xaa,0x76,0x6b,0x80,0xc0,0x90,0xb3,0x9c, - 0x3a,0x5d,0x88,0x5d,0x03,0x5d,0x2b,0x48,0x4f,0x50,0x50,0x49,0x4e,0x47,0x46,0x49, - 0x52,0x45,0x2d,0x63,0x31,0x2d,0x31,0x2d,0x67,0x63,0x63,0x65,0x34,0x66,0x36,0x38, - 0x2d,0x6d,0x6f,0x64,0x64,0x65,0x64,0x00,0x00,0x00,0x00,0x00,0x8a,0xcb,0xd4,0xc7, - 0xa1,0x65,0x55,0xb3,0x65,0x01,0x02,0x1d,0x3f,0x42,0x7f,0x3d,0xdb,0x58,0x2b,0xcb, - 0x78,0x5d,0x24,0xf1,0x67,0xc6,0xc7,0xd8,0x6e,0x6c,0x5b,0xf8,0xfb,0x27,0x17,0x58, - 0xaa,0x7e,0x46,0x86,0x49,0x66,0x21,0x47,0x76,0xbd,0xf2,0x2d,0xae,0x29,0xf0,0x6f, - 0x17,0x6e,0xf2,0x7f,0x01,0xda,0x16,0xa9,0x1d,0x6a,0x61,0x4c,0x41,0x71,0x19,0x4d, - 0x37,0xac,0x8a,0x28,0xd8,0x62,0xfb,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca, - 0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7, - 0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00,0x00,0x65, - 0x55,0xb3,0x65,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x80, - 0x00,0x01,0xbc,0x39,0xdb,0xc7,0xdf,0x00,0x00,0x00,0x00,0x10,0x08,0x00,0x00,0x00, - 0x00,0x00,0x0f,0x42,0x40,0x01,0xb0,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x22,0x6e,0x46,0x11, - 0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e, - 0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00, - 0x01,0x00,0x00,0x02,0x2d,0x22,0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44, - 0x7c,0x85,0xc4,0x6c,0x92,0x3d,0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e, - 0x3c,0xa3,0x1d,0x59,0x02,0x66,0xe4,0x59,0x8d,0x1d,0x3c,0x41,0x5f,0x57,0x2a,0x84, - 0x88,0x83,0x0b,0x60,0xf7,0xe7,0x44,0xed,0x92,0x35,0xeb,0x0b,0x1b,0xa9,0x32,0x83, - 0xb3,0x15,0xc0,0x35,0x18,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64,0x40,0x99,0x5d, - 0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19,0xff,0x9c,0x17, - 0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64,0x40,0x99, - 0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19,0xff,0x9c, - 0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x80,0x00,0x00,0x8e,0x85,0x67,0x9b,0xcf,0x00, - 0x00,0x00,0x00,0x10,0x06,0x00,0x8a,0x01,0x02,0x27,0x9e,0x4e,0x43,0x39,0xa6,0x92, - 0x19,0x35,0x50,0x19,0xbb,0x51,0x5b,0xf9,0xac,0xdb,0xda,0x9c,0xde,0x81,0x8b,0x56, - 0x2e,0x0a,0x3d,0xd5,0xb0,0x5f,0x10,0x2e,0x6c,0x22,0x55,0x7e,0x07,0xc2,0x5f,0x4b, - 0x9c,0xc1,0x21,0x6e,0x07,0x66,0x41,0x60,0xde,0x3e,0xe2,0x24,0xa5,0x9e,0xec,0xaf, - 0xd7,0xcc,0x3f,0x87,0x7c,0x32,0x29,0xca,0xe7,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00, - 0x00,0x65,0x55,0xb3,0x6c,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b,0x02,0x33, - 0x80,0x80,0x00,0x00,0x8e,0xca,0xa4,0x8e,0x70,0x00,0x00,0x00,0x00,0x10,0x06,0x00, - 0x8a,0x01,0x02,0x4e,0x3f,0x3b,0x6d,0xda,0xcc,0xd3,0xef,0x5f,0xaf,0x26,0x76,0x16, - 0x64,0xa2,0x82,0x97,0xe8,0xb4,0xe4,0xb1,0x2d,0xec,0xa1,0x9e,0x91,0x69,0xd3,0xde, - 0xe9,0x58,0xc7,0x19,0x06,0x90,0x42,0x86,0x97,0xf3,0x88,0xca,0x35,0xd5,0xec,0x79, - 0x5e,0x59,0x33,0x31,0xf4,0x0c,0xdb,0x55,0x5d,0x78,0xd7,0x22,0x59,0xa2,0xe5,0x8d, - 0xeb,0x65,0x41,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43, - 0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1, - 0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x6c,0x01, - 0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00, - 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x00,0x8a,0xbf, - 0x0d,0x7a,0xbc,0x65,0x55,0xb3,0x6f,0x01,0x02,0x59,0xc9,0x36,0x84,0xaa,0x13,0x70, - 0x73,0x7d,0xa7,0xe6,0x8a,0x72,0x05,0x15,0xa9,0x06,0x69,0xe0,0xc0,0x0d,0x15,0x6d, - 0x22,0x5c,0xd6,0x1a,0x3e,0x56,0xaa,0x9d,0x98,0x68,0x5a,0x3c,0xdc,0x38,0x71,0xee, - 0x0d,0x02,0x3e,0x2f,0xd0,0x97,0xd7,0xab,0xe2,0x0d,0xdf,0xf5,0xa6,0xd1,0x9c,0xb6, - 0xb9,0x30,0xd6,0xdd,0x10,0xa7,0xa3,0xce,0x5e,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00, - 0x00,0x65,0x55,0xb3,0x6f,0x01,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33, - 0x80,0x80,0x00,0x00,0x8e,0x80,0x00,0x75,0xd3,0x00,0x00,0x00,0x00,0x10,0x06,0x00, - 0x8a,0x01,0x02,0x26,0x62,0x25,0x68,0x2d,0x27,0x06,0x9a,0x3b,0xaa,0x1a,0x43,0x93, - 0xac,0xb5,0x05,0x14,0x1c,0x09,0x22,0x8d,0xfb,0x54,0x43,0x73,0x3a,0x88,0xaa,0xa3, - 0x18,0xc8,0xd8,0x0d,0x36,0xd4,0x26,0x10,0xe9,0x82,0xa6,0x3c,0xb4,0x38,0x24,0xaf, - 0xdc,0x06,0xd5,0x3d,0xe3,0x7a,0xe8,0x39,0xdd,0xd4,0x5f,0xd8,0x92,0xf5,0x4b,0xe7, - 0x8d,0xf7,0xab,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43, - 0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1, - 0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x71,0x01, - 0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00, - 0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x80,0x00,0x00,0x8e,0xdc, - 0x5e,0x38,0x7a,0x00,0x00,0x00,0x00,0x10,0x06,0x00,0x8a,0x01,0x02,0x5d,0xf9,0x14, - 0xa0,0xd1,0x46,0x69,0x9a,0x2a,0x97,0xf7,0xb3,0x83,0xf0,0x30,0x73,0x48,0x5e,0x7b, - 0x3e,0x6c,0x45,0xd3,0xf1,0x51,0xf8,0xbc,0x4b,0xd7,0xae,0x02,0xd9,0x57,0x51,0x73, - 0x8f,0x0f,0xd0,0xdd,0x38,0x36,0x48,0xcd,0xda,0xea,0x01,0x24,0x16,0x3d,0x14,0x7e, - 0x5a,0x9e,0xac,0xcd,0x81,0xd1,0x96,0x30,0xd9,0xbc,0xa6,0xe4,0xe8,0x06,0x22,0x6e, - 0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f, - 0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e, - 0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x71,0x01,0x00,0x00,0x06,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00, - 0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x01,0xb0,0xb8,0x17,0x71,0xe9,0x65,0x55,0xb3, - 0x71,0x01,0x00,0x0d,0xef,0xc0,0x12,0x4f,0xd4,0xe4,0x1c,0xce,0xc3,0xf4,0x83,0x17, - 0x21,0x7e,0x33,0x11,0x5a,0x8f,0x8c,0x39,0xae,0xbb,0x06,0x2b,0x4e,0x73,0xa6,0x52, - 0xee,0x0e,0x61,0x03,0xf9,0xf1,0x16,0x7b,0x22,0xbd,0xcb,0x92,0xcf,0x82,0x2d,0x0f, - 0xcc,0x26,0xcb,0x11,0x9f,0x0a,0xf5,0x23,0xf4,0x26,0x97,0x86,0x7e,0x21,0x51,0x85, - 0x16,0x3a,0x0e,0x4b,0x1f,0x64,0xf3,0x62,0xf4,0x2f,0x13,0x54,0x5c,0x9a,0xed,0xd6, - 0x9d,0x5d,0xf5,0x17,0x85,0xcc,0x5d,0x53,0xa4,0x84,0x36,0x9b,0xe9,0xa7,0x4a,0x04, - 0x1f,0x8c,0x1c,0x28,0xc7,0x1e,0xab,0xb3,0xf2,0x53,0x3d,0xf8,0xb6,0xcd,0xd8,0x45, - 0x6e,0xc3,0x77,0xdb,0xb1,0x2b,0xa5,0xdd,0xc9,0xbf,0x69,0xa7,0xdf,0x98,0x2c,0x54, - 0xa7,0xa8,0xf5,0x4f,0xc3,0xd1,0x57,0x81,0x98,0x60,0xa0,0xb6,0x32,0x83,0x08,0x65, - 0x1e,0x79,0x24,0xf4,0xbe,0xfe,0x14,0xa6,0xfd,0x31,0x9b,0xfa,0x15,0x6f,0x1b,0xab, - 0x64,0x8b,0x69,0x48,0xb4,0x2e,0x99,0xca,0xd7,0x46,0x97,0x57,0x17,0x1d,0x5d,0x53, - 0xe5,0xde,0xae,0xbb,0x2d,0x74,0x86,0xa5,0xe5,0x8e,0x02,0x5c,0x2c,0x8d,0x0d,0x78, - 0xdc,0xf1,0xa0,0x05,0xb8,0xbd,0x64,0x7d,0x51,0x47,0x5e,0x39,0xaa,0x08,0x2d,0xf2, - 0xd0,0x1a,0x96,0x59,0xab,0x88,0x94,0xa2,0xf7,0x2e,0x09,0x3b,0xd0,0xed,0xfc,0xb4, - 0x1b,0xaa,0xba,0x34,0xf8,0x7b,0x11,0x51,0x36,0x7b,0x13,0x84,0x6f,0xc9,0x4a,0x58, - 0x92,0x3c,0x0c,0xa2,0x4c,0x8f,0xc5,0x78,0xb4,0x4a,0xad,0x98,0x08,0x4e,0x4a,0xf3, - 0x7a,0x28,0x80,0x00,0x00,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12, - 0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7, - 0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x02,0x2d,0x22, - 0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44,0x7c,0x85,0xc4,0x6c,0x92,0x3d, - 0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e,0x3c,0xa3,0x1d,0x59,0x02,0x66, - 0xe4,0x59,0x8d,0x1d,0x3c,0x41,0x5f,0x57,0x2a,0x84,0x88,0x83,0x0b,0x60,0xf7,0xe7, - 0x44,0xed,0x92,0x35,0xeb,0x0b,0x1b,0xa9,0x32,0x83,0xb3,0x15,0xc0,0x35,0x18,0x03, - 0xbb,0xee,0x60,0xc3,0x95,0x05,0x6b,0x8a,0x12,0x01,0xe0,0x6e,0xd7,0x9e,0x29,0x14, - 0xc1,0x1a,0x61,0xd7,0xb1,0xaa,0x78,0x18,0x46,0x46,0x8d,0x02,0x48,0x9d,0xba,0x69, - 0x02,0x32,0x42,0x66,0xde,0x84,0x03,0xb3,0xab,0x15,0x7a,0x09,0xf1,0xf7,0x84,0xd5, - 0x87,0xaf,0x61,0x83,0x1c,0x99,0x8c,0x15,0x1b,0xcc,0x21,0xbb,0x74,0xc2,0xb2,0x31, - 0x4b,0x00,0x00,0x00,0x0a,0x91,0x11,0x83,0xf6,0x00,0x00,0x00,0x00,0x10,0x05,0x00, - 0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x00,0x00,0x00,0x8a,0xe1,0x31,0x6b,0x76,0x65, - 0x55,0xb3,0x71,0x01,0x02,0x5d,0xf9,0x14,0xa0,0xd1,0x46,0x69,0x9a,0x2a,0x97,0xf7, - 0xb3,0x83,0xf0,0x30,0x73,0x48,0x5e,0x7b,0x3e,0x6c,0x45,0xd3,0xf1,0x51,0xf8,0xbc, - 0x4b,0xd7,0xae,0x02,0xd9,0x57,0x51,0x73,0x8f,0x0f,0xd0,0xdd,0x38,0x36,0x48,0xcd, - 0xda,0xea,0x01,0x24,0x16,0x3d,0x14,0x7e,0x5a,0x9e,0xac,0xcd,0x81,0xd1,0x96,0x30, - 0xd9,0xbc,0xa6,0xe4,0xe8,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12, - 0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7, - 0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3, - 0x71,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x00, - 0x8a,0xbd,0x6f,0x26,0xdf,0x65,0x55,0xb3,0x71,0x01,0x02,0x26,0x62,0x25,0x68,0x2d, - 0x27,0x06,0x9a,0x3b,0xaa,0x1a,0x43,0x93,0xac,0xb5,0x05,0x14,0x1c,0x09,0x22,0x8d, - 0xfb,0x54,0x43,0x73,0x3a,0x88,0xaa,0xa3,0x18,0xc8,0xd8,0x0d,0x36,0xd4,0x26,0x10, - 0xe9,0x82,0xa6,0x3c,0xb4,0x38,0x24,0xaf,0xdc,0x06,0xd5,0x3d,0xe3,0x7a,0xe8,0x39, - 0xdd,0xd4,0x5f,0xd8,0x92,0xf5,0x4b,0xe7,0x8d,0xf7,0xab,0x06,0x22,0x6e,0x46,0x11, - 0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e, - 0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00, - 0x01,0x00,0x00,0x65,0x55,0xb3,0x71,0x01,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b, - 0x02,0x33,0x80,0x00,0x00,0x00,0x95,0xf6,0xd7,0x24,0x69,0x65,0x55,0xb3,0x79,0x01, - 0x01,0x42,0x62,0x2b,0xf0,0x0a,0x44,0xc3,0xe0,0x78,0x3c,0xa7,0x08,0x5a,0xa0,0xaa, - 0x9d,0xb2,0xdc,0x2e,0xa7,0x29,0x0a,0x39,0x3d,0x01,0x57,0xbf,0x34,0x23,0x0f,0xb9, - 0x19,0x33,0x6f,0xc2,0xfb,0x9f,0x25,0xac,0xc4,0xc9,0x47,0xb7,0x41,0x92,0xdb,0x2f, - 0xd5,0xf0,0x1f,0x24,0x32,0xdf,0x38,0x30,0x89,0x93,0xeb,0xc9,0xec,0xf1,0x09,0x51, - 0xbb,0x00,0x07,0x88,0xa0,0x00,0x0a,0x0a,0x69,0xa2,0x65,0x55,0xb3,0x79,0x02,0x66, - 0xe4,0x59,0x8d,0x1d,0x3c,0x41,0x5f,0x57,0x2a,0x84,0x88,0x83,0x0b,0x60,0xf7,0xe7, - 0x44,0xed,0x92,0x35,0xeb,0x0b,0x1b,0xa9,0x32,0x83,0xb3,0x15,0xc0,0x35,0x18,0x02, - 0x66,0xe4,0x4a,0x55,0x4e,0x49,0x4f,0x52,0x42,0x45,0x41,0x4d,0x2d,0x72,0x63,0x31, - 0x2d,0x31,0x2d,0x67,0x63,0x63,0x65,0x34,0x66,0x36,0x38,0x2d,0x6d,0x6f,0x64,0x64, - 0x65,0x64,0x00,0x00, -}; - -/* not_mcf sets NDEBUG, so assert() is useless */ -#define ASSERT(x) do { if (!(x)) abort(); } while(0) - -static const char *print_flows( - const tal_t *ctx, - const char *desc, - const struct gossmap *gossmap, - struct flow **flows) -{ - tal_t *this_ctx = tal(ctx,tal_t); - char *buff = tal_fmt(ctx,"%s: %zu subflows\n", desc, tal_count(flows)); - for (size_t i = 0; i < tal_count(flows); i++) { - struct amount_msat fee, delivered; - tal_append_fmt(&buff," "); - for (size_t j = 0; j < tal_count(flows[i]->path); j++) { - struct short_channel_id scid - = gossmap_chan_scid(gossmap, - flows[i]->path[j]); - tal_append_fmt(&buff,"%s%s", j ? "->" : "", - fmt_short_channel_id(this_ctx, scid)); - } - delivered = flows[i]->amount; - if (!flow_fee(&fee, flows[i])) - abort(); - tal_append_fmt(&buff," prob %.2f, %s delivered with fee %s\n", - flows[i]->success_prob, - fmt_amount_msat(this_ctx, delivered), - fmt_amount_msat(this_ctx, fee)); - } - tal_free(this_ctx); - return buff; -} - -static void remove_file(char *fname) { assert(!remove(fname)); } - -int main(int argc, char *argv[]) -{ - int fd; - char *gossfile; - struct gossmap *gossmap; - struct node_id l1, l2, l3; - struct flow **flows; - struct route **routes; - struct short_channel_id scid12, scid23; - struct sha256 payment_hash; - struct amount_msat *amounts; - char *errmsg; - - if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102", - strlen("0001020304050607080900010203040506070809000102030405060708090102"), - &payment_hash, sizeof(payment_hash))) - abort(); - - common_setup(argv[0]); - - fd = tmpdir_mkstemp(tmpctx, "run-not_mcf.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - assert(write_all(fd, canned_map, sizeof(canned_map))); - - gossmap = gossmap_load(tmpctx, gossfile, NULL); - assert(gossmap); - - /* There is a public channel 2<->3 (103x1x0), and 1<->2 (110x1x1). */ - assert(node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", 66, &l1)); - assert(node_id_from_hexstr("022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", 66, &l2)); - assert(node_id_from_hexstr("035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", 66, &l3)); - assert(short_channel_id_from_str("110x1x0", 7, &scid12)); - assert(short_channel_id_from_str("103x1x0", 7, &scid23)); - - struct uncertainty *uncertainty = uncertainty_new(tmpctx); - int skipped_count = uncertainty_update(uncertainty, gossmap); - assert(skipped_count == 0); - - bitmap *disabled = tal_arrz( - tmpctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - - printf("All set, now let's call minflow ...\n"); - - flows = minflow(tmpctx, gossmap, - gossmap_find_node(gossmap, &l1), - gossmap_find_node(gossmap, &l3), - uncertainty_get_chan_extra_map(uncertainty), - disabled, - /* Half the capacity */ - AMOUNT_MSAT(500000000), - /* max_fee = */ AMOUNT_MSAT(1000000), // 1k sats - /* min probability = */ 0.1, - /* base probability = */ 1.0, - /* delay fee factor = */ 1, - /* base fee penalty */ 1, - /* prob cost factor = */ 10, - &errmsg); - printf("minflow completed.\n"); - - if (!flows) { - printf("Minflow has failed with: %s", errmsg); - assert(0 && "minflow failed"); - } - - routes = flows_to_routes(tmpctx, 1, 1, payment_hash, 10, gossmap, flows); - assert(routes); - for (size_t i = 0; i < tal_count(routes); i++) { - uncertainty_commit_htlcs(uncertainty, routes[i]); - } - - printf("%s\n", - print_flows(tmpctx,"Flow via single path l1->l2->l3", gossmap, flows)); - - - printf("Checking results.\n"); - /* Should go 1->2->3 */ - amounts = tal_flow_amounts(tmpctx, flows[0], true); - assert(amounts); - assert(tal_count(flows) == 1); - assert(tal_count(flows[0]->path) == 2); - assert(tal_count(flows[0]->dirs) == 2); - assert(tal_count(amounts) == 2); - - assert(flows[0]->path[0] == gossmap_find_chan(gossmap, &scid12)); - assert(flows[0]->path[1] == gossmap_find_chan(gossmap, &scid23)); - assert(flows[0]->dirs[0] == 1); - assert(flows[0]->dirs[1] == 0); - assert(amount_msat_eq(amounts[1], AMOUNT_MSAT(500000000))); - /* fee_base_msat == 20, fee_proportional_millionths == 1000 */ - assert(amount_msat_eq(amounts[0], AMOUNT_MSAT(500000000 + 500000 + 20))); - - /* Each one has probability ~ 0.5 */ - assert(flows[0]->success_prob > 0.249); - assert(flows[0]->success_prob <= 0.251); - - - /* Should have filled in some extra data! */ - struct chan_extra *ce = uncertainty_find_channel(uncertainty, scid12); - assert(short_channel_id_eq(ce->scid, scid12)); - /* l1->l2 dir is 1 */ - assert(ce->half[1].num_htlcs == 1); - assert(amount_msat_eq(ce->half[1].htlc_total, AMOUNT_MSAT(500000000 + 500000 + 20))); - assert(amount_msat_eq(ce->half[1].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[1].known_max, AMOUNT_MSAT(1000000000))); - assert(ce->half[0].num_htlcs == 0); - assert(amount_msat_eq(ce->half[0].htlc_total, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[0].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[0].known_max, AMOUNT_MSAT(1000000000))); - - ce = uncertainty_find_channel(uncertainty, scid23); - assert(short_channel_id_eq(ce->scid, scid23)); - /* l2->l3 dir is 0 */ - assert(ce->half[0].num_htlcs == 1); - assert(amount_msat_eq(ce->half[0].htlc_total, AMOUNT_MSAT(500000000))); - assert(amount_msat_eq(ce->half[0].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[0].known_max, AMOUNT_MSAT(1000000000))); - assert(ce->half[1].num_htlcs == 0); - assert(amount_msat_eq(ce->half[1].htlc_total, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[1].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[1].known_max, AMOUNT_MSAT(1000000000))); - - /* Clear that */ - for (size_t i = 0; i < tal_count(routes); i++) { - uncertainty_remove_htlcs(uncertainty, routes[i]); - } - - // /* Now try adding a local channel scid */ - - struct short_channel_id scid13; - struct gossmap_localmods *mods = gossmap_localmods_new(tmpctx); - assert(short_channel_id_from_str("111x1x1", 7, &scid13)); - - /* 400,000sat channel from 1->3, basefee 0, ppm 1000, delay 5 */ - assert(gossmap_local_addchan(mods, &l1, &l3, scid13, NULL)); - assert(gossmap_local_updatechan(mods, scid13, - AMOUNT_MSAT(0), - AMOUNT_MSAT(400000000), - 0, 1000, 5, - true, - 0)); - - /* Apply changes, check they work. */ - gossmap_apply_localmods(gossmap, mods); - struct gossmap_chan *local_chan = gossmap_find_chan(gossmap, &scid13); - assert(local_chan); - - /* The local chans have no "capacity", so set it manually. */ - uncertainty_add_channel(uncertainty, scid13, AMOUNT_MSAT(400000000)); - - // flows = minflow(tmpctx, gossmap, - // gossmap_find_node(gossmap, &l1), - // gossmap_find_node(gossmap, &l3), - // chan_extra_map, NULL, - // /* This will go first via 1-2-3, then 1->3. */ - // AMOUNT_MSAT(500000000), - // /* max_fee = */ AMOUNT_MSAT(1000000), // 1k sats - // /* min probability = */ 0.4, - // /* delay fee factor = */ 1, - // /* base fee penalty */ 1, - // /* prob cost factor = */ 10); - - // print_flows("Flow via two paths, low mu", gossmap, flows); - - // assert(tal_count(flows) == 2); - // - // if(tal_count(flows[0]->path)path)) - // { - // struct flow* tmp = flows[0]; - // flows[0] = flows[1]; - // flows[1]=tmp; - // } - // - // assert(tal_count(flows[0]->path) == 2); - // assert(tal_count(flows[0]->dirs) == 2); - // assert(tal_count(flows[0]->amounts) == 2); - - // assert(flows[0]->path[0] == gossmap_find_chan(gossmap, &scid12)); - // assert(flows[0]->path[1] == gossmap_find_chan(gossmap, &scid23)); - // assert(flows[0]->dirs[0] == 1); - // assert(flows[0]->dirs[1] == 0); - - // /* First one has probability ~ 50% */ - // assert(flows[0]->success_prob < 0.55); - // assert(flows[0]->success_prob > 0.45); - - // assert(tal_count(flows[1]->path) == 1); - // assert(tal_count(flows[1]->dirs) == 1); - // assert(tal_count(flows[1]->amounts) == 1); - - // /* We will try cheaper path first, but not to fill it! */ - // assert(flows[1]->path[0] == gossmap_find_chan(gossmap, &scid13)); - // assert(flows[1]->dirs[0] == 0); - // assert(amount_msat_less(flows[1]->amounts[0], AMOUNT_MSAT(400000000))); - - // /* Second one has probability ~ 50% */ - // assert(flows[1]->success_prob < 0.55); - // assert(flows[1]->success_prob > 0.45); - - // /* Delivered amount must be the total! */ - // assert(flows[0]->amounts[1].millisatoshis - // + flows[1]->amounts[0].millisatoshis == 500000000); - - // /* Clear them. */ - // remove_completed_flow(gossmap, chan_extra_map, flows[0]); - // remove_completed_flow(gossmap, chan_extra_map, flows[1]); - - printf("All set, let's call minflow ... \n"); - - /* Higher mu values mean we pay more for certainty! */ - struct flow **flows2 = minflow(tmpctx, gossmap, - gossmap_find_node(gossmap, &l1), - gossmap_find_node(gossmap, &l3), - uncertainty_get_chan_extra_map(uncertainty), - disabled, - /* This will go 400000000 via 1->3, rest via 1-2-3. */ - /* amount = */ AMOUNT_MSAT(500000000), //500k sats - /* max_fee = */ AMOUNT_MSAT(1000000), // 1k sats - /* min probability = */ 0.1, // 10% - /* base probability = */ 1.0, - /* delay fee factor = */ 1, - /* base fee penalty */ 1, - /* prob cost factor = */ 10, - &errmsg); - - printf("minflow completed execution.\n"); - - if (!flows) { - printf("Minflow has failed with: %s", errmsg); - assert(0 && "minflow failed"); - } - printf("%s\n", - print_flows(tmpctx,"Flow via two paths, high mu", gossmap, flows2)); - - printf("Verifying results ... \n"); - - assert(tal_count(flows2) == 2); - - /* The solution is composed by two paths, one with lenght 1 and the - * other with lenght 2. There is no guaranteed order of the solutions - * returning from minflow, hence we need to test them. */ - int ID1 = 0, ID2 = 1; - if(tal_count(flows2[ID1]->path)==2) - { - swap(&ID1,&ID2); - } - assert(tal_count(flows2[ID1]->path) == 1); - assert(tal_count(flows2[ID2]->path) == 2); - - // /* Sends more via 1->3, since it's more expensive (but lower prob) */ - assert(amount_msat_greater(flows2[ID1]->amount, flows2[ID2]->amount)); - assert(flows2[ID1]->success_prob < flows2[ID2]->success_prob); - - /* Delivered amount must be the total! */ - assert(flows2[ID1]->amount.millisatoshis - + flows2[ID2]->amount.millisatoshis == 500000000); - - common_shutdown(); -} diff --git a/plugins/renepay/test/run-missingcapacity.c b/plugins/renepay/test/run-missingcapacity.c deleted file mode 100644 index 4dc53ec4825f..000000000000 --- a/plugins/renepay/test/run-missingcapacity.c +++ /dev/null @@ -1,179 +0,0 @@ -/* Checks that uncertainty_update and get_routes can handle a gossmap where the - * capacity of some channels are missing. - * - * */ -#include "config.h" - -#include "../disabledmap.c" -#include "../errorcodes.c" -#include "../flow.c" -#include "../mcf.c" -#include "../route.c" -#include "../routebuilder.c" -#include "../uncertainty.c" -#include "common.h" - -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static u8 empty_map[] = {10}; - -#define NUM_NODES 4 - -static void remove_file(char *fname) { assert(!remove(fname)); } - -int main(int argc, char *argv[]) -{ - int fd; - char *gossfile; - struct gossmap *gossmap; - struct node_id nodes[NUM_NODES]; - - common_setup(argv[0]); - chainparams = chainparams_for_network("regtest"); - - fd = tmpdir_mkstemp(tmpctx, "run-missingcapacity.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - assert(write(fd, empty_map, sizeof(empty_map)) == sizeof(empty_map)); - - gossmap = gossmap_load(tmpctx, gossfile, NULL); - assert(gossmap); - - for (size_t i = 0; i < NUM_NODES; i++) { - struct privkey tmp; - memset(&tmp, i+1, sizeof(tmp)); - node_id_from_privkey(&tmp, &nodes[i]); - } - - /* We will try a payment from 1 to 4. - * There are two possible routes 1->2->4 or 1->3->4. - * However, we will simulate that we don't have channel 3->4's capacity - * in the gossmap (see #7194). We expect that 3->4 it's simply ignored - * and only route through 1->2->4 is used. - * - * +--2--+ - * | | - * 1 4 - * | | - * +--3--+ - * - * */ - struct short_channel_id scid; - - assert(mk_short_channel_id(&scid, 1, 2, 0)); - add_connection(fd, &nodes[0], &nodes[1], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - /* add capacity? = */ true); - - assert(mk_short_channel_id(&scid, 2, 4, 0)); - add_connection(fd, &nodes[1], &nodes[3], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - /* add capacity? = */ true); - - assert(mk_short_channel_id(&scid, 1, 3, 0)); - add_connection(fd, &nodes[0], &nodes[2], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - /* add capacity? = */ true); - - assert(mk_short_channel_id(&scid, 3, 4, 0)); - add_connection(fd, &nodes[2], &nodes[3], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000), - /* add capacity? = */ false); - - assert(gossmap_refresh(gossmap, NULL)); - struct uncertainty *uncertainty = uncertainty_new(tmpctx); - int skipped_count = - uncertainty_update(uncertainty, gossmap); - assert(skipped_count==1); - - struct preimage preimage; - - struct amount_msat maxfee = AMOUNT_MSAT(20*1000); - struct payment_info pinfo; - pinfo.invstr = NULL; - pinfo.label = NULL; - pinfo.description = NULL; - pinfo.payment_secret = NULL; - pinfo.payment_metadata = NULL; - pinfo.routehints = NULL; - pinfo.destination = nodes[3]; - pinfo.amount = AMOUNT_MSAT(100 * 1000 * 1000); - - assert(amount_msat_add(&pinfo.maxspend, maxfee, pinfo.amount)); - pinfo.maxdelay = 100; - pinfo.final_cltv = 5; - - pinfo.start_time = time_now(); - pinfo.stop_time = timeabs_add(pinfo.start_time, time_from_sec(10000)); - - pinfo.base_fee_penalty = 1e-5; - pinfo.prob_cost_factor = 1e-5; - pinfo.delay_feefactor = 1e-6; - pinfo.min_prob_success = 0.9; - pinfo.base_prob_success = 1.0; - pinfo.use_shadow = false; - - randombytes_buf(&preimage, sizeof(preimage)); - sha256(&pinfo.payment_hash, &preimage, sizeof(preimage)); - - struct disabledmap *disabledmap = disabledmap_new(tmpctx); - - enum jsonrpc_errcode errcode; - const char *err_msg; - - u64 groupid = 1; - u64 next_partid=1; - - struct route **routes = get_routes( - /* ctx */tmpctx, - /* payment */&pinfo, - /* source */&nodes[0], - /* destination */&nodes[3], - /* gossmap */gossmap, - /* uncertainty */uncertainty, - disabledmap, - /* amount */ pinfo.amount, - /* feebudget */maxfee, - &next_partid, - groupid, - &errcode, - &err_msg); - - assert(routes); - - if (!routes) { - printf("get_route failed with error %d: %s", errcode, err_msg); - } else { - printf("get_routes: %s\n", print_routes(tmpctx, routes)); - assert(tal_count(routes) == 1); - assert(tal_count(routes[0]->hops) == 2); - assert(node_id_eq(&routes[0]->hops[0].node_id, &nodes[1])); - assert(node_id_eq(&routes[0]->hops[1].node_id, &nodes[3])); - } - - common_shutdown(); -} - diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c index 5d907bae1547..7f0069ace0fc 100644 --- a/plugins/renepay/test/run-testflow.c +++ b/plugins/renepay/test/run-testflow.c @@ -17,7 +17,6 @@ #include "../flow.c" #include "../route.c" #include "../uncertainty.c" -#include "../mcf.c" /* AUTOGENERATED MOCKS START */ /* Generated stub for disabledmap_add_channel */ From 90cea4c72b88a20a08496288e507fc9fc622f2f2 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Sat, 21 Sep 2024 08:31:01 +0100 Subject: [PATCH 19/30] renepay: remove uncertainty tools The new uncertainty network will be handled by askrene. Signed-off-by: Lagrang3 --- plugins/renepay/Makefile | 2 - plugins/renepay/main.c | 10 -- plugins/renepay/payplugin.h | 4 - plugins/renepay/routetracker.c | 20 ++- plugins/renepay/test/run-testflow.c | 1 - plugins/renepay/uncertainty.c | 200 ---------------------------- plugins/renepay/uncertainty.h | 60 --------- 7 files changed, 9 insertions(+), 288 deletions(-) delete mode 100644 plugins/renepay/uncertainty.c delete mode 100644 plugins/renepay/uncertainty.h diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index 48c3dba92364..b53bca539c36 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -7,7 +7,6 @@ PLUGIN_RENEPAY_SRC := \ plugins/renepay/route.c \ plugins/renepay/routetracker.c \ plugins/renepay/routefail.c \ - plugins/renepay/uncertainty.c \ plugins/renepay/mods.c \ plugins/renepay/errorcodes.c \ plugins/renepay/json.c @@ -23,7 +22,6 @@ PLUGIN_RENEPAY_HDRS := \ plugins/renepay/route.h \ plugins/renepay/routetracker.h \ plugins/renepay/routefail.h \ - plugins/renepay/uncertainty.h \ plugins/renepay/mods.h \ plugins/renepay/errorcodes.h \ plugins/renepay/json.c diff --git a/plugins/renepay/main.c b/plugins/renepay/main.c index f159d5d285fb..dc3a77e9da56 100644 --- a/plugins/renepay/main.c +++ b/plugins/renepay/main.c @@ -29,8 +29,6 @@ struct pay_plugin *pay_plugin; static void memleak_mark(struct plugin *p, struct htable *memtable) { memleak_scan_obj(memtable, pay_plugin); - memleak_scan_htable(memtable, - &pay_plugin->uncertainty->chan_extra_map->raw); memleak_scan_htable(memtable, &pay_plugin->payment_map->raw); memleak_scan_htable(memtable, &pay_plugin->pending_routes->raw); } @@ -82,14 +80,6 @@ static const char *init(struct plugin *p, plugin_log(p, LOG_DBG, "gossmap ignored %zu channel updates", num_channel_updates_rejected); - pay_plugin->uncertainty = uncertainty_new(pay_plugin); - int skipped_count = - uncertainty_update(pay_plugin->uncertainty, pay_plugin->gossmap); - if (skipped_count) - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "%s: uncertainty was updated but %d channels have " - "been ignored.", - __func__, skipped_count); plugin_set_memleak_handler(p, memleak_mark); return NULL; diff --git a/plugins/renepay/payplugin.h b/plugins/renepay/payplugin.h index a0da496f048d..1c9aff0113cf 100644 --- a/plugins/renepay/payplugin.h +++ b/plugins/renepay/payplugin.h @@ -7,7 +7,6 @@ #include #include #include -#include // TODO(eduardo): renepaystatus should be similar to paystatus @@ -59,9 +58,6 @@ struct pay_plugin { /* All the struct payment */ struct payment_map *payment_map; - /* Per-channel metadata: some persists between payments */ - struct uncertainty *uncertainty; - /* Pending sendpays. Each pending route has an associated HTLC data in * the uncertainty network. Pending routes are matched against sendpay * notifications. */ diff --git a/plugins/renepay/routetracker.c b/plugins/renepay/routetracker.c index fdbf6cb34d0b..9fada578b176 100644 --- a/plugins/renepay/routetracker.c +++ b/plugins/renepay/routetracker.c @@ -91,7 +91,7 @@ static void route_success_register(struct routetracker *routetracker, struct route *route) { if(route->hops){ - uncertainty_route_success(pay_plugin->uncertainty, route); + // uncertainty_route_success(pay_plugin->uncertainty, route); } routetracker_add_to_final(routetracker, route); } @@ -118,9 +118,9 @@ void route_failure_register(struct routetracker *routetracker, /* All channels before the erring node could forward the * payment. */ for (int i = 0; i <= last_good_channel; i++) { - uncertainty_channel_can_send(pay_plugin->uncertainty, - route->hops[i].scid, - route->hops[i].direction); + // uncertainty_channel_can_send(pay_plugin->uncertainty, + // route->hops[i].scid, + // route->hops[i].direction); } if (result->failcode == WIRE_TEMPORARY_CHANNEL_FAILURE && @@ -129,10 +129,10 @@ void route_failure_register(struct routetracker *routetracker, * enough liquidity to forward the payment or cannot add * one more HTLC. */ - uncertainty_channel_cannot_send( - pay_plugin->uncertainty, - route->hops[last_good_channel + 1].scid, - route->hops[last_good_channel + 1].direction); + // uncertainty_channel_cannot_send( + // pay_plugin->uncertainty, + // route->hops[last_good_channel + 1].scid, + // route->hops[last_good_channel + 1].direction); } } routetracker_add_to_final(routetracker, route); @@ -141,7 +141,7 @@ void route_failure_register(struct routetracker *routetracker, static void remove_route(struct route *route, struct route_map *map) { route_map_del(map, route); - uncertainty_remove_htlcs(pay_plugin->uncertainty, route); + // uncertainty_remove_htlcs(pay_plugin->uncertainty, route); } /* This route is pending, ie. locked in HTLCs. @@ -172,8 +172,6 @@ void route_pending_register(struct routetracker *routetracker, __func__, fmt_routekey(tmpctx, &route->key)); - uncertainty_commit_htlcs(pay_plugin->uncertainty, route); - if (!tal_steal(pay_plugin->pending_routes, route) || !route_map_add(pay_plugin->pending_routes, route) || !tal_add_destructor2(route, remove_route, diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c index 7f0069ace0fc..5d449cee3348 100644 --- a/plugins/renepay/test/run-testflow.c +++ b/plugins/renepay/test/run-testflow.c @@ -16,7 +16,6 @@ #include "../payment.c" #include "../flow.c" #include "../route.c" -#include "../uncertainty.c" /* AUTOGENERATED MOCKS START */ /* Generated stub for disabledmap_add_channel */ diff --git a/plugins/renepay/uncertainty.c b/plugins/renepay/uncertainty.c deleted file mode 100644 index 00c50ebc1134..000000000000 --- a/plugins/renepay/uncertainty.c +++ /dev/null @@ -1,200 +0,0 @@ -#include "config.h" -#include -#include - -void uncertainty_route_success(struct uncertainty *uncertainty, - const struct route *route) -{ - if (!route->hops) - return; - - for (size_t i = 0; i < tal_count(route->hops); i++) { - const struct route_hop *hop = &route->hops[i]; - struct short_channel_id_dir scidd = {hop->scid, hop->direction}; - - // FIXME: check errors here, report back - chan_extra_sent_success(uncertainty->chan_extra_map, &scidd, - route->hops[i].amount); - } -} -void uncertainty_remove_htlcs(struct uncertainty *uncertainty, - const struct route *route) -{ - // FIXME: how could we get the route details of a sendpay that we did - // not send? - if (!route->hops) - return; - - const size_t pathlen = tal_count(route->hops); - for (size_t i = 0; i < pathlen; i++) { - const struct route_hop *hop = &route->hops[i]; - struct short_channel_id_dir scidd = {hop->scid, hop->direction}; - - // FIXME: check error - chan_extra_remove_htlc(uncertainty->chan_extra_map, &scidd, - hop->amount); - } -} - -void uncertainty_commit_htlcs(struct uncertainty *uncertainty, - const struct route *route) -{ - // FIXME: how could we get the route details of a sendpay that we did - // not send? - if (!route->hops) - return; - - const size_t pathlen = tal_count(route->hops); - for (size_t i = 0; i < pathlen; i++) { - const struct route_hop *hop = &route->hops[i]; - struct short_channel_id_dir scidd = {hop->scid, hop->direction}; - - // FIXME: check error - chan_extra_commit_htlc(uncertainty->chan_extra_map, &scidd, - hop->amount); - } -} - -void uncertainty_channel_can_send(struct uncertainty *uncertainty, - struct short_channel_id scid, int direction) -{ - struct short_channel_id_dir scidd = {scid, direction}; - // FIXME: check error - chan_extra_can_send(uncertainty->chan_extra_map, &scidd); -} -void uncertainty_channel_cannot_send(struct uncertainty *uncertainty, - struct short_channel_id scid, - int direction) -{ - struct short_channel_id_dir scidd = {scid, direction}; - // FIXME: check error - chan_extra_cannot_send(uncertainty->chan_extra_map, &scidd); -} - -int uncertainty_update(struct uncertainty *uncertainty, struct gossmap *gossmap) -{ - /* Each channel in chan_extra_map should be either in gossmap or in - * local_gossmods. */ - assert(uncertainty); - struct chan_extra_map *chan_extra_map = uncertainty->chan_extra_map; - assert(chan_extra_map); - struct chan_extra **del_list = tal_arr(NULL, struct chan_extra*, 0); - struct chan_extra_map_iter it; - for (struct chan_extra *ch = chan_extra_map_first(chan_extra_map, &it); - ch; ch = chan_extra_map_next(chan_extra_map, &it)) { - - /* If we cannot find that channel in the gossmap, add it to the - * delete list. */ - if (!gossmap_find_chan(gossmap, &ch->scid)) - tal_arr_expand(&del_list, ch); - } - for(size_t i=0;ichan_extra_map = tal(uncertainty, struct chan_extra_map); - if (uncertainty->chan_extra_map == NULL) - goto function_fail; - - chan_extra_map_init(uncertainty->chan_extra_map); - - return uncertainty; - -function_fail: - return tal_free(uncertainty); -} - -struct chan_extra_map * -uncertainty_get_chan_extra_map(struct uncertainty *uncertainty) -{ - // TODO: do we really need this function? - return uncertainty->chan_extra_map; -} - -/* Add channel to the Uncertainty Network if it doesn't already exist. */ -const struct chan_extra * -uncertainty_add_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid, - struct amount_msat capacity) -{ - const struct chan_extra *ce = - chan_extra_map_get(uncertainty->chan_extra_map, scid); - if (ce) - return ce; - - return new_chan_extra(uncertainty->chan_extra_map, scid, capacity); -} - -bool uncertainty_set_liquidity(struct uncertainty *uncertainty, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max) -{ - // FIXME check error - enum renepay_errorcode err = chan_extra_set_liquidity( - uncertainty->chan_extra_map, scidd, min, max); - - return err == RENEPAY_NOERROR; -} - -struct chan_extra *uncertainty_find_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid) -{ - return chan_extra_map_get(uncertainty->chan_extra_map, scid); -} - -enum renepay_errorcode uncertainty_relax(struct uncertainty *uncertainty, - double seconds) -{ - assert(seconds >= 0); - const double fraction = MIN(seconds / TIMER_FORGET_SEC, 1.0); - struct chan_extra_map *chan_extra_map = - uncertainty_get_chan_extra_map(uncertainty); - struct chan_extra_map_iter it; - for (struct chan_extra *ce = chan_extra_map_first(chan_extra_map, &it); - ce; ce = chan_extra_map_next(chan_extra_map, &it)) { - enum renepay_errorcode err = - chan_extra_relax_fraction(ce, fraction); - - if (err) - return err; - } - return RENEPAY_NOERROR; -} diff --git a/plugins/renepay/uncertainty.h b/plugins/renepay/uncertainty.h deleted file mode 100644 index efde2e3c3fad..000000000000 --- a/plugins/renepay/uncertainty.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_H -#define LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_H -#include "config.h" -#include -#include -#include -#include - -struct uncertainty { - struct chan_extra_map *chan_extra_map; -}; - -/* FIXME: add bool return value and WARN_UNUSED_RESULT */ -void uncertainty_route_success(struct uncertainty *uncertainty, - const struct route *route); -void uncertainty_remove_htlcs(struct uncertainty *uncertainty, - const struct route *route); - -void uncertainty_commit_htlcs(struct uncertainty *uncertainty, - const struct route *route); - -void uncertainty_channel_can_send(struct uncertainty *uncertainty, - struct short_channel_id scid, int direction); - -void uncertainty_channel_cannot_send(struct uncertainty *uncertainty, - struct short_channel_id scid, - int direction); - -WARN_UNUSED_RESULT int uncertainty_update(struct uncertainty *uncertainty, - struct gossmap *gossmap); - -struct uncertainty *uncertainty_new(const tal_t *ctx); - -struct chan_extra_map * -uncertainty_get_chan_extra_map(struct uncertainty *uncertainty); - -const struct chan_extra * -uncertainty_add_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid, - struct amount_msat capacity); - -bool uncertainty_set_liquidity(struct uncertainty *uncertainty, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max); - -struct chan_extra *uncertainty_find_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid); - -/* Adds randomness to the current state simulating the natural evolution of the - * liquidity in the network. It should be a markovian process with the minimum - * requirement that the transition operator T satisfies: - * T(t1) T(t2) = T(t1+t2) - * i.e. the transition operator is a one parameter semigroup. - * For the moment we omit the continuous and linear aspects of the problem for a - * lack for formulation. */ -enum renepay_errorcode uncertainty_relax(struct uncertainty *uncertainty, - double seconds); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_H */ From 57153b8b251c5d14671ec24025da1d0365247aee Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 23 Sep 2024 08:37:44 +0100 Subject: [PATCH 20/30] renepay: disable channels with askrene Disable channels and nodes using askrene API. Signed-off-by: Lagrang3 --- plugins/renepay/Makefile | 2 - plugins/renepay/disabledmap.c | 125 ---------------------------- plugins/renepay/disabledmap.h | 64 -------------- plugins/renepay/mods.c | 93 ++++++++++++++++++--- plugins/renepay/payment.c | 63 +++++++------- plugins/renepay/payment.h | 4 +- plugins/renepay/test/run-testflow.c | 21 ----- 7 files changed, 113 insertions(+), 259 deletions(-) delete mode 100644 plugins/renepay/disabledmap.c delete mode 100644 plugins/renepay/disabledmap.h diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index b53bca539c36..4c660e96cfac 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -1,7 +1,6 @@ PLUGIN_RENEPAY_SRC := \ plugins/renepay/main.c \ plugins/renepay/flow.c \ - plugins/renepay/disabledmap.c \ plugins/renepay/payment.c \ plugins/renepay/chan_extra.c \ plugins/renepay/route.c \ @@ -14,7 +13,6 @@ PLUGIN_RENEPAY_SRC := \ PLUGIN_RENEPAY_HDRS := \ plugins/renepay/payplugin.h \ plugins/renepay/flow.h \ - plugins/renepay/disabledmap.h \ plugins/renepay/payment.h \ plugins/renepay/payment_info.h \ plugins/renepay/chan_extra.h \ diff --git a/plugins/renepay/disabledmap.c b/plugins/renepay/disabledmap.c deleted file mode 100644 index 68492eced369..000000000000 --- a/plugins/renepay/disabledmap.c +++ /dev/null @@ -1,125 +0,0 @@ -#include "config.h" -#include - -struct disabledmap *disabledmap_new(const tal_t *ctx) -{ - struct disabledmap *obj = tal(ctx, struct disabledmap); - if (!obj) - return NULL; - - obj->disabled_map = tal(obj, struct scidd_map); - obj->warned_map = tal(obj, struct scidd_map); - obj->disabled_ctx = tal(obj, tal_t); - obj->warned_ctx = tal(obj, tal_t); - obj->disabled_nodes = tal_arr(obj, struct node_id, 0); - - if (!obj->disabled_map || !obj->warned_map || !obj->disabled_nodes || - !obj->disabled_ctx || !obj->warned_ctx) - return tal_free(obj); - - scidd_map_init(obj->disabled_map); - scidd_map_init(obj->warned_map); - return obj; -} - -// FIXME: check success -void disabledmap_reset(struct disabledmap *p) -{ - /* This will remove and free every element in the maps. */ - p->warned_ctx = tal_free(p->warned_ctx); - p->disabled_ctx = tal_free(p->disabled_ctx); - - tal_resize(&p->disabled_nodes, 0); - - p->disabled_ctx = tal(p, tal_t); - p->warned_ctx = tal(p, tal_t); -} - -static void remove_scidd(struct short_channel_id_dir *scidd, - struct scidd_map *map) -{ - scidd_map_del(map, scidd); -} - -// FIXME: check success -void disabledmap_add_channel(struct disabledmap *p, - struct short_channel_id_dir scidd) -{ - struct short_channel_id_dir *ptr_scidd = - scidd_map_get(p->disabled_map, scidd); - if (ptr_scidd) { - /* htable allows for duplicates, but we don't want duplicates. - */ - return; - } - ptr_scidd = - tal_dup(p->disabled_ctx, struct short_channel_id_dir, &scidd); - scidd_map_add(p->disabled_map, ptr_scidd); - tal_add_destructor2(ptr_scidd, remove_scidd, p->disabled_map); -} - -// FIXME: check success -void disabledmap_warn_channel(struct disabledmap *p, - struct short_channel_id_dir scidd) -{ - struct short_channel_id_dir *ptr_scidd = - scidd_map_get(p->warned_map, scidd); - if (ptr_scidd) { - /* htable allows for duplicates, but we don't want duplicates. - */ - return; - } - ptr_scidd = tal_dup(p->warned_ctx, struct short_channel_id_dir, &scidd); - scidd_map_add(p->warned_map, ptr_scidd); - tal_add_destructor2(ptr_scidd, remove_scidd, p->warned_map); -} - -// FIXME: check success -void disabledmap_add_node(struct disabledmap *p, struct node_id node) -{ - tal_arr_expand(&p->disabled_nodes, node); -} - -bool disabledmap_channel_is_warned(struct disabledmap *p, - struct short_channel_id_dir scidd) -{ - return scidd_map_get(p->warned_map, scidd) != NULL; -} - -bitmap *tal_disabledmap_get_bitmap(const tal_t *ctx, struct disabledmap *p, - const struct gossmap *gossmap) -{ - bitmap *disabled = tal_arrz( - ctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - if (!disabled) - return NULL; - - /* Disable every channel in the list of disabled scids. */ - struct scidd_map_iter it; - for(struct short_channel_id_dir *scidd = scidd_map_first(p->disabled_map,&it); - scidd; - scidd = scidd_map_next(p->disabled_map, &it)){ - - struct gossmap_chan *c = - gossmap_find_chan(gossmap, &scidd->scid); - if (c) - bitmap_set_bit(disabled, - gossmap_chan_idx(gossmap, c) * 2 + - scidd->dir); - } - - /* Disable all channels that lead to a disabled node. */ - for (size_t i = 0; i < tal_count(p->disabled_nodes); i++) { - const struct gossmap_node *node = - gossmap_find_node(gossmap, &p->disabled_nodes[i]); - - for (size_t j = 0; j < node->num_chans; j++) { - int half; - const struct gossmap_chan *c = - gossmap_nth_chan(gossmap, node, j, &half); - bitmap_set_bit(disabled, - gossmap_chan_idx(gossmap, c) * 2 + half); - } - } - return disabled; -} diff --git a/plugins/renepay/disabledmap.h b/plugins/renepay/disabledmap.h deleted file mode 100644 index 2dea547b076f..000000000000 --- a/plugins/renepay/disabledmap.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_DISABLEDMAP_H -#define LIGHTNING_PLUGINS_RENEPAY_DISABLEDMAP_H - -#include "config.h" -#include -#include -#include -#include -#include - -static inline size_t hash_scidd(const struct short_channel_id_dir scidd) -{ - /* scids cost money to generate, so simple hash works here. Letting same - * scid with two directions collide. */ - return (scidd.scid.u64 >> 32) ^ (scidd.scid.u64 >> 16) ^ scidd.scid.u64; -} - -static inline struct short_channel_id_dir -self_scidd(const struct short_channel_id_dir *self) -{ - return *self; -} - -static inline bool -my_short_channel_id_dir_eq(const struct short_channel_id_dir *scidd_a, - const struct short_channel_id_dir scidd_b) -{ - return short_channel_id_eq(scidd_a->scid, scidd_b.scid) && - scidd_a->dir == scidd_b.dir; -} - -/* A htable for short_channel_id_dir, the structure itself is the element key. - */ -HTABLE_DEFINE_TYPE(struct short_channel_id_dir, self_scidd, hash_scidd, - my_short_channel_id_dir_eq, scidd_map); - -struct disabledmap { - /* Channels we decided to disable for various reasons. */ - struct scidd_map *disabled_map; - tal_t *disabled_ctx; - - /* Channels that we flagged for failures. If warned two times we will - * disable it. */ - struct scidd_map *warned_map; - tal_t *warned_ctx; - - /* nodes we disable */ - // FIXME: use a map also for nodes - struct node_id *disabled_nodes; -}; - -void disabledmap_reset(struct disabledmap *p); -struct disabledmap *disabledmap_new(const tal_t *ctx); -void disabledmap_add_channel(struct disabledmap *p, - struct short_channel_id_dir scidd); -void disabledmap_warn_channel(struct disabledmap *p, - struct short_channel_id_dir scidd); -void disabledmap_add_node(struct disabledmap *p, struct node_id node); -bool disabledmap_channel_is_warned(struct disabledmap *p, - struct short_channel_id_dir scidd); -bitmap *tal_disabledmap_get_bitmap(const tal_t *ctx, struct disabledmap *p, - const struct gossmap *gossmap); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_DISABLEDMAP_H */ diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 852f2c65e7ba..ce9ae0d634b4 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -1272,6 +1272,27 @@ askrene_disable_channel_fail(struct command *cmd, const char *buf, return askrene_disable_channel_done(cmd, buf, result, payment); } +static struct command_result * +askrene_disable_node_done(struct command *cmd, const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct payment *payment) +{ + assert(payment->pending_rpcs > 0); + payment->pending_rpcs--; + return payment_continue(payment); +} + +static struct command_result *askrene_disable_node_fail(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct payment *payment) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, + "failed to disable node with askrene-disable-node: %.*s", + json_tok_full_len(result), json_tok_full(buf, result)); + return askrene_disable_node_done(cmd, buf, result, payment); +} + static struct command_result *channelfilter_cb(struct payment *payment) { assert(payment); @@ -1344,6 +1365,57 @@ static struct command_result *channelfilter_cb(struct payment *payment) REGISTER_PAYMENT_MODIFIER(channelfilter, channelfilter_cb); + +/***************************************************************************** + * manualexclusions + * + * Disable some channels and nodes specified in the command line or during our + * internal workflow. + */ + +static struct command_result *manualexclusions_cb(struct payment *payment) +{ + assert(payment); + struct command *cmd = payment_command(payment); + assert(cmd); + + for(size_t i=0;iexclusions);i++){ + const struct route_exclusion *ex = &payment->exclusions[i]; + if (ex->type == EXCLUDE_CHANNEL) { + /* FIXME: there is no askrene-disable-channel, + * we will fake its disabling by setting its + * liquidity to 0 */ + struct out_req *req = jsonrpc_request_start( + cmd->plugin, cmd, "askrene-inform-channel", + askrene_disable_channel_done, + askrene_disable_channel_fail, payment); + json_add_string(req->js, "layer", + payment->private_layer); + json_add_short_channel_id(req->js, "short_channel_id", + ex->u.chan_id.scid); + json_add_num(req->js, "direction", ex->u.chan_id.dir); + json_add_amount_msat(req->js, "maximum_msat", + AMOUNT_MSAT(0)); + send_outreq(cmd->plugin, req); + payment->pending_rpcs++; + } else { + struct out_req *req = jsonrpc_request_start( + cmd->plugin, cmd, "askrene-disable-node", + askrene_disable_node_done, + askrene_disable_node_fail, payment); + json_add_string(req->js, "layer", + payment->private_layer); + json_add_node_id(req->js, "node", &ex->u.node_id); + send_outreq(cmd->plugin, req); + payment->pending_rpcs++; + } + } + tal_resize(&payment->exclusions, 0); + return payment_continue(payment); +} + +REGISTER_PAYMENT_MODIFIER(manualexclusions, manualexclusions_cb); + /***************************************************************************** * alwaystrue * @@ -1402,17 +1474,18 @@ void *payment_virtual_program[] = { /*10*/OP_CALL, &channelfilter_pay_mod, // TODO shadow_additions /* do */ - /*12*/ OP_CALL, &pendingsendpays_pay_mod, - /*14*/ OP_CALL, &checktimeout_pay_mod, - /*16*/ OP_CALL, &compute_routes_pay_mod, - /*18*/ OP_CALL, &send_routes_pay_mod, - /*20*/ OP_CALL, &reserve_routes_pay_mod, + /*12*/OP_CALL, &manualexclusions_pay_mod, + /*14*/ OP_CALL, &pendingsendpays_pay_mod, + /*16*/ OP_CALL, &checktimeout_pay_mod, + /*18*/ OP_CALL, &compute_routes_pay_mod, + /*20*/ OP_CALL, &send_routes_pay_mod, + /*22*/ OP_CALL, &reserve_routes_pay_mod, /*do*/ - /*22*/ OP_CALL, &sleep_pay_mod, - /*24*/ OP_CALL, &collect_results_pay_mod, + /*24*/ OP_CALL, &sleep_pay_mod, + /*26*/ OP_CALL, &collect_results_pay_mod, /*while*/ - /*26*/ OP_IF, ¬haveresults_pay_cond, (void *)22, + /*28*/ OP_IF, ¬haveresults_pay_cond, (void *)24, /* while */ - /*29*/ OP_IF, &retry_pay_cond, (void *)12, - /*32*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ + /*31*/ OP_IF, &retry_pay_cond, (void *)12, + /*34*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ /*32*/ NULL}; diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c index 70d959961f05..b0a14112560f 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -104,15 +104,11 @@ struct payment *payment_new( p->next_partid = 1; p->cmd_array = tal_arr(p, struct command *, 0); p->local_gossmods = NULL; - p->disabledmap = disabledmap_new(p); - - for (size_t i = 0; i < tal_count(exclusions); i++) { - const struct route_exclusion *ex = exclusions[i]; - if (ex->type == EXCLUDE_CHANNEL) - disabledmap_add_channel(p->disabledmap, ex->u.chan_id); - else - disabledmap_add_node(p->disabledmap, ex->u.node_id); - } + + p->exclusions = + tal_arr(p, struct route_exclusion, tal_count(exclusions)); + for (size_t i = 0; i < tal_count(p->exclusions); i++) + p->exclusions[i] = *exclusions[i]; p->have_results = false; p->retry = false; @@ -140,14 +136,8 @@ static void payment_cleanup(struct payment *p) p->pending_rpcs = 0; tal_resize(&p->cmd_array, 0); p->local_gossmods = tal_free(p->local_gossmods); - - /* FIXME: for optimization, a cleanup should prune all the data that has - * no use after a payent is completed. The entire disablemap structure - * is no longer needed, hence I guess we should free it not just reset - * it. */ - disabledmap_reset(p->disabledmap); + p->exclusions = tal_free(p->exclusions); p->waitresult_timer = tal_free(p->waitresult_timer); - routetracker_cleanup(p->routetracker); } @@ -218,16 +208,11 @@ bool payment_update( p->local_gossmods = tal_free(p->local_gossmods); - assert(p->disabledmap); - disabledmap_reset(p->disabledmap); - - for (size_t i = 0; i < tal_count(exclusions); i++) { - const struct route_exclusion *ex = exclusions[i]; - if (ex->type == EXCLUDE_CHANNEL) - disabledmap_add_channel(p->disabledmap, ex->u.chan_id); - else - disabledmap_add_node(p->disabledmap, ex->u.node_id); - } + p->exclusions = tal_free(p->exclusions); + p->exclusions = + tal_arr(p, struct route_exclusion, tal_count(exclusions)); + for (size_t i = 0; i < tal_count(p->exclusions); i++) + p->exclusions[i] = *exclusions[i]; p->have_results = false; p->retry = false; @@ -407,7 +392,11 @@ void payment_disable_chan(struct payment *p, struct short_channel_id_dir scidd, enum log_level lvl, const char *fmt, ...) { assert(p); - assert(p->disabledmap); + assert(p->exclusions); + struct route_exclusion ex; + ex.type = EXCLUDE_CHANNEL; + ex.u.chan_id = scidd; + va_list ap; const char *str; @@ -415,16 +404,14 @@ void payment_disable_chan(struct payment *p, struct short_channel_id_dir scidd, str = tal_vfmt(tmpctx, fmt, ap); va_end(ap); payment_note(p, lvl, "disabling %s: %s", - fmt_short_channel_id_dir(tmpctx, &scidd), - str); - disabledmap_add_channel(p->disabledmap, scidd); + fmt_short_channel_id_dir(tmpctx, &scidd), str); + tal_arr_expand(&p->exclusions, ex); } void payment_warn_chan(struct payment *p, struct short_channel_id_dir scidd, enum log_level lvl, const char *fmt, ...) { assert(p); - assert(p->disabledmap); va_list ap; const char *str; @@ -432,7 +419,10 @@ void payment_warn_chan(struct payment *p, struct short_channel_id_dir scidd, str = tal_vfmt(tmpctx, fmt, ap); va_end(ap); - if (disabledmap_channel_is_warned(p->disabledmap, scidd)) { + /* FIXME: add a data structure to remember metadata for channels and + * nodes, so that we can evaluate those channels that cause trouble + * frequently and disable them. Here we disable them by default. */ + if (true) { payment_disable_chan(p, scidd, lvl, "%s, channel warned twice", str); return; @@ -441,14 +431,17 @@ void payment_warn_chan(struct payment *p, struct short_channel_id_dir scidd, payment_note( p, lvl, "flagged for warning %s: %s, next time it will be disabled", fmt_short_channel_id_dir(tmpctx, &scidd), str); - disabledmap_warn_channel(p->disabledmap, scidd); } void payment_disable_node(struct payment *p, struct node_id node, enum log_level lvl, const char *fmt, ...) { assert(p); - assert(p->disabledmap); + assert(p->exclusions); + struct route_exclusion ex; + ex.type = EXCLUDE_NODE; + ex.u.node_id = node; + va_list ap; const char *str; @@ -458,5 +451,5 @@ void payment_disable_node(struct payment *p, struct node_id node, payment_note(p, lvl, "disabling node %s: %s", fmt_node_id(tmpctx, &node), str); - disabledmap_add_node(p->disabledmap, node); + tal_arr_expand(&p->exclusions, ex); } diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index 7be915dc159c..07179c56a262 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -4,7 +4,6 @@ #include #include #include -#include #include enum payment_status { PAYMENT_PENDING, PAYMENT_SUCCESS, PAYMENT_FAIL }; @@ -59,7 +58,8 @@ struct payment { /* Localmods to apply to gossip_map for our own use. */ struct gossmap_localmods *local_gossmods; - struct disabledmap *disabledmap; + /* Here we queue channel and node disabling orders. */ + struct route_exclusion *exclusions; /* Flag to indicate wether we have collected enough results to make a * decision on the payment progress. */ diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c index 5d449cee3348..f23415e67974 100644 --- a/plugins/renepay/test/run-testflow.c +++ b/plugins/renepay/test/run-testflow.c @@ -18,27 +18,6 @@ #include "../route.c" /* AUTOGENERATED MOCKS START */ -/* Generated stub for disabledmap_add_channel */ -void disabledmap_add_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_add_channel called!\n"); abort(); } -/* Generated stub for disabledmap_add_node */ -void disabledmap_add_node(struct disabledmap *p UNNEEDED, struct node_id node UNNEEDED) -{ fprintf(stderr, "disabledmap_add_node called!\n"); abort(); } -/* Generated stub for disabledmap_channel_is_warned */ -bool disabledmap_channel_is_warned(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_channel_is_warned called!\n"); abort(); } -/* Generated stub for disabledmap_new */ -struct disabledmap *disabledmap_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "disabledmap_new called!\n"); abort(); } -/* Generated stub for disabledmap_reset */ -void disabledmap_reset(struct disabledmap *p UNNEEDED) -{ fprintf(stderr, "disabledmap_reset called!\n"); abort(); } -/* Generated stub for disabledmap_warn_channel */ -void disabledmap_warn_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_warn_channel called!\n"); abort(); } /* Generated stub for json_add_payment */ void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED) { fprintf(stderr, "json_add_payment called!\n"); abort(); } From d50e83968f8a6b50e466d02acf97aecef4f016e5 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 23 Sep 2024 08:55:38 +0100 Subject: [PATCH 21/30] renepay: remove unused struct flow Signed-off-by: Lagrang3 --- plugins/renepay/Makefile | 2 - plugins/renepay/flow.c | 489 --------------------------- plugins/renepay/flow.h | 95 ------ plugins/renepay/payplugin.h | 1 - plugins/renepay/route.c | 73 ---- plugins/renepay/route.h | 11 - plugins/renepay/test/run-route_map.c | 1 - plugins/renepay/test/run-testflow.c | 232 ------------- 8 files changed, 904 deletions(-) delete mode 100644 plugins/renepay/flow.c delete mode 100644 plugins/renepay/flow.h diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index 4c660e96cfac..ea35454ea3d7 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -1,6 +1,5 @@ PLUGIN_RENEPAY_SRC := \ plugins/renepay/main.c \ - plugins/renepay/flow.c \ plugins/renepay/payment.c \ plugins/renepay/chan_extra.c \ plugins/renepay/route.c \ @@ -12,7 +11,6 @@ PLUGIN_RENEPAY_SRC := \ PLUGIN_RENEPAY_HDRS := \ plugins/renepay/payplugin.h \ - plugins/renepay/flow.h \ plugins/renepay/payment.h \ plugins/renepay/payment_info.h \ plugins/renepay/chan_extra.h \ diff --git a/plugins/renepay/flow.c b/plugins/renepay/flow.c deleted file mode 100644 index 6d4d25aa1ccd..000000000000 --- a/plugins/renepay/flow.c +++ /dev/null @@ -1,489 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef SUPERVERBOSE -#define SUPERVERBOSE(...) -#else -#define SUPERVERBOSE_ENABLED 1 -#endif - -struct amount_msat *tal_flow_amounts(const tal_t *ctx, const struct flow *flow, - bool compute_fees) -{ - const size_t pathlen = tal_count(flow->path); - struct amount_msat *amounts = tal_arr(ctx, struct amount_msat, pathlen); - amounts[pathlen - 1] = flow->amount; - - for (int i = (int)pathlen - 2; i >= 0; i--) { - const struct half_chan *h = flow_edge(flow, i + 1); - amounts[i] = amounts[i + 1]; - if (compute_fees && - !amount_msat_add_fee(&amounts[i], h->base_fee, - h->proportional_fee)) - goto function_fail; - } - - return amounts; - -function_fail: - return tal_free(amounts); -} - -const char *fmt_flows(const tal_t *ctx, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - struct flow **flows) -{ - tal_t *this_ctx = tal(ctx, tal_t); - char *buff = tal_fmt(ctx, "%zu subflows\n", tal_count(flows)); - for (size_t i = 0; i < tal_count(flows); i++) { - struct amount_msat fee, delivered; - tal_append_fmt(&buff, " "); - for (size_t j = 0; j < tal_count(flows[i]->path); j++) { - struct short_channel_id scid = - gossmap_chan_scid(gossmap, flows[i]->path[j]); - tal_append_fmt(&buff, "%s%s", j ? "->" : "", - fmt_short_channel_id(this_ctx, scid)); - } - delivered = flows[i]->amount; - if (!flow_fee(&fee, flows[i])) { - abort(); - } - tal_append_fmt(&buff, " prob %.2f, %s delivered with fee %s\n", - flows[i]->success_prob, - fmt_amount_msat(this_ctx, delivered), - fmt_amount_msat(this_ctx, fee)); - } - - tal_free(this_ctx); - return buff; -} - -/* Returns the greatest amount we can deliver to the destination using this - * route. It takes into account the current knowledge, pending HTLC, - * htlc_max and fees. - * - * It fails if the maximum that we can - * deliver at node i is smaller than the minimum required to forward the least - * amount greater than zero to the next node. */ -enum renepay_errorcode -flow_maximum_deliverable(struct amount_msat *max_deliverable, - const struct flow *flow, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan **bad_channel) -{ - assert(tal_count(flow->path) > 0); - assert(tal_count(flow->dirs) > 0); - assert(tal_count(flow->path) == tal_count(flow->dirs)); - struct amount_msat x; - enum renepay_errorcode err; - - err = channel_liquidity(&x, gossmap, chan_extra_map, flow->path[0], - flow->dirs[0]); - if(err){ - if(bad_channel)*bad_channel = flow->path[0]; - return err; - } - x = amount_msat_min(x, gossmap_chan_htlc_max(flow->path[0], flow->dirs[0])); - - if(amount_msat_is_zero(x)) - { - if(bad_channel)*bad_channel = flow->path[0]; - return RENEPAY_BAD_CHANNEL; - } - - for (size_t i = 1; i < tal_count(flow->path); ++i) { - // ith node can forward up to 'liquidity_cap' because of the ith - // channel liquidity bound - struct amount_msat liquidity_cap; - - err = channel_liquidity(&liquidity_cap, gossmap, chan_extra_map, - flow->path[i], flow->dirs[i]); - if(err) { - if(bad_channel)*bad_channel = flow->path[i]; - return err; - } - - /* ith node can receive up to 'x', therefore he will not forward - * more than 'forward_cap' that we compute below inverting the - * fee equation. */ - struct amount_msat forward_cap; - err = channel_maximum_forward(&forward_cap, flow->path[i], - flow->dirs[i], x); - if(err) - { - if(bad_channel)*bad_channel = flow->path[i]; - return err; - } - struct amount_msat x_new = - amount_msat_min(forward_cap, liquidity_cap); - x_new = amount_msat_min( - x_new, gossmap_chan_htlc_max(flow->path[i], flow->dirs[i])); - - /* safety check: amounts decrease along the route */ - assert(amount_msat_less_eq(x_new, x)); - - if(amount_msat_is_zero(x_new)) - { - if(bad_channel)*bad_channel = flow->path[i]; - return RENEPAY_BAD_CHANNEL; - } - - /* safety check: the max liquidity in the next hop + fees cannot - be greater than the max liquidity in the current hop, IF the - next hop is non-zero. */ - struct amount_msat x_check = x_new; - assert( - amount_msat_add_fee(&x_check, flow_edge(flow, i)->base_fee, - flow_edge(flow, i)->proportional_fee)); - assert(amount_msat_less_eq(x_check, x)); - - x = x_new; - } - assert(!amount_msat_is_zero(x)); - *max_deliverable = x; - return RENEPAY_NOERROR; -} - -/* Returns the smallest amount we can send so that the destination can get one - * HTLC of any size. It takes into account htlc_min and fees. - * */ -// static enum renepay_errorcode -// flow_minimum_sendable(struct amount_msat *min_sendable UNUSED, -// const struct flow *flow UNUSED, -// const struct gossmap *gossmap UNUSED, -// struct chan_extra_map *chan_extra_map UNUSED) -// { -// // TODO -// return RENEPAY_NOERROR; -// } - -/* How much do we deliver to destination using this set of routes */ -bool flowset_delivers(struct amount_msat *delivers, struct flow **flows) -{ - struct amount_msat final = AMOUNT_MSAT(0); - for (size_t i = 0; i < tal_count(flows); i++) { - if (!amount_msat_accumulate(&final, flows[i]->amount)) - return false; - } - *delivers = final; - return true; -} - -/* FIXME: pass a pointer to const here */ -size_t flowset_size(struct flow **flows) -{ - size_t size = 0; - for (size_t i = 0; i < tal_count(flows); i++) - size += tal_count(flows[i]->path); - return size; -} - -/* Checks if the flows satisfy the liquidity bounds imposed by the known maximum - * liquidity and pending HTLCs. - * - * FIXME The function returns false even in the case of failure. The caller has - * no way of knowing the difference between a failure of evaluation and a - * negative answer. */ -// static bool check_liquidity_bounds(struct flow **flows, -// const struct gossmap *gossmap, -// struct chan_extra_map *chan_extra_map) -// { -// bool check = true; -// for (size_t i = 0; i < tal_count(flows); ++i) { -// struct amount_msat max_deliverable; -// if (!flow_maximum_deliverable(&max_deliverable, flows[i], -// gossmap, chan_extra_map)) -// return false; -// struct amount_msat delivers = flow_delivers(flows[i]); -// check &= amount_msat_less_eq(delivers, max_deliverable); -// } -// return check; -// } - -/* Compute the prob. of success of a set of concurrent set of flows. - * - * IMPORTANT: this is not simply the multiplication of the prob. of success of - * all of them, because they're not independent events. A flow that passes - * through a channel c changes that channel's liquidity and then if another flow - * passes through that same channel the previous liquidity change must be taken - * into account. - * - * P(A and B) != P(A) * P(B), - * - * but - * - * P(A and B) = P(A) * P(B | A) - * - * also due to the linear form of P() we have - * - * P(A and B) = P(A + B) - * */ -struct chan_inflight_flow -{ - struct amount_msat half[2]; -}; - -/* @ctx: allocator - * @flows: flows for which the probability is computed - * @gossmap: gossip - * @chan_extra_map: knowledge - * @compute_fees: compute fees along the way or not - * @fail: if a failure occurs, returns a message to the caller - * */ -// TODO(eduardo): here chan_extra_map should be const -// TODO(eduardo): here flows should be const -double flowset_probability(const tal_t *ctx, struct flow **flows, - const struct gossmap *const gossmap, - struct chan_extra_map *chan_extra_map, - bool compute_fees, - char **fail) -{ - assert(flows); - assert(gossmap); - assert(chan_extra_map); - tal_t *this_ctx = tal(ctx, tal_t); - double prob = 1.0; - - // TODO(eduardo): should it be better to use a map instead of an array - // here? - const size_t max_num_chans = gossmap_max_chan_idx(gossmap); - struct chan_inflight_flow *in_flight = - tal_arr(this_ctx, struct chan_inflight_flow, max_num_chans); - - if (!in_flight) { - if (fail) - *fail = tal_fmt(ctx, "failed to allocate memory"); - goto function_fail; - } - - for (size_t i = 0; i < max_num_chans; ++i) { - in_flight[i].half[0] = in_flight[i].half[1] = AMOUNT_MSAT(0); - } - - for (size_t i = 0; i < tal_count(flows); ++i) { - const struct flow *f = flows[i]; - const size_t pathlen = tal_count(f->path); - struct amount_msat *amounts = - tal_flow_amounts(this_ctx, f, compute_fees); - if (!amounts) - { - if (fail) - *fail = tal_fmt( - ctx, - "failed to compute amounts along the path"); - goto function_fail; - } - - for (size_t j = 0; j < pathlen; ++j) { - const struct chan_extra_half *h = - get_chan_extra_half_by_chan(gossmap, chan_extra_map, - f->path[j], f->dirs[j]); - if (!h) { - if (fail) - *fail = tal_fmt( - ctx, - "channel not found in chan_extra_map"); - goto function_fail; - } - const u32 c_idx = gossmap_chan_idx(gossmap, f->path[j]); - const int c_dir = f->dirs[j]; - - const struct amount_msat deliver = amounts[j]; - - struct amount_msat prev_flow, all_inflight; - if (!amount_msat_add(&prev_flow, h->htlc_total, - in_flight[c_idx].half[c_dir]) || - !amount_msat_add(&all_inflight, prev_flow, - deliver)) { - if (fail) - *fail = tal_fmt( - ctx, - "in-flight amount_msat overflow"); - goto function_fail; - } - - if (!amount_msat_less_eq(all_inflight, h->known_max)) { - if (fail) - *fail = tal_fmt( - ctx, - "in-flight (%s) exceeds known_max " - "(%s)", - fmt_amount_msat(ctx, all_inflight), - fmt_amount_msat(ctx, h->known_max)); - goto function_fail; - } - - double edge_prob = edge_probability( - h->known_min, h->known_max, prev_flow, deliver); - - if (edge_prob < 0) { - if (fail) - *fail = tal_fmt(ctx, - "edge_probability failed"); - goto function_fail; - } - prob *= edge_prob; - - if (!amount_msat_add(&in_flight[c_idx].half[c_dir], - in_flight[c_idx].half[c_dir], - deliver)) { - if (fail) - *fail = tal_fmt( - ctx, "in-flight amount_msat overflow"); - goto function_fail; - } - } - } - tal_free(this_ctx); - return prob; - - function_fail: - tal_free(this_ctx); - return -1; -} - -bool flow_spend(struct amount_msat *ret, struct flow *flow) -{ - assert(ret); - assert(flow); - const size_t pathlen = tal_count(flow->path); - struct amount_msat spend = flow->amount; - - for (int i = (int)pathlen - 2; i >= 0; i--) { - const struct half_chan *h = flow_edge(flow, i + 1); - if (!amount_msat_add_fee(&spend, h->base_fee, - h->proportional_fee)) - goto function_fail; - } - - *ret = spend; - return true; - -function_fail: - return false; -} - -bool flow_fee(struct amount_msat *ret, struct flow *flow) -{ - assert(ret); - assert(flow); - struct amount_msat fee; - struct amount_msat spend; - if (!flow_spend(&spend, flow)) - goto function_fail; - if (!amount_msat_sub(&fee, spend, flow->amount)) - goto function_fail; - - *ret = fee; - return true; - -function_fail: - return false; -} - -bool flowset_fee(struct amount_msat *ret, struct flow **flows) -{ - assert(ret); - assert(flows); - struct amount_msat fee = AMOUNT_MSAT(0); - for (size_t i = 0; i < tal_count(flows); i++) { - struct amount_msat this_fee; - if (!flow_fee(&this_fee, flows[i])) - return false; - if (!amount_msat_add(&fee, this_fee, fee)) - return false; - } - *ret = fee; - return true; -} - -/* Helper to access the half chan at flow index idx */ -const struct half_chan *flow_edge(const struct flow *flow, size_t idx) -{ - assert(flow); - assert(idx < tal_count(flow->path)); - return &flow->path[idx]->half[flow->dirs[idx]]; -} - -/* Assign the delivered amount to the flow if it fits - the path maximum capacity. */ -bool flow_assign_delivery(struct flow *flow, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - struct amount_msat requested_amount) -{ - struct amount_msat max_deliverable = AMOUNT_MSAT(0); - if (flow_maximum_deliverable(&max_deliverable, flow, gossmap, - chan_extra_map, NULL)) - return false; - assert(!amount_msat_is_zero(max_deliverable)); - flow->amount = amount_msat_min(requested_amount, max_deliverable); - return true; -} - -/* Helper function to find the success_prob for a single flow - * - * IMPORTANT: flow->success_prob is misleading, because that's the prob. of - * success provided that there are no other flows in the current MPP flow set. - * */ -double flow_probability(struct flow *flow, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - bool compute_fees) -{ - assert(flow); - assert(gossmap); - assert(chan_extra_map); - const size_t pathlen = tal_count(flow->path); - struct amount_msat spend = flow->amount; - double prob = 1.0; - - for (int i = (int)pathlen - 1; i >= 0; i--) { - const struct half_chan *h = flow_edge(flow, i); - const struct chan_extra_half *eh = get_chan_extra_half_by_chan( - gossmap, chan_extra_map, flow->path[i], flow->dirs[i]); - - prob *= edge_probability(eh->known_min, eh->known_max, - eh->htlc_total, spend); - - if (prob < 0) - goto function_fail; - if (compute_fees && !amount_msat_add_fee(&spend, h->base_fee, - h->proportional_fee)) - goto function_fail; - } - - return prob; - -function_fail: - return -1.; -} - -u64 flow_delay(const struct flow *flow) -{ - u64 delay = 0; - for (size_t i = 0; i < tal_count(flow->path); i++) - delay += flow_edge(flow, i)->delay; - return delay; -} - -u64 flows_worst_delay(struct flow **flows) -{ - u64 maxdelay = 0; - for (size_t i = 0; i < tal_count(flows); i++) { - u64 delay = flow_delay(flows[i]); - if (delay > maxdelay) - maxdelay = delay; - } - return maxdelay; -} - -#ifndef SUPERVERBOSE_ENABLED -#undef SUPERVERBOSE -#endif diff --git a/plugins/renepay/flow.h b/plugins/renepay/flow.h deleted file mode 100644 index 959eb42f3f64..000000000000 --- a/plugins/renepay/flow.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_FLOW_H -#define LIGHTNING_PLUGINS_RENEPAY_FLOW_H -#include "config.h" -#include -#include -#include -#include -#include -#include - -/* An actual partial flow. */ -struct flow { - const struct gossmap_chan **path; - /* The directions to traverse. */ - int *dirs; - /* Amounts for this flow (fees mean this shrinks across path). */ - double success_prob; - struct amount_msat amount; -}; - -const char *fmt_flows(const tal_t *ctx, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - struct flow **flows); - -/* Helper to access the half chan at flow index idx */ -const struct half_chan *flow_edge(const struct flow *flow, size_t idx); - -/* A big number, meaning "don't bother" (not infinite, since you may add) */ -#define FLOW_INF_COST 100000000.0 - -/* Cost function to send @f msat through @c in direction @dir, - * given we already have a flow of prev_flow. */ -double flow_edge_cost(const struct gossmap *gossmap, - const struct gossmap_chan *c, int dir, - const struct amount_msat known_min, - const struct amount_msat known_max, - struct amount_msat prev_flow, - struct amount_msat f, - double mu, - double basefee_penalty, - double delay_riskfactor); - -/* Compute the prob. of success of a set of concurrent set of flows. */ -double flowset_probability(const tal_t *ctx, struct flow **flows, - const struct gossmap *const gossmap, - struct chan_extra_map *chan_extra_map, - bool compute_fees, char **fail); - -/* How much do we need to send to make this flow arrive. */ -bool flow_spend(struct amount_msat *ret, struct flow *flow); - -/* How much do we pay in fees to make this flow arrive. */ -bool flow_fee(struct amount_msat *ret, struct flow *flow); - -bool flowset_fee(struct amount_msat *fee, struct flow **flows); - -bool flowset_delivers(struct amount_msat *delivers, struct flow **flows); - -/* how many channels are being used */ -size_t flowset_size(struct flow **flows); - -static inline struct amount_msat flow_delivers(const struct flow *flow) -{ - return flow->amount; -} - -struct amount_msat *tal_flow_amounts(const tal_t *ctx, const struct flow *flow, - bool compute_fees); - -enum renepay_errorcode -flow_maximum_deliverable(struct amount_msat *max_deliverable, - const struct flow *flow, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan **bad_channel); - -/* Assign the delivered amount to the flow if it fits - the path maximum capacity. */ -bool flow_assign_delivery(struct flow *flow, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - struct amount_msat requested_amount); - -double flow_probability(struct flow *flow, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - bool compute_fees); - -u64 flow_delay(const struct flow *flow); -u64 flows_worst_delay(struct flow **flows); - -struct flow ** -flows_ensure_liquidity_constraints(const tal_t *ctx, struct flow **flows TAKES, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_FLOW_H */ diff --git a/plugins/renepay/payplugin.h b/plugins/renepay/payplugin.h index 1c9aff0113cf..d2c43b0e1473 100644 --- a/plugins/renepay/payplugin.h +++ b/plugins/renepay/payplugin.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/plugins/renepay/route.c b/plugins/renepay/route.c index 3be6f756384d..3af704537cc5 100644 --- a/plugins/renepay/route.c +++ b/plugins/renepay/route.c @@ -23,79 +23,6 @@ struct route *new_route(const tal_t *ctx, u32 groupid, return route; } -/* Construct a route from a flow. - * - * @ctx: allocator - * @groupid, @partid, @payment_hash: unique identification keys for this route - * @final_cltv: final delay required by the payment - * @gossmap: global gossmap - * @flow: the flow to convert to route */ -struct route *flow_to_route(const tal_t *ctx, - u32 groupid, u32 partid, struct sha256 payment_hash, - u32 final_cltv, struct gossmap *gossmap, - struct flow *flow) -{ - struct route *route = - new_route(ctx, groupid, partid, payment_hash, - AMOUNT_MSAT(0), AMOUNT_MSAT(0)); - - size_t pathlen = tal_count(flow->path); - route->hops = tal_arr(route, struct route_hop, pathlen); - - for (size_t i = 0; i < pathlen; i++) { - struct route_hop *hop = &route->hops[i]; - struct gossmap_node *n; - n = gossmap_nth_node(gossmap, flow->path[i], !flow->dirs[i]); - gossmap_node_get_id(gossmap, n, &hop->node_id); - - hop->scid = gossmap_chan_scid(gossmap, flow->path[i]); - hop->direction = flow->dirs[i]; - } - - /* Calculate cumulative delays (backwards) */ - route->hops[pathlen - 1].delay = final_cltv; - route->hops[pathlen - 1].amount = flow->amount; - - for (int i = (int)pathlen - 2; i >= 0; i--) { - const struct half_chan *h = flow_edge(flow, i + 1); - - route->hops[i].delay = route->hops[i + 1].delay + h->delay; - route->hops[i].amount = route->hops[i + 1].amount; - if (!amount_msat_add_fee(&route->hops[i].amount, h->base_fee, - h->proportional_fee)) - goto function_fail; - } - route->success_prob = flow->success_prob; - route->amount = route->hops[pathlen - 1].amount; - route->amount_sent = route->hops[0].amount; - return route; - -function_fail: - return tal_free(route); -} - -struct route **flows_to_routes(const tal_t *ctx, - u32 groupid, u32 partid, - struct sha256 payment_hash, u32 final_cltv, - struct gossmap *gossmap, struct flow **flows) -{ - assert(gossmap); - assert(flows); - const size_t N = tal_count(flows); - struct route **routes = tal_arr(ctx, struct route *, N); - for (size_t i = 0; i < N; i++) { - routes[i] = - flow_to_route(routes, groupid, partid++, - payment_hash, final_cltv, gossmap, flows[i]); - if (!routes[i]) - goto function_fail; - } - return routes; - -function_fail: - return tal_free(routes); -} - const char *fmt_route_path(const tal_t *ctx, const struct route *route) { tal_t *this_ctx = tal(ctx, tal_t); diff --git a/plugins/renepay/route.h b/plugins/renepay/route.h index 52a80d7c2165..272f94236c5c 100644 --- a/plugins/renepay/route.h +++ b/plugins/renepay/route.h @@ -11,7 +11,6 @@ #include #include #include -#include #include struct payment; @@ -114,16 +113,6 @@ struct route *new_route(const tal_t *ctx, u32 groupid, struct amount_msat amount, struct amount_msat amount_sent); -struct route *flow_to_route(const tal_t *ctx, - u32 groupid, u32 partid, struct sha256 payment_hash, - u32 final_cltv, struct gossmap *gossmap, - struct flow *flow); - -struct route **flows_to_routes(const tal_t *ctx, - u32 groupid, u32 partid, - struct sha256 payment_hash, u32 final_cltv, - struct gossmap *gossmap, struct flow **flows); - static inline struct short_channel_id_dir hop_to_scidd(const struct route_hop *hop) { diff --git a/plugins/renepay/test/run-route_map.c b/plugins/renepay/test/run-route_map.c index 5dad081810a1..980f3d887fe7 100644 --- a/plugins/renepay/test/run-route_map.c +++ b/plugins/renepay/test/run-route_map.c @@ -17,7 +17,6 @@ #include #define RENEPAY_UNITTEST -#include "../flow.c" #include "../route.c" /* AUTOGENERATED MOCKS START */ diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c index f23415e67974..7c5f548a8c0f 100644 --- a/plugins/renepay/test/run-testflow.c +++ b/plugins/renepay/test/run-testflow.c @@ -500,237 +500,6 @@ static void test_edge_probability(void) static void remove_file(char *fname) { assert(!remove(fname)); } -static void test_flow_to_route(void) -{ - const double eps = 1e-8; - - const tal_t *this_ctx = tal(tmpctx, tal_t); - - char *gossfile; - int fd = tmpdir_mkstemp(this_ctx,"run-testflow.XXXXXX",&gossfile); - tal_add_destructor(gossfile, remove_file); - - assert(write_all(fd,canned_map,sizeof(canned_map))); - struct gossmap *gossmap = gossmap_load(this_ctx,gossfile,NULL); - assert(gossmap); - - // for(struct gossmap_node *node = gossmap_first_node(gossmap); - // node; - // node=gossmap_next_node(gossmap,node)) - // { - // struct node_id node_id; - // gossmap_node_get_id(gossmap,node,&node_id); - // const char *node_hex = node_id_to_hexstr(this_ctx,&node_id); - // printf("node: %s\n",node_hex); - // for(size_t i=0;inum_chans;++i) - // { - // int half; - // const struct gossmap_chan *c = gossmap_nth_chan(gossmap,node,i,&half); - // if(!gossmap_chan_set(c,half)) - // continue; - // - // const struct short_channel_id scid = gossmap_chan_scid(gossmap,c); - // - // const char *scid_str = short_channel_id_to_str(this_ctx,&scid); - // - // printf(" scid: %s, half: %d\n",scid_str,half); - // } - // } - - struct node_id l1, l2, l3, l4, l5; - assert(node_id_from_hexstr("024f9da0d726adf0d9a4a3ac32e328b93ad527ccb9db7077c57a12c6f9fa9add74", 66, &l1)); - assert(node_id_from_hexstr("037f97af8e4fa67c9b8d6970849536f888c304316d015af5129e268e0164961b5e", 66, &l2)); - assert(node_id_from_hexstr("02451e9baff81faf5f410b0bbe9e36ee83bf8f79698e6605a51a9748bc73c7dc28", 66, &l3)); - assert(node_id_from_hexstr("02d1ab24fe53cfcac4a477745235c7ac3e75161a5bf8426ea8e0182ad58cab367f", 66, &l4)); - assert(node_id_from_hexstr("03caec54085508da06f14ffb836166ca22042d0fda69690356232a24b8366c8f85", 66, &l5)); - - struct short_channel_id scid12, scid23, scid34,scid45; - assert(short_channel_id_from_str("113x1x1", 7, &scid12)); - assert(short_channel_id_from_str("113x3x1", 7, &scid23)); - assert(short_channel_id_from_str("113x2x0", 7, &scid34)); - assert(short_channel_id_from_str("113x4x1", 7, &scid45)); - - struct chan_extra_map *chan_extra_map = tal(this_ctx, struct chan_extra_map); - chan_extra_map_init(chan_extra_map); - - struct chan_extra_half *h0,*h1; - struct gossmap_chan *c; - struct amount_sat cap; - struct amount_msat sum_min1_max0,sum_min0_max1; - - // check the bounds channel 1--2 - c = gossmap_find_chan(gossmap,&scid12); - assert(gossmap_chan_get_capacity(gossmap,c,&cap)); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(0); - h0->known_max = AMOUNT_MSAT(500000000); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(500000000); - h1->known_max = AMOUNT_MSAT(1000000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq_sat(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq_sat(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq_sat(sum_min1_max0,cap)); - assert(amount_msat_eq_sat(sum_min0_max1,cap)); - - // check the bounds channel 2--3 - c = gossmap_find_chan(gossmap,&scid23); - assert(gossmap_chan_get_capacity(gossmap,c,&cap)); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(0); - h1->known_max = AMOUNT_MSAT(2000000000); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(0); - h0->known_max = AMOUNT_MSAT(2000000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq_sat(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq_sat(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq_sat(sum_min1_max0,cap)); - assert(amount_msat_eq_sat(sum_min0_max1,cap)); - - // check the bounds channel 3--4 - c = gossmap_find_chan(gossmap,&scid34); - assert(gossmap_chan_get_capacity(gossmap,c,&cap)); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(500000000); - h0->known_max = AMOUNT_MSAT(1000000000); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(0); - h1->known_max = AMOUNT_MSAT(500000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq_sat(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq_sat(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq_sat(sum_min1_max0,cap)); - assert(amount_msat_eq_sat(sum_min0_max1,cap)); - - // check the bounds channel 4--5 - c = gossmap_find_chan(gossmap,&scid45); - assert(gossmap_chan_get_capacity(gossmap,c,&cap)); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(1000000000); - h0->known_max = AMOUNT_MSAT(2000000000); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(1000000000); - h1->known_max = AMOUNT_MSAT(2000000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq_sat(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq_sat(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq_sat(sum_min1_max0,cap)); - assert(amount_msat_eq_sat(sum_min0_max1,cap)); - - struct flow *F; - struct route *route; - struct sha256 payment_hash; - struct amount_msat deliver; - - if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102", - strlen("0001020304050607080900010203040506070809000102030405060708090102"), - &payment_hash, sizeof(payment_hash))) - abort(); - - // flow 1->2 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,1); - F->dirs = tal_arr(F,int,1); - F->path[0]=gossmap_find_chan(gossmap,&scid12); - F->dirs[0]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount = deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, deliver)); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 0.5)4->5 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,2); - F->dirs = tal_arr(F,int,2); - F->path[0]=gossmap_find_chan(gossmap,&scid34); - F->path[1]=gossmap_find_chan(gossmap,&scid45); - F->dirs[0]=0; - F->dirs[1]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount=deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, amount_msat(250050016))); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 1.)3->4->5 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,3); - F->dirs = tal_arr(F,int,3); - F->path[0]=gossmap_find_chan(gossmap,&scid23); - F->path[1]=gossmap_find_chan(gossmap,&scid34); - F->path[2]=gossmap_find_chan(gossmap,&scid45); - F->dirs[0]=1; - F->dirs[1]=0; - F->dirs[2]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount=deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, amount_msat(250087534))); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 1. + 250.087534/2000)2->3->4->5 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,4); - F->dirs = tal_arr(F,int,4); - F->path[0]=gossmap_find_chan(gossmap,&scid12); - F->path[1]=gossmap_find_chan(gossmap,&scid23); - F->path[2]=gossmap_find_chan(gossmap,&scid34); - F->path[3]=gossmap_find_chan(gossmap,&scid45); - F->dirs[0]=0; - F->dirs[1]=1; - F->dirs[2]=0; - F->dirs[3]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount=deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, amount_msat(250112544))); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 0.43728117) Date: Mon, 23 Sep 2024 09:01:14 +0100 Subject: [PATCH 22/30] renepay: remove unused struct chan_extra Signed-off-by: Lagrang3 --- plugins/renepay/Makefile | 2 - plugins/renepay/chan_extra.c | 682 ---------------------------- plugins/renepay/chan_extra.h | 209 --------- plugins/renepay/test/Makefile | 1 - plugins/renepay/test/run-testflow.c | 587 ------------------------ 5 files changed, 1481 deletions(-) delete mode 100644 plugins/renepay/chan_extra.c delete mode 100644 plugins/renepay/chan_extra.h delete mode 100644 plugins/renepay/test/run-testflow.c diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index ea35454ea3d7..2b8ff31502f7 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -1,7 +1,6 @@ PLUGIN_RENEPAY_SRC := \ plugins/renepay/main.c \ plugins/renepay/payment.c \ - plugins/renepay/chan_extra.c \ plugins/renepay/route.c \ plugins/renepay/routetracker.c \ plugins/renepay/routefail.c \ @@ -13,7 +12,6 @@ PLUGIN_RENEPAY_HDRS := \ plugins/renepay/payplugin.h \ plugins/renepay/payment.h \ plugins/renepay/payment_info.h \ - plugins/renepay/chan_extra.h \ plugins/renepay/renepayconfig.h \ plugins/renepay/route.h \ plugins/renepay/routetracker.h \ diff --git a/plugins/renepay/chan_extra.c b/plugins/renepay/chan_extra.c deleted file mode 100644 index 57183227ea3b..000000000000 --- a/plugins/renepay/chan_extra.c +++ /dev/null @@ -1,682 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include - -bool chan_extra_is_busy(const struct chan_extra *const ce) -{ - if (ce == NULL) - return false; - return ce->half[0].num_htlcs || ce->half[1].num_htlcs; -} - -const char *fmt_chan_extra_map(const tal_t *ctx, - struct chan_extra_map *chan_extra_map) -{ - tal_t *this_ctx = tal(ctx, tal_t); - char *buff = tal_fmt(ctx, "Uncertainty network:\n"); - struct chan_extra_map_iter it; - for (struct chan_extra *ch = chan_extra_map_first(chan_extra_map, &it); - ch; ch = chan_extra_map_next(chan_extra_map, &it)) { - const char *scid_str = fmt_short_channel_id(this_ctx, ch->scid); - for (int dir = 0; dir < 2; ++dir) { - tal_append_fmt( - &buff, "%s[%d]:(%s,%s) htlc: %s\n", scid_str, dir, - fmt_amount_msat(this_ctx, ch->half[dir].known_min), - fmt_amount_msat(this_ctx, ch->half[dir].known_max), - fmt_amount_msat(this_ctx, ch->half[dir].htlc_total)); - } - } - tal_free(this_ctx); - return buff; -} - -const char *fmt_chan_extra_details(const tal_t *ctx, - const struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - const tal_t *this_ctx = tal(ctx, tal_t); - const struct chan_extra *ce = - chan_extra_map_get(chan_extra_map, scidd->scid); - const struct chan_extra_half *ch; - char *str = tal_strdup(ctx, ""); - char sep = '('; - - if (!ce) { - // we have no information on this channel - tal_append_fmt(&str, "()"); - goto finished; - } - - ch = &ce->half[scidd->dir]; - if (ch->num_htlcs != 0) { - tal_append_fmt(&str, "%c%s in %zu htlcs", sep, - fmt_amount_msat(this_ctx, ch->htlc_total), - ch->num_htlcs); - sep = ','; - } - /* Happens with local channels, where we're certain. */ - if (amount_msat_eq(ch->known_min, ch->known_max)) { - tal_append_fmt(&str, "%cmin=max=%s", sep, - fmt_amount_msat(this_ctx, ch->known_min)); - sep = ','; - } else { - if (amount_msat_greater(ch->known_min, AMOUNT_MSAT(0))) { - tal_append_fmt( - &str, "%cmin=%s", sep, - fmt_amount_msat(this_ctx, ch->known_min)); - sep = ','; - } - if (!amount_msat_eq(ch->known_max, ce->capacity)) { - tal_append_fmt( - &str, "%cmax=%s", sep, - fmt_amount_msat(this_ctx, ch->known_max)); - sep = ','; - } - } - if (!streq(str, "")) - tal_append_fmt(&str, ")"); - -finished: - tal_free(this_ctx); - return str; -} - -struct chan_extra *new_chan_extra(struct chan_extra_map *chan_extra_map, - const struct short_channel_id scid, - struct amount_msat capacity) -{ - assert(chan_extra_map); - struct chan_extra *ce = tal(chan_extra_map, struct chan_extra); - if (!ce) - return ce; - - ce->scid = scid; - ce->capacity = capacity; - for (size_t i = 0; i <= 1; i++) { - ce->half[i].num_htlcs = 0; - ce->half[i].htlc_total = AMOUNT_MSAT(0); - ce->half[i].known_min = AMOUNT_MSAT(0); - ce->half[i].known_max = capacity; - } - if (!chan_extra_map_add(chan_extra_map, ce)) { - return tal_free(ce); - } - - /* Remove self from map when done */ - // TODO(eduardo): - // Is this desctructor really necessary? the chan_extra will deallocated - // when the chan_extra_map is freed. Anyways valgrind complains that the - // hash table is removing the element with a freed pointer. - // tal_add_destructor2(ce, destroy_chan_extra, chan_extra_map); - return ce; -} - -/* Based on the knowledge that we have and HTLCs, returns the greatest - * amount that we can send through this channel. */ -enum renepay_errorcode channel_liquidity(struct amount_msat *liquidity, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, - const int dir) -{ - const struct chan_extra_half *h = - get_chan_extra_half_by_chan(gossmap, chan_extra_map, chan, dir); - if (!h) - return RENEPAY_CHANNEL_NOT_FOUND; - struct amount_msat value_liquidity = h->known_max; - if (!amount_msat_sub(&value_liquidity, value_liquidity, h->htlc_total)) - return RENEPAY_AMOUNT_OVERFLOW; - *liquidity = value_liquidity; - return RENEPAY_NOERROR; -} - -/* Checks BOLT 7 HTLC fee condition: - * recv >= base_fee + (send*proportional_fee)/1000000 */ -bool check_fee_inequality(struct amount_msat recv, struct amount_msat send, - u64 base_fee, u64 proportional_fee) -{ - // nothing to forward, any incoming amount is good - if (amount_msat_is_zero(send)) - return true; - // FIXME If this addition fails we return false. The caller will not be - // able to know that there was an addition overflow, he will just assume - // that the fee inequality was not satisfied. - if (!amount_msat_add_fee(&send, base_fee, proportional_fee)) - return false; - return amount_msat_greater_eq(recv, send); -} - -/* Let `recv` be the maximum amount this channel can receive, this function - * computes the maximum amount this channel can forward `send`. - * From BOLT7 specification wee need to satisfy the following inequality: - * - * recv-send >= base_fee + floor(send*proportional_fee/1000000) - * - * That is equivalent to have - * - * send <= Bound(recv,send) - * - * where - * - * Bound(recv, send) = ((recv - base_fee)*1000000 + (send*proportional_fee) - *% 1000000)/(proportional_fee+1000000) - * - * However the quantity we want to determine, `send`, appears on both sides of - * the equation. However the term `send*proportional_fee) % 1000000` only - * contributes by increasing the bound by at most one so that we can neglect - * the extra term and use instead - * - * Bound_simple(recv) = ((recv - - *base_fee)*1000000)/(proportional_fee+1000000) - * - * as the upper bound for `send`. Formally one can check that - * - * Bound_simple(recv) <= Bound(recv, send) < Bound_simple(recv) + 2 - * - * So that if one wishes to find the very highest value of `send` that - * satisfies - * - * send <= Bound(recv, send) - * - * it is enough to compute - * - * send = Bound_simple(recv) - * - * which already satisfies the fee equation and then try to go higher - * with send+1, send+2, etc. But we know that it is enough to try up to - * send+1 because Bound(recv, send) < Bound_simple(recv) + 2. - * */ -enum renepay_errorcode channel_maximum_forward(struct amount_msat *max_forward, - const struct gossmap_chan *chan, - const int dir, - struct amount_msat recv) -{ - const u64 b = chan->half[dir].base_fee, - p = chan->half[dir].proportional_fee; - - const u64 one_million = 1000000; - u64 x_msat = - recv.millisatoshis; /* Raw: need to invert the fee equation */ - - // special case, when recv - base_fee <= 0, we cannot forward anything - if (x_msat <= b) { - *max_forward = amount_msat(0); - return RENEPAY_NOERROR; - } - - x_msat -= b; - - if (mul_overflows_u64(one_million, x_msat)) - return RENEPAY_AMOUNT_OVERFLOW; - - struct amount_msat best_send = - AMOUNT_MSAT_INIT((one_million * x_msat) / (one_million + p)); - - /* Try to increase the value we send (up tp the last millisat) until we - * fail to fulfill the fee inequality. It takes only one iteration - * though. */ - for (size_t i = 0; i < 10; ++i) { - struct amount_msat next_send; - if (!amount_msat_add(&next_send, best_send, amount_msat(1))) - return RENEPAY_AMOUNT_OVERFLOW; - - if (check_fee_inequality(recv, next_send, b, p)) - best_send = next_send; - else - break; - } - *max_forward = best_send; - return RENEPAY_NOERROR; -} - -/* This helper function preserves the uncertainty network invariant after the - * knowledge is updated. It assumes that the (channel,!dir) knowledge is - * correct. */ -static enum renepay_errorcode chan_extra_adjust_half(struct chan_extra *ce, - int dir) -{ - assert(ce); - assert(dir == 0 || dir == 1); - - struct amount_msat new_known_max, new_known_min; - - if (!amount_msat_sub(&new_known_max, ce->capacity, - ce->half[!dir].known_min) || - !amount_msat_sub(&new_known_min, ce->capacity, - ce->half[!dir].known_max)) - return RENEPAY_AMOUNT_OVERFLOW; - - ce->half[dir].known_max = new_known_max; - ce->half[dir].known_min = new_known_min; - return RENEPAY_NOERROR; -} - -/* Update the knowledge that this (channel,direction) can send x msat.*/ -static enum renepay_errorcode -chan_extra_can_send_(struct chan_extra *ce, int dir, struct amount_msat x) -{ - assert(ce); - assert(dir == 0 || dir == 1); - enum renepay_errorcode err; - - if (amount_msat_greater(x, ce->capacity)) - return RENEPAY_PRECONDITION_ERROR; - - struct amount_msat known_min, known_max; - - // in case we fail, let's remember the original state - known_min = ce->half[dir].known_min; - known_max = ce->half[dir].known_max; - - ce->half[dir].known_min = amount_msat_max(ce->half[dir].known_min, x); - ce->half[dir].known_max = amount_msat_max(ce->half[dir].known_max, x); - - err = chan_extra_adjust_half(ce, !dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - - return RENEPAY_NOERROR; - -restore_and_fail: - // we fail, thus restore the original state - ce->half[dir].known_min = known_min; - ce->half[dir].known_max = known_max; - return err; -} - -enum renepay_errorcode -chan_extra_can_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - assert(scidd); - assert(chan_extra_map); - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - return chan_extra_can_send_(ce, scidd->dir, - ce->half[scidd->dir].htlc_total); -} - -/* Update the knowledge that this (channel,direction) cannot send.*/ -enum renepay_errorcode -chan_extra_cannot_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - assert(scidd); - assert(chan_extra_map); - struct amount_msat x; - enum renepay_errorcode err; - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - - /* Note: sent is already included in htlc_total! */ - if (!amount_msat_sub(&x, ce->half[scidd->dir].htlc_total, - AMOUNT_MSAT(1))) - return RENEPAY_AMOUNT_OVERFLOW; - - struct amount_msat known_min, known_max; - // in case we fail, let's remember the original state - known_min = ce->half[scidd->dir].known_min; - known_max = ce->half[scidd->dir].known_max; - - /* If we "knew" the capacity was at least this, we just showed we're - * wrong! */ - if (amount_msat_less(x, ce->half[scidd->dir].known_min)) { - /* Skip to half of x, since we don't know (rounds down) */ - ce->half[scidd->dir].known_min = amount_msat_div(x, 2); - } - - ce->half[scidd->dir].known_max = - amount_msat_min(ce->half[scidd->dir].known_max, x); - - err = chan_extra_adjust_half(ce, !scidd->dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - return err; - -restore_and_fail: - // we fail, thus restore the original state - ce->half[scidd->dir].known_min = known_min; - ce->half[scidd->dir].known_max = known_max; - return err; -} - -/* Update the knowledge that this (channel,direction) has liquidity x.*/ -// FIXME for being this low level API, I thinkg it's too much to have verbose -// error messages -static enum renepay_errorcode chan_extra_set_liquidity_(struct chan_extra *ce, - int dir, - struct amount_msat min, - struct amount_msat max) -{ - assert(ce); - assert(dir == 0 || dir == 1); - enum renepay_errorcode err; - - if (amount_msat_greater(max, ce->capacity) || - amount_msat_greater(min, max)) - return RENEPAY_PRECONDITION_ERROR; - - // in case we fail, let's remember the original state - struct amount_msat known_min, known_max; - known_min = ce->half[dir].known_min; - known_max = ce->half[dir].known_max; - - ce->half[dir].known_min = min; - ce->half[dir].known_max = max; - - err = chan_extra_adjust_half(ce, !dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - return err; - -restore_and_fail: - // we fail, thus restore the original state - ce->half[dir].known_min = known_min; - ce->half[dir].known_max = known_max; - return err; -} - -enum renepay_errorcode -chan_extra_set_liquidity(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max) -{ - assert(scidd); - assert(chan_extra_map); - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - - return chan_extra_set_liquidity_(ce, scidd->dir, min, max); -} - -/* Update the knowledge that this (channel,direction) has sent x msat.*/ -enum renepay_errorcode -chan_extra_sent_success(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat x) -{ - assert(scidd); - assert(chan_extra_map); - - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - - // if we sent amount x, it first means that all htlcs on this channel - // fit in the liquidity - enum renepay_errorcode err; - err = chan_extra_can_send(chan_extra_map, scidd); - if (err != RENEPAY_NOERROR) - return err; - - if (amount_msat_greater(x, ce->capacity)) - return RENEPAY_PRECONDITION_ERROR; - - // in case we fail, let's remember the original state - struct amount_msat known_min, known_max; - known_min = ce->half[scidd->dir].known_min; - known_max = ce->half[scidd->dir].known_max; - - struct amount_msat new_a, new_b; - - if (!amount_msat_sub(&new_a, ce->half[scidd->dir].known_min, x)) - new_a = AMOUNT_MSAT(0); - if (!amount_msat_sub(&new_b, ce->half[scidd->dir].known_max, x)) - new_b = AMOUNT_MSAT(0); - - ce->half[scidd->dir].known_min = new_a; - ce->half[scidd->dir].known_max = new_b; - - err = chan_extra_adjust_half(ce, !scidd->dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - - return err; - -// we fail, thus restore the original state -restore_and_fail: - ce->half[scidd->dir].known_min = known_min; - ce->half[scidd->dir].known_max = known_max; - return err; -} - -/* Forget a bit about this (channel,direction) state. */ -static enum renepay_errorcode chan_extra_relax(struct chan_extra *ce, int dir, - struct amount_msat down, - struct amount_msat up) -{ - assert(ce); - assert(dir == 0 || dir == 1); - struct amount_msat new_a, new_b; - enum renepay_errorcode err; - - if (!amount_msat_sub(&new_a, ce->half[dir].known_min, down)) - new_a = AMOUNT_MSAT(0); - if (!amount_msat_add(&new_b, ce->half[dir].known_max, up)) - new_b = ce->capacity; - new_b = amount_msat_min(new_b, ce->capacity); - - // in case we fail, let's remember the original state - struct amount_msat known_min, known_max; - known_min = ce->half[dir].known_min; - known_max = ce->half[dir].known_max; - - ce->half[dir].known_min = new_a; - ce->half[dir].known_max = new_b; - - err = chan_extra_adjust_half(ce, !dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - return err; - -// we fail, thus restore the original state -restore_and_fail: - ce->half[dir].known_min = known_min; - ce->half[dir].known_max = known_max; - return err; -} - -/* Forget the channel information by a fraction of the capacity. */ -enum renepay_errorcode chan_extra_relax_fraction(struct chan_extra *ce, - double fraction) -{ - assert(ce); - assert(fraction >= 0); - /* Allow to have values greater than 1 to indicate full relax. */ - // assert(fraction<=1); - fraction = fabs(fraction); // this number is always non-negative - fraction = MIN(1.0, fraction); // this number cannot be greater than 1. - struct amount_msat delta = - amount_msat(ce->capacity.millisatoshis*fraction); /* Raw: get a fraction of the capacity */ - - /* The direction here is not important because the 'down' and the 'up' - * limits are changed by the same amount. - * Notice that if chan[0] with capacity C changes from (a,b) to - * (a-d,b+d) then its counterpart chan[1] changes from (C-b,C-a) to - * (C-b-d,C-a+d), hence both dirs are applied the same transformation. - */ - return chan_extra_relax(ce, /*dir=*/0, delta, delta); -} - -/* Returns either NULL, or an entry from the hash */ -struct chan_extra_half * -get_chan_extra_half_by_scid(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - assert(scidd); - assert(chan_extra_map); - struct chan_extra *ce; - - ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return NULL; - return &ce->half[scidd->dir]; -} -/* Helper if we have a gossmap_chan */ -struct chan_extra_half * -get_chan_extra_half_by_chan(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir) -{ - assert(chan); - assert(dir == 0 || dir == 1); - assert(gossmap); - assert(chan_extra_map); - struct short_channel_id_dir scidd; - - scidd.scid = gossmap_chan_scid(gossmap, chan); - scidd.dir = dir; - return get_chan_extra_half_by_scid(chan_extra_map, &scidd); -} - -// static void destroy_chan_extra(struct chan_extra *ce, -// struct chan_extra_map *chan_extra_map) -// { -// chan_extra_map_del(chan_extra_map, ce); -// } -/* Helper to get the chan_extra_half. If it doesn't exist create a new one. */ -struct chan_extra_half * -get_chan_extra_half_by_chan_verify(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir) -{ - assert(chan); - assert(dir == 0 || dir == 1); - assert(gossmap); - assert(chan_extra_map); - struct short_channel_id_dir scidd; - - scidd.scid = gossmap_chan_scid(gossmap, chan); - scidd.dir = dir; - struct chan_extra_half *h = - get_chan_extra_half_by_scid(chan_extra_map, &scidd); - if (!h) { - struct amount_sat cap; - struct amount_msat cap_msat; - - if (!gossmap_chan_get_capacity(gossmap, chan, &cap) || - !amount_sat_to_msat(&cap_msat, cap)) { - return NULL; - } - h = &new_chan_extra(chan_extra_map, scidd.scid, cap_msat) - ->half[scidd.dir]; - } - return h; -} - -/* Assuming a uniform distribution, what is the chance this f gets through? - * Here we compute the conditional probability of success for a flow f, given - * the knowledge that the liquidity is in the range [a,b) and some amount - * x is already committed on another part of the payment. - * - * The probability equation for x=0 is: - * - * prob(f) = - * - * for f=f>=a: (b-f)/(b-a) - * for b0 the prob. of success for passing x and f is: - * - * prob(f and x) = prob(x) * prob(f|x) - * - * and it can be shown to be equal to - * - * prob(f and x) = prob(f+x) - * - * The purpose of this function is to obtain prob(f|x), i.e. the probability of - * getting f through provided that we already succeeded in getting x. - * This conditional probability comes with 4 cases: - * - * prob(f|x) = - * - * for x=a-x: (b-x-f)/(b-a) - * for x>=a: (b-x-f)/(b-x) - * for f>b-x: 0. - * - * This is the same as the probability of success of f when the bounds are - * shifted by x amount, the new bounds be [MAX(0,a-x),b-x). - */ -double edge_probability(struct amount_msat min, struct amount_msat max, - struct amount_msat in_flight, struct amount_msat f) -{ - assert(amount_msat_less_eq(min, max)); - assert(amount_msat_less_eq(in_flight, max)); - - const struct amount_msat one = AMOUNT_MSAT(1); - struct amount_msat B = max; // = max +1 - in_flight - - // one past the last known value, makes computations simpler - if (!amount_msat_accumulate(&B, one)) - goto function_fail; - - // in_flight cannot be greater than max - if (!amount_msat_sub(&B, B, in_flight)) - goto function_fail; - - struct amount_msat A = min; // = MAX(0,min-in_flight); - - if (!amount_msat_sub(&A, A, in_flight)) - A = AMOUNT_MSAT(0); - - struct amount_msat denominator; // = B-A - - // B cannot be smaller than or equal A - if (!amount_msat_sub(&denominator, B, A) || amount_msat_less_eq(B, A)) - goto function_fail; - - struct amount_msat numerator; // MAX(0,B-f) - - if (!amount_msat_sub(&numerator, B, f)) - numerator = AMOUNT_MSAT(0); - - return amount_msat_less_eq(f, A) - ? 1.0 - : amount_msat_ratio(numerator, denominator); - -function_fail: - return -1; -} - -enum renepay_errorcode -chan_extra_remove_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount) -{ - struct chan_extra_half *h = - get_chan_extra_half_by_scid(chan_extra_map, scidd); - if (!h) - return RENEPAY_CHANNEL_NOT_FOUND; - if (h->num_htlcs <= 0) - return RENEPAY_PRECONDITION_ERROR; - - if (!amount_msat_sub(&h->htlc_total, h->htlc_total, amount)) - return RENEPAY_AMOUNT_OVERFLOW; - h->num_htlcs--; - return RENEPAY_NOERROR; -} - -enum renepay_errorcode -chan_extra_commit_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount) -{ - struct chan_extra_half *h = - get_chan_extra_half_by_scid(chan_extra_map, scidd); - if (!h) - return RENEPAY_CHANNEL_NOT_FOUND; - if (!amount_msat_accumulate(&h->htlc_total, amount)) - return RENEPAY_AMOUNT_OVERFLOW; - h->num_htlcs++; - return RENEPAY_NOERROR; -} diff --git a/plugins/renepay/chan_extra.h b/plugins/renepay/chan_extra.h deleted file mode 100644 index cf06d166a8fd..000000000000 --- a/plugins/renepay/chan_extra.h +++ /dev/null @@ -1,209 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_CHAN_EXTRA_H -#define LIGHTNING_PLUGINS_RENEPAY_CHAN_EXTRA_H - -#include "config.h" -#include -#include -#include -#include -#include - -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) - -/* Any implementation needs to keep some data on channels which are - * in-use (or about which we have extra information). We use a hash - * table here, since most channels are not in use. */ -// TODO(eduardo): if we know the liquidity of channel (X,dir) is [A,B] -// then we also know that the liquidity of channel (X,!dir) is [Cap-B,Cap-A]. -// This means that it is redundant to store known_min and known_max for both -// halves of the channel and it also means that once we update the knowledge of -// (X,dir) the knowledge of (X,!dir) is updated as well. -struct chan_extra { - struct short_channel_id scid; - struct amount_msat capacity; - - struct chan_extra_half { - /* How many htlcs we've directed through it */ - size_t num_htlcs; - - /* The total size of those HTLCs */ - struct amount_msat htlc_total; - - /* The known minimum / maximum capacity (if nothing known, - * 0/capacity */ - struct amount_msat known_min, known_max; - } half[2]; -}; - -bool chan_extra_is_busy(const struct chan_extra *const ce); - -static inline const struct short_channel_id -chan_extra_scid(const struct chan_extra *cd) -{ - return cd->scid; -} - -static inline bool chan_extra_eq_scid(const struct chan_extra *cd, - const struct short_channel_id scid) -{ - return short_channel_id_eq(scid, cd->scid); -} - -HTABLE_DEFINE_TYPE(struct chan_extra, chan_extra_scid, short_channel_id_hash, - chan_extra_eq_scid, chan_extra_map); - -/* Helpers for chan_extra_map */ -/* Channel knowledge invariants: - * - * 0<=a<=b<=capacity - * - * a_inv = capacity-b - * b_inv = capacity-a - * - * where a,b are the known minimum and maximum liquidities, and a_inv and b_inv - * are the known minimum and maximum liquidities for the channel in the opposite - * direction. - * - * Knowledge update operations can be: - * - * 1. set liquidity (x) - * (a,b) -> (x,x) - * - * The entropy is minimum here (=0). - * - * 2. can send (x): - * xb = min(x,capacity) - * (a,b) -> (max(a,xb),max(b,xb)) - * - * If x<=a then there is no new knowledge and the entropy remains - * the same. - * If x>a the entropy decreases. - * - * - * 3. can't send (x): - * xb = max(0,x-1) - * (a,b) -> (min(a,xb),min(b,xb)) - * - * If x>b there is no new knowledge and the entropy remains. - * If x<=b then the entropy decreases. - * - * 4. sent success (x): - * (a,b) -> (max(0,a-x),max(0,b-x)) - * - * If x<=a there is no new knowledge and the entropy remains. - * If a (max(0,a-x),min(capacity,b+y)) - * - * Entropy increases unless it is already maximum. - * */ - -const char *fmt_chan_extra_map(const tal_t *ctx, - struct chan_extra_map *chan_extra_map); - -/* Returns "" if nothing useful known about channel, otherwise - * "(details)" */ -const char *fmt_chan_extra_details(const tal_t *ctx, - const struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); - -/* Creates a new chan_extra and adds it to the chan_extra_map. */ -struct chan_extra *new_chan_extra(struct chan_extra_map *chan_extra_map, - const struct short_channel_id scid, - struct amount_msat capacity); - -/* Update the knowledge that this (channel,direction) can send x msat.*/ -enum renepay_errorcode -chan_extra_can_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); - -/* Update the knowledge that this (channel,direction) cannot send x msat.*/ -enum renepay_errorcode -chan_extra_cannot_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); - -enum renepay_errorcode -chan_extra_remove_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount); - -enum renepay_errorcode -chan_extra_commit_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount); - - -/* Update the knowledge that this (channel,direction) has liquidity x.*/ -enum renepay_errorcode -chan_extra_set_liquidity(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max); - -/* Update the knowledge that this (channel,direction) has sent x msat.*/ -enum renepay_errorcode -chan_extra_sent_success(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat x); - -/* Forget the channel information by a fraction of the capacity. */ -enum renepay_errorcode chan_extra_relax_fraction(struct chan_extra *ce, - double fraction); - -/* Returns either NULL, or an entry from the hash */ -struct chan_extra_half * -get_chan_extra_half_by_scid(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); -/* If the channel is not registered, then a new entry is created. scid must be - * present in the gossmap. */ -struct chan_extra_half * -get_chan_extra_half_by_chan_verify(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir); - -/* Helper if we have a gossmap_chan */ -struct chan_extra_half * -get_chan_extra_half_by_chan(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir); - -/* Based on the knowledge that we have and HTLCs, returns the greatest - * amount that we can send through this channel. */ -enum renepay_errorcode channel_liquidity(struct amount_msat *liquidity, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, - const int dir); - -/* inputs - * @chan: a channel - * @recv: how much can we send to this channels - * - * output - * @max_forward: how much can we ask this channel to forward to the next hop - * */ -enum renepay_errorcode channel_maximum_forward(struct amount_msat *max_forward, - const struct gossmap_chan *chan, - const int dir, - struct amount_msat recv); - -/* Assume a uniform distribution: - * @min, @max: the bounds of liquidity - * @in_flight: htlcs - * - * @f: the amount we want to forward - * - * returns the probability that this forward request gets through. - * */ -double edge_probability(struct amount_msat min, struct amount_msat max, - struct amount_msat in_flight, struct amount_msat f); - -/* Checks BOLT 7 HTLC fee condition: - * recv >= base_fee + (send*proportional_fee)/1000000 */ -bool check_fee_inequality(struct amount_msat recv, struct amount_msat send, - u64 base_fee, u64 proportional_fee); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_CHAN_EXTRA_H */ diff --git a/plugins/renepay/test/Makefile b/plugins/renepay/test/Makefile index 9e3087963457..a80a0d0b6d67 100644 --- a/plugins/renepay/test/Makefile +++ b/plugins/renepay/test/Makefile @@ -9,7 +9,6 @@ ALL_TEST_PROGRAMS += $(PLUGIN_RENEPAY_TEST_PROGRAMS) $(PLUGIN_RENEPAY_TEST_OBJS): $(PLUGIN_RENEPAY_SRC) PLUGIN_RENEPAY_TEST_COMMON_OBJS := \ - plugins/renepay/chan_extra.o \ bitcoin/chainparams.o \ common/gossmap.o \ common/fp16.o \ diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c deleted file mode 100644 index 7c5f548a8c0f..000000000000 --- a/plugins/renepay/test/run-testflow.c +++ /dev/null @@ -1,587 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MYLOG "/tmp/debug.txt" -#define RENEPAY_UNITTEST // logs are written in MYLOG -#include "../payment.c" -#include "../flow.c" -#include "../route.c" - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for json_add_payment */ -void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED) -{ fprintf(stderr, "json_add_payment called!\n"); abort(); } -/* Generated stub for new_routetracker */ -struct routetracker *new_routetracker(const tal_t *ctx UNNEEDED, struct payment *payment UNNEEDED) -{ fprintf(stderr, "new_routetracker called!\n"); abort(); } -/* Generated stub for pay_plugin */ -struct pay_plugin *pay_plugin; -/* Generated stub for routetracker_cleanup */ -void routetracker_cleanup(struct routetracker *routetracker UNNEEDED) -{ fprintf(stderr, "routetracker_cleanup called!\n"); abort(); } -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static const u8 canned_map[] = { -0x0c, 0x80, 0x00, 0x01, 0xbc, 0x86, 0xe4, 0xbf, 0x95, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, -0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x22, 0x6e, -0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, -0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, -0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x4f, 0x9d, 0xa0, 0xd7, 0x26, 0xad, 0xf0, 0xd9, 0xa4, 0xa3, -0xac, 0x32, 0xe3, 0x28, 0xb9, 0x3a, 0xd5, 0x27, 0xcc, 0xb9, 0xdb, 0x70, 0x77, 0xc5, 0x7a, 0x12, -0xc6, 0xf9, 0xfa, 0x9a, 0xdd, 0x74, 0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, -0x69, 0x70, 0x84, 0x95, 0x36, 0xf8, 0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, -0x26, 0x8e, 0x01, 0x64, 0x96, 0x1b, 0x5e, 0x03, 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, -0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, -0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f, 0x03, 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, -0x40, 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, -0xff, 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f, 0x40, 0x00, 0x01, 0xb0, 0x24, 0x3a, 0xa3, -0x76, 0x64, 0x62, 0x19, 0xec, 0x01, 0x00, 0x66, 0x7f, 0x0f, 0xad, 0x6d, 0x9d, 0x58, 0x1b, 0x28, -0x8a, 0x67, 0x9d, 0xf8, 0xd1, 0x9d, 0x79, 0x4e, 0x67, 0xc8, 0x76, 0xbb, 0xdd, 0x4d, 0x8e, 0x45, -0x0d, 0xc9, 0x0e, 0x24, 0x76, 0xda, 0x44, 0x68, 0x7b, 0xe2, 0x14, 0xe8, 0x48, 0xfa, 0xd7, 0xc2, -0x35, 0xc5, 0x98, 0xd9, 0x7a, 0x6c, 0xcb, 0xb1, 0x4b, 0x19, 0xf9, 0xfa, 0xb2, 0x19, 0x3f, 0x87, -0xc1, 0xe9, 0x47, 0x51, 0x16, 0x64, 0x36, 0x2a, 0xeb, 0xc5, 0xaa, 0x20, 0x59, 0x4e, 0xdf, 0xae, -0x4e, 0x10, 0x38, 0x34, 0x8e, 0x06, 0x6e, 0x5d, 0x1b, 0x44, 0x30, 0xfb, 0x20, 0xed, 0xea, 0xde, -0x83, 0xcd, 0xa4, 0x8a, 0x5c, 0xad, 0x70, 0x2d, 0x8b, 0x04, 0xfb, 0xa2, 0xbd, 0x95, 0x7c, 0xdd, -0x66, 0xb5, 0x4e, 0xd6, 0xc6, 0x27, 0xdb, 0xa8, 0xe1, 0x26, 0x22, 0x81, 0x57, 0xe2, 0xaa, 0xe4, -0x82, 0xbe, 0x9e, 0x90, 0xc5, 0xc2, 0x59, 0x56, 0x9b, 0x79, 0xf3, 0xc3, 0xfe, 0x0c, 0xb3, 0x35, -0xeb, 0xba, 0xad, 0xf7, 0xd3, 0x24, 0x4e, 0x16, 0x15, 0x2d, 0x86, 0xd9, 0xe9, 0xd2, 0x38, 0x9b, -0xf9, 0xb3, 0x5f, 0x2c, 0x9b, 0xeb, 0xe0, 0x1c, 0xb3, 0xf0, 0x0f, 0xc1, 0x9d, 0x0b, 0x20, 0xa2, -0x19, 0xeb, 0x1a, 0x05, 0x8b, 0x8d, 0xb1, 0x22, 0x74, 0x7c, 0xa4, 0x39, 0x94, 0x6f, 0xfc, 0x34, -0x1b, 0xe5, 0x9f, 0x45, 0x8e, 0x12, 0x6e, 0x65, 0x73, 0x28, 0x21, 0x80, 0xfd, 0x9c, 0x0c, 0x89, -0x2b, 0xcb, 0x43, 0x2e, 0x7f, 0x47, 0xa1, 0xd7, 0x7e, 0xa9, 0xd7, 0x3e, 0xdd, 0xa0, 0xf8, 0x60, -0x9d, 0xde, 0x51, 0x3d, 0xc4, 0x21, 0x06, 0x61, 0xb3, 0x4d, 0xd8, 0x94, 0x4a, 0x3a, 0xc9, 0xb9, -0xc3, 0xcb, 0x09, 0xa3, 0x2f, 0x7b, 0x96, 0x53, 0x13, 0x1d, 0x6d, 0x7a, 0x28, 0xdd, 0xc8, 0x8d, -0xe4, 0x10, 0xad, 0x4c, 0xc6, 0xa0, 0x1b, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x00, -0x01, 0x02, 0x4f, 0x9d, 0xa0, 0xd7, 0x26, 0xad, 0xf0, 0xd9, 0xa4, 0xa3, 0xac, 0x32, 0xe3, 0x28, -0xb9, 0x3a, 0xd5, 0x27, 0xcc, 0xb9, 0xdb, 0x70, 0x77, 0xc5, 0x7a, 0x12, 0xc6, 0xf9, 0xfa, 0x9a, -0xdd, 0x74, 0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, 0x69, 0x70, 0x84, 0x95, -0x36, 0xf8, 0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, 0x26, 0x8e, 0x01, 0x64, -0x96, 0x1b, 0x5e, 0x02, 0xca, 0x1a, 0xac, 0x5f, 0x7b, 0x86, 0x3a, 0x01, 0xc8, 0x69, 0x90, 0x82, -0xdf, 0x9a, 0x4d, 0xf8, 0x14, 0x0d, 0xd6, 0xe7, 0x10, 0x59, 0xd4, 0xec, 0x7f, 0x48, 0x13, 0xb0, -0x96, 0xb4, 0xa3, 0xad, 0x02, 0x21, 0x55, 0x92, 0x46, 0x1c, 0x84, 0x3d, 0x40, 0xe6, 0x01, 0x8d, -0x3d, 0x0c, 0xb6, 0xf4, 0xe1, 0x61, 0xe2, 0x4b, 0x59, 0x41, 0xdb, 0x3b, 0x20, 0x44, 0xbc, 0x0c, -0xb2, 0x0e, 0x4d, 0x3f, 0x9b, 0x00, 0x00, 0x00, 0x0a, 0x91, 0x11, 0x83, 0xf6, 0x00, 0x00, 0x00, -0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0xc0, 0x00, 0x00, 0x8a, 0x01, -0x3d, 0x6f, 0x9a, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x4c, 0x45, 0x7e, 0x21, 0xb8, 0xd5, 0x36, -0x98, 0xcd, 0x45, 0x03, 0x78, 0xa6, 0x51, 0xf1, 0xda, 0x1a, 0xb4, 0x46, 0xed, 0xfb, 0xed, 0x86, -0xf9, 0x31, 0x85, 0x2e, 0x3d, 0x80, 0x77, 0xf2, 0x13, 0x76, 0x91, 0x08, 0xe7, 0x52, 0x3d, 0xf4, -0xe5, 0x2e, 0x3b, 0x80, 0x2a, 0xbf, 0x54, 0xf8, 0x80, 0xbb, 0x77, 0x6f, 0xc6, 0xca, 0x9e, 0x3f, -0xe8, 0x96, 0xfa, 0x54, 0x7e, 0x94, 0x78, 0x0a, 0xec, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x00, -0x01, 0x64, 0x62, 0x19, 0xec, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, -0x80, 0x40, 0x00, 0x00, 0xa4, 0x07, 0xd2, 0xf1, 0x5d, 0x64, 0x62, 0x19, 0xf1, 0x01, 0x01, 0x4d, -0xbe, 0x8a, 0xf5, 0xd8, 0x19, 0x2b, 0x99, 0xb0, 0xa0, 0xde, 0x24, 0x36, 0x32, 0x06, 0xac, 0x40, -0x4c, 0x41, 0x94, 0xc1, 0xd3, 0x85, 0xb5, 0xb8, 0x76, 0xbf, 0x98, 0xa9, 0x8e, 0xdb, 0xca, 0x43, -0x73, 0x98, 0xa0, 0xe0, 0x11, 0xa9, 0x95, 0xf3, 0xce, 0xde, 0xe5, 0x85, 0x80, 0x63, 0x8c, 0x12, -0x11, 0xee, 0xee, 0xa1, 0x3e, 0xcf, 0x4e, 0xd5, 0xae, 0x8d, 0x93, 0x22, 0xce, 0xbb, 0x02, 0x00, -0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, 0x19, 0xf1, 0x02, 0x4f, 0x9d, 0xa0, -0xd7, 0x26, 0xad, 0xf0, 0xd9, 0xa4, 0xa3, 0xac, 0x32, 0xe3, 0x28, 0xb9, 0x3a, 0xd5, 0x27, 0xcc, -0xb9, 0xdb, 0x70, 0x77, 0xc5, 0x7a, 0x12, 0xc6, 0xf9, 0xfa, 0x9a, 0xdd, 0x74, 0x02, 0x4f, 0x9d, -0x4c, 0x4f, 0x55, 0x44, 0x54, 0x52, 0x41, 0x57, 0x4c, 0x2d, 0x2e, 0x30, 0x32, 0x2d, 0x33, 0x37, -0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, 0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, -0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, 0x4c, 0x4b, -0x40, 0xc0, 0x00, 0x00, 0x8a, 0x06, 0x22, 0xaa, 0xb5, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x2b, -0x9e, 0x17, 0x25, 0x0f, 0x3d, 0x8c, 0x1c, 0x07, 0x6b, 0xb8, 0x7f, 0xdc, 0xc4, 0x30, 0xf4, 0xa7, -0xf8, 0x8b, 0x91, 0x53, 0xd6, 0xc1, 0x9d, 0x06, 0xb9, 0x18, 0xfb, 0xf0, 0x0b, 0x9a, 0x79, 0x2a, -0x56, 0x12, 0x35, 0x75, 0x4e, 0xf4, 0xb8, 0xb4, 0x2e, 0x72, 0x10, 0x3c, 0x8d, 0x76, 0x69, 0x1c, -0x67, 0xb0, 0x7f, 0x94, 0x07, 0xee, 0xb4, 0x38, 0x11, 0x0b, 0x7f, 0x62, 0x4e, 0x2a, 0x2d, 0x06, -0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, -0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, -0x00, 0x71, 0x00, 0x00, 0x01, 0x00, 0x01, 0x64, 0x62, 0x19, 0xec, 0x01, 0x01, 0x00, 0x06, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, -0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0xa4, 0xde, 0x6a, 0x84, 0x4d, 0x64, -0x62, 0x19, 0xf1, 0x01, 0x01, 0x47, 0x72, 0x62, 0xe8, 0xc7, 0x43, 0xa8, 0x2e, 0x1c, 0x97, 0x2a, -0x06, 0xce, 0x2f, 0xa2, 0xfa, 0x27, 0x4f, 0x28, 0x7f, 0x55, 0x32, 0x19, 0x62, 0x58, 0xc6, 0x18, -0x07, 0x23, 0x5f, 0x8a, 0x59, 0x00, 0x52, 0x4d, 0xc9, 0x18, 0x22, 0x9e, 0xf7, 0x87, 0xa3, 0x36, -0x9d, 0x01, 0x73, 0x7c, 0x5b, 0xb8, 0xb4, 0x08, 0x50, 0x0f, 0x89, 0x52, 0x3f, 0x2e, 0x44, 0xa0, -0xe0, 0x32, 0x3a, 0xf7, 0x20, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, -0x19, 0xf1, 0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, 0x69, 0x70, 0x84, 0x95, -0x36, 0xf8, 0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, 0x26, 0x8e, 0x01, 0x64, -0x96, 0x1b, 0x5e, 0x03, 0x7f, 0x97, 0x53, 0x4c, 0x49, 0x43, 0x4b, 0x45, 0x52, 0x43, 0x48, 0x49, -0x50, 0x4d, 0x55, 0x4e, 0x4b, 0x2d, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, -0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, -0x00, 0x00, 0x00, 0x02, 0x4c, 0x4b, 0x40, 0x00, 0x00, 0x01, 0xb0, 0x31, 0xd6, 0x97, 0xf8, 0x64, -0x62, 0x19, 0xec, 0x01, 0x00, 0x3f, 0x22, 0x04, 0x81, 0x00, 0xfb, 0xfe, 0x52, 0x4e, 0xdf, 0x7e, -0xef, 0x65, 0xff, 0x41, 0xcf, 0xfc, 0x33, 0xfc, 0x27, 0xba, 0x5b, 0x5f, 0xc5, 0x40, 0xd7, 0xff, -0x65, 0x20, 0x37, 0x3f, 0x00, 0x0d, 0x7c, 0x9b, 0xa9, 0xf1, 0x8c, 0xc6, 0xf1, 0xf7, 0x30, 0xd8, -0x1a, 0x44, 0xea, 0x6a, 0xf8, 0x95, 0xde, 0xe9, 0x35, 0x5f, 0x2b, 0x09, 0xc8, 0x5e, 0xf4, 0xa4, -0x58, 0x5a, 0xef, 0x24, 0x14, 0x1e, 0x17, 0x5b, 0xb1, 0xa7, 0xbf, 0x69, 0xb6, 0x44, 0xbe, 0xcc, -0x37, 0xb3, 0x48, 0x0a, 0x83, 0x37, 0xfa, 0xdb, 0x1d, 0x2a, 0x57, 0x83, 0x50, 0x88, 0x39, 0xd7, -0x2d, 0xa6, 0x70, 0x19, 0x94, 0x63, 0xa3, 0x09, 0x57, 0x47, 0x80, 0x47, 0xa7, 0x9b, 0xb5, 0x20, -0x4a, 0x33, 0x67, 0xf7, 0x5c, 0x5d, 0x4c, 0xa3, 0xc3, 0x05, 0x81, 0x48, 0xa7, 0x5e, 0x10, 0x13, -0x5d, 0x64, 0x4c, 0x2e, 0x53, 0x28, 0xd1, 0x82, 0xc3, 0x7d, 0xbf, 0xb2, 0xcd, 0x36, 0xcc, 0x1e, -0xc6, 0xc7, 0x42, 0x65, 0x12, 0x61, 0x82, 0x5d, 0xc7, 0x3b, 0x6a, 0xaf, 0x71, 0xd4, 0xf0, 0xe9, -0xff, 0xdd, 0x75, 0x33, 0x96, 0x3e, 0xb7, 0x92, 0xc2, 0xcd, 0x0e, 0xda, 0xec, 0x55, 0x43, 0x20, -0x07, 0xe8, 0x9e, 0xff, 0x3f, 0xea, 0x2f, 0x44, 0x64, 0x43, 0xe9, 0xfd, 0x82, 0x0a, 0xd4, 0x1d, -0xf6, 0x14, 0x02, 0x30, 0x78, 0x34, 0x02, 0x62, 0x73, 0x90, 0x41, 0x38, 0xbe, 0xc0, 0xd2, 0xac, -0x59, 0xc1, 0x82, 0xd2, 0x6f, 0x4e, 0x28, 0xd9, 0x2e, 0x3c, 0x6d, 0x4b, 0xa2, 0x25, 0xc9, 0x46, -0x42, 0x95, 0x64, 0xb9, 0x89, 0x73, 0x30, 0xce, 0xb7, 0xca, 0x1a, 0x78, 0xac, 0xa8, 0x72, 0x71, -0xe8, 0x1e, 0x48, 0xe9, 0x7c, 0xe5, 0x49, 0x78, 0x16, 0x50, 0x3e, 0x26, 0x15, 0x4f, 0xaf, 0x7f, -0x53, 0x17, 0x14, 0xeb, 0xa6, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, -0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, -0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, -0x45, 0x1e, 0x9b, 0xaf, 0xf8, 0x1f, 0xaf, 0x5f, 0x41, 0x0b, 0x0b, 0xbe, 0x9e, 0x36, 0xee, 0x83, -0xbf, 0x8f, 0x79, 0x69, 0x8e, 0x66, 0x05, 0xa5, 0x1a, 0x97, 0x48, 0xbc, 0x73, 0xc7, 0xdc, 0x28, -0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, 0x69, 0x70, 0x84, 0x95, 0x36, 0xf8, -0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, 0x26, 0x8e, 0x01, 0x64, 0x96, 0x1b, -0x5e, 0x02, 0xd0, 0xbf, 0x3d, 0xe0, 0x25, 0x7a, 0xe4, 0x02, 0x4a, 0x88, 0x2b, 0x20, 0x63, 0xb4, -0x68, 0x6b, 0x72, 0x27, 0x91, 0xc2, 0xe4, 0x7a, 0xd1, 0x75, 0x93, 0x5d, 0xf3, 0x3a, 0xbe, 0x99, -0x7b, 0x76, 0x02, 0x49, 0x4d, 0xb8, 0x75, 0x3e, 0x66, 0xc7, 0x73, 0x63, 0xec, 0xf4, 0x40, 0xa4, -0xcb, 0xe5, 0xe0, 0x3e, 0xc6, 0x28, 0x2b, 0xea, 0x8a, 0xd3, 0x3f, 0x66, 0x4b, 0xa3, 0x9b, 0x86, -0x37, 0xf7, 0x7b, 0x00, 0x00, 0x00, 0x0a, 0xea, 0x64, 0x27, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10, -0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x84, 0x80, 0x80, 0x00, 0x00, 0x8a, 0x67, 0x5f, 0xf2, -0xad, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x28, 0x06, 0xbe, 0x81, 0x8d, 0x13, 0xe3, 0xe9, 0x45, -0x09, 0xdd, 0x6a, 0xbe, 0x96, 0xb5, 0x08, 0xe4, 0x87, 0xca, 0xfd, 0x72, 0xc1, 0xfd, 0xa9, 0xe8, -0x32, 0x68, 0x95, 0x97, 0x06, 0x47, 0x57, 0x3a, 0x38, 0x28, 0x22, 0xa1, 0x78, 0x45, 0x22, 0xd5, -0xac, 0x0d, 0x1d, 0x2f, 0x25, 0xf0, 0x3a, 0x11, 0x85, 0x34, 0xcc, 0xae, 0xf8, 0xdd, 0x44, 0x05, -0xdd, 0xe6, 0x6d, 0xfc, 0xc2, 0xa0, 0x7e, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, -0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, -0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x03, 0x00, 0x01, 0x64, -0x62, 0x19, 0xec, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x76, 0x04, 0x67, 0x00, 0x80, -0x00, 0x00, 0x8a, 0xdc, 0x8e, 0xb4, 0xa3, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x27, 0x9a, 0x87, -0xb6, 0x8b, 0xcb, 0xc9, 0x41, 0xea, 0xc3, 0x1b, 0x18, 0xf5, 0x51, 0x2f, 0x9b, 0x71, 0xe3, 0x8d, -0x24, 0x8d, 0x1e, 0x53, 0xdc, 0x83, 0x6f, 0x30, 0xfe, 0x00, 0xeb, 0xbb, 0x6b, 0x35, 0xc3, 0x20, -0xea, 0xae, 0x27, 0xb4, 0x8a, 0xdc, 0x30, 0x9f, 0xb5, 0xee, 0xbf, 0x3c, 0x16, 0x58, 0xe1, 0xa6, -0xec, 0x87, 0xfd, 0xb0, 0x43, 0x8c, 0xed, 0x4d, 0x00, 0x2d, 0x85, 0x33, 0xbe, 0x06, 0x22, 0x6e, -0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, -0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, -0x00, 0x00, 0x03, 0x00, 0x01, 0x64, 0x62, 0x19, 0xec, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, -0x00, 0x76, 0x04, 0x67, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x1e, 0xb8, 0x7e, 0x0a, 0x64, 0x62, 0x19, -0xf1, 0x01, 0x01, 0x70, 0xc5, 0x12, 0xaa, 0x59, 0xee, 0xe5, 0xb5, 0x1f, 0x4c, 0x56, 0x77, 0xa1, -0xc5, 0x3c, 0x6b, 0x03, 0x37, 0xf9, 0x8f, 0xa9, 0x50, 0xa7, 0xe3, 0x22, 0x7b, 0x6e, 0x37, 0xd5, -0x46, 0x03, 0xff, 0x12, 0x91, 0x0a, 0xb8, 0x4f, 0x35, 0x63, 0xdf, 0xda, 0x03, 0xda, 0xee, 0x86, -0xe4, 0x43, 0xef, 0xa0, 0x8a, 0x90, 0xeb, 0xa8, 0xf3, 0x7f, 0x05, 0x84, 0x8a, 0xd8, 0xb0, 0xf8, -0x1b, 0x4b, 0xcf, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, 0x19, 0xf1, -0x02, 0x45, 0x1e, 0x9b, 0xaf, 0xf8, 0x1f, 0xaf, 0x5f, 0x41, 0x0b, 0x0b, 0xbe, 0x9e, 0x36, 0xee, -0x83, 0xbf, 0x8f, 0x79, 0x69, 0x8e, 0x66, 0x05, 0xa5, 0x1a, 0x97, 0x48, 0xbc, 0x73, 0xc7, 0xdc, -0x28, 0x02, 0x45, 0x1e, 0x4c, 0x4f, 0x55, 0x44, 0x54, 0x4f, 0x54, 0x45, 0x2d, 0x33, 0x2e, 0x30, -0x32, 0x2d, 0x33, 0x37, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, 0x6d, 0x6f, -0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, 0x00, 0x00, -0x00, 0x02, 0x4c, 0x4b, 0x40, 0x00, 0x00, 0x01, 0xb0, 0x17, 0xe0, 0xd7, 0x83, 0x64, 0x62, 0x19, -0xed, 0x01, 0x00, 0x48, 0xa3, 0x33, 0x5f, 0x33, 0x6c, 0x33, 0x85, 0x0f, 0xc7, 0xeb, 0x46, 0x04, -0x5a, 0xe7, 0x1a, 0x2d, 0xe1, 0x37, 0xb0, 0xc3, 0x8a, 0xa7, 0x6a, 0xe0, 0xa2, 0xfd, 0x1f, 0x30, -0x9f, 0xdc, 0x8d, 0x38, 0x05, 0xf7, 0xaf, 0x0b, 0xe6, 0xb3, 0x4d, 0x62, 0xb9, 0xa4, 0x9c, 0x53, -0x7d, 0x6e, 0x59, 0x5b, 0xb2, 0x2b, 0x5c, 0xda, 0x35, 0xf9, 0x90, 0x63, 0x21, 0xa8, 0xb1, 0x53, -0xc3, 0x35, 0x7c, 0x36, 0x76, 0x21, 0x76, 0xae, 0xa3, 0xad, 0x05, 0x53, 0xa7, 0xbd, 0x9d, 0x38, -0x54, 0x03, 0xc0, 0x98, 0x1d, 0x66, 0xc1, 0x04, 0x39, 0xc1, 0x88, 0xd1, 0x1f, 0x90, 0x08, 0x96, -0xbc, 0x59, 0x54, 0x4f, 0x5f, 0xa2, 0x70, 0xcd, 0xf0, 0xda, 0x96, 0x3c, 0x51, 0x04, 0x67, 0x5c, -0x1f, 0x07, 0xed, 0xf9, 0x9e, 0x98, 0xd0, 0x3b, 0x5e, 0x51, 0xa9, 0xa6, 0x82, 0xc1, 0xed, 0x35, -0x45, 0xa1, 0xd6, 0x36, 0x3b, 0xa1, 0xe6, 0x5d, 0x1f, 0xec, 0xe2, 0xb7, 0xf8, 0xa2, 0xe4, 0x45, -0xf9, 0xb6, 0xa7, 0x07, 0x18, 0xc7, 0xb5, 0x0c, 0x08, 0xd7, 0x50, 0x36, 0x98, 0x82, 0xd3, 0xc8, -0x40, 0xc8, 0xdc, 0x64, 0x27, 0xe2, 0x14, 0x42, 0x44, 0x0a, 0xe4, 0x1d, 0x41, 0x61, 0x57, 0x88, -0xfe, 0xd2, 0x51, 0x99, 0x24, 0x55, 0x1e, 0x3b, 0xaa, 0x8d, 0xa7, 0xb4, 0xc0, 0x6e, 0xf5, 0x70, -0x8c, 0x2a, 0xe3, 0x75, 0xcc, 0x36, 0xbf, 0xbe, 0xfc, 0x3f, 0x09, 0x83, 0x5e, 0xe4, 0x20, 0x9a, -0xcc, 0x11, 0x48, 0x8e, 0x2b, 0xc8, 0x8a, 0xef, 0xc0, 0x78, 0x45, 0xee, 0x1e, 0xc7, 0xce, 0x00, -0xfc, 0x3c, 0x0e, 0x32, 0xd2, 0x8f, 0x15, 0x8c, 0x02, 0xb3, 0x7b, 0x4c, 0xa9, 0x7a, 0x9c, 0xec, -0x5e, 0x6e, 0xf2, 0xd3, 0xd9, 0x15, 0x32, 0xa3, 0x74, 0x14, 0xbf, 0x1f, 0xdd, 0x2f, 0x63, 0x3c, -0x47, 0x04, 0x6c, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, -0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, -0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x45, 0x1e, -0x9b, 0xaf, 0xf8, 0x1f, 0xaf, 0x5f, 0x41, 0x0b, 0x0b, 0xbe, 0x9e, 0x36, 0xee, 0x83, 0xbf, 0x8f, -0x79, 0x69, 0x8e, 0x66, 0x05, 0xa5, 0x1a, 0x97, 0x48, 0xbc, 0x73, 0xc7, 0xdc, 0x28, 0x02, 0xd1, -0xab, 0x24, 0xfe, 0x53, 0xcf, 0xca, 0xc4, 0xa4, 0x77, 0x74, 0x52, 0x35, 0xc7, 0xac, 0x3e, 0x75, -0x16, 0x1a, 0x5b, 0xf8, 0x42, 0x6e, 0xa8, 0xe0, 0x18, 0x2a, 0xd5, 0x8c, 0xab, 0x36, 0x7f, 0x02, -0x7e, 0x2a, 0xc0, 0xec, 0x93, 0xfd, 0xb3, 0xfb, 0xe3, 0x8d, 0x7a, 0x3f, 0x5e, 0xa0, 0xa6, 0x3d, -0xdb, 0xa9, 0x8a, 0x51, 0xb7, 0x7a, 0xf5, 0x51, 0x6f, 0xe5, 0xca, 0x10, 0x10, 0xd7, 0x95, 0x34, -0x02, 0x17, 0xd5, 0xb1, 0x80, 0x7d, 0x8b, 0x95, 0x7c, 0xe1, 0x0b, 0xb0, 0xaf, 0xf3, 0xc1, 0x84, -0x81, 0xee, 0x2f, 0xed, 0x6a, 0x7b, 0x65, 0x9c, 0xbf, 0xfd, 0x48, 0x20, 0xd0, 0x9d, 0x1a, 0xfd, -0xa4, 0x00, 0x00, 0x00, 0x0a, 0x91, 0x11, 0x83, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, -0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0x80, 0x00, 0x00, 0x8a, 0xbd, 0x52, 0xa0, 0x78, 0x64, -0x62, 0x19, 0xed, 0x01, 0x02, 0x40, 0xf0, 0x06, 0x07, 0x97, 0xb8, 0x87, 0xef, 0x73, 0xdc, 0x1b, -0xf0, 0x20, 0x31, 0x55, 0xc9, 0xb9, 0x6f, 0xec, 0x6f, 0xad, 0x46, 0x86, 0x0a, 0xcc, 0xd9, 0x95, -0x61, 0x62, 0x15, 0x84, 0x70, 0x2a, 0x47, 0xd7, 0x68, 0xa9, 0xbc, 0x98, 0xb3, 0x1f, 0xc4, 0xbc, -0x78, 0xab, 0x5d, 0xf2, 0xf7, 0xc4, 0x97, 0x75, 0x21, 0x13, 0xcf, 0xfc, 0xd4, 0x36, 0xcd, 0xf6, -0xb4, 0x85, 0x7c, 0xad, 0x01, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, -0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, -0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x02, 0x00, 0x00, 0x64, 0x62, 0x19, -0xed, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x80, 0x00, 0x00, -0x8a, 0xf5, 0x5d, 0xd1, 0x12, 0x64, 0x62, 0x19, 0xed, 0x01, 0x02, 0x08, 0x97, 0x08, 0x72, 0xbe, -0xc8, 0x1e, 0xd0, 0xb9, 0xb8, 0x4b, 0x0f, 0x63, 0x5c, 0xeb, 0x28, 0xa5, 0xf8, 0x7a, 0x3d, 0xa1, -0x6a, 0xb3, 0xb4, 0x30, 0x91, 0x31, 0x57, 0xd4, 0x5b, 0x69, 0x26, 0x4d, 0xd1, 0xbb, 0xd5, 0x49, -0x95, 0xe9, 0x75, 0x53, 0xa4, 0xae, 0x87, 0xe9, 0x88, 0xf6, 0x86, 0x1f, 0x31, 0x8f, 0x35, 0xf9, -0x15, 0xcc, 0x04, 0x0a, 0x01, 0xed, 0x6e, 0x47, 0xe0, 0xea, 0x68, 0x06, 0x22, 0x6e, 0x46, 0x11, -0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, -0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, -0x02, 0x00, 0x00, 0x64, 0x62, 0x19, 0xed, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x3b, -0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x3d, 0xb7, 0xfe, 0x64, 0x62, 0x19, 0xf2, 0x01, -0x01, 0x29, 0x2a, 0x41, 0x8f, 0xb7, 0x24, 0xc2, 0x82, 0xc5, 0x75, 0x0e, 0x28, 0xd9, 0x8b, 0xd4, -0xad, 0xa1, 0xb1, 0x9a, 0x65, 0xa8, 0x7a, 0x78, 0xc7, 0x6c, 0xc8, 0x94, 0xcb, 0xf7, 0xb1, 0xb8, -0x3b, 0x29, 0xce, 0xbf, 0xcc, 0x47, 0x1b, 0x5a, 0xb4, 0xec, 0xab, 0xa3, 0xbe, 0xaf, 0xd1, 0xde, -0xd7, 0x0e, 0x8b, 0xcc, 0xaa, 0xdb, 0x6b, 0x88, 0x51, 0xb9, 0x7a, 0x0c, 0xcd, 0x1c, 0x9c, 0x4d, -0x5c, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, 0x19, 0xf2, 0x02, 0xd1, -0xab, 0x24, 0xfe, 0x53, 0xcf, 0xca, 0xc4, 0xa4, 0x77, 0x74, 0x52, 0x35, 0xc7, 0xac, 0x3e, 0x75, -0x16, 0x1a, 0x5b, 0xf8, 0x42, 0x6e, 0xa8, 0xe0, 0x18, 0x2a, 0xd5, 0x8c, 0xab, 0x36, 0x7f, 0x02, -0xd1, 0xab, 0x43, 0x48, 0x49, 0x4c, 0x4c, 0x59, 0x46, 0x49, 0x52, 0x45, 0x2d, 0x30, 0x32, 0x2d, -0x33, 0x37, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, 0x6d, 0x6f, 0x64, 0x64, -0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, -0x4c, 0x4b, 0x40, 0x00, 0x00, 0x01, 0xb0, 0x5f, 0xad, 0x58, 0xa1, 0x64, 0x62, 0x19, 0xed, 0x01, -0x00, 0x63, 0x42, 0xba, 0xf1, 0x21, 0xe0, 0x09, 0x57, 0x0d, 0x40, 0xa4, 0xc6, 0x05, 0x78, 0x02, -0x8e, 0x35, 0x71, 0x66, 0x7c, 0x24, 0x51, 0x3f, 0x58, 0x3a, 0xaa, 0x14, 0x65, 0x5a, 0x2b, 0xbd, -0x09, 0x5b, 0xd3, 0xa8, 0x4e, 0x73, 0x3e, 0x38, 0xd3, 0x4d, 0x19, 0x9c, 0x18, 0x04, 0x60, 0x57, -0x32, 0xd3, 0x75, 0xf5, 0x36, 0x15, 0xc4, 0x6a, 0xf1, 0x1b, 0x3d, 0xc9, 0x07, 0x89, 0x08, 0x7a, -0x37, 0x2f, 0xf1, 0x89, 0x12, 0xdb, 0xf2, 0xff, 0x04, 0xbd, 0x93, 0x23, 0x00, 0x3c, 0x10, 0x05, -0x8a, 0x58, 0x9b, 0x96, 0xf3, 0x76, 0x94, 0x16, 0x29, 0x51, 0xc8, 0x76, 0x89, 0x61, 0xc3, 0x21, -0xc0, 0x0e, 0x47, 0xac, 0xa3, 0xbe, 0xc7, 0xfd, 0xa2, 0x6b, 0xe9, 0x1d, 0xe2, 0x11, 0x1c, 0x3e, -0xfc, 0x6d, 0x4d, 0x0b, 0x85, 0xff, 0xe9, 0x8a, 0x39, 0x3a, 0xb3, 0x0e, 0x2f, 0x28, 0x96, 0x6b, -0x96, 0x59, 0x4d, 0x53, 0x71, 0xd5, 0x38, 0x23, 0xe1, 0xe0, 0xad, 0x0a, 0xbf, 0x00, 0x58, 0x15, -0xbf, 0x53, 0x07, 0xe1, 0x13, 0x06, 0x88, 0xb3, 0xf8, 0x31, 0x06, 0x72, 0x92, 0x6f, 0xd1, 0xf0, -0x9b, 0x3b, 0xf2, 0x8f, 0x9c, 0xc6, 0x73, 0xf8, 0x91, 0x3e, 0x84, 0xc0, 0xed, 0xdf, 0x92, 0x43, -0x92, 0x5f, 0x4a, 0x6b, 0x96, 0x02, 0xaf, 0xd9, 0xd9, 0xd9, 0xf9, 0x65, 0xae, 0x08, 0xd8, 0x62, -0x93, 0x2b, 0xb7, 0xd3, 0x48, 0xe3, 0x02, 0x19, 0x53, 0xf9, 0x49, 0x24, 0xfa, 0x22, 0x24, 0x87, -0xc2, 0xd2, 0x0b, 0xc0, 0x56, 0xae, 0x09, 0x5a, 0x94, 0xc3, 0x54, 0x59, 0xb5, 0xe7, 0xbe, 0xa6, -0x4a, 0x47, 0xc1, 0x79, 0x80, 0xe8, 0xc2, 0xd1, 0xc5, 0xda, 0x6b, 0x25, 0x85, 0xc6, 0x02, 0x32, -0x8b, 0x52, 0x0e, 0x7f, 0x18, 0x1c, 0x5b, 0xf6, 0xb9, 0xaf, 0x69, 0xdc, 0xc6, 0x3d, 0x93, 0xc1, -0x27, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, -0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, -0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0xd1, 0xab, 0x24, 0xfe, -0x53, 0xcf, 0xca, 0xc4, 0xa4, 0x77, 0x74, 0x52, 0x35, 0xc7, 0xac, 0x3e, 0x75, 0x16, 0x1a, 0x5b, -0xf8, 0x42, 0x6e, 0xa8, 0xe0, 0x18, 0x2a, 0xd5, 0x8c, 0xab, 0x36, 0x7f, 0x03, 0xca, 0xec, 0x54, -0x08, 0x55, 0x08, 0xda, 0x06, 0xf1, 0x4f, 0xfb, 0x83, 0x61, 0x66, 0xca, 0x22, 0x04, 0x2d, 0x0f, -0xda, 0x69, 0x69, 0x03, 0x56, 0x23, 0x2a, 0x24, 0xb8, 0x36, 0x6c, 0x8f, 0x85, 0x03, 0xf6, 0x19, -0x62, 0x15, 0xf2, 0x5c, 0xfc, 0x5c, 0xae, 0x8c, 0xb6, 0x90, 0xa7, 0x81, 0xe0, 0x14, 0xb5, 0xc1, -0xc5, 0xda, 0xf9, 0x6d, 0x44, 0x6d, 0x1a, 0x6e, 0x24, 0x4f, 0xb6, 0x42, 0x3f, 0xdb, 0x03, 0xf9, -0x84, 0xe3, 0xec, 0xa9, 0x24, 0x5d, 0x1b, 0xba, 0xd2, 0xc7, 0xf3, 0x5a, 0x32, 0xaa, 0x6e, 0xdb, -0x21, 0xb6, 0xe8, 0xb1, 0x86, 0x5b, 0x18, 0x30, 0xe8, 0x4d, 0x23, 0xa4, 0x45, 0x23, 0x88, 0x00, -0x00, 0x00, 0x0a, 0x08, 0x85, 0x8a, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, -0x00, 0x00, 0x2d, 0xc6, 0xc0, 0x80, 0x00, 0x00, 0x8a, 0xe9, 0x51, 0x74, 0x9b, 0x64, 0x62, 0x19, -0xed, 0x01, 0x02, 0x4b, 0x82, 0x87, 0x3b, 0xc9, 0x03, 0x1c, 0x6e, 0xc9, 0xbe, 0x96, 0x22, 0x97, -0xf7, 0xa8, 0xb0, 0xb2, 0x7c, 0x22, 0x69, 0x23, 0x2d, 0x97, 0xfb, 0x9b, 0xc2, 0xf1, 0x1e, 0x66, -0xfb, 0xfd, 0x80, 0x5d, 0xd7, 0xf0, 0x23, 0x31, 0x47, 0xaa, 0x54, 0x8d, 0x95, 0xbb, 0xdd, 0x33, -0x13, 0x32, 0x6d, 0x91, 0xc6, 0x45, 0xd5, 0x84, 0xf4, 0x76, 0x6c, 0x74, 0xf3, 0x51, 0x45, 0x24, -0xee, 0x5b, 0xc3, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, -0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, -0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, 0x01, 0x64, 0x62, 0x19, 0xed, 0x01, -0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, -0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, 0x80, 0x80, 0x00, 0x00, 0x8a, 0xd2, -0xc8, 0xd2, 0x7c, 0x64, 0x62, 0x19, 0xed, 0x01, 0x02, 0x0f, 0x90, 0xcb, 0xb6, 0xa1, 0x44, 0x65, -0x10, 0x00, 0xae, 0x2f, 0x60, 0x26, 0x2f, 0x41, 0x58, 0x5b, 0xab, 0xde, 0xff, 0x7e, 0x11, 0x44, -0xf4, 0x2e, 0x96, 0x96, 0xfa, 0x98, 0x09, 0xee, 0xb1, 0x5d, 0x43, 0xff, 0x44, 0x7b, 0xa6, 0x03, -0xf6, 0x4a, 0x07, 0x38, 0x97, 0x59, 0xee, 0x5e, 0xee, 0xcb, 0xdb, 0x77, 0x69, 0xab, 0x61, 0xd8, -0xc3, 0x42, 0xb3, 0x1f, 0x57, 0xea, 0xf3, 0xfd, 0xe2, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, -0x01, 0x64, 0x62, 0x19, 0xed, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, -0x80, 0x40, 0x00, 0x00, 0x8a, 0x64, 0xa7, 0x4f, 0x57, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x02, 0x28, -0x15, 0x9f, 0xa7, 0x51, 0x3a, 0xbb, 0x33, 0xd9, 0x25, 0xaa, 0x7d, 0xe8, 0xfb, 0x3a, 0x92, 0x45, -0x41, 0xb3, 0x22, 0x9b, 0x12, 0x3b, 0xb0, 0x16, 0x47, 0xd6, 0xf7, 0x61, 0x44, 0x1d, 0xa7, 0x35, -0xfe, 0xa9, 0x7b, 0xa6, 0x42, 0x91, 0x3f, 0x5e, 0xe4, 0xca, 0x98, 0x1c, 0x0f, 0x2d, 0xed, 0x36, -0x0e, 0x2b, 0x2e, 0x08, 0x81, 0x2e, 0xcc, 0xc1, 0x76, 0x61, 0xf9, 0x1b, 0xd3, 0x44, 0x3e, 0x06, -0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, -0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, -0x00, 0x71, 0x00, 0x00, 0x01, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x00, 0x00, 0x06, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x64, 0x00, -0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0xa4, 0x30, 0x77, 0x80, 0xee, 0x64, -0x62, 0x19, 0xf2, 0x01, 0x01, 0x67, 0x07, 0x1d, 0x3b, 0x62, 0x2d, 0xb7, 0x1a, 0xba, 0xb8, 0x93, -0x56, 0xaa, 0xfa, 0xb1, 0x47, 0x4f, 0x0e, 0x02, 0x8b, 0x73, 0xd5, 0x5b, 0xce, 0xd6, 0x40, 0x55, -0xaf, 0xa7, 0x29, 0xd0, 0x51, 0x24, 0x5a, 0x19, 0x22, 0xc6, 0x7b, 0x6e, 0x4a, 0xae, 0x57, 0x9c, -0x16, 0x99, 0x46, 0x6c, 0xc3, 0x64, 0xd8, 0x20, 0x10, 0x44, 0x1e, 0xd0, 0x6b, 0x8d, 0x36, 0xdc, -0xae, 0x75, 0x06, 0x6e, 0xdc, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, -0x19, 0xf2, 0x03, 0xca, 0xec, 0x54, 0x08, 0x55, 0x08, 0xda, 0x06, 0xf1, 0x4f, 0xfb, 0x83, 0x61, -0x66, 0xca, 0x22, 0x04, 0x2d, 0x0f, 0xda, 0x69, 0x69, 0x03, 0x56, 0x23, 0x2a, 0x24, 0xb8, 0x36, -0x6c, 0x8f, 0x85, 0x03, 0xca, 0xec, 0x56, 0x49, 0x4f, 0x4c, 0x45, 0x54, 0x53, 0x45, 0x54, 0x2d, -0x2e, 0x30, 0x32, 0x2d, 0x33, 0x37, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, -0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, -0x00, 0x00, 0x00, 0x02, 0x4c, 0x4b, 0x40, 0x40, 0x00, 0x00, 0x8a, 0x07, 0xf6, 0xe8, 0x9b, 0x64, -0x62, 0x19, 0xf7, 0x01, 0x02, 0x62, 0xf7, 0xdc, 0xf1, 0xa6, 0x3c, 0xf0, 0xae, 0x64, 0x9c, 0x03, -0x62, 0x98, 0x6a, 0x18, 0x78, 0x97, 0xed, 0x8c, 0x2e, 0x3f, 0xc4, 0x1d, 0x9f, 0xa7, 0xfb, 0x58, -0x26, 0x48, 0x2e, 0x96, 0x9d, 0x33, 0x75, 0x60, 0x6e, 0x33, 0x95, 0xf7, 0x6e, 0x9f, 0x4f, 0xa2, -0xed, 0xd6, 0xa9, 0x83, 0x1b, 0x94, 0x79, 0xee, 0x4f, 0xdc, 0x20, 0xc5, 0x39, 0x74, 0x0d, 0x31, -0x52, 0xc7, 0x25, 0x36, 0x47, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, -0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, -0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x00, 0x01, 0x64, 0x62, 0x19, -0xf7, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x14, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, -0x8a, 0x16, 0x2b, 0xff, 0x08, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x02, 0x39, 0x36, 0x2a, 0x56, 0x61, -0xad, 0x48, 0x3f, 0x4e, 0x13, 0x15, 0x66, 0x43, 0x58, 0xc5, 0xc2, 0x14, 0x6e, 0xb2, 0x72, 0xfa, -0x73, 0xd7, 0xb5, 0x2d, 0x86, 0x14, 0xc2, 0xe8, 0xf7, 0x53, 0x8f, 0x38, 0xea, 0x35, 0x5c, 0xec, -0xe3, 0xc7, 0xc0, 0x46, 0x1c, 0x9f, 0x1d, 0x93, 0x94, 0x31, 0x1f, 0xf8, 0x49, 0xb1, 0x50, 0x4c, -0x2c, 0x2f, 0xc7, 0xe4, 0x0c, 0xaa, 0xd0, 0xa9, 0x53, 0x14, 0xca, 0x06, 0x22, 0x6e, 0x46, 0x11, -0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, -0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, -0x03, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x76, -0x04, 0x67, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xd5, 0x4a, 0x70, 0x0c, 0x64, 0x62, 0x19, 0xf7, 0x01, -0x02, 0x54, 0x16, 0x95, 0x41, 0x4f, 0x0e, 0x0f, 0xdf, 0x49, 0xb5, 0x87, 0xdc, 0x26, 0xb4, 0xef, -0x73, 0x3c, 0xb8, 0x19, 0x96, 0x62, 0x87, 0xfa, 0x4f, 0x02, 0x53, 0xbe, 0x12, 0x53, 0x93, 0x4b, -0x57, 0x3b, 0xe9, 0xb9, 0x26, 0x46, 0xda, 0x77, 0xaa, 0xdd, 0x8d, 0xf6, 0x86, 0x22, 0xf0, 0x3f, -0xd5, 0x56, 0xdd, 0xaa, 0xa2, 0x4e, 0x4a, 0x9a, 0x70, 0x81, 0xf8, 0xf9, 0x72, 0x7b, 0xd7, 0x90, -0x48, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, -0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, -0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x03, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x00, 0x00, -0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0xc8, 0x00, 0x00, 0x00, 0x00, 0x76, 0x04, 0x67, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xc5, 0x6d, 0x8a, -0x5a, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x02, 0x1d, 0x80, 0x09, 0x30, 0x1a, 0x4b, 0x26, 0x60, 0x6b, -0x9a, 0x54, 0x8d, 0x7f, 0x9b, 0x35, 0x78, 0x76, 0x7a, 0xc1, 0xe5, 0x22, 0xdc, 0x08, 0x77, 0xac, -0x54, 0xc7, 0xc0, 0x9b, 0x13, 0x85, 0x20, 0x2c, 0xa4, 0xa3, 0x7e, 0xc5, 0xde, 0xfd, 0x60, 0x43, -0xdb, 0x2e, 0xb0, 0x5b, 0xcc, 0x95, 0xc1, 0xf3, 0x02, 0x09, 0x8a, 0xe1, 0x55, 0x2a, 0x8a, 0x9a, -0x18, 0xe5, 0xa9, 0xee, 0xcd, 0x11, 0x27, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, -0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, -0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x02, 0x00, 0x00, 0x64, -0x62, 0x19, 0xf6, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, -0x00, 0x00, 0x8a, 0x67, 0xa5, 0x58, 0xd4, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x02, 0x5a, 0xa5, 0x3e, -0xb8, 0x73, 0xf5, 0xdf, 0xfc, 0x72, 0x16, 0x52, 0xa1, 0x07, 0x8a, 0x2b, 0xf1, 0xc3, 0x92, 0xc5, -0x87, 0xa4, 0x45, 0x07, 0x1e, 0xb3, 0x7d, 0x4c, 0x1c, 0x47, 0x41, 0x2c, 0x93, 0x14, 0x46, 0x16, -0xba, 0xe4, 0xf9, 0xc9, 0x52, 0x4c, 0x5e, 0x6c, 0x4f, 0xc9, 0xec, 0xde, 0x83, 0x15, 0xe0, 0x8e, -0x39, 0xbe, 0xa9, 0x8f, 0x9d, 0xfe, 0xcf, 0xc4, 0x12, 0x32, 0xa4, 0x17, 0x2b, 0x06, 0x22, 0x6e, -0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, -0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, -0x00, 0x00, 0x02, 0x00, 0x00, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, -0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0x8a, 0x2f, 0x71, 0xed, 0xec, 0x64, 0x62, 0x19, -0xf6, 0x01, 0x02, 0x75, 0x4f, 0x11, 0x1c, 0x56, 0x9f, 0x4a, 0x9d, 0x6f, 0x98, 0x96, 0x1c, 0x5a, -0x9f, 0x0f, 0xb9, 0x24, 0x23, 0x82, 0x7d, 0x86, 0xcf, 0xbc, 0x41, 0x14, 0x38, 0x76, 0x2e, 0x86, -0x47, 0x96, 0xef, 0x14, 0x91, 0x2e, 0x30, 0xe2, 0x4b, 0x1c, 0x47, 0x2d, 0x4a, 0xdc, 0xf6, 0x79, -0xb6, 0x11, 0x80, 0xcc, 0x51, 0xbb, 0xc4, 0x29, 0x33, 0x60, 0xc1, 0x78, 0x1e, 0x82, 0xe3, 0x40, -0xc0, 0xf7, 0x25, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, -0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, -0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf6, 0x01, -0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, -0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, 0x80, 0x00, 0x00, 0x00, 0x8a, 0xc8, -0x56, 0xb7, 0xb1, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x02, 0x3c, 0x76, 0x7a, 0x28, 0x5e, 0x65, 0x30, -0xac, 0x0d, 0x0f, 0x43, 0x31, 0x02, 0x56, 0xcd, 0x14, 0x51, 0x46, 0x69, 0x33, 0xa0, 0x12, 0x61, -0x9c, 0x34, 0xc5, 0xd8, 0x9a, 0x0c, 0x81, 0x94, 0xad, 0x5e, 0x98, 0xc4, 0xd0, 0x45, 0x3d, 0x32, -0x84, 0xdd, 0xd7, 0x18, 0x2b, 0xdb, 0x13, 0xa8, 0xfc, 0xb2, 0x0d, 0xd6, 0xf6, 0x8a, 0x97, 0xc7, -0xe9, 0x7e, 0x27, 0xb7, 0x86, 0x7a, 0x3e, 0xee, 0xfa, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, -0x01, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, -0x80 - }; - -static void test_edge_probability(void) -{ - const double eps = 1e-8; - - struct amount_msat min = AMOUNT_MSAT(10); // known min - struct amount_msat max = AMOUNT_MSAT(19); // known max - - struct amount_msat X = AMOUNT_MSAT(0); // in flight - struct amount_msat f; - - for(int i=0;i<=min.millisatoshis;++i) - { - f.millisatoshis = i; - // prob = 1 - assert(fabs(edge_probability(min,max,X,f)-1.0)< eps); - } - for(int i=max.millisatoshis+1;i<=100;++i) - { - f.millisatoshis = i; - // prob = 0 - assert(fabs(edge_probability(min,max,X,f))< eps); - } - f.millisatoshis=11; - assert(fabs(edge_probability(min,max,X,f)-0.9)< eps); - - f.millisatoshis=12; - assert(fabs(edge_probability(min,max,X,f)-0.8)< eps); - - f.millisatoshis=13; - assert(fabs(edge_probability(min,max,X,f)-0.7)< eps); - - f.millisatoshis=14; - assert(fabs(edge_probability(min,max,X,f)-0.6)< eps); - - f.millisatoshis=15; - assert(fabs(edge_probability(min,max,X,f)-0.5)< eps); - - f.millisatoshis=16; - assert(fabs(edge_probability(min,max,X,f)-0.4)< eps); - - f.millisatoshis=17; - assert(fabs(edge_probability(min,max,X,f)-0.3)< eps); - - f.millisatoshis=18; - assert(fabs(edge_probability(min,max,X,f)-0.2)< eps); - - f.millisatoshis=19; - assert(fabs(edge_probability(min,max,X,f)-0.1)< eps); - - X = AMOUNT_MSAT(5); - - // X=B-X - for(int i=15;i<100;++i) - { - f.millisatoshis = i; - // prob = 0 - assert(fabs(edge_probability(min,max,X,f))< eps); - } - // X=B-X - for(int i=5;i<100;++i) - { - f.millisatoshis = i; - assert(fabs(edge_probability(min,max,X,f))< eps); - } - - // X>=A, 0<=f<=B-X - f.millisatoshis=0; - assert(fabs(edge_probability(min,max,X,f)-1.0)< eps); - f.millisatoshis=1; - assert(fabs(edge_probability(min,max,X,f)-0.8)< eps); - f.millisatoshis=2; - assert(fabs(edge_probability(min,max,X,f)-0.6)< eps); - f.millisatoshis=3; - assert(fabs(edge_probability(min,max,X,f)-0.4)< eps); - f.millisatoshis=4; - assert(fabs(edge_probability(min,max,X,f)-0.2)< eps); - f.millisatoshis=5; - assert(fabs(edge_probability(min,max,X,f)-0.0)< eps); -} - -static void remove_file(char *fname) { assert(!remove(fname)); } - -/* Test channel_maximum_forward function */ -static void test_channel_maximum_forward(void) -{ - const tal_t *this_ctx = tal(tmpctx, tal_t); - - char *gossfile; - int fd = tmpdir_mkstemp(this_ctx, "run-testflow.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - - assert(write_all(fd, canned_map, sizeof(canned_map))); - struct gossmap *gossmap = gossmap_load(this_ctx, gossfile, NULL); - assert(gossmap); - - struct short_channel_id scid12; - assert(short_channel_id_from_str("113x1x1", 7, &scid12)); - - struct gossmap_chan *c; - - // check the bounds channel 1--2 - c = gossmap_find_chan(gossmap, &scid12); - - const u64 test_in[] = { - 0, 1, 2, 3, - 10, 11, 12, 20, - 30, 100, 110, 111, - 200, 300, 1000, 2000, - 3000, 10000, 10025, 20000, - 1000000, 1100032, 2000000, 3000000, - 1000000000, 2000000000, 3000000000, 1000000000000 /*10 BTC*/}; - const u64 test_basefee[] = { - 0, 1, 2, 3, 4, 10, 20, 30, - 100, 200, 1000, 2000, 10000, 10001, 10002, 10010, - 10011, 10012, 20000, 1000000, 2000000, 1 << 23, (1 << 24) - 1}; - const u64 test_ppm[] = {0, 1, 2, 3, 4, - 10, 20, 30, 100, 200, - 300, 1000, 2000, 3000, 10000, - 20000, 30000, 11111, 100000, 100001, - 200000, 500000, 900000, 999999, 1000000}; - - const size_t N_in = sizeof(test_in) / sizeof(test_in[0]); - for (int i = 0; i < N_in; ++i) { - const struct amount_msat in = amount_msat(test_in[i]); - - const size_t N_base = - sizeof(test_basefee) / sizeof(test_basefee[0]); - for (int j = 0; j < N_base; ++j) { - const u64 basefee = test_basefee[j]; - - const size_t N_ppm = - sizeof(test_ppm) / sizeof(test_ppm[0]); - for (int k = 0; k < N_ppm; ++k) { - const u64 ppm = test_ppm[k]; - - c->half[0].base_fee = basefee; - c->half[0].proportional_fee = ppm; - struct amount_msat out; - - assert(channel_maximum_forward( - &out, c, 0, in) == RENEPAY_NOERROR); - - // do we satisfy the fee constraint? - assert(check_fee_inequality(in, out, basefee, - ppm)); - - // is this the best we can do? - assert( - amount_msat_add(&out, out, amount_msat(1))); - assert(!check_fee_inequality(in, out, basefee, - ppm)); - } - } - } - tal_free(this_ctx); -} - -int main(int argc, char *argv[]) -{ - common_setup(argv[0]); - - test_edge_probability(); - test_channel_maximum_forward(); - - common_shutdown(); -} - From d47a6f82ab55ce13aeb2a50a06fbf3897e424f66 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Mon, 23 Sep 2024 09:25:54 +0100 Subject: [PATCH 23/30] renepay: remove unused internal errorcodes Signed-off-by: Lagrang3 --- plugins/renepay/Makefile | 2 -- plugins/renepay/errorcodes.c | 29 ----------------------------- plugins/renepay/errorcodes.h | 20 -------------------- 3 files changed, 51 deletions(-) delete mode 100644 plugins/renepay/errorcodes.c delete mode 100644 plugins/renepay/errorcodes.h diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index 2b8ff31502f7..aa3163eca6b2 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -5,7 +5,6 @@ PLUGIN_RENEPAY_SRC := \ plugins/renepay/routetracker.c \ plugins/renepay/routefail.c \ plugins/renepay/mods.c \ - plugins/renepay/errorcodes.c \ plugins/renepay/json.c PLUGIN_RENEPAY_HDRS := \ @@ -17,7 +16,6 @@ PLUGIN_RENEPAY_HDRS := \ plugins/renepay/routetracker.h \ plugins/renepay/routefail.h \ plugins/renepay/mods.h \ - plugins/renepay/errorcodes.h \ plugins/renepay/json.c PLUGIN_RENEPAY_OBJS := $(PLUGIN_RENEPAY_SRC:.c=.o) diff --git a/plugins/renepay/errorcodes.c b/plugins/renepay/errorcodes.c deleted file mode 100644 index fcf21b15f898..000000000000 --- a/plugins/renepay/errorcodes.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "config.h" -#include -#include -#include - -const char *renepay_errorcode_name(enum renepay_errorcode e) -{ - static char invalidbuf[sizeof("INVALID ") + STR_MAX_CHARS(e)]; - - switch (e) { - case RENEPAY_NOERROR: - return "RENEPAY_NOERROR"; - case RENEPAY_AMOUNT_OVERFLOW: - return "RENEPAY_AMOUNT_OVERFLOW"; - case RENEPAY_CHANNEL_NOT_FOUND: - return "RENEPAY_CHANNEL_NOT_FOUND"; - case RENEPAY_BAD_CHANNEL: - return "RENEPAY_BAD_CHANNEL"; - case RENEPAY_BAD_ALLOCATION: - return "RENEPAY_BAD_ALLOCATION"; - case RENEPAY_PRECONDITION_ERROR: - return "RENEPAY_PRECONDITION_ERROR"; - case RENEPAY_UNEXPECTED: - return "RENEPAY_UNEXPECTED"; - } - - snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e); - return invalidbuf; -} diff --git a/plugins/renepay/errorcodes.h b/plugins/renepay/errorcodes.h deleted file mode 100644 index 6b9c09db7752..000000000000 --- a/plugins/renepay/errorcodes.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_ERRORCODES_H -#define LIGHTNING_PLUGINS_RENEPAY_ERRORCODES_H - -#include "config.h" - -/* Common types of failures for low level functions in renepay. */ -enum renepay_errorcode { - RENEPAY_NOERROR = 0, - - RENEPAY_AMOUNT_OVERFLOW, - RENEPAY_CHANNEL_NOT_FOUND, - RENEPAY_BAD_CHANNEL, - RENEPAY_BAD_ALLOCATION, - RENEPAY_PRECONDITION_ERROR, - RENEPAY_UNEXPECTED, -}; - -const char *renepay_errorcode_name(enum renepay_errorcode e); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_ERRORCODES_H */ From b3f46318d77cdb5738ce9d84e61205409bdb671d Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Wed, 25 Sep 2024 13:06:21 +0100 Subject: [PATCH 24/30] renepay: use askrene-disable-channel Use the dedicated API for disabling channels instead of workarounds. Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index ce9ae0d634b4..42c7639a90ae 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -830,11 +830,8 @@ static struct command_result *reserve_routes_cb(struct payment *payment) route->key.partid, route->final_msg, fmt_short_channel_id_dir(tmpctx, &scidd)); - /* FIXME: there is no askrene-disable-channel, - * we will fake its disabling by setting its - * liquidity to 0 */ struct out_req *req = jsonrpc_request_start( - cmd->plugin, cmd, "askrene-inform-channel", + cmd->plugin, cmd, "askrene-disable-channel", askrene_disable_channel_done, askrene_disable_channel_fail, payment); @@ -844,8 +841,6 @@ static struct command_result *reserve_routes_cb(struct payment *payment) json_add_short_channel_id(req->js, "short_channel_id", scidd.scid); json_add_num(req->js, "direction", scidd.dir); - json_add_amount_msat(req->js, "maximum_msat", - AMOUNT_MSAT(0)); send_outreq(cmd->plugin, req); payment->pending_rpcs++; @@ -1267,7 +1262,7 @@ askrene_disable_channel_fail(struct command *cmd, const char *buf, { plugin_log( cmd->plugin, LOG_UNUSUAL, - "failed to disable channel with askrene-inform-channel: %.*s", + "failed to disable channel with askrene-disable-channel: %.*s", json_tok_full_len(result), json_tok_full(buf, result)); return askrene_disable_channel_done(cmd, buf, result, payment); } @@ -1330,11 +1325,8 @@ static struct command_result *channelfilter_cb(struct payment *payment) pay_plugin->gossmap, chan), .dir = dir}; - /* FIXME: there is no askrene-disable-channel, - * we will fake its disabling by setting its - * liquidity to 0 */ struct out_req *req = jsonrpc_request_start( - cmd->plugin, cmd, "askrene-inform-channel", + cmd->plugin, cmd, "askrene-disable-channel", askrene_disable_channel_done, askrene_disable_channel_fail, payment); @@ -1345,8 +1337,6 @@ static struct command_result *channelfilter_cb(struct payment *payment) json_add_short_channel_id( req->js, "short_channel_id", scidd.scid); json_add_num(req->js, "direction", scidd.dir); - json_add_amount_msat(req->js, "maximum_msat", - AMOUNT_MSAT(0)); send_outreq(cmd->plugin, req); payment->pending_rpcs++; @@ -1378,15 +1368,12 @@ static struct command_result *manualexclusions_cb(struct payment *payment) assert(payment); struct command *cmd = payment_command(payment); assert(cmd); - + for(size_t i=0;iexclusions);i++){ const struct route_exclusion *ex = &payment->exclusions[i]; if (ex->type == EXCLUDE_CHANNEL) { - /* FIXME: there is no askrene-disable-channel, - * we will fake its disabling by setting its - * liquidity to 0 */ struct out_req *req = jsonrpc_request_start( - cmd->plugin, cmd, "askrene-inform-channel", + cmd->plugin, cmd, "askrene-disable-channel", askrene_disable_channel_done, askrene_disable_channel_fail, payment); json_add_string(req->js, "layer", @@ -1394,8 +1381,6 @@ static struct command_result *manualexclusions_cb(struct payment *payment) json_add_short_channel_id(req->js, "short_channel_id", ex->u.chan_id.scid); json_add_num(req->js, "direction", ex->u.chan_id.dir); - json_add_amount_msat(req->js, "maximum_msat", - AMOUNT_MSAT(0)); send_outreq(cmd->plugin, req); payment->pending_rpcs++; } else { From 192932e9f851c9a8568613805a680c002b035cd7 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Wed, 25 Sep 2024 13:08:14 +0100 Subject: [PATCH 25/30] renepay: fix plugin bugs - remove unused local_gossmods - fix failed RPC call to askrene-age when RENEPAY_LAYER does not yet exist - fix plugin hanging if there are no routehints in the invoice Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 8 ++++---- plugins/renepay/payment.c | 4 ---- plugins/renepay/payment.h | 3 --- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 42c7639a90ae..8db5fbbe52d9 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -337,7 +337,6 @@ static struct command_result *refreshgossmap_cb(struct payment *payment) { assert(pay_plugin->gossmap); // gossmap must be already initialized assert(payment); - assert(payment->local_gossmods); size_t num_channel_updates_rejected = 0; bool gossmap_changed = @@ -451,7 +450,7 @@ static struct command_result *routehints_cb(struct payment *payment) end = &r[j].pubkey; } } - return command_still_pending(cmd); + return payment_continue(payment); } REGISTER_PAYMENT_MODIFIER(routehints, routehints_cb); @@ -1216,10 +1215,11 @@ static struct command_result *knowledgerelax_cb(struct payment *payment) struct command *cmd = payment_command(payment); assert(cmd); - // FIXME: should relax other layers too? + /* askrene-age may fail if the layer has not yet been created, in that + * case we do nothing. */ struct out_req *req = jsonrpc_request_start( cmd->plugin, cmd, "askrene-age", askreneage_success, - payment_rpc_failure, payment); + askreneage_success, payment); json_add_string(req->js, "layer", RENEPAY_LAYER); json_add_u64(req->js, "cutoff", cutoff); diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c index b0a14112560f..c10e149c759f 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -103,7 +103,6 @@ struct payment *payment_new( p->pending_rpcs = 0; p->next_partid = 1; p->cmd_array = tal_arr(p, struct command *, 0); - p->local_gossmods = NULL; p->exclusions = tal_arr(p, struct route_exclusion, tal_count(exclusions)); @@ -135,7 +134,6 @@ static void payment_cleanup(struct payment *p) p->exec_state = INVALID_STATE; p->pending_rpcs = 0; tal_resize(&p->cmd_array, 0); - p->local_gossmods = tal_free(p->local_gossmods); p->exclusions = tal_free(p->exclusions); p->waitresult_timer = tal_free(p->waitresult_timer); routetracker_cleanup(p->routetracker); @@ -206,8 +204,6 @@ bool payment_update( assert(p->cmd_array); assert(tal_count(p->cmd_array) == 0); - p->local_gossmods = tal_free(p->local_gossmods); - p->exclusions = tal_free(p->exclusions); p->exclusions = tal_arr(p, struct route_exclusion, tal_count(exclusions)); diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index 07179c56a262..e72114f29c2b 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -55,9 +55,6 @@ struct payment { /* Running commands that want this payment */ struct command **cmd_array; - /* Localmods to apply to gossip_map for our own use. */ - struct gossmap_localmods *local_gossmods; - /* Here we queue channel and node disabling orders. */ struct route_exclusion *exclusions; From b115d8b3218e1c27ace80109f23dcaafeb2dbd91 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Fri, 27 Sep 2024 08:03:35 +0100 Subject: [PATCH 26/30] renepay: remove spurious "have_results" flag Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 5 +---- plugins/renepay/payment.c | 2 -- plugins/renepay/payment.h | 4 ---- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 8db5fbbe52d9..1695b5067cc2 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -913,7 +913,6 @@ REGISTER_PAYMENT_MODIFIER(sleep, sleep_cb); static struct command_result *collect_results_cb(struct payment *payment) { assert(payment); - payment->have_results = false; payment->retry = false; /* pending sendpay callbacks should be zero */ @@ -954,12 +953,10 @@ static struct command_result *collect_results_cb(struct payment *payment) payment->payment_info.amount)) { /* There are no succeeds but we are still pending delivering the * entire payment. We still need to collect more results. */ - payment->have_results = false; payment->retry = false; } else { /* We have some failures so that now we are short of * total_delivering, we may retry. */ - payment->have_results = true; // FIXME: we seem to always retry here if we don't fail // inmediately. But I am going to leave this variable here, @@ -1419,7 +1416,7 @@ REGISTER_PAYMENT_CONDITION(alwaystrue, alwaystrue_cb); */ static bool nothaveresults_cb(const struct payment *payment) { - return !payment->have_results; + return !routetracker_have_results(payment->routetracker); } REGISTER_PAYMENT_CONDITION(nothaveresults, nothaveresults_cb); diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c index c10e149c759f..cd3f665e4c20 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -109,7 +109,6 @@ struct payment *payment_new( for (size_t i = 0; i < tal_count(p->exclusions); i++) p->exclusions[i] = *exclusions[i]; - p->have_results = false; p->retry = false; p->waitresult_timer = NULL; @@ -210,7 +209,6 @@ bool payment_update( for (size_t i = 0; i < tal_count(p->exclusions); i++) p->exclusions[i] = *exclusions[i]; - p->have_results = false; p->retry = false; p->waitresult_timer = tal_free(p->waitresult_timer); diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index e72114f29c2b..ca52604ba589 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -58,10 +58,6 @@ struct payment { /* Here we queue channel and node disabling orders. */ struct route_exclusion *exclusions; - /* Flag to indicate wether we have collected enough results to make a - * decision on the payment progress. */ - bool have_results; - /* Flag to indicate wether we would like to retry the payment. */ bool retry; From fc8788f4c30efa7d8785555ed50b55fb34a2a9d4 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Fri, 27 Sep 2024 08:29:05 +0100 Subject: [PATCH 27/30] renepay: remove spurious payment->retry flag Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 23 +++-------------------- plugins/renepay/payment.c | 2 -- plugins/renepay/payment.h | 3 --- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 1695b5067cc2..5e76109905b6 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -913,8 +913,6 @@ REGISTER_PAYMENT_MODIFIER(sleep, sleep_cb); static struct command_result *collect_results_cb(struct payment *payment) { assert(payment); - payment->retry = false; - /* pending sendpay callbacks should be zero */ if (!routetracker_have_results(payment->routetracker)) return payment_continue(payment); @@ -948,23 +946,6 @@ static struct command_result *collect_results_cb(struct payment *payment) * inmediately finish the payment. */ return payment_fail(payment, final_error, "%s", final_msg); } - - if (amount_msat_greater_eq(payment->total_delivering, - payment->payment_info.amount)) { - /* There are no succeeds but we are still pending delivering the - * entire payment. We still need to collect more results. */ - payment->retry = false; - } else { - /* We have some failures so that now we are short of - * total_delivering, we may retry. */ - - // FIXME: we seem to always retry here if we don't fail - // inmediately. But I am going to leave this variable here, - // cause we might decide in the future to put some conditions on - // retries, like a maximum number of retries. - payment->retry = true; - } - return payment_continue(payment); } @@ -1425,8 +1406,10 @@ REGISTER_PAYMENT_CONDITION(nothaveresults, nothaveresults_cb); * retry * * A payment condition that returns true if we should retry the payment. + * + * FIXME: always true? */ -static bool retry_cb(const struct payment *payment) { return payment->retry; } +static bool retry_cb(const struct payment *payment) { return true; } REGISTER_PAYMENT_CONDITION(retry, retry_cb); diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c index cd3f665e4c20..0104410bec83 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -109,7 +109,6 @@ struct payment *payment_new( for (size_t i = 0; i < tal_count(p->exclusions); i++) p->exclusions[i] = *exclusions[i]; - p->retry = false; p->waitresult_timer = NULL; p->routetracker = new_routetracker(p, p); @@ -209,7 +208,6 @@ bool payment_update( for (size_t i = 0; i < tal_count(p->exclusions); i++) p->exclusions[i] = *exclusions[i]; - p->retry = false; p->waitresult_timer = tal_free(p->waitresult_timer); return true; diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index ca52604ba589..7a12866f99b9 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -58,9 +58,6 @@ struct payment { /* Here we queue channel and node disabling orders. */ struct route_exclusion *exclusions; - /* Flag to indicate wether we would like to retry the payment. */ - bool retry; - /* Timer we use to wait for results. */ struct plugin_timer *waitresult_timer; From 86dd2e2791fb4c942dc4e2f35758aa17c8e6ebd4 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Fri, 27 Sep 2024 09:06:08 +0100 Subject: [PATCH 28/30] renepay: simplify collect_results Signed-off-by: Lagrang3 --- plugins/renepay/mods.c | 3 ++- plugins/renepay/routetracker.c | 49 +++++++--------------------------- plugins/renepay/routetracker.h | 8 +++--- 3 files changed, 15 insertions(+), 45 deletions(-) diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 5e76109905b6..81cdc559d757 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -922,7 +922,8 @@ static struct command_result *collect_results_cb(struct payment *payment) enum jsonrpc_errcode final_error = LIGHTNINGD; const char *final_msg = NULL; - payment_collect_results(payment, &payment_preimage, &final_error, &final_msg); + tal_collect_results(tmpctx, payment->routetracker, &payment_preimage, + &final_error, &final_msg); if (payment_preimage) { /* If we have the preimage that means one succeed, we diff --git a/plugins/renepay/routetracker.c b/plugins/renepay/routetracker.c index 9fada578b176..81070440e9a7 100644 --- a/plugins/renepay/routetracker.c +++ b/plugins/renepay/routetracker.c @@ -65,10 +65,9 @@ static void routetracker_add_to_final(struct routetracker *routetracker, enum jsonrpc_errcode final_error = LIGHTNINGD; const char *final_msg = NULL; - /* Finalized routes must be processed and removed in order to - * free the uncertainty network's HTLCs. */ - payment_collect_results(payment, &payment_preimage, - &final_error, &final_msg); + tal_collect_results(tmpctx, payment->routetracker, + &payment_preimage, &final_error, + &final_msg); if (payment_preimage) { /* If we have the preimage that means one succeed, we @@ -190,13 +189,11 @@ void route_pending_register(struct routetracker *routetracker, } } -void payment_collect_results(struct payment *payment, - struct preimage **payment_preimage, - enum jsonrpc_errcode *final_error, - const char **final_msg) +void tal_collect_results(const tal_t *ctx, struct routetracker *routetracker, + struct preimage **payment_preimage, + enum jsonrpc_errcode *final_error, + const char **final_msg) { - assert(payment); - struct routetracker *routetracker = payment->routetracker; assert(routetracker); const size_t ncompleted = tal_count(routetracker->finalized_routes); for (size_t i = 0; i < ncompleted; i++) { @@ -211,45 +208,17 @@ void payment_collect_results(struct payment *payment, if (r->result->status == SENDPAY_COMPLETE && payment_preimage) { assert(r->result->payment_preimage); *payment_preimage = - tal_dup(tmpctx, struct preimage, + tal_dup(ctx, struct preimage, r->result->payment_preimage); break; } - /* We should never start a new groupid while there are pending - * onions with a different groupid. We ignore any failure that - * does not have the same groupid as the one we used for our - * routes. */ - if (payment->groupid != r->key.groupid) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "%s: current groupid=%" PRIu64 - ", but recieved a sendpay result with " - "groupid=%" PRIu64, - __func__, payment->groupid, - r->key.groupid); - continue; - } - - assert(r->result->status == SENDPAY_FAILED && - payment->groupid == r->key.groupid); - if (r->final_msg) { if (final_error) *final_error = r->final_error; if (final_msg) - *final_msg = tal_strdup(tmpctx, r->final_msg); - } - - if (!amount_msat_sub(&payment->total_delivering, - payment->total_delivering, - route_delivers(r)) || - !amount_msat_sub(&payment->total_sent, payment->total_sent, - route_sends(r))) { - plugin_err(pay_plugin->plugin, - "%s: routes do not add up to " - "payment total amount.", - __func__); + *final_msg = tal_strdup(ctx, r->final_msg); } } for (size_t i = 0; i < ncompleted; i++) diff --git a/plugins/renepay/routetracker.h b/plugins/renepay/routetracker.h index 2065922ea5ae..e9acf333bab7 100644 --- a/plugins/renepay/routetracker.h +++ b/plugins/renepay/routetracker.h @@ -29,10 +29,10 @@ bool routetracker_have_results(struct routetracker *routetracker); * - update the commited amounts, * - update the uncertainty network, * - and free the allocated memory. */ -void payment_collect_results(struct payment *payment, - struct preimage **payment_preimage, - enum jsonrpc_errcode *final_error, - const char **final_msg); +void tal_collect_results(const tal_t *ctx, struct routetracker *routetracker, + struct preimage **payment_preimage, + enum jsonrpc_errcode *final_error, + const char **final_msg); void route_pending_register(struct routetracker *routetracker, struct route *route); From 13a8e79c05cb71caf63710b911d549db6c7ee414 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Fri, 27 Sep 2024 16:38:45 +0100 Subject: [PATCH 29/30] renepay: mark routes when reserved Signed-off-by: Lagrang3 --- plugins/renepay/json.c | 1 + plugins/renepay/mods.c | 33 ++++++++++++++++++++++++++------- plugins/renepay/route.c | 1 + plugins/renepay/route.h | 12 ++++++++++++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/plugins/renepay/json.c b/plugins/renepay/json.c index b98496338137..296120852847 100644 --- a/plugins/renepay/json.c +++ b/plugins/renepay/json.c @@ -64,6 +64,7 @@ struct route *tal_route_from_json(const tal_t *ctx, const char *buf, route->hops = NULL; route->final_msg = NULL; route->final_error = LIGHTNINGD; + route->is_reserved = false; return route; fail: diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 81cdc559d757..956bbeee2ec1 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -776,23 +776,38 @@ static struct command_result * askrene_disable_channel_fail(struct command *cmd, const char *buf, const jsmntok_t *result, struct payment *payment); +struct reserve_request{ + struct payment *payment; + struct route *route; +}; + static struct command_result * -askrene_reserve_done(struct command *cmd, const char *buf UNUSED, - const jsmntok_t *result UNUSED, struct payment *payment) +askrene_reserve_continue(struct reserve_request *r) { + struct payment *payment = r->payment; + r = tal_free(r); assert(payment->pending_rpcs > 0); payment->pending_rpcs--; return payment_continue(payment); } static struct command_result * -askrene_reserve_fail(struct command *cmd, const char *buf, - const jsmntok_t *result, struct payment *payment) +askrene_reserve_done(struct command *cmd, const char *buf UNUSED, + const jsmntok_t *result UNUSED, struct reserve_request *r) +{ + route_mark_reserved(r->route); + return askrene_reserve_continue(r); +} + +static struct command_result *askrene_reserve_fail(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct reserve_request *r) { plugin_log(cmd->plugin, LOG_UNUSUAL, "failed to reserve liquidity with askrene-reserve: %.*s", json_tok_full_len(result), json_tok_full(buf, result)); - return askrene_reserve_done(cmd, buf, result, payment); + return askrene_reserve_continue(r); } static struct command_result *reserve_routes_cb(struct payment *payment) @@ -847,10 +862,14 @@ static struct command_result *reserve_routes_cb(struct payment *payment) } else { // FIXME: don't forget to unreserve, maybe add a // destructor to the route that calls unreserve + struct reserve_request *r = + tal(NULL, struct reserve_request); + r->payment = payment; + r->route = route; + struct out_req *req = jsonrpc_request_start( cmd->plugin, cmd, "askrene-reserve", - askrene_reserve_done, askrene_reserve_fail, - payment); + askrene_reserve_done, askrene_reserve_fail, r); json_array_start(req->js, "path"); for (size_t j = 0; j < tal_count(route->hops); j++) { diff --git a/plugins/renepay/route.c b/plugins/renepay/route.c index 3af704537cc5..010b9f51ed6e 100644 --- a/plugins/renepay/route.c +++ b/plugins/renepay/route.c @@ -17,6 +17,7 @@ struct route *new_route(const tal_t *ctx, u32 groupid, route->hops = NULL; route->success_prob = 0.0; route->result = NULL; + route->is_reserved = false; route->amount = amount; route->amount_sent = amount_sent; diff --git a/plugins/renepay/route.h b/plugins/renepay/route.h index 272f94236c5c..15bc3c4e6cdc 100644 --- a/plugins/renepay/route.h +++ b/plugins/renepay/route.h @@ -67,8 +67,20 @@ struct route { /* result of waitsenday */ struct payment_result *result; + + /* Mark if we called askrene-reserve with this route */ + bool is_reserved; }; +static inline bool route_is_reserved(const struct route *r) +{ + return r->is_reserved; +} +static inline void route_mark_reserved(struct route *r) +{ + r->is_reserved = true; +} + static inline struct routekey routekey(const struct sha256 *hash, u64 groupid, u64 partid) { From 7faebad8174b38a956fba04d2c5fdb13858cff1e Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Fri, 27 Sep 2024 16:52:09 +0100 Subject: [PATCH 30/30] renepay: refactor routefail and routetracker - use a single datatype for RPC calls in routetracker and routefail, - call askrene-unreserve for every route after sendpay notification, - call askrene-inform-channel for failed routes. Signed-off-by: Lagrang3 --- plugins/renepay/routefail.c | 142 +++++++++++++++++++++++---------- plugins/renepay/routefail.h | 5 +- plugins/renepay/routetracker.c | 128 ++++++++++++++--------------- plugins/renepay/routetracker.h | 18 ++--- 4 files changed, 172 insertions(+), 121 deletions(-) diff --git a/plugins/renepay/routefail.c b/plugins/renepay/routefail.c index 175b7d5b03d4..8ebc513eef78 100644 --- a/plugins/renepay/routefail.c +++ b/plugins/renepay/routefail.c @@ -3,7 +3,6 @@ #include #include #include -#include #include enum node_type { @@ -13,45 +12,21 @@ enum node_type { UNKNOWN_NODE }; -struct routefail { - struct command *cmd; - struct payment *payment; - struct route *route; -}; - -static struct command_result *update_gossip(struct routefail *r); -static struct command_result *handle_failure(struct routefail *r); +static struct command_result * +routefail_update_gossip(struct route_notification *r); +static struct command_result * +routefail_handle_cases(struct route_notification *r); +static struct command_result * +routefail_update_knowledge(struct route_notification *r); -struct command_result *routefail_start(const tal_t *ctx, struct route *route, - struct command *cmd) +struct command_result *routefail_start(struct route_notification *r) { - assert(route); - struct routefail *r = tal(ctx, struct routefail); - struct payment *payment = - payment_map_get(pay_plugin->payment_map, route->key.payment_hash); - - if (payment == NULL) - plugin_err(pay_plugin->plugin, - "%s: payment with hash %s not found.", - __func__, - fmt_sha256(tmpctx, &route->key.payment_hash)); - - r->payment = payment; - r->route = route; - r->cmd = cmd; - assert(route->result); - return update_gossip(r); + return routefail_update_gossip(r); } -static struct command_result *routefail_end(struct routefail *r TAKES) +static struct command_result *routefail_end(struct route_notification *r) { - /* Notify the tracker that route has failed and routefail have completed - * handling all possible errors cases. */ - struct command *cmd = r->cmd; - route_failure_register(r->payment->routetracker, r->route); - if (taken(r)) - r = tal_steal(tmpctx, r); - return notification_handled(cmd); + return route_unreserve(r); } /***************************************************************************** @@ -112,15 +87,15 @@ static u8 *channel_update_from_onion_error(const tal_t *ctx, static struct command_result *update_gossip_done(struct command *cmd UNUSED, const char *buf UNUSED, const jsmntok_t *result UNUSED, - struct routefail *r) + struct route_notification *r) { - return handle_failure(r); + return routefail_handle_cases(r); } static struct command_result *update_gossip_failure(struct command *cmd UNUSED, const char *buf, const jsmntok_t *result, - struct routefail *r) + struct route_notification *r) { assert(r); assert(r->payment); @@ -139,7 +114,8 @@ static struct command_result *update_gossip_failure(struct command *cmd UNUSED, return update_gossip_done(cmd, buf, result, r); } -static struct command_result *update_gossip(struct routefail *r) +static struct command_result * +routefail_update_gossip(struct route_notification *r) { /* if there is no raw_message we continue */ if (!r->route->result->raw_message) @@ -158,7 +134,7 @@ static struct command_result *update_gossip(struct routefail *r) return send_outreq(r->cmd->plugin, req); skip_update_gossip: - return handle_failure(r); + return routefail_handle_cases(r); } /***************************************************************************** @@ -186,7 +162,8 @@ static void route_final_error(struct route *route, enum jsonrpc_errcode error, route->final_msg = tal_strdup(route, what); } -static struct command_result *handle_failure(struct routefail *r) +static struct command_result * +routefail_handle_cases(struct route_notification *r) { /* BOLT #4: * @@ -437,5 +414,86 @@ static struct command_result *handle_failure(struct routefail *r) break; } - return routefail_end(take(r)); + return routefail_update_knowledge(r); +} + +/***************************************************************************** + * update_knowledge + * + * Make RPC calls to askrene to pass information about the liquidity constraint + * we have learned from the errors. + */ + +static struct command_result * +askrene_inform_done(struct command *cmd UNUSED, const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct route_notification *r) +{ + return routefail_end(r); +} + +static struct command_result * +askrene_inform_fail(struct command *cmd UNUSED, const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct route_notification *r) +{ + plugin_log( + cmd->plugin, LOG_UNUSUAL, + "%s: failed RPC call to askrene-inform-channel, returned: %.*s", + __func__, json_tok_full_len(result), json_tok_full(buf, result)); + return askrene_inform_done(cmd, buf, result, r); +} + +static struct command_result * +routefail_update_knowledge(struct route_notification *r) +{ + /* If we don't have the hops we can't learn anything. */ + if (!r->route->hops || !r->route->result + || !r->route->result->erring_index) { + plugin_log(r->cmd->plugin, LOG_DBG, + "Cannot update knowledge from route %s, missing " + "hops or result.", + fmt_routekey(tmpctx, &r->route->key)); + goto skip_update_knowledge; + } + + const int path_len = tal_count(r->route->hops); + const int last_good_channel = *r->route->result->erring_index - 1; + + if (last_good_channel >= path_len) + plugin_err(r->cmd->plugin, + "%s: last_good_channel (%d) >= path_len (%d)", + __func__, last_good_channel, path_len); + + if(r->route->result->failcode!=WIRE_TEMPORARY_CHANNEL_FAILURE || + (last_good_channel + 1)>=path_len) + goto skip_update_knowledge; + + /* A WIRE_TEMPORARY_CHANNEL_FAILURE could mean not enough liquidity to + * forward the payment or cannot add one more HTLC. */ + const struct short_channel_id scid = + r->route->hops[last_good_channel + 1].scid; + const int direction = r->route->hops[last_good_channel + 1].direction; + const struct amount_msat this_amt = + r->route->hops[last_good_channel + 1].amount; + + // FIXME: call askrene-query-reserve + // if this route was reseved then + // call askrene-inform-channel for amt + // else + // call askrene-inform-channel for amt+this_amnt + + struct out_req *req = jsonrpc_request_start( + r->cmd->plugin, r->cmd, "askrene-inform-channel", + askrene_inform_done, askrene_inform_fail, r); + + json_add_string(req->js, "layer", RENEPAY_LAYER); + json_add_short_channel_id(req->js, "short_channel_id", scid); + json_add_num(req->js, "direction", direction); + json_add_amount_msat(req->js, "maximum_msat", this_amt); + + return send_outreq(r->cmd->plugin, req); + +skip_update_knowledge: + return routefail_end(r); } diff --git a/plugins/renepay/routefail.h b/plugins/renepay/routefail.h index bad3aabef283..213764170cab 100644 --- a/plugins/renepay/routefail.h +++ b/plugins/renepay/routefail.h @@ -4,9 +4,8 @@ /* This module provides the state machine for handling route failures. */ #include "config.h" -#include +#include -struct command_result *routefail_start(const tal_t *ctx, struct route *route, - struct command *cmd); +struct command_result *routefail_start(struct route_notification *r); #endif /* LIGHTNING_PLUGINS_RENEPAY_ROUTEFAIL_H */ diff --git a/plugins/renepay/routetracker.c b/plugins/renepay/routetracker.c index 81070440e9a7..c9608e708af1 100644 --- a/plugins/renepay/routetracker.c +++ b/plugins/renepay/routetracker.c @@ -45,15 +45,19 @@ void routetracker_cleanup(struct routetracker *routetracker) // TODO } -static void routetracker_add_to_final(struct routetracker *routetracker, - struct route *route) +static struct command_result * +routetracker_add_to_final(struct route_notification *r) { + struct payment *payment = r->payment; + struct command *cmd = r->cmd; + struct route *route = r->route; + struct routetracker *routetracker = payment->routetracker; + + r = tal_free(r); + tal_arr_expand(&routetracker->finalized_routes, route); tal_steal(routetracker->finalized_routes, route); - struct payment *payment = - payment_map_get(pay_plugin->payment_map, route->key.payment_hash); - assert(payment); if (payment->exec_state == INVALID_STATE) { /* payment is offline, collect results now and set the payment * state accordingly. */ @@ -74,73 +78,20 @@ static void routetracker_add_to_final(struct routetracker *routetracker, * inmediately finish the payment. */ register_payment_success(payment, take(payment_preimage)); - return; } - if (final_msg) { + else if (final_msg) { /* We received a sendpay result with a final error * message, we inmediately finish the payment. */ register_payment_fail(payment, final_error, "%s", final_msg); - return; } } -} - -static void route_success_register(struct routetracker *routetracker, - struct route *route) -{ - if(route->hops){ - // uncertainty_route_success(pay_plugin->uncertainty, route); - } - routetracker_add_to_final(routetracker, route); -} -void route_failure_register(struct routetracker *routetracker, - struct route *route) -{ - struct payment_result *result = route->result; - assert(result); - - /* Update the knowledge in the uncertaity network. */ - if (route->hops) { - assert(result->erring_index); - int path_len = tal_count(route->hops); - - /* index of the last channel before the erring node */ - const int last_good_channel = *result->erring_index - 1; - - if (last_good_channel >= path_len) { - plugin_err(pay_plugin->plugin, - "last_good_channel (%d) >= path_len (%d)", - last_good_channel, path_len); - } - - /* All channels before the erring node could forward the - * payment. */ - for (int i = 0; i <= last_good_channel; i++) { - // uncertainty_channel_can_send(pay_plugin->uncertainty, - // route->hops[i].scid, - // route->hops[i].direction); - } - - if (result->failcode == WIRE_TEMPORARY_CHANNEL_FAILURE && - (last_good_channel + 1) < path_len) { - /* A WIRE_TEMPORARY_CHANNEL_FAILURE could mean not - * enough liquidity to forward the payment or cannot add - * one more HTLC. - */ - // uncertainty_channel_cannot_send( - // pay_plugin->uncertainty, - // route->hops[last_good_channel + 1].scid, - // route->hops[last_good_channel + 1].direction); - } - } - routetracker_add_to_final(routetracker, route); + return notification_handled(cmd); } static void remove_route(struct route *route, struct route_map *map) { route_map_del(map, route); - // uncertainty_remove_htlcs(pay_plugin->uncertainty, route); } /* This route is pending, ie. locked in HTLCs. @@ -226,6 +177,44 @@ void tal_collect_results(const tal_t *ctx, struct routetracker *routetracker, tal_resize(&routetracker->finalized_routes, 0); } +static struct command_result * +askrene_unreserve_done(struct command *cmd, const char *buf, + const jsmntok_t *tok, struct route_notification *r) +{ + return routetracker_add_to_final(r); +} +static struct command_result * +askrene_unreserve_fail(struct command *cmd, const char *buf, + const jsmntok_t *tok, struct route_notification *r) +{ + /* FIXME: we should implement a safer way to add and remove reserves. */ + plugin_log(cmd->plugin, LOG_UNUSUAL, "askrene-unreserve failed: %.*s", + json_tok_full_len(tok), json_tok_full(buf, tok)); + return askrene_unreserve_done(cmd, buf, tok, r); +} + +struct command_result *route_unreserve(struct route_notification *r) +{ + if (!route_is_reserved(r->route)) + return routetracker_add_to_final(r); + + struct out_req *req = jsonrpc_request_start( + r->cmd->plugin, r->cmd, "askrene-unreserve", askrene_unreserve_done, + askrene_unreserve_fail, r); + json_array_start(req->js, "path"); + for (size_t i = 0; i < tal_count(r->route->hops); i++) { + json_object_start(req->js, NULL); + json_add_short_channel_id(req->js, "short_channel_id", + r->route->hops[i].scid); + json_add_num(req->js, "direction", r->route->hops[i].direction); + json_add_amount_msat(req->js, "amount_msat", + r->route->hops[i].amount); + json_object_end(req->js); + } + json_array_end(req->js); + return send_outreq(r->cmd->plugin, req); +} + struct command_result *notification_sendpay_failure(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -288,9 +277,12 @@ struct command_result *notification_sendpay_failure(struct command *cmd, route->result->status = SENDPAY_FAILED; } - /* we do some error processing steps before calling - * route_failure_register. */ - return routefail_start(payment, route, cmd); + /* we do some error processing steps before calling */ + struct route_notification *r = tal(NULL, struct route_notification); + r->cmd = cmd; + r->payment = payment; + r->route = route; + return routefail_start(r); } struct command_result *notification_sendpay_success(struct command *cmd, @@ -318,8 +310,6 @@ struct command_result *notification_sendpay_success(struct command *cmd, return notification_handled(cmd); } - struct routetracker *routetracker = payment->routetracker; - assert(routetracker); struct route *route = route_map_get(pay_plugin->pending_routes, key); if (!route) { @@ -340,6 +330,10 @@ struct command_result *notification_sendpay_success(struct command *cmd, json_tok_full_len(sub), json_tok_full(buf, sub)); assert(route->result->status == SENDPAY_COMPLETE); - route_success_register(payment->routetracker, route); - return notification_handled(cmd); + + struct route_notification *r = tal(NULL, struct route_notification); + r->cmd = cmd; + r->payment = payment; + r->route = route; + return route_unreserve(r); } diff --git a/plugins/renepay/routetracker.h b/plugins/renepay/routetracker.h index e9acf333bab7..efe8e25b401a 100644 --- a/plugins/renepay/routetracker.h +++ b/plugins/renepay/routetracker.h @@ -19,6 +19,15 @@ struct routetracker{ struct route **finalized_routes; }; +/* Data used to make RPC calls after a sendpay notification was received. */ +struct route_notification { + struct command *cmd; + struct payment *payment; + struct route *route; +}; + +struct command_result *route_unreserve(struct route_notification *r); + struct routetracker *new_routetracker(const tal_t *ctx, struct payment *payment); void routetracker_cleanup(struct routetracker *routetracker); @@ -45,13 +54,4 @@ struct command_result *notification_sendpay_success(struct command *cmd, const char *buf, const jsmntok_t *params); -/* Notify the tracker that this route has failed. */ -void route_failure_register(struct routetracker *routetracker, - struct route *route); - -// FIXME: double-check that we actually get one notification for each sendpay, -// ie. that after some time we don't have yet pending sendpays for old failed or -// successful payments that we havent processed because we haven't received the -// notification - #endif /* LIGHTNING_PLUGINS_RENEPAY_ROUTETRACKER_H */