Skip to content
Open
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
92 changes: 55 additions & 37 deletions plugins/xpay/xpay.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ struct xpay {
/* Suppress calls to askrene-age */
bool dev_no_age;
const char **user_layers;
/* We cannot initialize xpay with rpc_scan, we use instead asynchronous
* requests to fetch the node_id, the blockheight and create the xpay
* layer in askrene. Hence we need a flag to signal when we are ready
* for processing payments otherwise we get a race condition: a payment
* arrives and we don't know our own node_id yet, for example. In
* practice this will be more useful for tests. */
bool ready;
};

static struct xpay *xpay_of(struct plugin *plugin)
Expand Down Expand Up @@ -2303,6 +2310,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
const jsmntok_t *params,
bool as_pay)
{
struct xpay *xpay = xpay_of(cmd->plugin);
struct amount_msat *msat, *maxfee, *partial;
const char *invstring;
const char **layers;
Expand All @@ -2329,6 +2337,8 @@ static struct command_result *json_xpay_params(struct command *cmd,
p_opt_dev("dev_use_shadow", param_bool, &dev_use_shadow, true),
NULL))
return command_param_failed();
if (!xpay->ready)
return command_fail(cmd, PLUGIN_ERROR, "xpay is initializing");

/* Is this a one-shot vibe payment? Kids these days! */
if (!as_pay && bolt12_has_offer_prefix(invstring)) {
Expand Down Expand Up @@ -2513,6 +2523,8 @@ static struct payment *new_payment(const tal_t *ctx,
bool attempt_ongoing(struct plugin *plugin, const struct sha256 *payment_hash)
{
struct xpay *xpay = xpay_of(plugin);
if (!xpay->ready)
return false;
const struct payment *payment;

list_for_each(&xpay->payments, payment, list) {
Expand Down Expand Up @@ -2815,6 +2827,7 @@ static struct command_result *json_sendamount(struct command *cmd,
const char *buffer,
const jsmntok_t *params)
{
struct xpay *xpay = xpay_of(cmd->plugin);
struct amount_msat *send_msat, *maxfee;
struct amount_msat invoice_msat;
const char *invstring;
Expand All @@ -2837,6 +2850,8 @@ static struct command_result *json_sendamount(struct command *cmd,
p_opt("label", param_label, &label),
NULL))
return command_param_failed();
if (!xpay->ready)
return command_fail(cmd, PLUGIN_ERROR, "xpay is initializing");

// FIXME: why does xpay returns this only after
// preapproveinvoice_succeed?
Expand Down Expand Up @@ -2901,21 +2916,15 @@ static struct command_result *json_sendamount(struct command *cmd,
label, NULL, false, false, send_msat);
}

static struct command_result *getchaininfo_done(struct command *aux_cmd,
const char *method,
const char *buf,
const jsmntok_t *result,
void *unused)
static struct command_result *xpay_layer_created(struct command *aux_cmd,
const char *method,
const char *buf,
const jsmntok_t *result,
void *unused)
{
struct xpay *xpay = xpay_of(aux_cmd->plugin);

/* We use headercount from the backend, in case we're still syncing */
if (!json_to_u32(buf, json_get_member(buf, result, "headercount"),
&xpay->blockheight)) {
plugin_err(aux_cmd->plugin, "Bad getchaininfo '%.*s'",
json_tok_full_len(result),
json_tok_full(buf, result));
}
xpay->ready = true;
plugin_log(aux_cmd->plugin, LOG_INFORM, "xpay is ready");
return aux_command_done(aux_cmd);
}

Expand All @@ -2927,6 +2936,7 @@ static struct command_result *getinfo_done(struct command *aux_cmd,
{
struct xpay *xpay = xpay_of(aux_cmd->plugin);
const char *err;
struct out_req *req;

err = json_scan(tmpctx, buf, result,
"{id:%}", JSON_SCAN(json_to_pubkey, &xpay->local_id));
Expand All @@ -2936,7 +2946,35 @@ static struct command_result *getinfo_done(struct command *aux_cmd,
json_tok_full(buf, result),
err);
}
return aux_command_done(aux_cmd);

req = jsonrpc_request_start(aux_cmd, "askrene-create-layer",
xpay_layer_created, plugin_broken_cb,
"askrene-create-layer");
json_add_string(req->js, "layer", "xpay");
json_add_bool(req->js, "persistent", true);
return send_outreq(req);
}

static struct command_result *getchaininfo_done(struct command *aux_cmd,
const char *method,
const char *buf,
const jsmntok_t *result,
void *unused)
{
struct xpay *xpay = xpay_of(aux_cmd->plugin);
struct out_req *req;

/* We use headercount from the backend, in case we're still syncing */
if (!json_to_u32(buf, json_get_member(buf, result, "headercount"),
&xpay->blockheight)) {
plugin_err(aux_cmd->plugin, "Bad getchaininfo '%.*s'",
json_tok_full_len(result),
json_tok_full(buf, result));
}

req = jsonrpc_request_start(aux_cmd, "getinfo", getinfo_done,
plugin_broken_cb, "getinfo");
return send_outreq(req);
}

static struct command_result *populate_private_layer(struct command *cmd,
Expand Down Expand Up @@ -2968,15 +3006,6 @@ static struct command_result *age_layer(struct command *cmd, struct payment *pay
return send_outreq(req);
}

static struct command_result *xpay_layer_created(struct command *aux_cmd,
const char *method,
const char *buf,
const jsmntok_t *result,
void *unused)
{
return aux_command_done(aux_cmd);
}

static struct command_result *json_xkeysend(struct command *cmd,
const char *buf,
const jsmntok_t *params)
Expand Down Expand Up @@ -3007,6 +3036,8 @@ static struct command_result *json_xkeysend(struct command *cmd,
p_opt("extratlvs", param_extra_tlvs, &extra_fields),
NULL))
return command_param_failed();
if (!xpay->ready)
return command_fail(cmd, PLUGIN_ERROR, "xpay is initializing");

randbytes(&preimage, sizeof(preimage));
sha256(&payment_hash, &preimage, sizeof(preimage));
Expand Down Expand Up @@ -3100,20 +3131,6 @@ static const char *init(struct command *init_cmd,
json_add_u32(req->js, "last_height", 0);
send_outreq(req);

req = jsonrpc_request_start(aux_command(init_cmd), "getinfo",
getinfo_done,
plugin_broken_cb,
"getinfo");
send_outreq(req);

req = jsonrpc_request_start(aux_command(init_cmd), "askrene-create-layer",
xpay_layer_created,
plugin_broken_cb,
"askrene-create-layer");
json_add_string(req->js, "layer", "xpay");
json_add_bool(req->js, "persistent", true);
send_outreq(req);

return NULL;
}

Expand Down Expand Up @@ -3408,6 +3425,7 @@ int main(int argc, char *argv[])
xpay->slow_mode = false;
xpay->dev_no_age = false;
xpay->user_layers = tal_arr(xpay, const char *, 0);
xpay->ready = false;
list_head_init(&xpay->payments);
plugin_main(argv, init, take(xpay),
PLUGIN_RESTARTABLE, true, NULL,
Expand Down
2 changes: 2 additions & 0 deletions tests/test_xpay.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ def test_xpay_fake_channeld(node_factory, bitcoind, chainparams, slow_mode):
{'allow_bad_gossip': True,
'log-level': 'info',
}])
l1.daemon.logsearch_start = 0
l1.daemon.wait_for_log('xpay is ready')

# l1 needs to know l2's shaseed for the channel so it can make revocations
hsmfile = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
Expand Down
Loading