Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pay: Use the networkheight as current height #7190

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 6 additions & 16 deletions lightningd/test/run-invoice-select-inchan.c
Original file line number Diff line number Diff line change
Expand Up @@ -578,16 +578,14 @@ void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNN
uint64_t value UNNEEDED)
{ fprintf(stderr, "json_add_u64 called!\n"); abort(); }
/* Generated stub for json_add_uncommitted_channel */
void json_add_uncommitted_channel(struct json_stream *response UNNEEDED,
const struct uncommitted_channel *uc UNNEEDED,
/* Only set for listpeerchannels */
const struct peer *peer UNNEEDED)
void json_add_uncommitted_channel(struct json_stream *response UNNEEDED,
const struct uncommitted_channel *uc UNNEEDED,
const struct peer *peer UNNEEDED)
{ fprintf(stderr, "json_add_uncommitted_channel called!\n"); abort(); }
/* Generated stub for json_add_unsaved_channel */
void json_add_unsaved_channel(struct json_stream *response UNNEEDED,
const struct channel *channel UNNEEDED,
/* Only set for listpeerchannels */
const struct peer *peer UNNEEDED)
void json_add_unsaved_channel(struct json_stream *response UNNEEDED,
const struct channel *channel UNNEEDED,
const struct peer *peer UNNEEDED)
{ fprintf(stderr, "json_add_unsaved_channel called!\n"); abort(); }
/* Generated stub for json_array_end */
void json_array_end(struct json_stream *js UNNEEDED)
Expand Down Expand Up @@ -671,14 +669,6 @@ struct jsonrpc_request *jsonrpc_request_start_(
void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED,
const char *why UNNEEDED)
{ fprintf(stderr, "kill_uncommitted_channel called!\n"); abort(); }
/* Generated stub for lightningd_deprecated_out_ok */
bool lightningd_deprecated_out_ok(struct lightningd *ld UNNEEDED,
bool deprecated_apis UNNEEDED,
const char *subsys UNNEEDED,
const char *api UNNEEDED,
const char *start UNNEEDED,
const char *end UNNEEDED)
{ fprintf(stderr, "lightningd_deprecated_out_ok called!\n"); abort(); }
/* Generated stub for lockin_complete */
void lockin_complete(struct channel *channel UNNEEDED,
enum channel_state expected_state UNNEEDED)
Expand Down
1 change: 0 additions & 1 deletion plugins/keysend.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ struct payment_modifier *pay_mods[] = {
&shadowroute_pay_mod,
&routehints_pay_mod,
&exemptfee_pay_mod,
&waitblockheight_pay_mod,
&retry_pay_mod,
NULL,
};
Expand Down
166 changes: 60 additions & 106 deletions plugins/libplugin-pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
p->invstring = parent->invstring;
p->description = parent->description;
p->mods = parent->mods;
p->chainlag = parent->chainlag;
} else {
assert(cmd != NULL);
p->partid = 0;
Expand All @@ -132,6 +133,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
p->local_invreq_id = NULL;
p->groupid = 0;
p->mods = NULL;
p->chainlag = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this initialization is overzealous? We shouldn't access chainlag without purposefully setting it first as far as I can tell.

}

/* Initialize all modifier data so we can point to the fields when
Expand Down Expand Up @@ -276,16 +278,53 @@ struct payment_tree_result payment_collect_result(struct payment *p)
return res;
}

static struct command_result *payment_waitblockheight_cb(struct command *cmd,
const char *buffer,
const jsmntok_t *toks,
struct payment *p)
{
u32 syncheight;
json_scan(tmpctx, buffer, toks, "{blockheight:%}",
JSON_SCAN(json_to_u32, &syncheight));
paymod_log(p, LOG_DBG, "waitblockheight reports syncheight=%d",
syncheight);
p->chainlag = p->start_block - syncheight;
if (p->chainlag > 0)
paymod_log(p, LOG_INFORM,
"Starting the payment with chainlag=%d "
"(syncheight=%d < headercount=%d)",
p->chainlag, syncheight, p->start_block);

payment_continue(p);
return command_still_pending(cmd);
}

static struct command_result *
payment_getblockheight_success(struct command *cmd,
const char *buffer,
const jsmntok_t *toks,
struct payment *p)
{
const jsmntok_t *blockheighttok =
json_get_member(buffer, toks, "blockheight");
json_to_number(buffer, blockheighttok, &p->start_block);
payment_continue(p);
struct out_req *req;
u32 blockcount, headercount;

json_scan(tmpctx, buffer, toks, "{blockcount:%,headercount:%}",
JSON_SCAN(json_to_u32, &blockcount),
JSON_SCAN(json_to_u32, &headercount));
paymod_log(p, LOG_DBG,
"Received getchaininfo blockcount=%d, headercount=%d",
blockcount, headercount);

p->start_block = headercount;

/* Now we just need to ask `lightningd` what height it has
* synced up to, and we remember that as chainlag. */
req = jsonrpc_request_start(p->plugin, NULL, "waitblockheight",
&payment_waitblockheight_cb,
&payment_rpc_failure, p);
json_add_u32(req->js, "blockheight", 0);
send_outreq(p->plugin, req);

return command_still_pending(cmd);
}

Expand Down Expand Up @@ -323,17 +362,15 @@ void payment_start_at_blockheight(struct payment *p, u32 blockheight)
return payment_continue(p);
}

/* `waitblockheight 0` can be used as a query for the current
* block height.
* This is slightly better than `getinfo` since `getinfo`
* counts the channels and addresses and pushes more data
* onto the RPC but all we care about is the blockheight.
/* Check with the backend what it believes the network's
* height to be. We'll base all of our offsets based on that
* height, allowing us to send while still syncing.
*/
struct out_req *req;
req = jsonrpc_request_start(p->plugin, NULL, "waitblockheight",
req = jsonrpc_request_start(p->plugin, NULL, "getchaininfo",
&payment_getblockheight_success,
&payment_rpc_failure, p);
json_add_u32(req->js, "blockheight", 0);
json_add_u32(req->js, "last_height", 0);
send_outreq(p->plugin, req);
}

Expand Down Expand Up @@ -1640,6 +1677,13 @@ static struct command_result *payment_createonion_success(struct command *cmd,
struct secret *secrets;
struct payment *root = payment_root(p);

/* The delay on the first hop needs to be offset by chainlag,
* as it would otherwise use the current height in
* `lightningd`. All other hops have already been adjusted
* during the payload encoding.
*/
u32 delay = first->delay + p->chainlag;

p->createonion_response = json_to_createonion_response(p, buffer, toks);

req = jsonrpc_request_start(p->plugin, NULL, "sendonion",
Expand All @@ -1649,7 +1693,7 @@ static struct command_result *payment_createonion_success(struct command *cmd,

json_object_start(req->js, "first_hop");
json_add_amount_msat(req->js, "amount_msat", first->amount);
json_add_num(req->js, "delay", first->delay);
json_add_num(req->js, "delay", delay);
json_add_node_id(req->js, "id", &first->node_id);
json_add_short_channel_id(req->js, "channel", first->scid);
json_object_end(req->js);
Expand Down Expand Up @@ -1711,6 +1755,9 @@ static void payment_add_hop_onion_payload(struct payment *p,
const u8 *payment_metadata)
{
struct createonion_request *cr = p->createonion_request;

/* The start_block takes chainlag into consideration, so no
* need to adjust it here. */
u32 cltv = p->start_block + next->delay + 1;
u64 msat = next->amount.millisatoshis; /* Raw: TLV payload generation*/
struct tlv_field **fields;
Expand All @@ -1724,6 +1771,7 @@ static void payment_add_hop_onion_payload(struct payment *p,
fields = &dst->tlv_payload->fields;
tlvstream_set_tu64(fields, TLV_PAYLOAD_AMT_TO_FORWARD,
msat);

tlvstream_set_tu32(fields, TLV_PAYLOAD_OUTGOING_CLTV_VALUE,
cltv);

Expand Down Expand Up @@ -3419,100 +3467,6 @@ static struct direct_pay_data *direct_pay_init(struct payment *p)
REGISTER_PAYMENT_MODIFIER(directpay, struct direct_pay_data *, direct_pay_init,
direct_pay_cb);

static struct command_result *waitblockheight_rpc_cb(struct command *cmd,
const char *buffer,
const jsmntok_t *toks,
struct payment *p)
{
const jsmntok_t *blockheighttok, *codetok;

u32 blockheight;
int code;
struct payment *subpayment;

blockheighttok = json_get_member(buffer, toks, "blockheight");

if (!blockheighttok ||
!json_to_number(buffer, blockheighttok, &blockheight)) {
codetok = json_get_member(buffer, toks, "code");
json_to_int(buffer, codetok, &code);
if (code == WAIT_TIMEOUT) {
payment_fail(
p,
"Timed out while attempting to sync to blockheight "
"returned by destination. Please finish syncing "
"with the blockchain and try again.");

} else {
plugin_err(
p->plugin,
"Unexpected result from waitblockheight: %.*s",
json_tok_full_len(toks),
json_tok_full(buffer, toks));
}
} else {
subpayment = payment_new(p, NULL, p, p->modifiers);
payment_start_at_blockheight(subpayment, blockheight);
payment_set_step(p, PAYMENT_STEP_RETRY);
subpayment->why = tal_fmt(
subpayment, "Retrying after waiting for blockchain sync.");
paymod_log(
p, LOG_DBG,
"Retrying after waitblockheight, new partid %" PRIu32,
subpayment->partid);
payment_continue(p);
}
return command_still_pending(cmd);
}

static void waitblockheight_cb(void *d, struct payment *p)
{
struct out_req *req;
struct timeabs now = time_now();
struct timerel remaining;
u32 blockheight;
if (p->step != PAYMENT_STEP_FAILED)
return payment_continue(p);

/* If we don't have an error message to parse we can't wait for blockheight. */
if (p->result == NULL)
return payment_continue(p);

/* Check if we'd be waiting more than 0 seconds. If we have
* less than a second then waitblockheight would return
* immediately resulting in a loop. */
if (time_after(now, p->deadline))
return payment_continue(p);

remaining = time_between(p->deadline, now);
if (time_to_sec(remaining) < 1)
return payment_continue(p);

/* *Was* it a blockheight disagreement that caused the failure? */
if (!failure_is_blockheight_disagreement(p, &blockheight))
return payment_continue(p);

paymod_log(p, LOG_INFORM,
"Remote node appears to be on a longer chain, which causes "
"CLTV timeouts to be incorrect. Waiting up to %" PRIu64
" seconds to catch up to block %d before retrying.",
time_to_sec(remaining), blockheight);

/* Set temporarily set the state of the payment to not failed, so
* interim status queries don't show this as terminally failed. We're
* in control for this payment so nobody else could be fooled by
* this. The callback will set it to retry anyway. */
payment_set_step(p, PAYMENT_STEP_RETRY);

req = jsonrpc_request_start(p->plugin, NULL, "waitblockheight",
waitblockheight_rpc_cb,
waitblockheight_rpc_cb, p);
json_add_u32(req->js, "blockheight", blockheight);
json_add_u32(req->js, "timeout", time_to_sec(remaining));
send_outreq(p->plugin, req);
}

REGISTER_PAYMENT_MODIFIER(waitblockheight, void *, NULL, waitblockheight_cb);

static u32 payment_max_htlcs(const struct payment *p)
{
Expand Down
6 changes: 5 additions & 1 deletion plugins/libplugin-pay.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,11 @@ struct payment {
* explanation if a payment is aborted. */
char *aborterror;

/* How many blocks are we lagging behind the rest of the
network? This needs to be taken into consideration when
sending payments before being fully caught up.*/
u32 chainlag;

/* Callback to be called when the entire payment process
* completes successfully. */
void (*on_payment_success)(struct payment *p);
Expand Down Expand Up @@ -445,7 +450,6 @@ REGISTER_PAYMENT_MODIFIER_HEADER(routehints, struct routehints_data);
REGISTER_PAYMENT_MODIFIER_HEADER(exemptfee, struct exemptfee_data);
REGISTER_PAYMENT_MODIFIER_HEADER(shadowroute, struct shadow_route_data);
REGISTER_PAYMENT_MODIFIER_HEADER(directpay, struct direct_pay_data);
extern struct payment_modifier waitblockheight_pay_mod;
REGISTER_PAYMENT_MODIFIER_HEADER(presplit, struct presplit_mod_data);
REGISTER_PAYMENT_MODIFIER_HEADER(adaptive_splitter, struct adaptive_split_mod_data);

Expand Down
1 change: 0 additions & 1 deletion plugins/pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,6 @@ struct payment_modifier *paymod_mods[] = {
*/
&routehints_pay_mod,
&payee_incoming_limit_pay_mod,
&waitblockheight_pay_mod,
&retry_pay_mod,
&adaptive_splitter_pay_mod,
NULL,
Expand Down
7 changes: 7 additions & 0 deletions plugins/test/run-route-calc.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ void json_object_end(struct json_stream *js UNNEEDED)
/* Generated stub for json_object_start */
void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_object_start called!\n"); abort(); }
/* Generated stub for json_scan */
const char *json_scan(const tal_t *ctx UNNEEDED,
const char *buffer UNNEEDED,
const jsmntok_t *tok UNNEEDED,
const char *guide UNNEEDED,
...)
{ fprintf(stderr, "json_scan called!\n"); abort(); }
/* Generated stub for json_strdup */
char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_strdup called!\n"); abort(); }
Expand Down
7 changes: 7 additions & 0 deletions plugins/test/run-route-overlong.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ void json_object_end(struct json_stream *js UNNEEDED)
/* Generated stub for json_object_start */
void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_object_start called!\n"); abort(); }
/* Generated stub for json_scan */
const char *json_scan(const tal_t *ctx UNNEEDED,
const char *buffer UNNEEDED,
const jsmntok_t *tok UNNEEDED,
const char *guide UNNEEDED,
...)
{ fprintf(stderr, "json_scan called!\n"); abort(); }
/* Generated stub for json_strdup */
char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_strdup called!\n"); abort(); }
Expand Down
Loading
Loading