diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index e69de29bb..e1e26ed01 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -0,0 +1,146 @@ +#include + +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(); +} diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 21238fab8..bb29932cb 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -60,6 +60,14 @@ enum payment_step { }; struct payment { + /* The command that triggered this payment. */ + struct command *cmd; + + const char *json_buffer; + const jsmntok_t *json_toks; + + /* The current phase we are in. */ + enum payment_step step; /* Real destination we want to route to */ struct node_id *destination; @@ -94,6 +102,41 @@ struct payment { struct short_channel_id *exclusions; + /* Tree structure of payments and subpayments. */ + struct payment *parent, **children; + + /* Null-terminated array of modifiers to apply to the payment. NULL + * terminated mainly so we can build a stack of modifiers at + * compile-time instead of allocating a list for each payment + * specifically. */ + struct payment_modifier **modifiers; + void **modifier_data; + int current_modifier; +}; + +struct payment_modifier { + const char *name; + void *(*data_init)(struct payment *p); + void (*post_step_cb)(void *data, struct payment *p); }; +#define REGISTER_PAYMENT_MODIFIER(name, data_type, data_init_cb, step_cb) \ + struct payment_modifier name##_pay_mod = { \ + stringify(name), \ + typesafe_cb_cast(void *(*)(struct payment *), \ + data_type (*)(struct payment *), data_init_cb), \ + typesafe_cb_cast(void (*)(void *, struct payment *), \ + void (*)(data_type, struct payment *), step_cb), \ + }; + +/* List of globally available payment modifiers. */ +extern struct payment_modifier dummy_pay_mod; + +struct payment *payment_new(tal_t *ctx, struct command *cmd, + struct payment *parent, + struct payment_modifier **mods); + +void payment_start(struct payment *p); +void payment_continue(struct payment *p); + #endif /* LIGHTNING_PLUGINS_LIBPLUGIN_PAY_H */