mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
pay: Have sendpay wait for payment to be saved.
The payment should be stored in a "timely" manner, i.e. within 10ms.
This commit is contained in:
parent
a7a18b96cf
commit
a0c2686ebd
@ -68,6 +68,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
|
|||||||
ld->rgb = NULL;
|
ld->rgb = NULL;
|
||||||
list_head_init(&ld->connects);
|
list_head_init(&ld->connects);
|
||||||
list_head_init(&ld->waitsendpay_commands);
|
list_head_init(&ld->waitsendpay_commands);
|
||||||
|
list_head_init(&ld->sendpay_commands);
|
||||||
ld->wireaddrs = tal_arr(ld, struct wireaddr, 0);
|
ld->wireaddrs = tal_arr(ld, struct wireaddr, 0);
|
||||||
ld->portnum = DEFAULT_PORT;
|
ld->portnum = DEFAULT_PORT;
|
||||||
timers_init(&ld->timers, time_mono());
|
timers_init(&ld->timers, time_mono());
|
||||||
|
@ -135,6 +135,8 @@ struct lightningd {
|
|||||||
|
|
||||||
/* Outstanding waitsendpay commands. */
|
/* Outstanding waitsendpay commands. */
|
||||||
struct list_head waitsendpay_commands;
|
struct list_head waitsendpay_commands;
|
||||||
|
/* Outstanding sendpay commands. */
|
||||||
|
struct list_head sendpay_commands;
|
||||||
|
|
||||||
/* Maintained by invoices.c */
|
/* Maintained by invoices.c */
|
||||||
struct invoices *invoices;
|
struct invoices *invoices;
|
||||||
|
144
lightningd/pay.c
144
lightningd/pay.c
@ -36,10 +36,10 @@ static void destroy_sendpay_command(struct sendpay_command *pc)
|
|||||||
list_del(&pc->list);
|
list_del(&pc->list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Owned by cxt; if cxt is deleted, then cb will
|
/* Owned by cxt, if cxt is deleted, then cb will
|
||||||
* no longer be called. */
|
* no longer be called. */
|
||||||
static void
|
static void
|
||||||
add_payment_waiter(const tal_t *cxt,
|
add_sendpay_waiter(const tal_t *cxt,
|
||||||
const struct sha256 *payment_hash,
|
const struct sha256 *payment_hash,
|
||||||
struct lightningd *ld,
|
struct lightningd *ld,
|
||||||
void (*cb)(const struct sendpay_result *, void*),
|
void (*cb)(const struct sendpay_result *, void*),
|
||||||
@ -47,6 +47,24 @@ add_payment_waiter(const tal_t *cxt,
|
|||||||
{
|
{
|
||||||
struct sendpay_command *pc = tal(cxt, struct sendpay_command);
|
struct sendpay_command *pc = tal(cxt, struct sendpay_command);
|
||||||
|
|
||||||
|
pc->payment_hash = *payment_hash;
|
||||||
|
pc->cb = cb;
|
||||||
|
pc->cbarg = cbarg;
|
||||||
|
list_add(&ld->sendpay_commands, &pc->list);
|
||||||
|
tal_add_destructor(pc, destroy_sendpay_command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Owned by cxt; if cxt is deleted, then cb will
|
||||||
|
* no longer be called. */
|
||||||
|
static void
|
||||||
|
add_waitsendpay_waiter(const tal_t *cxt,
|
||||||
|
const struct sha256 *payment_hash,
|
||||||
|
struct lightningd *ld,
|
||||||
|
void (*cb)(const struct sendpay_result *, void*),
|
||||||
|
void *cbarg)
|
||||||
|
{
|
||||||
|
struct sendpay_command *pc = tal(cxt, struct sendpay_command);
|
||||||
|
|
||||||
pc->payment_hash = *payment_hash;
|
pc->payment_hash = *payment_hash;
|
||||||
pc->cb = cb;
|
pc->cb = cb;
|
||||||
pc->cbarg = cbarg;
|
pc->cbarg = cbarg;
|
||||||
@ -368,7 +386,28 @@ static void report_routing_failure(struct log *log,
|
|||||||
void payment_store(struct lightningd *ld,
|
void payment_store(struct lightningd *ld,
|
||||||
const struct sha256 *payment_hash)
|
const struct sha256 *payment_hash)
|
||||||
{
|
{
|
||||||
|
const tal_t *tmpctx = tal_tmpctx(ld);
|
||||||
|
struct sendpay_command *pc;
|
||||||
|
struct sendpay_command *next;
|
||||||
|
struct sendpay_result *result;
|
||||||
|
|
||||||
wallet_payment_store(ld->wallet, payment_hash);
|
wallet_payment_store(ld->wallet, payment_hash);
|
||||||
|
|
||||||
|
/* Invent a sendpay result with PAY_IN_PROGRESS. */
|
||||||
|
result = sendpay_result_simple_fail(tmpctx, PAY_IN_PROGRESS,
|
||||||
|
"Payment is still in progress");
|
||||||
|
|
||||||
|
/* Trigger any sendpay commands waiting for the store to occur. */
|
||||||
|
list_for_each_safe(&ld->sendpay_commands, pc, next, list) {
|
||||||
|
if (!structeq(payment_hash, &pc->payment_hash))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Delete later if callback did not delete. */
|
||||||
|
tal_steal(tmpctx, pc);
|
||||||
|
pc->cb(result, pc->cbarg);
|
||||||
|
}
|
||||||
|
|
||||||
|
tal_free(tmpctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void payment_failed(struct lightningd *ld, const struct htlc_out *hout,
|
void payment_failed(struct lightningd *ld, const struct htlc_out *hout,
|
||||||
@ -490,7 +529,7 @@ bool wait_payment(const tal_t *cxt,
|
|||||||
void (*cb)(const struct sendpay_result *, void*),
|
void (*cb)(const struct sendpay_result *, void*),
|
||||||
void *cbarg)
|
void *cbarg)
|
||||||
{
|
{
|
||||||
const tal_t *tmpctx = tal_tmpctx(cxt);
|
const tal_t *tmpctx = tal_tmpctx(NULL);
|
||||||
struct wallet_payment *payment;
|
struct wallet_payment *payment;
|
||||||
struct sendpay_result *result;
|
struct sendpay_result *result;
|
||||||
char const *details;
|
char const *details;
|
||||||
@ -520,7 +559,7 @@ bool wait_payment(const tal_t *cxt,
|
|||||||
|
|
||||||
switch (payment->status) {
|
switch (payment->status) {
|
||||||
case PAYMENT_PENDING:
|
case PAYMENT_PENDING:
|
||||||
add_payment_waiter(cxt, payment_hash, ld, cb, cbarg);
|
add_waitsendpay_waiter(cxt, payment_hash, ld, cb, cbarg);
|
||||||
cb_not_called = true;
|
cb_not_called = true;
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
@ -575,13 +614,14 @@ end:
|
|||||||
return cb_not_called;
|
return cb_not_called;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the result if available now, or NULL if the
|
/* Returns false if cb was called, true if cb not yet called. */
|
||||||
* sendpay was deferred for later. */
|
bool
|
||||||
struct sendpay_result *
|
|
||||||
send_payment(const tal_t *ctx,
|
send_payment(const tal_t *ctx,
|
||||||
struct lightningd* ld,
|
struct lightningd* ld,
|
||||||
const struct sha256 *rhash,
|
const struct sha256 *rhash,
|
||||||
const struct route_hop *route)
|
const struct route_hop *route,
|
||||||
|
void (*cb)(const struct sendpay_result *, void*),
|
||||||
|
void *cbarg)
|
||||||
{
|
{
|
||||||
const u8 *onion;
|
const u8 *onion;
|
||||||
u8 sessionkey[32];
|
u8 sessionkey[32];
|
||||||
@ -629,39 +669,42 @@ send_payment(const tal_t *ctx,
|
|||||||
log_debug(ld->log, "send_payment: found previous");
|
log_debug(ld->log, "send_payment: found previous");
|
||||||
if (payment->status == PAYMENT_PENDING) {
|
if (payment->status == PAYMENT_PENDING) {
|
||||||
log_add(ld->log, "Payment is still in progress");
|
log_add(ld->log, "Payment is still in progress");
|
||||||
tal_free(tmpctx);
|
result = sendpay_result_simple_fail(tmpctx,
|
||||||
return sendpay_result_simple_fail(ctx,
|
PAY_IN_PROGRESS,
|
||||||
PAY_IN_PROGRESS,
|
"Payment is still in progress");
|
||||||
"Payment is still in progress");
|
cb(result, cbarg);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (payment->status == PAYMENT_COMPLETE) {
|
if (payment->status == PAYMENT_COMPLETE) {
|
||||||
log_add(ld->log, "... succeeded");
|
log_add(ld->log, "... succeeded");
|
||||||
/* Must match successful payment parameters. */
|
/* Must match successful payment parameters. */
|
||||||
if (payment->msatoshi != hop_data[n_hops-1].amt_forward) {
|
if (payment->msatoshi != hop_data[n_hops-1].amt_forward) {
|
||||||
char *msg = tal_fmt(ctx,
|
char *msg = tal_fmt(tmpctx,
|
||||||
"Already succeeded "
|
"Already succeeded "
|
||||||
"with amount %"PRIu64,
|
"with amount %"PRIu64,
|
||||||
payment->msatoshi);
|
payment->msatoshi);
|
||||||
tal_free(tmpctx);
|
result = sendpay_result_simple_fail(tmpctx,
|
||||||
return sendpay_result_simple_fail(ctx,
|
PAY_RHASH_ALREADY_USED,
|
||||||
PAY_RHASH_ALREADY_USED,
|
msg);
|
||||||
msg);
|
cb(result, cbarg);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (!structeq(&payment->destination, &ids[n_hops-1])) {
|
if (!structeq(&payment->destination, &ids[n_hops-1])) {
|
||||||
char *msg = tal_fmt(ctx,
|
char *msg = tal_fmt(tmpctx,
|
||||||
"Already succeeded to %s",
|
"Already succeeded to %s",
|
||||||
type_to_string(tmpctx,
|
type_to_string(tmpctx,
|
||||||
struct pubkey,
|
struct pubkey,
|
||||||
&payment->destination));
|
&payment->destination));
|
||||||
tal_free(tmpctx);
|
result = sendpay_result_simple_fail(tmpctx,
|
||||||
return sendpay_result_simple_fail(ctx,
|
PAY_RHASH_ALREADY_USED,
|
||||||
PAY_RHASH_ALREADY_USED,
|
msg);
|
||||||
msg);
|
cb(result, cbarg);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
result = sendpay_result_success(ctx,
|
result = sendpay_result_success(tmpctx,
|
||||||
payment->payment_preimage);
|
payment->payment_preimage);
|
||||||
tal_free(tmpctx);
|
cb(result, cbarg);
|
||||||
return result;
|
return false;
|
||||||
}
|
}
|
||||||
wallet_payment_delete(ld->wallet, rhash);
|
wallet_payment_delete(ld->wallet, rhash);
|
||||||
log_add(ld->log, "... retrying");
|
log_add(ld->log, "... retrying");
|
||||||
@ -676,10 +719,11 @@ send_payment(const tal_t *ctx,
|
|||||||
report_routing_failure(ld->log, ld->gossip, fail);
|
report_routing_failure(ld->log, ld->gossip, fail);
|
||||||
|
|
||||||
/* Report routing failure to caller */
|
/* Report routing failure to caller */
|
||||||
tal_free(tmpctx);
|
result = sendpay_result_route_failure(tmpctx, true, fail, NULL,
|
||||||
return sendpay_result_route_failure(ctx, true, fail, NULL,
|
"No connection to first "
|
||||||
"No connection to first "
|
"peer found");
|
||||||
"peer found");
|
cb(result, cbarg);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
randombytes_buf(&sessionkey, sizeof(sessionkey));
|
randombytes_buf(&sessionkey, sizeof(sessionkey));
|
||||||
@ -703,9 +747,10 @@ send_payment(const tal_t *ctx,
|
|||||||
report_routing_failure(ld->log, ld->gossip, fail);
|
report_routing_failure(ld->log, ld->gossip, fail);
|
||||||
|
|
||||||
/* Report routing failure to caller */
|
/* Report routing failure to caller */
|
||||||
tal_free(tmpctx);
|
result = sendpay_result_route_failure(tmpctx, true, fail, NULL,
|
||||||
return sendpay_result_route_failure(ctx, true, fail, NULL,
|
"First peer not ready");
|
||||||
"First peer not ready");
|
cb(result, cbarg);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy channels used along the route. */
|
/* Copy channels used along the route. */
|
||||||
@ -729,8 +774,10 @@ send_payment(const tal_t *ctx,
|
|||||||
/* We write this into db when HTLC is actually sent. */
|
/* We write this into db when HTLC is actually sent. */
|
||||||
wallet_payment_setup(ld->wallet, payment);
|
wallet_payment_setup(ld->wallet, payment);
|
||||||
|
|
||||||
|
add_sendpay_waiter(ctx, rhash, ld, cb, cbarg);
|
||||||
|
|
||||||
tal_free(tmpctx);
|
tal_free(tmpctx);
|
||||||
return NULL;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------
|
||||||
@ -827,7 +874,19 @@ static void json_sendpay_on_resolve(const struct sendpay_result* r,
|
|||||||
void *vcmd)
|
void *vcmd)
|
||||||
{
|
{
|
||||||
struct command *cmd = (struct command*) vcmd;
|
struct command *cmd = (struct command*) vcmd;
|
||||||
json_waitsendpay_on_resolve(r, cmd);
|
struct json_result *response;
|
||||||
|
|
||||||
|
if (!r->succeeded && r->errorcode == PAY_IN_PROGRESS) {
|
||||||
|
/* This is normal for sendpay. Succeed. */
|
||||||
|
response = new_json_result(cmd);
|
||||||
|
json_object_start(response, NULL);
|
||||||
|
json_add_string(response, "message",
|
||||||
|
"Monitor status with listpayments or waitsendpay");
|
||||||
|
json_add_bool(response, "completed", false);
|
||||||
|
json_object_end(response);
|
||||||
|
command_success(cmd, response);
|
||||||
|
} else
|
||||||
|
json_waitsendpay_on_resolve(r, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void json_sendpay(struct command *cmd,
|
static void json_sendpay(struct command *cmd,
|
||||||
@ -838,8 +897,6 @@ static void json_sendpay(struct command *cmd,
|
|||||||
size_t n_hops;
|
size_t n_hops;
|
||||||
struct sha256 rhash;
|
struct sha256 rhash;
|
||||||
struct route_hop *route;
|
struct route_hop *route;
|
||||||
struct sendpay_result *r;
|
|
||||||
struct json_result *response;
|
|
||||||
|
|
||||||
if (!json_get_params(cmd, buffer, params,
|
if (!json_get_params(cmd, buffer, params,
|
||||||
"route", &routetok,
|
"route", &routetok,
|
||||||
@ -918,18 +975,9 @@ static void json_sendpay(struct command *cmd,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = send_payment(cmd, cmd->ld, &rhash, route);
|
if (send_payment(cmd, cmd->ld, &rhash, route,
|
||||||
if (r)
|
&json_sendpay_on_resolve, cmd))
|
||||||
json_sendpay_on_resolve(r, cmd);
|
command_still_pending(cmd);
|
||||||
else {
|
|
||||||
response = new_json_result(cmd);
|
|
||||||
json_object_start(response, NULL);
|
|
||||||
json_add_string(response, "message",
|
|
||||||
"Monitor status with listpayments or waitsendpay");
|
|
||||||
json_add_bool(response, "completed", false);
|
|
||||||
json_object_end(response);
|
|
||||||
command_success(cmd, response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct json_command sendpay_command = {
|
static const struct json_command sendpay_command = {
|
||||||
|
@ -40,15 +40,25 @@ struct sendpay_result {
|
|||||||
const char *details;
|
const char *details;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Initiate a payment. Return NULL if the payment will be
|
/* Initiate a payment. Return true if the callback will be
|
||||||
* scheduled for later, or a result if the result is available
|
* scheduled for later, or false if the callback has already
|
||||||
* immediately. If returning an immediate result, the returned
|
* been called. If the given context is freed before the
|
||||||
* object is allocated from the given context. Otherwise, the
|
* callback is called, then the callback will no longer be
|
||||||
* return context is ignored. */
|
* called.
|
||||||
struct sendpay_result *send_payment(const tal_t *ctx,
|
*
|
||||||
struct lightningd* ld,
|
* This will call the callback "soon" in 10ms or less.
|
||||||
const struct sha256 *rhash,
|
*
|
||||||
const struct route_hop *route);
|
* Typically the callback will be called with a failed
|
||||||
|
* sendpay_result indicating an error code of PAY_IN_PROGRESS.
|
||||||
|
* It will only call the callback with successful sendpay_result
|
||||||
|
* if the payment has already completed with the same amount
|
||||||
|
* and destination before. */
|
||||||
|
bool send_payment(const tal_t *ctx,
|
||||||
|
struct lightningd* ld,
|
||||||
|
const struct sha256 *rhash,
|
||||||
|
const struct route_hop *route,
|
||||||
|
void (*cb)(const struct sendpay_result *, void*),
|
||||||
|
void *cbarg);
|
||||||
/* Wait for a previous send_payment to complete in definite
|
/* Wait for a previous send_payment to complete in definite
|
||||||
* success or failure. If the given context is freed before
|
* success or failure. If the given context is freed before
|
||||||
* the callback is called, then the callback will no longer
|
* the callback is called, then the callback will no longer
|
||||||
|
@ -189,6 +189,12 @@ static void json_pay_sendpay_resolve(const struct sendpay_result *r,
|
|||||||
|
|
||||||
why = should_delay_retry(pay->try_parent, r);
|
why = should_delay_retry(pay->try_parent, r);
|
||||||
if (why) {
|
if (why) {
|
||||||
|
/* We have some reason to delay retrying. */
|
||||||
|
|
||||||
|
/* Clear previous try memory. */
|
||||||
|
pay->try_parent = tal_free(pay->try_parent);
|
||||||
|
pay->try_parent = tal(pay, char);
|
||||||
|
|
||||||
log_info(pay->cmd->ld->log,
|
log_info(pay->cmd->ld->log,
|
||||||
"pay(%p): Delay before retry: %s", pay, why);
|
"pay(%p): Delay before retry: %s", pay, why);
|
||||||
/* Delay for 3 seconds if needed. FIXME: random
|
/* Delay for 3 seconds if needed. FIXME: random
|
||||||
@ -224,6 +230,26 @@ static void log_route(struct pay *pay, struct route_hop *route)
|
|||||||
tal_free(tmpctx);
|
tal_free(tmpctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void json_pay_sendpay_resume(const struct sendpay_result *r,
|
||||||
|
void *vpay)
|
||||||
|
{
|
||||||
|
struct pay *pay = (struct pay *) vpay;
|
||||||
|
bool completed = r->succeeded || r->errorcode != PAY_IN_PROGRESS;
|
||||||
|
|
||||||
|
if (completed)
|
||||||
|
/* Already completed. */
|
||||||
|
json_pay_sendpay_resolve(r, pay);
|
||||||
|
else {
|
||||||
|
/* Clear previous try memory. */
|
||||||
|
pay->try_parent = tal_free(pay->try_parent);
|
||||||
|
pay->try_parent = tal(pay, char);
|
||||||
|
|
||||||
|
/* Not yet complete? Wait for it. */
|
||||||
|
wait_payment(pay->try_parent, pay->cmd->ld, &pay->payment_hash,
|
||||||
|
json_pay_sendpay_resolve, pay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void json_pay_getroute_reply(struct subd *gossip UNUSED,
|
static void json_pay_getroute_reply(struct subd *gossip UNUSED,
|
||||||
const u8 *reply, const int *fds UNUSED,
|
const u8 *reply, const int *fds UNUSED,
|
||||||
struct pay *pay)
|
struct pay *pay)
|
||||||
@ -234,7 +260,6 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED,
|
|||||||
double feepercent;
|
double feepercent;
|
||||||
bool fee_too_high;
|
bool fee_too_high;
|
||||||
struct json_result *data;
|
struct json_result *data;
|
||||||
struct sendpay_result *result;
|
|
||||||
|
|
||||||
fromwire_gossip_getroute_reply(reply, reply, &route);
|
fromwire_gossip_getroute_reply(reply, reply, &route);
|
||||||
|
|
||||||
@ -295,17 +320,9 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED,
|
|||||||
|
|
||||||
log_route(pay, route);
|
log_route(pay, route);
|
||||||
|
|
||||||
result = send_payment(pay->try_parent,
|
send_payment(pay->try_parent,
|
||||||
pay->cmd->ld, &pay->payment_hash, route);
|
pay->cmd->ld, &pay->payment_hash, route,
|
||||||
/* Resolved immediately? */
|
&json_pay_sendpay_resume, pay);
|
||||||
if (result)
|
|
||||||
json_pay_sendpay_resolve(result, pay);
|
|
||||||
/* Wait for resolution */
|
|
||||||
else
|
|
||||||
wait_payment(pay->try_parent,
|
|
||||||
pay->cmd->ld,
|
|
||||||
&pay->payment_hash,
|
|
||||||
&json_pay_sendpay_resolve, pay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start a payment attempt. Return true if deferred,
|
/* Start a payment attempt. Return true if deferred,
|
||||||
|
Loading…
Reference in New Issue
Block a user