mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 07:04:22 +01:00
paymod: Implement base state-machine transitions
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.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user