Skip to content

Is a tree the correct structure for the payment flow? #3863

Open
@cdecker

Description

@cdecker

@ZmnSCPxj raised some good points in the MPP fixup discussion. I'd like to address these and maybe discuss this independently of a fixup PR. I'll report the comment here for context:

Perhaps late to bring this up, but: do we need to actually arrange payments in a tree as opposed to a flat array of sub-payments?

The reason I bring this up is because we might want to merge sub-payments. For example, consider the case where the only viable paths for two sub-payments cost too much (i.e. fee budget is violated), but if we merge them (and their fee budgets) we could succeed.

But if the sub-payments we want to merge are cousins instead of siblings in the tree of payments, how do we viably merge them? For example, consider the case below:

        0
       / \
      /   \
     1     2
    / \   / \
   3   4 5   6

Suppose 4 and 6 are currently ongoing (possibly because they reached the destination, so the destination is waiting on the other parts). And 3 and 5 are failing getroute because the viable routes are too expensive (e.g. 4 and 6 have locked up funds on cheaper routes). However, if we merge 3 and 5, it may be feasible (because they would pool their fee budgets together).

As each subpayment needs a unique numeric id, we could use a monotonically-increasing counter and add an explicit field. Merged payments can get any of the ids of the merged payments.

And of course modules have to handle merges as well, if their data structures are not void.

I'm pretty happy with the tree structure since it describes the lineage of a payment attempt, and the reason it was initiated (just follow the links up the tree), though we currently don't make good use of that debuggability (might be an idea for a plugin that walls us through each step and prints nice lineage and routing graphviz graphs).

I do share your concerns that splitting might get us into a dead-end, however I don't think that the tree structure is to blame here, since we can still implement work-stealing across the tree branches, similar to how process and task schedulers work in operating systems. But let's first consider if the dead end is even likely to happen:

  • If they happen we can detect these situations in the pay plugin, since the getroute call doesn't specify a fee and just returns a route, independently of whether we then consider it to be too expensive.
  • At the size where splitting becomes relevant it is very likely that the fee_proportional dominates the routing costs, and the fee_base becomes negligible. This means that the fee is dependent mostly on the amount that is being transferred and the type of merge you mention doesn't really change that: 2x the amounts ~~~> 2x the fees. The adaptive splitter stops splitting at a size that is imho above the point where fee_base become relevant again, so we should not end up in a situation in which the fees for a route are dominated by fee_base.
  • Fee stealing from completed payments looks like a more promising idea: if we have some parts that have been pending for some time we can tap into their fee-budget to pay for the payments we don't have sufficient budget for. Splitting the fee along with the amount really is just a guideline of what we expect to be sufficient.

What do you think? Is there something that I'm missing?

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions