@@ -319,6 +319,28 @@ static void set_capacity(s64 *capacity, u64 value, u64 *cap_on_capacity)
319319 * cap_on_capacity -= * capacity ;
320320}
321321
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+
322344// TODO(eduardo): unit test this
323345/* Split a directed channel into parts with linear cost function. */
324346static void linearize_channel (const struct pay_parameters * params ,
@@ -867,6 +889,47 @@ get_flow_paths(const tal_t *ctx,
867889 return flows ;
868890}
869891
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+ tal_arr_expand (& flows , f );
930+ return flows ;
931+ }
932+
870933// TODO(eduardo): choose some default values for the minflow parameters
871934/* eduardo: I think it should be clear that this module deals with linear
872935 * flows, ie. base fees are not considered. Hence a flow along a path is
@@ -1025,6 +1088,186 @@ static struct amount_msat linear_flows_cost(struct flow **flows,
10251088 return total ;
10261089}
10271090
1091+ /* Initialize the data vectors for the single-path solver. */
1092+ static void init_linear_network_single_path (
1093+ const tal_t * ctx , const struct pay_parameters * params , struct graph * * graph ,
1094+ double * * arc_prob_cost , s64 * * arc_fee_cost , s64 * * arc_capacity )
1095+ {
1096+ const size_t max_num_chans = gossmap_max_chan_idx (params -> rq -> gossmap );
1097+ const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL ;
1098+ const size_t max_num_nodes = gossmap_max_node_idx (params -> rq -> gossmap );
1099+
1100+ * graph = graph_new (ctx , max_num_nodes , max_num_arcs , ARC_DUAL_BITOFF );
1101+ * arc_prob_cost = tal_arr (ctx , double , max_num_arcs );
1102+ for (size_t i = 0 ; i < max_num_arcs ; ++ i )
1103+ (* arc_prob_cost )[i ] = DBL_MAX ;
1104+
1105+ * arc_fee_cost = tal_arr (ctx , s64 , max_num_arcs );
1106+ for (size_t i = 0 ; i < max_num_arcs ; ++ i )
1107+ (* arc_fee_cost )[i ] = INT64_MAX ;
1108+ * arc_capacity = tal_arrz (ctx , s64 , max_num_arcs );
1109+
1110+ const struct gossmap * gossmap = params -> rq -> gossmap ;
1111+
1112+ for (struct gossmap_node * node = gossmap_first_node (gossmap ); node ;
1113+ node = gossmap_next_node (gossmap , node )) {
1114+ const u32 node_id = gossmap_node_idx (gossmap , node );
1115+
1116+ for (size_t j = 0 ; j < node -> num_chans ; ++ j ) {
1117+ int half ;
1118+ const struct gossmap_chan * c =
1119+ gossmap_nth_chan (gossmap , node , j , & half );
1120+ struct amount_msat mincap , maxcap ;
1121+
1122+ if (!gossmap_chan_set (c , half ) ||
1123+ !c -> half [half ].enabled )
1124+ continue ;
1125+
1126+ /* If a channel cannot forward the total amount we don't
1127+ * use it. */
1128+ if (amount_msat_less (params -> amount ,
1129+ gossmap_chan_htlc_min (c , half )) ||
1130+ amount_msat_greater (params -> amount ,
1131+ gossmap_chan_htlc_max (c , half )))
1132+ continue ;
1133+
1134+ get_constraints (params -> rq , c , half , & mincap , & maxcap );
1135+ /* Assume if min > max, min is wrong */
1136+ if (amount_msat_greater (mincap , maxcap ))
1137+ mincap = maxcap ;
1138+ /* It is preferable to work on 1msat past the known
1139+ * bound. */
1140+ if (!amount_msat_accumulate (& maxcap , amount_msat (1 )))
1141+ abort ();
1142+
1143+ /* If amount is greater than the known liquidity upper
1144+ * bound we get infinite probability cost. */
1145+ if (amount_msat_greater_eq (params -> amount , maxcap ))
1146+ continue ;
1147+
1148+ const u32 chan_id = gossmap_chan_idx (gossmap , c );
1149+
1150+ const struct gossmap_node * next =
1151+ gossmap_nth_node (gossmap , c , !half );
1152+
1153+ const u32 next_id = gossmap_node_idx (gossmap , next );
1154+
1155+ /* channel to self? */
1156+ if (node_id == next_id )
1157+ continue ;
1158+
1159+ struct arc arc =
1160+ arc_from_parts (chan_id , half , 0 , false);
1161+
1162+ graph_add_arc (* graph , arc , node_obj (node_id ),
1163+ node_obj (next_id ));
1164+
1165+ (* arc_capacity )[arc .idx ] = 1 ;
1166+ (* arc_prob_cost )[arc .idx ] =
1167+ (-1.0 ) * log (pickhardt_richter_probability (
1168+ mincap , maxcap , params -> amount ));
1169+
1170+ struct amount_msat fee ;
1171+ if (!amount_msat_fee (& fee , params -> amount ,
1172+ c -> half [half ].base_fee ,
1173+ c -> half [half ].proportional_fee ))
1174+ abort ();
1175+ u32 fee_msat ;
1176+ if (!amount_msat_to_u32 (fee , & fee_msat ))
1177+ continue ;
1178+ (* arc_fee_cost )[arc .idx ] =
1179+ fee_msat +
1180+ params -> delay_feefactor * c -> half [half ].delay ;
1181+ }
1182+ }
1183+ }
1184+
1185+ /* Similar to minflow but computes routes that have a single path. */
1186+ struct flow * * single_path_flow (const tal_t * ctx , const struct route_query * rq ,
1187+ const struct gossmap_node * source ,
1188+ const struct gossmap_node * target ,
1189+ struct amount_msat amount , u32 mu ,
1190+ double delay_feefactor )
1191+ {
1192+ struct flow * * flow_paths ;
1193+ /* We allocate everything off this, and free it at the end,
1194+ * as we can be called multiple times without cleaning tmpctx! */
1195+ tal_t * working_ctx = tal (NULL , char );
1196+ struct pay_parameters * params = tal (working_ctx , struct pay_parameters );
1197+
1198+ params -> rq = rq ;
1199+ params -> source = source ;
1200+ params -> target = target ;
1201+ params -> amount = amount ;
1202+ /* for the single-path solver the accuracy does not detriment
1203+ * performance */
1204+ params -> accuracy = amount ;
1205+ params -> delay_feefactor = delay_feefactor ;
1206+ params -> base_fee_penalty = base_fee_penalty_estimate (amount );
1207+
1208+ struct graph * graph ;
1209+ double * arc_prob_cost ;
1210+ s64 * arc_fee_cost ;
1211+ s64 * arc_capacity ;
1212+
1213+ init_linear_network_single_path (working_ctx , params , & graph ,
1214+ & arc_prob_cost , & arc_fee_cost ,
1215+ & arc_capacity );
1216+
1217+ const struct node dst = {.idx = gossmap_node_idx (rq -> gossmap , target )};
1218+ const struct node src = {.idx = gossmap_node_idx (rq -> gossmap , source )};
1219+
1220+ const size_t max_num_nodes = graph_max_num_nodes (graph );
1221+ const size_t max_num_arcs = graph_max_num_arcs (graph );
1222+
1223+ s64 * potential = tal_arrz (working_ctx , s64 , max_num_nodes );
1224+ s64 * distance = tal_arrz (working_ctx , s64 , max_num_nodes );
1225+ s64 * arc_cost = tal_arrz (working_ctx , s64 , max_num_arcs );
1226+ struct arc * prev = tal_arrz (working_ctx , struct arc , max_num_nodes );
1227+
1228+ combine_cost_function (working_ctx , graph , arc_prob_cost , arc_fee_cost ,
1229+ rq -> biases , mu , arc_cost );
1230+
1231+ /* We solve a linear cost flow problem. */
1232+ if (!dijkstra_path (working_ctx , graph , src , dst ,
1233+ /* prune = */ true, arc_capacity ,
1234+ /*threshold = */ 1 , arc_cost , potential , prev ,
1235+ distance )) {
1236+ /* This might fail if we are unable to find a suitable route, it
1237+ * doesn't mean the plugin is broken, that's why we LOG_INFORM. */
1238+ rq_log (tmpctx , rq , LOG_INFORM ,
1239+ "%s: could not find a feasible single path" , __func__ );
1240+ goto fail ;
1241+ }
1242+ const u64 pay_amount =
1243+ amount_msat_ratio_ceil (params -> amount , params -> accuracy );
1244+
1245+ /* We dissect the flow into payment routes.
1246+ * Actual amounts considering fees are computed for every
1247+ * channel in the routes. */
1248+ flow_paths = get_flow_singlepath (ctx , params , graph , rq -> gossmap ,
1249+ src , dst , pay_amount , prev );
1250+ if (!flow_paths ) {
1251+ rq_log (tmpctx , rq , LOG_BROKEN ,
1252+ "%s: failed to extract flow paths from the single-path "
1253+ "solution" ,
1254+ __func__ );
1255+ goto fail ;
1256+ }
1257+ if (tal_count (flow_paths ) != 1 ) {
1258+ rq_log (
1259+ tmpctx , rq , LOG_BROKEN ,
1260+ "%s: single-path solution returned a multi route solution" ,
1261+ __func__ );
1262+ goto fail ;
1263+ }
1264+ tal_free (working_ctx );
1265+ return flow_paths ;
1266+
1267+ fail :
1268+ tal_free (working_ctx );
1269+ return NULL ;
1270+ }
10281271
10291272const char * default_routes (const tal_t * ctx , struct route_query * rq ,
10301273 const struct gossmap_node * srcnode ,
0 commit comments