Browse Source
The actual steps are mocked out, but we can already run through the various phases of a payment, and the modifiers should be called for each state.paymod-02
2 changed files with 189 additions and 0 deletions
@ -0,0 +1,146 @@ |
|||||
|
#include <plugins/libplugin-pay.h> |
||||
|
|
||||
|
struct payment *payment_new(tal_t *ctx, struct command *cmd, |
||||
|
struct payment *parent, |
||||
|
struct payment_modifier **mods) |
||||
|
{ |
||||
|
struct payment *p = tal(ctx, struct payment); |
||||
|
p->children = tal_arr(p, struct payment *, 0); |
||||
|
p->parent = parent; |
||||
|
p->modifiers = mods; |
||||
|
p->cmd = cmd; |
||||
|
p->start_time = time_now(); |
||||
|
p->partid = partid++; |
||||
|
|
||||
|
/* Copy over the relevant pieces of information. */ |
||||
|
if (parent != NULL) { |
||||
|
tal_arr_expand(&parent->children, p); |
||||
|
p->destination = p->getroute_destination = parent->destination; |
||||
|
p->amount = parent->amount; |
||||
|
p->payment_hash = parent->payment_hash; |
||||
|
} |
||||
|
|
||||
|
/* Initialize all modifier data so we can point to the fields when
|
||||
|
* wiring into the param() call in a JSON-RPC handler. The callback |
||||
|
* can also just `memcpy` the parent if this outside access is not |
||||
|
* required. */ |
||||
|
p->modifier_data = tal_arr(p, void *, 0); |
||||
|
for (size_t i=0; mods[i] != NULL; i++) { |
||||
|
if (mods[i]->data_init != NULL) |
||||
|
tal_arr_expand(&p->modifier_data, |
||||
|
mods[i]->data_init(p)); |
||||
|
else |
||||
|
tal_arr_expand(&p->modifier_data, NULL); |
||||
|
} |
||||
|
|
||||
|
return p; |
||||
|
} |
||||
|
|
||||
|
void payment_start(struct payment *p) |
||||
|
{ |
||||
|
p->step = PAYMENT_STEP_INITIALIZED; |
||||
|
p->current_modifier = -1; |
||||
|
payment_continue(p); |
||||
|
} |
||||
|
|
||||
|
static void payment_getroute(struct payment *p) |
||||
|
{ |
||||
|
p->step = PAYMENT_STEP_GOT_ROUTE; |
||||
|
return payment_continue(p); |
||||
|
} |
||||
|
|
||||
|
static void payment_compute_onion_payloads(struct payment *p) |
||||
|
{ |
||||
|
p->step = PAYMENT_STEP_ONION_PAYLOAD; |
||||
|
/* Now allow all the modifiers to mess with the payloads, before we
|
||||
|
* serialize via a call to createonion in the next step. */ |
||||
|
payment_continue(p); |
||||
|
} |
||||
|
|
||||
|
static void payment_sendonion(struct payment *p) |
||||
|
{ |
||||
|
p->step = PAYMENT_STEP_FAILED; |
||||
|
return payment_continue(p); |
||||
|
} |
||||
|
|
||||
|
/* Mutual recursion. */ |
||||
|
static void payment_finished(struct payment *p); |
||||
|
|
||||
|
/* Function to bubble up completions to the root, which actually holds on to
|
||||
|
* the command that initiated the flow. */ |
||||
|
static struct command_result *payment_child_finished(struct payment *parent, |
||||
|
struct payment *child) |
||||
|
{ |
||||
|
/* TODO implement logic to wait for all parts instead of bubbling up
|
||||
|
* directly. */ |
||||
|
|
||||
|
/* Should we continue bubbling up? */ |
||||
|
if (parent->parent == NULL) { |
||||
|
assert(parent->cmd != NULL); |
||||
|
return payment_finished(parent); |
||||
|
} else { |
||||
|
return payment_child_finished(parent->parent, parent); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* This function is called whenever a payment ends up in a final state, or all
|
||||
|
* leafs in the subtree rooted in the payment are all in a final state. It is |
||||
|
* called only once, and it is guaranteed to be called in post-order |
||||
|
* traversal, i.e., all children are finished before the parent is called. */ |
||||
|
static void payment_finished(struct payment *p) |
||||
|
{ |
||||
|
/* TODO If this is a success bubble it back up through the part
|
||||
|
* tree. If it is a failure, decide here whether to split, retry or |
||||
|
* split (maybe in a modifier instead?). */ |
||||
|
if (p->parent == NULL) |
||||
|
return command_fail(p->cmd, JSONRPC2_INVALID_REQUEST, "Not functional yet"); |
||||
|
else |
||||
|
return payment_child_finished(p->parent, p); |
||||
|
} |
||||
|
|
||||
|
void payment_continue(struct payment *p) |
||||
|
{ |
||||
|
struct payment_modifier *mod; |
||||
|
void *moddata; |
||||
|
/* If we are in the middle of calling the modifiers, continue calling
|
||||
|
* them, otherwise we can continue with the payment state-machine. */ |
||||
|
p->current_modifier++; |
||||
|
mod = p->modifiers[p->current_modifier]; |
||||
|
|
||||
|
if (mod != NULL) { |
||||
|
/* There is another modifier, so call it. */ |
||||
|
moddata = p->modifier_data[p->current_modifier]; |
||||
|
return mod->post_step_cb(moddata, p); |
||||
|
} else { |
||||
|
/* There are no more modifiers, so reset the call chain and
|
||||
|
* proceed to the next state. */ |
||||
|
p->current_modifier = -1; |
||||
|
switch (p->step) { |
||||
|
case PAYMENT_STEP_INITIALIZED: |
||||
|
payment_getroute(p); |
||||
|
return; |
||||
|
|
||||
|
case PAYMENT_STEP_GOT_ROUTE: |
||||
|
payment_compute_onion_payloads(p); |
||||
|
return; |
||||
|
|
||||
|
case PAYMENT_STEP_ONION_PAYLOAD: |
||||
|
payment_sendonion(p); |
||||
|
return; |
||||
|
|
||||
|
case PAYMENT_STEP_SUCCESS: |
||||
|
case PAYMENT_STEP_FAILED: |
||||
|
payment_finished(p); |
||||
|
return; |
||||
|
|
||||
|
case PAYMENT_STEP_RETRY: |
||||
|
case PAYMENT_STEP_SPLIT: |
||||
|
/* Do nothing, we'll get pinged by a child succeeding
|
||||
|
* or failing. */ |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
/* We should never get here, it'd mean one of the state machine called
|
||||
|
* `payment_continue` after the final state. */ |
||||
|
abort(); |
||||
|
} |
Loading…
Reference in new issue