lightningd/peer_control.c: Implement waitblockheight.

This is needed to fully implement handling of blockheight disagreements
between us and payee.
If payee believes the blockheight is higher than ours, then `pay`
should wait for our node to achieve that blockheight.

Changelog-Add: Implement `waitblockheight` to wait for a specific blockheight.
This commit is contained in:
ZmnSCPxj
2019-12-26 10:19:09 +00:00
committed by Christian Decker
parent 44e8256338
commit 54cc735201
11 changed files with 244 additions and 2 deletions

View File

@@ -21,6 +21,7 @@
#include <common/initial_commit_tx.h>
#include <common/json_command.h>
#include <common/json_helpers.h>
#include <common/json_tok.h>
#include <common/jsonrpc_errors.h>
#include <common/key_derive.h>
#include <common/param.h>
@@ -1750,6 +1751,112 @@ static const struct json_command getinfo_command = {
};
AUTODATA(json_command, &getinfo_command);
/* Wait for at least a specific blockheight, then return, or time out. */
struct waitblockheight_waiter {
/* struct lightningd::waitblockheight_commands. */
struct list_node list;
/* Command structure. This is the parent of the close command. */
struct command *cmd;
/* The block height being waited for. */
u32 block_height;
/* Whether we have been removed from the list. */
bool removed;
};
/* Completes a pending waitblockheight. */
static struct command_result *
waitblockheight_complete(struct command *cmd,
u32 block_height)
{
struct json_stream *response;
response = json_stream_success(cmd);
json_add_num(response, "blockheight", block_height);
return command_success(cmd, response);
}
/* Called when command is destroyed without being resolved. */
static void
destroy_waitblockheight_waiter(struct waitblockheight_waiter *w)
{
if (!w->removed)
list_del(&w->list);
}
/* Called on timeout. */
static void
timeout_waitblockheight_waiter(struct waitblockheight_waiter *w)
{
list_del(&w->list);
w->removed = true;
tal_steal(tmpctx, w);
was_pending(command_fail(w->cmd, LIGHTNINGD,
"Timed out."));
}
/* Called by lightningd at each new block. */
void waitblockheight_notify_new_block(struct lightningd *ld,
u32 block_height)
{
struct waitblockheight_waiter *w, *n;
char *to_delete = tal(NULL, char);
/* Use safe since we could resolve commands and thus
* trigger removal of list elements.
*/
list_for_each_safe(&ld->waitblockheight_commands, w, n, list) {
/* Skip commands that have not been reached yet. */
if (w->block_height > block_height)
continue;
list_del(&w->list);
w->removed = true;
tal_steal(to_delete, w);
was_pending(waitblockheight_complete(w->cmd,
block_height));
}
tal_free(to_delete);
}
static struct command_result *json_waitblockheight(struct command *cmd,
const char *buffer,
const jsmntok_t *obj,
const jsmntok_t *params)
{
unsigned int *target_block_height;
u32 block_height;
unsigned int *timeout;
struct waitblockheight_waiter *w;
if (!param(cmd, buffer, params,
p_req("blockheight", param_number, &target_block_height),
p_opt_def("timeout", param_number, &timeout, 60),
NULL))
return command_param_failed();
/* Check if already reached anyway. */
block_height = get_block_height(cmd->ld->topology);
if (*target_block_height <= block_height)
return waitblockheight_complete(cmd, block_height);
/* Create a new waitblockheight command. */
w = tal(cmd, struct waitblockheight_waiter);
tal_add_destructor(w, &destroy_waitblockheight_waiter);
list_add(&cmd->ld->waitblockheight_commands, &w->list);
w->cmd = cmd;
w->block_height = *target_block_height;
w->removed = false;
/* Install the timeout. */
(void) new_reltimer(cmd->ld->timers, w, time_from_sec(*timeout),
&timeout_waitblockheight_waiter, w);
return command_still_pending(cmd);
}
static const struct json_command waitblockheight_command = {
"waitblockheight",
"utility",
&json_waitblockheight,
"Wait for the blockchain to reach {blockheight}, up to "
"{timeout} seconds."
};
AUTODATA(json_command, &waitblockheight_command);
static struct command_result *param_channel_or_all(struct command *cmd,
const char *name,
const char *buffer,