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

Issue 4305 - Added in_channel,out_channel,state params to listforwards #4349

Merged
merged 4 commits into from
Mar 2, 2021
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
12 changes: 9 additions & 3 deletions contrib/pyln-client/pyln/client/lightning.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,10 +916,16 @@ def listconfigs(self, config=None):
}
return self.call("listconfigs", payload)

def listforwards(self):
"""List all forwarded payments and their information.
def listforwards(self, status=None, in_channel=None, out_channel=None):
"""List all forwarded payments and their information matching
forward {status}, {in_channel} and {out_channel}.
"""
return self.call("listforwards")
payload = {
"status": status,
"in_channel": in_channel,
"out_channel": out_channel,
}
return self.call("listforwards", payload)

def listfunds(self, spent=False):
"""
Expand Down
12 changes: 10 additions & 2 deletions doc/lightning-listforwards.7

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

8 changes: 7 additions & 1 deletion doc/lightning-listforwards.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ lightning-listforwards -- Command showing all htlcs and their information
SYNOPSIS
--------

**listforwards**
**listforwards** \[*status*\] \[*in_channel*\] \[*out_channel*\]

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

The **listforwards** RPC command displays all htlcs that have been
attempted to be forwarded by the c-lightning node.

If *status* is specified, then only the forwards with the given status are returned.
*status* can be either *offered* or *settled* or *failed* or *local_failed*

If *in_channel* or *out_channel* is specified, then only the matching forwards
on the given in/out channel are returned.

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

Expand Down
31 changes: 24 additions & 7 deletions lightningd/peer_htlcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2596,11 +2596,15 @@ void json_format_forwarding_object(struct json_stream *response,
json_object_end(response);
}


static void listforwardings_add_forwardings(struct json_stream *response, struct wallet *wallet)
static void listforwardings_add_forwardings(struct json_stream *response,
struct wallet *wallet,
enum forward_status status,
const struct short_channel_id *chan_in,
const struct short_channel_id *chan_out)
{
const struct forwarding *forwardings;
forwardings = wallet_forwarded_payments_get(wallet, tmpctx);

forwardings = wallet_forwarded_payments_get(wallet, tmpctx, status, chan_in, chan_out);

json_array_start(response, "forwards");
for (size_t i=0; i<tal_count(forwardings); i++) {
Expand All @@ -2617,13 +2621,27 @@ static struct command_result *json_listforwards(struct command *cmd,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{

struct json_stream *response;

if (!param(cmd, buffer, params, NULL))
struct short_channel_id *chan_in;
struct short_channel_id *chan_out;

const char *status_str;
enum forward_status status = FORWARD_ANY;

if (!param(cmd, buffer, params,
p_opt("in_channel", param_short_channel_id, &chan_in),
p_opt("out_channel", param_short_channel_id, &chan_out),
p_opt("status", param_string, &status_str),
NULL))
return command_param_failed();

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

response = json_stream_success(cmd);
listforwardings_add_forwardings(response, cmd->ld->wallet);
listforwardings_add_forwardings(response, cmd->ld->wallet, status, chan_in, chan_out);

return command_success(cmd, response);
}
Expand All @@ -2632,8 +2650,7 @@ static const struct json_command listforwards_command = {
"listforwards",
"channels",
json_listforwards,
"List all forwarded payments and their information", false,
"List all forwarded payments and their information"
"List all forwarded payments and their information optionally filtering by [in_channel] [out_channel] and [state]"
};

AUTODATA(json_command, &listforwards_command);
59 changes: 59 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2448,3 +2448,62 @@ def test_listfunds(node_factory):
# 1 spent output (channel opening) and 1 unspent output
assert len(all_outputs) == 2
assert open_txid in txids


def test_listforwards(node_factory, bitcoind):
"""Test listfunds command."""
l1, l2, l3, l4 = node_factory.get_nodes(4, opts=[{}, {}, {}, {}])

l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l2.rpc.connect(l3.info['id'], 'localhost', l3.port)
l2.rpc.connect(l4.info['id'], 'localhost', l4.port)

c12, _ = l1.fundchannel(l2, 10**5)
c23, _ = l2.fundchannel(l3, 10**5)
c24, _ = l2.fundchannel(l4, 10**5)

# Wait until channels are active
bitcoind.generate_block(5)
l1.wait_channel_active(c23)

# successful payments
i31 = l3.rpc.invoice(1000, 'i31', 'desc')
l1.rpc.pay(i31['bolt11'])

i41 = l4.rpc.invoice(2000, 'i41', 'desc')
l1.rpc.pay(i41['bolt11'])

# failed payment
failed_payment_hash = l3.rpc.invoice(4000, 'failed', 'desc')['payment_hash']
failed_route = l1.rpc.getroute(l3.info['id'], 4000, 1)['route']

l2.rpc.close(c23, 1)

with pytest.raises(RpcError):
l1.rpc.sendpay(failed_route, failed_payment_hash)
l1.rpc.waitsendpay(failed_payment_hash)

all_forwards = l2.rpc.listforwards()['forwards']
print(json.dumps(all_forwards, indent=True))

assert len(all_forwards) == 3
assert i31['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards)
assert i41['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards)
assert failed_payment_hash in map(lambda x: x['payment_hash'], all_forwards)

# status=settled
settled_forwards = l2.rpc.listforwards(status='settled')['forwards']
assert len(settled_forwards) == 2
assert sum(x['out_msatoshi'] for x in settled_forwards) == 3000

# status=local_failed
failed_forwards = l2.rpc.listforwards(status='local_failed')['forwards']
assert len(failed_forwards) == 1

# in_channel=c23
c23_forwards = l2.rpc.listforwards(in_channel=c23, status='settled')['forwards']
assert len(c23_forwards) == 0

# out_channel=c24
c24_forwards = l2.rpc.listforwards(out_channel=c24)['forwards']
assert len(c24_forwards) == 1
3 changes: 2 additions & 1 deletion wallet/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,12 +669,13 @@ static struct migration dbmigrations[] = {
/* A reference into our own offers table, if it was made from one */
{SQL("ALTER TABLE payments ADD COLUMN local_offer_id BLOB DEFAULT NULL REFERENCES offers(offer_id);"), NULL},
{SQL("ALTER TABLE channels ADD funding_tx_remote_sigs_received INTEGER DEFAULT 0;"), NULL},

/* Speeds up deletion of one peer from the database, measurements suggest
* it cuts down the time by 80%. */
{SQL("CREATE INDEX forwarded_payments_out_htlc_id"
" ON forwarded_payments (out_htlc_id);"), NULL},
{SQL("UPDATE channel_htlcs SET malformed_onion = 0 WHERE malformed_onion IS NULL"), NULL},
/* Speed up forwarded_payments lookup based on state */
{SQL("CREATE INDEX forwarded_payments_state ON forwarded_payments (state)"), NULL},
};

/* Leak tracking. */
Expand Down
16 changes: 11 additions & 5 deletions wallet/db_postgres_sqlgen.c

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

16 changes: 11 additions & 5 deletions wallet/db_sqlite3_sqlgen.c

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

Loading