@@ -319,6 +319,28 @@ static void set_capacity(s64 *capacity, u64 value, u64 *cap_on_capacity)
319
319
* cap_on_capacity -= * capacity ;
320
320
}
321
321
322
+ /* FIXME: unit test this */
323
+ /* The probability of forwarding a payment amount given a high and low liquidity
324
+ * bounds.
325
+ * @low: the liquidity is known to be greater or equal than "low"
326
+ * @high: the liquidity is known to be less than "high"
327
+ * @amount: how much is required to forward */
328
+ static double pickhardt_richter_probability (struct amount_msat low ,
329
+ struct amount_msat high ,
330
+ struct amount_msat amount )
331
+ {
332
+ struct amount_msat all_states , good_states ;
333
+ if (amount_msat_greater_eq (amount , high ))
334
+ return 0.0 ;
335
+ if (!amount_msat_sub (& amount , amount , low ))
336
+ return 1.0 ;
337
+ if (!amount_msat_sub (& all_states , high , low ))
338
+ abort (); // we expect high > low
339
+ if (!amount_msat_sub (& good_states , all_states , amount ))
340
+ abort (); // we expect high > amount
341
+ return amount_msat_ratio (good_states , all_states );
342
+ }
343
+
322
344
// TODO(eduardo): unit test this
323
345
/* Split a directed channel into parts with linear cost function. */
324
346
static void linearize_channel (const struct pay_parameters * params ,
@@ -867,6 +889,46 @@ get_flow_paths(const tal_t *ctx,
867
889
return flows ;
868
890
}
869
891
892
+ /* Given a single path build a flow set. */
893
+ static struct flow * *
894
+ get_flow_singlepath (const tal_t * ctx , const struct pay_parameters * params ,
895
+ const struct graph * graph , const struct gossmap * gossmap ,
896
+ const struct node source , const struct node destination ,
897
+ const u64 pay_amount , const struct arc * prev )
898
+ {
899
+ struct flow * * flows , * f ;
900
+ flows = tal_arr (ctx , struct flow * , 1 );
901
+ f = flows [0 ] = tal (flows , struct flow );
902
+
903
+ size_t length = 0 ;
904
+
905
+ for (u32 cur_idx = destination .idx ; cur_idx != source .idx ;) {
906
+ assert (cur_idx != INVALID_INDEX );
907
+ length ++ ;
908
+ struct arc arc = prev [cur_idx ];
909
+ struct node next = arc_tail (graph , arc );
910
+ cur_idx = next .idx ;
911
+ }
912
+ f -> path = tal_arr (f , const struct gossmap_chan * , length );
913
+ f -> dirs = tal_arr (f , int , length );
914
+
915
+ for (u32 cur_idx = destination .idx ; cur_idx != source .idx ;) {
916
+ int chandir ;
917
+ u32 chanidx ;
918
+ struct arc arc = prev [cur_idx ];
919
+ arc_to_parts (arc , & chanidx , & chandir , NULL , NULL );
920
+
921
+ length -- ;
922
+ f -> path [length ] = gossmap_chan_byidx (gossmap , chanidx );
923
+ f -> dirs [length ] = chandir ;
924
+
925
+ struct node next = arc_tail (graph , arc );
926
+ cur_idx = next .idx ;
927
+ }
928
+ f -> delivers = params -> amount ;
929
+ return flows ;
930
+ }
931
+
870
932
// TODO(eduardo): choose some default values for the minflow parameters
871
933
/* eduardo: I think it should be clear that this module deals with linear
872
934
* flows, ie. base fees are not considered. Hence a flow along a path is
@@ -1025,6 +1087,186 @@ static struct amount_msat linear_flows_cost(struct flow **flows,
1025
1087
return total ;
1026
1088
}
1027
1089
1090
+ /* Initialize the data vectors for the single-path solver. */
1091
+ static void init_linear_network_single_path (
1092
+ const tal_t * ctx , const struct pay_parameters * params , struct graph * * graph ,
1093
+ double * * arc_prob_cost , s64 * * arc_fee_cost , s64 * * arc_capacity )
1094
+ {
1095
+ const size_t max_num_chans = gossmap_max_chan_idx (params -> rq -> gossmap );
1096
+ const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL ;
1097
+ const size_t max_num_nodes = gossmap_max_node_idx (params -> rq -> gossmap );
1098
+
1099
+ * graph = graph_new (ctx , max_num_nodes , max_num_arcs , ARC_DUAL_BITOFF );
1100
+ * arc_prob_cost = tal_arr (ctx , double , max_num_arcs );
1101
+ for (size_t i = 0 ; i < max_num_arcs ; ++ i )
1102
+ (* arc_prob_cost )[i ] = DBL_MAX ;
1103
+
1104
+ * arc_fee_cost = tal_arr (ctx , s64 , max_num_arcs );
1105
+ for (size_t i = 0 ; i < max_num_arcs ; ++ i )
1106
+ (* arc_fee_cost )[i ] = INT64_MAX ;
1107
+ * arc_capacity = tal_arrz (ctx , s64 , max_num_arcs );
1108
+
1109
+ const struct gossmap * gossmap = params -> rq -> gossmap ;
1110
+
1111
+ for (struct gossmap_node * node = gossmap_first_node (gossmap ); node ;
1112
+ node = gossmap_next_node (gossmap , node )) {
1113
+ const u32 node_id = gossmap_node_idx (gossmap , node );
1114
+
1115
+ for (size_t j = 0 ; j < node -> num_chans ; ++ j ) {
1116
+ int half ;
1117
+ const struct gossmap_chan * c =
1118
+ gossmap_nth_chan (gossmap , node , j , & half );
1119
+ struct amount_msat mincap , maxcap ;
1120
+
1121
+ if (!gossmap_chan_set (c , half ) ||
1122
+ !c -> half [half ].enabled )
1123
+ continue ;
1124
+
1125
+ /* If a channel cannot forward the total amount we don't
1126
+ * use it. */
1127
+ if (amount_msat_less (params -> amount ,
1128
+ gossmap_chan_htlc_min (c , half )) ||
1129
+ amount_msat_greater (params -> amount ,
1130
+ gossmap_chan_htlc_max (c , half )))
1131
+ continue ;
1132
+
1133
+ get_constraints (params -> rq , c , half , & mincap , & maxcap );
1134
+ /* Assume if min > max, min is wrong */
1135
+ if (amount_msat_greater (mincap , maxcap ))
1136
+ mincap = maxcap ;
1137
+ /* It is preferable to work on 1msat past the known
1138
+ * bound. */
1139
+ if (!amount_msat_accumulate (& maxcap , amount_msat (1 )))
1140
+ abort ();
1141
+
1142
+ /* If amount is greater than the known liquidity upper
1143
+ * bound we get infinite probability cost. */
1144
+ if (amount_msat_greater_eq (params -> amount , maxcap ))
1145
+ continue ;
1146
+
1147
+ const u32 chan_id = gossmap_chan_idx (gossmap , c );
1148
+
1149
+ const struct gossmap_node * next =
1150
+ gossmap_nth_node (gossmap , c , !half );
1151
+
1152
+ const u32 next_id = gossmap_node_idx (gossmap , next );
1153
+
1154
+ /* channel to self? */
1155
+ if (node_id == next_id )
1156
+ continue ;
1157
+
1158
+ struct arc arc =
1159
+ arc_from_parts (chan_id , half , 0 , false);
1160
+
1161
+ graph_add_arc (* graph , arc , node_obj (node_id ),
1162
+ node_obj (next_id ));
1163
+
1164
+ (* arc_capacity )[arc .idx ] = 1 ;
1165
+ (* arc_prob_cost )[arc .idx ] =
1166
+ (-1.0 ) * log (pickhardt_richter_probability (
1167
+ mincap , maxcap , params -> amount ));
1168
+
1169
+ struct amount_msat fee ;
1170
+ if (!amount_msat_fee (& fee , params -> amount ,
1171
+ c -> half [half ].base_fee ,
1172
+ c -> half [half ].proportional_fee ))
1173
+ abort ();
1174
+ u32 fee_msat ;
1175
+ if (!amount_msat_to_u32 (fee , & fee_msat ))
1176
+ continue ;
1177
+ (* arc_fee_cost )[arc .idx ] =
1178
+ fee_msat +
1179
+ params -> delay_feefactor * c -> half [half ].delay ;
1180
+ }
1181
+ }
1182
+ }
1183
+
1184
+ /* Similar to minflow but computes routes that have a single path. */
1185
+ struct flow * * single_path_flow (const tal_t * ctx , const struct route_query * rq ,
1186
+ const struct gossmap_node * source ,
1187
+ const struct gossmap_node * target ,
1188
+ struct amount_msat amount , u32 mu ,
1189
+ double delay_feefactor )
1190
+ {
1191
+ struct flow * * flow_paths ;
1192
+ /* We allocate everything off this, and free it at the end,
1193
+ * as we can be called multiple times without cleaning tmpctx! */
1194
+ tal_t * working_ctx = tal (NULL , char );
1195
+ struct pay_parameters * params = tal (working_ctx , struct pay_parameters );
1196
+
1197
+ params -> rq = rq ;
1198
+ params -> source = source ;
1199
+ params -> target = target ;
1200
+ params -> amount = amount ;
1201
+ /* for the single-path solver the accuracy does not detriment
1202
+ * performance */
1203
+ params -> accuracy = amount ;
1204
+ params -> delay_feefactor = delay_feefactor ;
1205
+ params -> base_fee_penalty = base_fee_penalty_estimate (amount );
1206
+
1207
+ struct graph * graph ;
1208
+ double * arc_prob_cost ;
1209
+ s64 * arc_fee_cost ;
1210
+ s64 * arc_capacity ;
1211
+
1212
+ init_linear_network_single_path (working_ctx , params , & graph ,
1213
+ & arc_prob_cost , & arc_fee_cost ,
1214
+ & arc_capacity );
1215
+
1216
+ const struct node dst = {.idx = gossmap_node_idx (rq -> gossmap , target )};
1217
+ const struct node src = {.idx = gossmap_node_idx (rq -> gossmap , source )};
1218
+
1219
+ const size_t max_num_nodes = graph_max_num_nodes (graph );
1220
+ const size_t max_num_arcs = graph_max_num_arcs (graph );
1221
+
1222
+ s64 * potential = tal_arrz (working_ctx , s64 , max_num_nodes );
1223
+ s64 * distance = tal_arrz (working_ctx , s64 , max_num_nodes );
1224
+ s64 * arc_cost = tal_arrz (working_ctx , s64 , max_num_arcs );
1225
+ struct arc * prev = tal_arrz (working_ctx , struct arc , max_num_nodes );
1226
+
1227
+ combine_cost_function (working_ctx , graph , arc_prob_cost , arc_fee_cost ,
1228
+ rq -> biases , mu , arc_cost );
1229
+
1230
+ /* We solve a linear cost flow problem. */
1231
+ if (!dijkstra_path (working_ctx , graph , src , dst ,
1232
+ /* prune = */ true, arc_capacity ,
1233
+ /*threshold = */ 1 , arc_cost , potential , prev ,
1234
+ distance )) {
1235
+ /* This might fail if we are unable to find a suitable route, it
1236
+ * doesn't mean the plugin is broken, that's why we LOG_INFORM. */
1237
+ rq_log (tmpctx , rq , LOG_INFORM ,
1238
+ "%s: could not find a feasible single path" , __func__ );
1239
+ goto fail ;
1240
+ }
1241
+ const u64 pay_amount =
1242
+ amount_msat_ratio_ceil (params -> amount , params -> accuracy );
1243
+
1244
+ /* We dissect the flow into payment routes.
1245
+ * Actual amounts considering fees are computed for every
1246
+ * channel in the routes. */
1247
+ flow_paths = get_flow_singlepath (ctx , params , graph , rq -> gossmap ,
1248
+ src , dst , pay_amount , prev );
1249
+ if (!flow_paths ) {
1250
+ rq_log (tmpctx , rq , LOG_BROKEN ,
1251
+ "%s: failed to extract flow paths from the single-path "
1252
+ "solution" ,
1253
+ __func__ );
1254
+ goto fail ;
1255
+ }
1256
+ if (tal_count (flow_paths ) != 1 ) {
1257
+ rq_log (
1258
+ tmpctx , rq , LOG_BROKEN ,
1259
+ "%s: single-path solution returned a multi route solution" ,
1260
+ __func__ );
1261
+ goto fail ;
1262
+ }
1263
+ tal_free (working_ctx );
1264
+ return flow_paths ;
1265
+
1266
+ fail :
1267
+ tal_free (working_ctx );
1268
+ return NULL ;
1269
+ }
1028
1270
1029
1271
const char * default_routes (const tal_t * ctx , struct route_query * rq ,
1030
1272
const struct gossmap_node * srcnode ,
0 commit comments