-
Notifications
You must be signed in to change notification settings - Fork 1k
Plugin: Another new notification type, forward_event #2799
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
Changes from all commits
3709811
577d6df
e3ee9fa
a416aca
ae2d797
f8f7d1e
44741a1
24da4a4
458a30b
d5edafa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -295,6 +295,89 @@ forms: | |
| `jcon fd <error_fd_to_jsonrpc>:`, `plugin-manager`; | ||
| 4. `log` is the context of the original log entry. | ||
|
|
||
| #### `forward_event` | ||
|
|
||
| A notification for topic `forward_event` is sent every time the status | ||
| of a forward payment is set. The json format is same as the API | ||
| `listforwards`. | ||
|
|
||
| ```json | ||
| { | ||
| "forward_event": { | ||
| "payment_hash": "f5a6a059a25d1e329d9b094aeeec8c2191ca037d3f5b0662e21ae850debe8ea2", | ||
| "in_channel": "103x2x1", | ||
| "out_channel": "103x1x1", | ||
| "in_msatoshi": 100001001, | ||
| "in_msat": "100001001msat", | ||
| "out_msatoshi": 100000000, | ||
| "out_msat": "100000000msat", | ||
| "fee": 1001, | ||
| "fee_msat": "1001msat", | ||
| "status": "settled", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The rest of your document refers to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you, I'll change |
||
| "received_time": 1560696342.368, | ||
| "resolved_time": 1560696342.556 | ||
| } | ||
| } | ||
| ``` | ||
| or | ||
|
|
||
| ```json | ||
| { | ||
| "forward_event": { | ||
| "payment_hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", | ||
| "in_channel": "103x2x1", | ||
| "out_channel": "110x1x0", | ||
| "in_msatoshi": 100001001, | ||
| "in_msat": "100001001msat", | ||
| "out_msatoshi": 100000000, | ||
| "out_msat": "100000000msat", | ||
| "fee": 1001, | ||
| "fee_msat": "1001msat", | ||
| "status": "local_failed", | ||
| "failcode": 16392, | ||
| "failreason": "WIRE_PERMANENT_CHANNEL_FAILURE", | ||
| "received_time": 1560696343.052 | ||
| } | ||
| } | ||
|
|
||
| ``` | ||
| - The status includes `offered`, `settled`, `failed` and `local_failed`, | ||
| and they are all string type in json. | ||
| - When the forward payment is valid for us, we'll set `offered` | ||
| and send the forward payment to next hop to resolve; | ||
| - When the payment forwarded by us gets paid eventually, the forward | ||
| payment will change the status from `offered` to `settled`; | ||
| - If payment fails locally(like failing to resolve locally) or the | ||
| corresponding htlc with next hop fails(like htlc timeout), we will | ||
| set the status as `local_failed`. `local_failed` may be set before | ||
| setting `offered` or after setting `offered`. In fact, from the | ||
| time we receive the htlc of the previous hop, all we can know the | ||
| cause of the failure is treated as `local_failed`. `local_failed` | ||
| only occuors locally or happens in the htlc between us and next hop; | ||
| - If `local_failed` is set before `offered`, this | ||
| means we just received htlc from the previous hop and haven't | ||
| generate htlc for next hop. In this case, the json of `forward_event` | ||
| sets the fields of `out_msatoshi`, `out_msat`,`fee` and `out_channel` | ||
| as 0; | ||
| - Note: In fact, for this case we may be not sure if this incoming | ||
| htlc represents a pay to us or a payment we need to forward. | ||
| We just simply treat all incoming failed to resolve as | ||
| `local_failed`. | ||
| - Only in `local_failed` case, json includes `failcode` and | ||
| `failreason` fields; | ||
| - `failed` means the payment forwarded by us fails in the | ||
| latter hops, and the failure isn't related to us, so we aren't | ||
| accessed to the fail reason. `failed` must be set after | ||
| `offered`. | ||
| - `failed` case doesn't include `failcode` and `failreason` | ||
| fields; | ||
| - `received_time` means when we received the htlc of this payment from | ||
| the previous peer. It will be contained into all status case; | ||
| - `resolved_time` means when the htlc of this payment between us and the | ||
| next peer was resolved. The resolved result may success or fail, so | ||
| only `settled` and `failed` case contain `resolved_time`; | ||
| - The `failcode` and `failreason` are defined in [BOLT 4][bolt4-failure-codes]. | ||
|
|
||
|
|
||
| ## Hooks | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -514,7 +514,7 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, | |
| details = wallet_invoice_details(info, wallet, invoice); | ||
|
|
||
| response = json_stream_success(info->cmd); | ||
| json_add_hex(response, "payment_hash", details->rhash.u.u8, | ||
| json_add_hex(response, "payment_hash", &details->rhash, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is common enough, maybe a botique
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good! What about adding it in anothor PR? |
||
| sizeof(details->rhash)); | ||
| json_add_u64(response, "expires_at", details->expiry_time); | ||
| json_add_string(response, "bolt11", details->bolt11); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,13 +1,16 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <ccan/array_size/array_size.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <lightningd/channel.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <lightningd/json.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <lightningd/notification.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <lightningd/peer_htlcs.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const char *notification_topics[] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "connect", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "disconnect", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "warning", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "invoice_payment", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "channel_opened" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "channel_opened", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "forward_event" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: For future reference, I prefer , on the trailing element, to avoid the extra line change like this. It's legal since C99 and it's been allowed by compilers for even longer, too.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you prefer |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool notifications_have_topic(const char *topic) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -93,3 +96,39 @@ void notify_channel_opened(struct lightningd *ld, struct node_id *node_id, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jsonrpc_notification_end(n); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugins_notify(ld->plugins, take(n)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void notify_forward_event(struct lightningd *ld, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const struct htlc_in *in, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const struct htlc_out *out, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enum forward_status state, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enum onion_type failcode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct timeabs *resolved_time) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct jsonrpc_notification *n = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jsonrpc_notification_start(NULL, "forward_event"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* Here is more neat to initial a forwarding structure than | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * to pass in a bunch of parameters directly*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct forwarding *cur = tal(tmpctx, struct forwarding); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->channel_in = *in->key.channel->scid; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->msat_in = in->msat; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (out) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->channel_out = *out->key.channel->scid; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->msat_out = out->msat; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert(amount_msat_sub(&cur->fee, in->msat, out->msat)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->channel_out.u64 = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->msat_out = AMOUNT_MSAT(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->fee = AMOUNT_MSAT(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->payment_hash = tal(cur, struct sha256_double); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->payment_hash->sha = in->payment_hash; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ZmnSCPxj My thoughts about Lines 189 to 198 in 2945b25
For another example, we used struct sha256 for payment_hash in struct wallet_payment:Lines 241 to 262 in 2945b25
Like BOLT#11 said,
Should we change the structure of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does look quite wrong... yes, we should probably fix this in a new PR. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->status = state; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->failcode = failcode; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->received_time = in->received_time; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur->resolved_time = tal_steal(cur, resolved_time); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| json_format_forwarding_object(n->stream, "forward_event", cur); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jsonrpc_notification_end(n); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plugins_notify(ld->plugins, take(n)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| #!/usr/bin/env python3 | ||
| """This plugin is used to check that forward_event calls are working correctly. | ||
| """ | ||
| from lightning import Plugin | ||
|
|
||
| plugin = Plugin() | ||
|
|
||
|
|
||
| def check(forward, dbforward): | ||
| # After finding the corresponding notification record, this function will | ||
| # make some changes on mutative fields of this record to make this record | ||
| # same as the ideal format with given status. | ||
| record = forward | ||
| if record['status'] == 'offered': | ||
| if dbforward['status'] == 'local_failed': | ||
| record['failcode'] = dbforward['failcode'] | ||
| record['failreason'] = dbforward['failreason'] | ||
| elif dbforward['status'] != 'offered': | ||
| record['resolved_time'] = dbforward['resolved_time'] | ||
| record['status'] = dbforward['status'] | ||
| if record == dbforward: | ||
| return True | ||
| else: | ||
| return False | ||
|
|
||
|
|
||
| @plugin.init() | ||
| def init(configuration, options, plugin): | ||
| plugin.forward_list = [] | ||
|
|
||
|
|
||
| @plugin.subscribe("forward_event") | ||
| def notify_warning(plugin, forward_event): | ||
| # One forward payment may have many notification records for different status, | ||
| # but one forward payment has only one record in 'listforwards' eventrually. | ||
| plugin.log("receive a forward recored, status: {}, payment_hash: {}".format(forward_event['status'], forward_event['payment_hash'])) | ||
| plugin.forward_list.append(forward_event) | ||
|
|
||
|
|
||
| @plugin.method('recordcheck') | ||
| def record_lookup(payment_hash, status, dbforward, plugin): | ||
| # Check if we received all notifications when forward changed. | ||
| # This check is based on the records of 'listforwards' | ||
| plugin.log("recordcheck: payment_hash: {}, status: {}".format(payment_hash, status)) | ||
| for forward in plugin.forward_list: | ||
| if forward['payment_hash'] == payment_hash and forward['status'] == status: | ||
| plugin.log("record exists") | ||
| check_result = check(forward, dbforward) | ||
| return check_result | ||
| plugin.log("no record") | ||
| return False | ||
|
|
||
|
|
||
| plugin.run() |
Uh oh!
There was an error while loading. Please reload this page.