lightningd: add in_htlc_id / out_htlc_id to listforwards.

And document that we never know payment_hash.

Changelog-Added: JSON-RPC: `listforwards` now shows `in_htlc_id` and `out_htlc_id`
Changelog-Changed: JSON-RPC: `listforwards` now never shows `payment_hash`; use `listhtlcs`.
This commit is contained in:
Rusty Russell
2022-09-19 10:19:53 +09:30
committed by Christian Decker
parent d7c1325e38
commit 311807ff1f
15 changed files with 118 additions and 63 deletions

View File

@@ -574,8 +574,10 @@
"ListforwardsForwards": { "ListforwardsForwards": {
"ListForwards.forwards[].fee_msat": 7, "ListForwards.forwards[].fee_msat": 7,
"ListForwards.forwards[].in_channel": 1, "ListForwards.forwards[].in_channel": 1,
"ListForwards.forwards[].in_htlc_id": 10,
"ListForwards.forwards[].in_msat": 2, "ListForwards.forwards[].in_msat": 2,
"ListForwards.forwards[].out_channel": 5, "ListForwards.forwards[].out_channel": 5,
"ListForwards.forwards[].out_htlc_id": 11,
"ListForwards.forwards[].out_msat": 8, "ListForwards.forwards[].out_msat": 8,
"ListForwards.forwards[].payment_hash": 6, "ListForwards.forwards[].payment_hash": 6,
"ListForwards.forwards[].received_time": 4, "ListForwards.forwards[].received_time": 4,

View File

@@ -1229,11 +1229,12 @@ message ListforwardsForwards {
TLV = 1; TLV = 1;
} }
string in_channel = 1; string in_channel = 1;
uint64 in_htlc_id = 10;
Amount in_msat = 2; Amount in_msat = 2;
ListforwardsForwardsStatus status = 3; ListforwardsForwardsStatus status = 3;
double received_time = 4; double received_time = 4;
optional string out_channel = 5; optional string out_channel = 5;
optional bytes payment_hash = 6; optional uint64 out_htlc_id = 11;
optional ListforwardsForwardsStyle style = 9; optional ListforwardsForwardsStyle style = 9;
optional Amount fee_msat = 7; optional Amount fee_msat = 7;
optional Amount out_msat = 8; optional Amount out_msat = 8;

View File

@@ -897,11 +897,12 @@ impl From<&responses::ListforwardsForwards> for pb::ListforwardsForwards {
fn from(c: &responses::ListforwardsForwards) -> Self { fn from(c: &responses::ListforwardsForwards) -> Self {
Self { Self {
in_channel: c.in_channel.to_string(), // Rule #2 for type short_channel_id in_channel: c.in_channel.to_string(), // Rule #2 for type short_channel_id
in_htlc_id: c.in_htlc_id.clone(), // Rule #2 for type u64
in_msat: Some(c.in_msat.into()), // Rule #2 for type msat in_msat: Some(c.in_msat.into()), // Rule #2 for type msat
status: c.status as i32, status: c.status as i32,
received_time: c.received_time.clone(), // Rule #2 for type number received_time: c.received_time.clone(), // Rule #2 for type number
out_channel: c.out_channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? out_channel: c.out_channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id?
payment_hash: c.payment_hash.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? out_htlc_id: c.out_htlc_id.clone(), // Rule #2 for type u64?
style: c.style.map(|v| v as i32), style: c.style.map(|v| v as i32),
fee_msat: c.fee_msat.map(|f| f.into()), // Rule #2 for type msat? fee_msat: c.fee_msat.map(|f| f.into()), // Rule #2 for type msat?
out_msat: c.out_msat.map(|f| f.into()), // Rule #2 for type msat? out_msat: c.out_msat.map(|f| f.into()), // Rule #2 for type msat?

6
cln-rpc/src/model.rs generated
View File

@@ -2673,6 +2673,8 @@ pub mod responses {
pub struct ListforwardsForwards { pub struct ListforwardsForwards {
#[serde(alias = "in_channel")] #[serde(alias = "in_channel")]
pub in_channel: ShortChannelId, pub in_channel: ShortChannelId,
#[serde(alias = "in_htlc_id")]
pub in_htlc_id: u64,
#[serde(alias = "in_msat")] #[serde(alias = "in_msat")]
pub in_msat: Amount, pub in_msat: Amount,
// Path `ListForwards.forwards[].status` // Path `ListForwards.forwards[].status`
@@ -2682,8 +2684,8 @@ pub mod responses {
pub received_time: f64, pub received_time: f64,
#[serde(alias = "out_channel", skip_serializing_if = "Option::is_none")] #[serde(alias = "out_channel", skip_serializing_if = "Option::is_none")]
pub out_channel: Option<ShortChannelId>, pub out_channel: Option<ShortChannelId>,
#[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] #[serde(alias = "out_htlc_id", skip_serializing_if = "Option::is_none")]
pub payment_hash: Option<String>, pub out_htlc_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<ListforwardsForwardsStyle>, pub style: Option<ListforwardsForwardsStyle>,
#[serde(alias = "fee_msat", skip_serializing_if = "Option::is_none")] #[serde(alias = "fee_msat", skip_serializing_if = "Option::is_none")]

View File

@@ -802,11 +802,12 @@ def getroute2py(m):
def listforwards_forwards2py(m): def listforwards_forwards2py(m):
return remove_default({ return remove_default({
"in_channel": m.in_channel, # PrimitiveField in generate_composite "in_channel": m.in_channel, # PrimitiveField in generate_composite
"in_htlc_id": m.in_htlc_id, # PrimitiveField in generate_composite
"in_msat": amount2msat(m.in_msat), # PrimitiveField in generate_composite "in_msat": amount2msat(m.in_msat), # PrimitiveField in generate_composite
"status": str(m.status), # EnumField in generate_composite "status": str(m.status), # EnumField in generate_composite
"received_time": m.received_time, # PrimitiveField in generate_composite "received_time": m.received_time, # PrimitiveField in generate_composite
"out_channel": m.out_channel, # PrimitiveField in generate_composite "out_channel": m.out_channel, # PrimitiveField in generate_composite
"payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite "out_htlc_id": m.out_htlc_id, # PrimitiveField in generate_composite
"style": str(m.style), # EnumField in generate_composite "style": str(m.style), # EnumField in generate_composite
"fee_msat": amount2msat(m.fee_msat), # PrimitiveField in generate_composite "fee_msat": amount2msat(m.fee_msat), # PrimitiveField in generate_composite
"out_msat": amount2msat(m.out_msat), # PrimitiveField in generate_composite "out_msat": amount2msat(m.out_msat), # PrimitiveField in generate_composite

File diff suppressed because one or more lines are too long

View File

@@ -25,11 +25,12 @@ RETURN VALUE
On success, an object containing **forwards** is returned. It is an array of objects, where each object contains: On success, an object containing **forwards** is returned. It is an array of objects, where each object contains:
- **in\_channel** (short\_channel\_id): the channel that received the HTLC - **in\_channel** (short\_channel\_id): the channel that received the HTLC
- **in\_htlc\_id** (u64): the unique HTLC id the sender gave this
- **in\_msat** (msat): the value of the incoming HTLC - **in\_msat** (msat): the value of the incoming HTLC
- **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed") - **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed")
- **received\_time** (number): the UNIX timestamp when this was received - **received\_time** (number): the UNIX timestamp when this was received
- **out\_channel** (short\_channel\_id, optional): the channel that the HTLC (trying to) forward to - **out\_channel** (short\_channel\_id, optional): the channel that the HTLC (trying to) forward to
- **payment\_hash** (hex, optional): payment hash sought by HTLC (always 64 characters) - **out\_htlc\_id** (u64, optional): the unique HTLC id we gave this when sending
- **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") - **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv")
If **out\_msat** is present: If **out\_msat** is present:
@@ -63,4 +64,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:39c71b957590f6a9b321120e7f337216833efd94f0144560da5cd55c91fee35c) [comment]: # ( SHA256STAMP:5b2da52b7f3a28563d0103d3853b9d8f717dc41a9e9c6b395ff19f1b975ca5fd)

View File

@@ -14,6 +14,7 @@
"required": [ "required": [
"in_channel", "in_channel",
"in_msat", "in_msat",
"in_htlc_id",
"status", "status",
"received_time" "received_time"
], ],
@@ -22,6 +23,10 @@
"type": "short_channel_id", "type": "short_channel_id",
"description": "the channel that received the HTLC" "description": "the channel that received the HTLC"
}, },
"in_htlc_id": {
"type": "u64",
"description": "the unique HTLC id the sender gave this"
},
"in_msatoshi": { "in_msatoshi": {
"deprecated": true "deprecated": true
}, },
@@ -47,11 +52,9 @@
"type": "short_channel_id", "type": "short_channel_id",
"description": "the channel that the HTLC (trying to) forward to" "description": "the channel that the HTLC (trying to) forward to"
}, },
"payment_hash": { "out_htlc_id": {
"type": "hex", "type": "u64",
"description": "payment hash sought by HTLC", "description": "the unique HTLC id we gave this when sending"
"maxLength": 64,
"minLength": 64
}, },
"style": { "style": {
"type": "string", "type": "string",
@@ -74,10 +77,12 @@
"required": [ "required": [
"fee_msat", "fee_msat",
"out_msat", "out_msat",
"out_htlc_id",
"out_channel" "out_channel"
], ],
"properties": { "properties": {
"in_channel": {}, "in_channel": {},
"in_htlc_id": {},
"in_msatoshi": {}, "in_msatoshi": {},
"in_msat": {}, "in_msat": {},
"status": {}, "status": {},
@@ -85,7 +90,7 @@
"received_time": {}, "received_time": {},
"resolved_time": {}, "resolved_time": {},
"out_channel": {}, "out_channel": {},
"payment_hash": {}, "out_htlc_id": {},
"failcode": {}, "failcode": {},
"failreason": {}, "failreason": {},
"fee": { "fee": {
@@ -109,13 +114,13 @@
"required": [], "required": [],
"properties": { "properties": {
"in_channel": {}, "in_channel": {},
"in_htlc_id": {},
"in_msatoshi": {}, "in_msatoshi": {},
"in_msat": {}, "in_msat": {},
"status": {}, "status": {},
"style": {}, "style": {},
"received_time": {}, "received_time": {},
"resolved_time": {}, "resolved_time": {},
"payment_hash": {},
"failcode": {}, "failcode": {},
"failreason": {}, "failreason": {},
"out_channel": {} "out_channel": {}
@@ -141,13 +146,14 @@
], ],
"properties": { "properties": {
"in_channel": {}, "in_channel": {},
"in_htlc_id": {},
"in_msatoshi": {}, "in_msatoshi": {},
"in_msat": {}, "in_msat": {},
"status": {}, "status": {},
"style": {}, "style": {},
"received_time": {}, "received_time": {},
"out_channel": {}, "out_channel": {},
"payment_hash": {}, "out_htlc_id": {},
"fee": {}, "fee": {},
"fee_msat": {}, "fee_msat": {},
"out_msatoshi": {}, "out_msatoshi": {},
@@ -164,13 +170,14 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"in_channel": {}, "in_channel": {},
"in_htlc_id": {},
"in_msatoshi": {}, "in_msatoshi": {},
"in_msat": {}, "in_msat": {},
"status": {}, "status": {},
"style": {}, "style": {},
"received_time": {}, "received_time": {},
"out_channel": {}, "out_channel": {},
"payment_hash": {}, "out_htlc_id": {},
"fee": {}, "fee": {},
"fee_msat": {}, "fee_msat": {},
"failcode": {}, "failcode": {},
@@ -197,13 +204,14 @@
"required": [], "required": [],
"properties": { "properties": {
"in_channel": {}, "in_channel": {},
"in_htlc_id": {},
"in_msatoshi": {}, "in_msatoshi": {},
"in_msat": {}, "in_msat": {},
"status": {}, "status": {},
"style": {}, "style": {},
"received_time": {}, "received_time": {},
"out_channel": {}, "out_channel": {},
"payment_hash": {}, "out_htlc_id": {},
"fee": {}, "fee": {},
"fee_msat": {}, "fee_msat": {},
"out_msatoshi": {}, "out_msatoshi": {},
@@ -224,13 +232,14 @@
"required": [], "required": [],
"properties": { "properties": {
"in_channel": {}, "in_channel": {},
"in_htlc_id": {},
"in_msatoshi": {}, "in_msatoshi": {},
"in_msat": {}, "in_msat": {},
"status": {}, "status": {},
"style": {}, "style": {},
"received_time": {}, "received_time": {},
"out_channel": {}, "out_channel": {},
"payment_hash": {}, "out_htlc_id": {},
"fee": {}, "fee": {},
"fee_msat": {}, "fee_msat": {},
"out_msatoshi": {}, "out_msatoshi": {},

View File

@@ -328,14 +328,16 @@ static void forward_event_notification_serialize(struct json_stream *stream,
cur->msat_out = AMOUNT_MSAT(0); cur->msat_out = AMOUNT_MSAT(0);
cur->fee = AMOUNT_MSAT(0); cur->fee = AMOUNT_MSAT(0);
} }
cur->payment_hash = tal_dup(cur, struct sha256, &in->payment_hash); cur->htlc_id_out = NULL;
cur->status = state; cur->status = state;
cur->failcode = failcode; cur->failcode = failcode;
cur->received_time = in->received_time; cur->received_time = in->received_time;
cur->resolved_time = tal_steal(cur, resolved_time); cur->resolved_time = tal_steal(cur, resolved_time);
cur->forward_style = forward_style; cur->forward_style = forward_style;
cur->htlc_id_in = in->key.id;
json_format_forwarding_object(stream, "forward_event", cur); json_add_forwarding_object(stream, "forward_event",
cur, &in->payment_hash);
} }
REGISTER_NOTIFICATION(forward_event, REGISTER_NOTIFICATION(forward_event,

View File

@@ -2765,21 +2765,26 @@ AUTODATA(json_command, &dev_ignore_htlcs);
/* Warp this process to ensure the consistent json object structure /* Warp this process to ensure the consistent json object structure
* between 'listforwards' API and 'forward_event' notification. */ * between 'listforwards' API and 'forward_event' notification. */
void json_format_forwarding_object(struct json_stream *response, void json_add_forwarding_object(struct json_stream *response,
const char *fieldname, const char *fieldname,
const struct forwarding *cur) const struct forwarding *cur,
const struct sha256 *payment_hash)
{ {
json_object_start(response, fieldname); json_object_start(response, fieldname);
/* See 6d333f16cc0f3aac7097269bf0985b5fa06d59b4: we may have deleted HTLC. */ /* Only for forward_event */
if (cur->payment_hash) if (payment_hash)
json_add_sha256(response, "payment_hash", cur->payment_hash); json_add_sha256(response, "payment_hash", payment_hash);
json_add_short_channel_id(response, "in_channel", &cur->channel_in); json_add_short_channel_id(response, "in_channel", &cur->channel_in);
json_add_u64(response, "in_htlc_id", cur->htlc_id_in);
/* This can be unknown if we failed before channel lookup */ /* This can be unknown if we failed before channel lookup */
if (cur->channel_out.u64 != 0) if (cur->channel_out.u64 != 0) {
json_add_short_channel_id(response, "out_channel", json_add_short_channel_id(response, "out_channel",
&cur->channel_out); &cur->channel_out);
if (cur->htlc_id_out)
json_add_u64(response, "out_htlc_id", *cur->htlc_id_out);
}
json_add_amount_msat_compat(response, json_add_amount_msat_compat(response,
cur->msat_in, cur->msat_in,
"in_msatoshi", "in_msat"); "in_msatoshi", "in_msat");
@@ -2835,7 +2840,7 @@ static void listforwardings_add_forwardings(struct json_stream *response,
json_array_start(response, "forwards"); json_array_start(response, "forwards");
for (size_t i=0; i<tal_count(forwardings); i++) { for (size_t i=0; i<tal_count(forwardings); i++) {
const struct forwarding *cur = &forwardings[i]; const struct forwarding *cur = &forwardings[i];
json_format_forwarding_object(response, NULL, cur); json_add_forwarding_object(response, NULL, cur, NULL);
} }
json_array_end(response); json_array_end(response);

View File

@@ -77,8 +77,10 @@ void local_fail_in_htlc_needs_update(struct htlc_in *hin,
/* This json process will be used as the serialize method for /* This json process will be used as the serialize method for
* forward_event_notification_gen and be used in * forward_event_notification_gen and be used in
* `listforwardings_add_forwardings()`. */ * `listforwardings_add_forwardings()`. */
void json_format_forwarding_object(struct json_stream *response, const char *fieldname, void json_add_forwarding_object(struct json_stream *response,
const struct forwarding *cur); const char *fieldname,
const struct forwarding *cur,
const struct sha256 *payment_hash);
/* Helper to create (common) WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS */ /* Helper to create (common) WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS */
#define failmsg_incorrect_or_unknown(ctx, ld, hin) \ #define failmsg_incorrect_or_unknown(ctx, ld, hin) \

View File

@@ -2415,6 +2415,21 @@ def test_listforwards(node_factory, bitcoind):
all_forwards = l2.rpc.listforwards()['forwards'] all_forwards = l2.rpc.listforwards()['forwards']
assert len(all_forwards) == 3 assert len(all_forwards) == 3
# Not guaranteed to be in chronological order!
all_forwards.sort(key=lambda f: f['in_htlc_id'])
assert all_forwards[0]['in_channel'] == c12
assert all_forwards[0]['out_channel'] == c23
assert all_forwards[0]['in_htlc_id'] == 0
assert all_forwards[0]['out_htlc_id'] == 0
assert all_forwards[1]['in_channel'] == c12
assert all_forwards[1]['out_channel'] == c24
assert all_forwards[1]['in_htlc_id'] == 1
assert all_forwards[1]['out_htlc_id'] == 0
assert all_forwards[2]['in_channel'] == c12
assert all_forwards[2]['out_channel'] == c23
assert all_forwards[2]['in_htlc_id'] == 2
assert 'out_htlc_id' not in all_forwards[2]
# status=settled # status=settled
settled_forwards = l2.rpc.listforwards(status='settled')['forwards'] settled_forwards = l2.rpc.listforwards(status='settled')['forwards']
assert len(settled_forwards) == 2 assert len(settled_forwards) == 2

View File

@@ -1353,24 +1353,30 @@ def test_forward_event_notification(node_factory, bitcoind, executor):
expect = stats[0].copy() expect = stats[0].copy()
# First event won't have conclusion. # First event won't have conclusion.
del expect['resolved_time'] del expect['resolved_time']
del expect['out_htlc_id']
expect['status'] = 'offered' expect['status'] = 'offered'
assert plugin_stats[0] == expect assert plugin_stats[0] == expect
expect = stats[0].copy() expect = stats[0].copy()
del expect['out_htlc_id']
assert plugin_stats[1] == expect assert plugin_stats[1] == expect
expect = stats[1].copy() expect = stats[1].copy()
del expect['resolved_time'] del expect['resolved_time']
del expect['out_htlc_id']
expect['status'] = 'offered' expect['status'] = 'offered'
assert plugin_stats[2] == expect assert plugin_stats[2] == expect
expect = stats[1].copy() expect = stats[1].copy()
del expect['out_htlc_id']
assert plugin_stats[3] == expect assert plugin_stats[3] == expect
expect = stats[2].copy() expect = stats[2].copy()
del expect['failcode'] del expect['failcode']
del expect['failreason'] del expect['failreason']
del expect['out_htlc_id']
expect['status'] = 'offered' expect['status'] = 'offered'
assert plugin_stats[4] == expect assert plugin_stats[4] == expect
expect = stats[2].copy() expect = stats[2].copy()
del expect['out_htlc_id']
assert plugin_stats[5] == expect assert plugin_stats[5] == expect

View File

@@ -4552,6 +4552,8 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w,
", out_msatoshi" ", out_msatoshi"
", in_channel_scid" ", in_channel_scid"
", out_channel_scid" ", out_channel_scid"
", in_htlc_id"
", out_htlc_id"
", received_time" ", received_time"
", resolved_time" ", resolved_time"
", failcode " ", failcode "
@@ -4618,9 +4620,8 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w,
cur->fee = AMOUNT_MSAT(0); cur->fee = AMOUNT_MSAT(0);
} }
/* FIXME: This now requires complex join to determine! */
cur->payment_hash = NULL;
db_col_scid(stmt, "in_channel_scid", &cur->channel_in); db_col_scid(stmt, "in_channel_scid", &cur->channel_in);
cur->htlc_id_in = db_col_u64(stmt, "in_htlc_id");
if (!db_col_is_null(stmt, "out_channel_scid")) { if (!db_col_is_null(stmt, "out_channel_scid")) {
db_col_scid(stmt, "out_channel_scid", &cur->channel_out); db_col_scid(stmt, "out_channel_scid", &cur->channel_out);
@@ -4628,6 +4629,11 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w,
assert(cur->status == FORWARD_LOCAL_FAILED); assert(cur->status == FORWARD_LOCAL_FAILED);
cur->channel_out.u64 = 0; cur->channel_out.u64 = 0;
} }
if (!db_col_is_null(stmt, "out_htlc_id")) {
cur->htlc_id_out = tal(results, u64);
*cur->htlc_id_out = db_col_u64(stmt, "out_htlc_id");
} else
cur->htlc_id_out = NULL;
cur->received_time = db_col_timeabs(stmt, "received_time"); cur->received_time = db_col_timeabs(stmt, "received_time");

View File

@@ -272,9 +272,11 @@ static inline enum htlc_state htlc_state_in_db(enum htlc_state s)
} }
struct forwarding { struct forwarding {
/* channel_out is all-zero if unknown. */
struct short_channel_id channel_in, channel_out; struct short_channel_id channel_in, channel_out;
/* htlc_id_out is NULL if unknown. */
u64 htlc_id_in, *htlc_id_out;
struct amount_msat msat_in, msat_out, fee; struct amount_msat msat_in, msat_out, fee;
struct sha256 *payment_hash;
enum forward_style forward_style; enum forward_style forward_style;
enum forward_status status; enum forward_status status;
enum onion_wire failcode; enum onion_wire failcode;