|
|
|
#ifndef LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H
|
|
|
|
#define LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <ccan/autodata/autodata.h>
|
|
|
|
#include <ccan/tal/tal.h>
|
|
|
|
#include <ccan/typesafe_cb/typesafe_cb.h>
|
|
|
|
#include <common/json_stream.h>
|
|
|
|
#include <lightningd/lightningd.h>
|
|
|
|
#include <lightningd/plugin.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Plugin hooks are a way for plugins to implement custom behavior and
|
|
|
|
* reactions to certain things in `lightningd`. `lightningd` will ask
|
|
|
|
* plugins that have registered a hook for a given event how it'd like
|
|
|
|
* to proceed. This allows plugins to deviate from the default
|
|
|
|
* behavior that `lightningd` otherwise implements.
|
|
|
|
*
|
|
|
|
* Examples include storing an additional backup of important
|
|
|
|
* information belonging to the wallet before committing to it, or
|
|
|
|
* holding an incoming payment that is guaranteed to succeed for some
|
|
|
|
* time in order to check that the delivery of goods works correctly,
|
|
|
|
* giving the option of instantly refunding should something go wrong.
|
|
|
|
*
|
|
|
|
* Hooks are commonly structured into a number of converter functions
|
|
|
|
* and a callback. The converter functions convert from an internal
|
|
|
|
* struct representation of the method arguments to a JSON-object for
|
|
|
|
* delivery to the plugin, and from a JSON-object to the internal
|
|
|
|
* representation:
|
|
|
|
*
|
|
|
|
* - `serialize_payload` which takes a payload of type `cb_arg_type`
|
|
|
|
* and serializes it into the given `json_stream`. `
|
|
|
|
*
|
|
|
|
* For single-plugin hooks:
|
|
|
|
* - `single_response_cb` is called once the plugin has responded (or with
|
|
|
|
* buffer == NULL if there's no plugin). In addition an arbitrary
|
|
|
|
* additional argument of type `cb_arg_type` can be passed along
|
|
|
|
* that may contain any additional context necessary. It must free
|
|
|
|
* or otherwise take ownership of the cb_arg_type argument.
|
|
|
|
*
|
|
|
|
* For chained-plugin hooks:
|
|
|
|
* - `deserialize_cb` is called for each plugin, if it returns true the
|
|
|
|
* next one is called, otherwise the cb_arg_type argument is free.
|
|
|
|
* - If all `deserialize_cb` return true, `final_cb` is called. It must free
|
|
|
|
* or otherwise take ownership of the cb_arg_type argument.
|
|
|
|
*
|
|
|
|
* To make hook invocations easier, each hook provides a `plugin_hook_call_hookname`
|
|
|
|
* function that performs typechecking at compile time, and makes sure
|
|
|
|
* that all the provided functions for serialization, deserialization
|
|
|
|
* and callback have the correct type.
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum plugin_hook_type {
|
|
|
|
PLUGIN_HOOK_SINGLE,
|
|
|
|
PLUGIN_HOOK_CHAIN,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct plugin_hook {
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
/* Which type of plugin is this? It'll determine how many plugins can
|
|
|
|
* register this hook, and how the hooks are called. */
|
|
|
|
enum plugin_hook_type type;
|
|
|
|
|
|
|
|
/* For PLUGIN_HOOK_SINGLE hooks */
|
|
|
|
void (*single_response_cb)(void *arg,
|
|
|
|
const char *buffer, const jsmntok_t *toks);
|
|
|
|
|
|
|
|
/* For PLUGIN_HOOK_CHAIN hooks: */
|
|
|
|
/* Returns false if we should stop iterating (and free arg). */
|
|
|
|
bool (*deserialize_cb)(void *arg,
|
|
|
|
const char *buffer, const jsmntok_t *toks);
|
|
|
|
void (*final_cb)(void *arg);
|
|
|
|
|
|
|
|
/* To send the payload to the plugin */
|
|
|
|
void (*serialize_payload)(void *src, struct json_stream *dest);
|
|
|
|
|
|
|
|
/* Which plugins have registered this hook? This is a `tal_arr`
|
|
|
|
* initialized at creation. */
|
|
|
|
struct hook_instance **hooks;
|
|
|
|
};
|
|
|
|
AUTODATA_TYPE(hooks, struct plugin_hook);
|
|
|
|
|
|
|
|
/* Do not call this directly, rather use the `plugin_hook_call_name`
|
|
|
|
* wrappers generated by the `PLUGIN_HOOK_REGISTER` macro.
|
|
|
|
*
|
|
|
|
* Returns true if callback called immediately, otherwise false if it's
|
|
|
|
* still waiting on a plugin response.
|
|
|
|
*/
|
|
|
|
bool plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook,
|
|
|
|
tal_t *cb_arg STEALS);
|
|
|
|
|
|
|
|
/* Generic deserialize_cb: returns true iff 'result': 'continue' */
|
|
|
|
bool plugin_hook_continue(void *arg, const char *buffer, const jsmntok_t *toks);
|
|
|
|
|
|
|
|
/* Create a small facade in from of `plugin_hook_call_` to make sure
|
|
|
|
* arguments are of the correct type before downcasting them to `void
|
|
|
|
* *`. Not really necessary, but nice since it also makes sure that
|
|
|
|
* the method-name is correct for the call.
|
|
|
|
*/
|
|
|
|
/* FIXME: Find a way to avoid back-to-back declaration and definition */
|
|
|
|
#define PLUGIN_HOOK_CALL_DEF(name, cb_arg_type) \
|
|
|
|
UNNEEDED static inline bool plugin_hook_call_##name( \
|
|
|
|
struct lightningd *ld, cb_arg_type cb_arg STEALS) \
|
|
|
|
{ \
|
|
|
|
return plugin_hook_call_(ld, &name##_hook_gen, cb_arg); \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Typechecked registration of a plugin hook. We check that the
|
|
|
|
* serialize_payload function converts an object of type payload_type
|
|
|
|
* to a json_stream (.params object in the JSON-RPC request), that the
|
|
|
|
* deserialize_response function converts from the JSON-RPC response
|
|
|
|
* json_stream to an object of type response_type and that the
|
|
|
|
* response_cb function accepts the deserialized response format and
|
|
|
|
* an arbitrary extra argument used to maintain context.
|
|
|
|
*/
|
|
|
|
#define REGISTER_SINGLE_PLUGIN_HOOK(name, response_cb, \
|
|
|
|
serialize_payload, cb_arg_type) \
|
|
|
|
struct plugin_hook name##_hook_gen = { \
|
|
|
|
stringify(name), \
|
|
|
|
PLUGIN_HOOK_SINGLE, \
|
|
|
|
typesafe_cb_cast( \
|
|
|
|
void (*)(void *STEALS, const char *, const jsmntok_t *), \
|
|
|
|
void (*)(cb_arg_type STEALS, const char *, const jsmntok_t *), \
|
|
|
|
response_cb), \
|
|
|
|
NULL, NULL, \
|
|
|
|
typesafe_cb_cast(void (*)(void *, struct json_stream *), \
|
|
|
|
void (*)(cb_arg_type, struct json_stream *), \
|
|
|
|
serialize_payload), \
|
|
|
|
NULL, /* .plugins */ \
|
|
|
|
}; \
|
|
|
|
AUTODATA(hooks, &name##_hook_gen); \
|
|
|
|
PLUGIN_HOOK_CALL_DEF(name, cb_arg_type)
|
|
|
|
|
|
|
|
|
|
|
|
#define REGISTER_PLUGIN_HOOK(name, deserialize_cb, final_cb, \
|
|
|
|
serialize_payload, cb_arg_type) \
|
|
|
|
struct plugin_hook name##_hook_gen = { \
|
|
|
|
stringify(name), \
|
|
|
|
PLUGIN_HOOK_CHAIN, \
|
|
|
|
NULL, \
|
|
|
|
typesafe_cb_cast( \
|
|
|
|
bool (*)(void *, const char *, const jsmntok_t *), \
|
|
|
|
bool (*)(cb_arg_type, const char *, const jsmntok_t *), \
|
|
|
|
deserialize_cb), \
|
|
|
|
typesafe_cb_cast( \
|
|
|
|
void (*)(void *STEALS), \
|
|
|
|
void (*)(cb_arg_type STEALS), \
|
|
|
|
final_cb), \
|
|
|
|
typesafe_cb_cast(void (*)(void *, struct json_stream *), \
|
|
|
|
void (*)(cb_arg_type, struct json_stream *), \
|
|
|
|
serialize_payload), \
|
|
|
|
NULL, /* .plugins */ \
|
|
|
|
}; \
|
|
|
|
AUTODATA(hooks, &name##_hook_gen); \
|
|
|
|
PLUGIN_HOOK_CALL_DEF(name, cb_arg_type)
|
|
|
|
|
|
|
|
struct plugin_hook *plugin_hook_register(struct plugin *plugin,
|
|
|
|
const char *method);
|
|
|
|
|
|
|
|
/* Special sync plugin hook for db. */
|
|
|
|
void plugin_hook_db_sync(struct db *db);
|
|
|
|
|
|
|
|
/* Add dependencies for this hook. */
|
|
|
|
void plugin_hook_add_deps(struct plugin_hook *hook,
|
|
|
|
struct plugin *plugin,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *before,
|
|
|
|
const jsmntok_t *after);
|
|
|
|
|
|
|
|
/* Returns array of plugins which cannot be ordered (empty on success) */
|
|
|
|
struct plugin **plugin_hooks_make_ordered(const tal_t *ctx);
|
|
|
|
|
|
|
|
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */
|