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

delpay: introduced a new rpc method to delete a payment by hash #3899

Merged
merged 2 commits into from
Sep 10, 2020
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
1 change: 1 addition & 0 deletions common/jsonrpc_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ static const errcode_t PAY_INVOICE_EXPIRED = 207;
static const errcode_t PAY_NO_SUCH_PAYMENT = 208;
static const errcode_t PAY_UNSPECIFIED_ERROR = 209;
static const errcode_t PAY_STOPPED_RETRYING = 210;
static const errcode_t PAY_STATUS_UNEXPECTED = 211;

/* `fundchannel` or `withdraw` errors */
static const errcode_t FUND_MAX_EXCEEDED = 300;
Expand Down
1 change: 1 addition & 0 deletions doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ MANPAGES := doc/lightning-cli.1 \
doc/lightning-decodepay.7 \
doc/lightning-delexpiredinvoice.7 \
doc/lightning-delinvoice.7 \
doc/lightning-delpay.7 \
doc/lightning-dev-sendcustommsg.7 \
doc/lightning-disconnect.7 \
doc/lightning-feerates.7 \
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ c-lightning Documentation
lightning-decodepay <lightning-decodepay.7.md>
lightning-delexpiredinvoice <lightning-delexpiredinvoice.7.md>
lightning-delinvoice <lightning-delinvoice.7.md>
lightning-delpay <lightning-delpay.7.md>
lightning-dev-sendcustommsg <lightning-dev-sendcustommsg.7.md>
lightning-disconnect <lightning-disconnect.7.md>
lightning-feerates <lightning-feerates.7.md>
Expand Down
90 changes: 90 additions & 0 deletions doc/lightning-delpay.7

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 83 additions & 0 deletions doc/lightning-delpay.7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
lightning-delpay -- Command for removing a completed or failed payment
============================================================

SYNOPSIS
--------

**delpay** *payment\_hash* *status*

DESCRIPTION
-----------

The **delpay** RPC command deletes a payment with the given `payment_hash` if its status is either `complete` or `failed`. Deleting a `pending` payment is an error.

- *payment\_hash*: The unique identifier of a payment.
- *status*: Expected status of the payment.
Only deletes if the payment status matches.

ZmnSCPxj marked this conversation as resolved.
Show resolved Hide resolved
EXAMPLE JSON REQUEST
------------
```json
{
"id": 82,
"method": "delpay",
"params": {
"payment_hash": "4fa2f1b001067ec06d7f95b8695b8acd9ef04c1b4d1110e3b94e1fa0687bb1e0",
"status": "complete"
}
}
```

RETURN VALUE
------------

If successful the command returns a payment object, in the same format as **listsendpays**. If the payment is a multi-part payment (MPP) the command return a list of
payments will be return -- one payment object for each partid.

On failure, an error is returned. If the lightning process fails before responding, the
caller should use lightning-listsentpays(7) or lightning-listpays(7) to query whether this payment was deleted or not.

The following error codes may occur:

- -32602: Parameter missed or malformed;
- 211: Payment status mismatch. Check the correct status via **paystatus**;
- 208: Payment with payment\_hash not found.

EXAMPLE JSON RESPONSE
-----
```json
{
"payments": [
{
"id": 2,
"payment_hash": "8dfd6538eeb33811c9114a75f792a143728d7f05643f38c3d574d3097e8910c0",
"destination": "0219f8900ee78a89f050c24d8b69492954f9fdbabed753710845eb75d3a75a5880",
"msatoshi": 1000,
"amount_msat": "1000msat",
"msatoshi_sent": 1000,
"amount_sent_msat": "1000msat",
"created_at": 1596224858,
"status": "complete",
"payment_preimage": "35bd4e2b481a1a84a22215b5372672cf81460a671816960ddb206464359e1822",
"bolt11": "lntb10n1p0jga20pp53h7k2w8wkvuprjg3ff6l0y4pgdeg6lc9vsln3s74wnfsjl5fzrqqdqdw3jhxazldahx2xqyjw5qcqp2sp5wut5jnhr6n7jd5747ky2g5flmw7hgx9yjnqzu60ps2jf6f7tc0us9qy9qsqu2a0k37nckl62005p69xavlkydkvhnypk4dphffy4x09zltwh9437ad7xkl83tefdarzhu5t30ju5s56wlrg97qkx404pq3srfc425cq3ke9af"
}
]
}

```


AUTHOR
------

Vincenzo Palazzo <<[email protected]>> is mainly responsible.

SEE ALSO
--------

lightning-listpays(7), lightning-listsendpays(7), lightning-paystatus(7).

RESOURCES
---------

Main web site: <https://github.com/ElementsProject/lightning>
96 changes: 96 additions & 0 deletions lightningd/pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,36 @@ struct sendpay_command {
struct command *cmd;
};

static bool string_to_payment_status(const char *status_str, enum wallet_payment_status *status)
vincenzopalazzo marked this conversation as resolved.
Show resolved Hide resolved
{
if (streq(status_str, "complete")) {
*status = PAYMENT_COMPLETE;
return true;
} else if (streq(status_str, "pending")) {
*status = PAYMENT_PENDING;
return true;
} else if (streq(status_str, "failed")) {
*status = PAYMENT_FAILED;
return true;
}
return false;
}

static const char *payment_status_to_string(const enum wallet_payment_status status)
{
switch (status) {
case PAYMENT_COMPLETE:
return "complete";
case PAYMENT_FAILED:
return "failed";
case PAYMENT_PENDING:
return "pending";
}
//This should never happen
abort();
}


static void destroy_sendpay_command(struct sendpay_command *pc)
{
list_del(&pc->list);
Expand Down Expand Up @@ -1486,6 +1516,72 @@ static const struct json_command listsendpays_command = {
};
AUTODATA(json_command, &listsendpays_command);


static struct command_result *json_delpay(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
const struct wallet_payment **payments;
const char *status_str;
enum wallet_payment_status status;
struct sha256 *payment_hash;

if (!param(cmd, buffer, params,
p_req("payment_hash", param_sha256, &payment_hash),
p_req("status", param_string, &status_str),
NULL))
return command_param_failed();

if (!string_to_payment_status(status_str, &status))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str);

switch(status){
case PAYMENT_COMPLETE:
case PAYMENT_FAILED:
break;
case PAYMENT_PENDING:
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid status: %s",
payment_status_to_string(status));
}

payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash);

if (tal_count(payments) == 0)
return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s",
type_to_string(tmpctx, struct sha256, payment_hash));

for (int i = 0; i < tal_count(payments); i++) {
if (payments[i]->status != status) {
return command_fail(cmd, PAY_STATUS_UNEXPECTED, "Payment with hash %s has %s status but it should be %s",
type_to_string(tmpctx, struct sha256, payment_hash),
payment_status_to_string(payments[i]->status),
payment_status_to_string(status));
}
}

wallet_payment_delete_by_hash(cmd->ld->wallet, payment_hash);

response = json_stream_success(cmd);
json_array_start(response, "payments");
for (int i = 0; i < tal_count(payments); i++) {
json_object_start(response, NULL);
json_add_payment_fields(response, payments[i]);
json_object_end(response);
}
json_array_end(response);
return command_success(cmd, response);
}

static const struct json_command delpay_command = {
"delpay",
"payment",
json_delpay,
"Delete payment with {payment_hash} and {status}",
};
AUTODATA(json_command, &delpay_command);

static struct command_result *json_createonion(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
Expand Down
61 changes: 61 additions & 0 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -3263,6 +3263,67 @@ def test_mpp_presplit_routehint_conflict(node_factory, bitcoind):
l1.rpc.pay(inv)


def test_delpay_argument_invalid(node_factory, bitcoind):
"""
This test includes all possible combinations of input error inside the
delpay command.
"""

# Create the line graph l2 -> l1 with a channel of 10 ** 5 sat!
l2, l1 = node_factory.line_graph(2, fundamount=10**5, wait_for_announce=True)

with pytest.raises(RpcError):
l2.rpc.delpay()

# sanity check
inv = l1.rpc.invoice(10 ** 5, 'inv', 'inv')
payment_hash = "AA" * 32
with pytest.raises(RpcError):
l2.rpc.delpay(payment_hash, 'complete')

l2.rpc.pay(inv['bolt11'])

wait_for(lambda: l2.rpc.listpays(inv['bolt11'])['pays'][0]['status'] == 'complete')

payment_hash = inv['payment_hash']

# payment paid with wrong status (pending status is a illegal input)
with pytest.raises(RpcError):
l2.rpc.delpay(payment_hash, 'pending')

with pytest.raises(RpcError):
l2.rpc.delpay(payment_hash, 'invalid_status')

with pytest.raises(RpcError):
l2.rpc.delpay(payment_hash, 'failed')

# test if the node is still ready
payments = l2.rpc.delpay(payment_hash, 'complete')

assert payments['payments'][0]['bolt11'] == inv['bolt11']
assert len(payments['payments']) == 1
assert len(l2.rpc.listpays()['pays']) == 0


def test_delpay_payment_split(node_factory, bitcoind):
"""
Test behavior of delpay with an MPP
"""
MPP_TARGET_SIZE = 10**7 # Taken from libpluin-pay.c
amt = 5 * MPP_TARGET_SIZE

l1, l2, l3 = node_factory.line_graph(3, fundamount=10**5,
wait_for_announce=True)

inv = l3.rpc.invoice(amt, 'lbl', 'desc')
l1.rpc.pay(inv['bolt11'])

assert len(l1.rpc.listpays()['pays']) == 1
delpay_result = l1.rpc.delpay(inv['payment_hash'], 'complete')['payments']
assert len(delpay_result) >= 5
assert len(l1.rpc.listpays()['pays']) == 0


def test_listpay_result_with_paymod(node_factory, bitcoind):
"""
The object of this test is to verify the correct behavior
Expand Down
Loading