Skip to content

Actually check, cleanup, notification schemas #8376

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

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

rustyrussell
Copy link
Contributor

@rustyrussell rustyrussell commented Jun 26, 2025

We have schemas for (some) notifications, which GRPC seems to rely on, but they were completely untested. And thus, of course, wrong.

  1. Deprecate "null" for channel_state_changed scid.
  2. Don't wrap plugin notifications in "payload", let them look like native.
  3. Make custom plugin notifications follow the "wrap fields in a subobject of same name".
  4. Add dev hook to dump hook & notification io into files in a given dir, and use it to post-process check that they match schemas.

@rustyrussell rustyrussell added this to the v25.09 milestone Jun 26, 2025
@madelinevibes madelinevibes added the Status::Assigned The issue has been given to a team member for resolution. label Jun 26, 2025
@daywalker90
Copy link
Collaborator

daywalker90 commented Jun 26, 2025

Regarding the compile error with ConnectAddressType: Does the rpc method "connect" also have address type "websocket"? Right now the schema does not. The problem here is that both the rpc method and notification have the same path "connect" and so msggen is searching in the same path for the fields. Either the schemas have to match for "connect.address.type" or i have to submit a patch for mssgen.

@rustyrussell
Copy link
Contributor Author

Regarding the compile error with ConnectAddressType: Does the rpc method "connect" also have address type "websocket"? Right now the schema does not. The problem here is that both the rpc method and notification have the same path "connect" and so msggen is searching in the same path for the fields. Either the schemas have to match for "connect.address.type" or i have to submit a patch for mssgen.

Ah. We can't connect out to a web socket, so connect can't return that as type. Not sure what we want to do here; we could include it in connect for completeness, but it will never appear?

@daywalker90
Copy link
Collaborator

Ah. We can't connect out to a web socket, so connect can't return that as type. Not sure what we want to do here; we could include it in connect for completeness, but it will never appear?

Either that or https://github.com/daywalker90/lightning/tree/msggen-connect-notification-override

@rustyrussell
Copy link
Contributor Author

rustyrussell commented Jun 27, 2025

Here's the diff:

diff --git a/doc/schemas/notification/connect.json b/doc/schemas/notification/connect.json
index 8aaeb3eb8..1fbe9310f 100644
--- a/doc/schemas/notification/connect.json
+++ b/doc/schemas/notification/connect.json
@@ -53,7 +53,8 @@
               "ipv4",
               "ipv6",
               "torv2",
-              "torv3"
+              "torv3",
+              "websocket"
             ],
             "description": [
               "Type of connection (*torv2*/*torv3* only if **direction** is *out*)"

And here's the result with your commit applied:

rror[E0412]: cannot find type `ConnectAddressType` in this scope
   --> cln-rpc/src/notifications.rs:130:20
    |
130 |     pub item_type: ConnectAddressType,
    |                    ^^^^^^^^^^^^^^^^^^ not found in this scope
    |
help: consider importing this enum
    |
6   + use crate::model::responses::ConnectAddressType;
    |

error[E0412]: cannot find type `ConnectDirection` in this scope
   --> cln-rpc/src/notifications.rs:136:20
    |
47  | pub enum PeerConnectDirection {
    | ----------------------------- similarly named enum `PeerConnectDirection` defined here
...
136 |     pub direction: ConnectDirection,
    |                    ^^^^^^^^^^^^^^^^
    |
help: an enum with a similar name exists
    |
136 |     pub direction: PeerConnectDirection,
    |                    ~~~~~~~~~~~~~~~~~~~~
help: consider importing this enum
    |
6   + use crate::model::responses::ConnectDirection;
    |

For more information about this error, try `rustc --explain E0412`.
error: could not compile `cln-rpc` (lib) due to 2 previous errors
make: *** [plugins/Makefile:312: target/debug/clnrest] Error 101
make: *** Waiting for unfinished jobs....

@daywalker90
Copy link
Collaborator

Did you run make gen?

@rustyrussell rustyrussell force-pushed the guilt/check-notification-json branch from 6603b1b to de61108 Compare July 25, 2025 01:57
@rustyrussell rustyrussell marked this pull request as ready for review July 25, 2025 02:01
@rustyrussell rustyrussell requested a review from cdecker as a code owner July 25, 2025 02:01
@rustyrussell rustyrussell force-pushed the guilt/check-notification-json branch from de61108 to bf111ad Compare July 25, 2025 02:01
serde_json::Value::Object(map) => map.clone(),
_ => return Err(anyhow::anyhow!("params must be a JSON object")),
};
params.insert(method.clone(), json!(v));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intended change here? Right now it constructs a json dict with the old style and the new style like this:

{ "foo":"bar", "method":{"foo":"bar"}}

Is that intended? The thing is it only works for params that are dict's (serde_json::Value::Object doesn't mean json object in the sense that it is json but specifically a json dict)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the intention is to do both the "raw" style, and the modern "wrapped" style, at least for now? I got ChatGPT to write this, so...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fine under the assumption that params always is a json dict. If it's not, the conversion to such raw and wrapped style wouldn't be possible anyways without also wrapping the raw again.

@daywalker90
Copy link
Collaborator

Omg today i learned why noone reacts to my GitHub Reviews: i never finalize them and keep them in pending...

@madelinevibes madelinevibes added Status::Ready for Review The work has been completed and is now awaiting evaluation or approval. PLEASE clear CI 🫠 and removed Status::Assigned The issue has been given to a team member for resolution. labels Aug 6, 2025
@cdecker
Copy link
Member

cdecker commented Aug 12, 2025

Omg today i learned why noone reacts to my GitHub Reviews: i never finalize them and keep them in pending...

I might know that feeling somehow 🤔 😅

@rustyrussell rustyrussell force-pushed the guilt/check-notification-json branch from bf111ad to 10c2c70 Compare August 13, 2025 07:31
@cdecker
Copy link
Member

cdecker commented Aug 13, 2025

ACK 10c2c70

I filed #8451 to bump the cln crate versions after these schema changes (yes, in Rust making a field optional is a type change, and therefore breaking semver).

@@ -4153,7 +4153,7 @@ message ChannelStateChangedNotification {
}
bytes peer_id = 1;
bytes channel_id = 2;
string short_channel_id = 3;
optional string short_channel_id = 3;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a semver breaking change. We need to roll the minor version of cln-grpc.

Comment on lines +193 to +198
#[serde(skip_serializing_if = "Option::is_none")]
pub short_channel_id: Option<ShortChannelId>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semver breaking change for cln-rpc.

@daywalker90
Copy link
Collaborator

This should fix the build error: rustyrussell#15

@rustyrussell
Copy link
Contributor Author

The problem is it's actually optional. So the current code is broken :(

@rustyrussell rustyrussell force-pushed the guilt/check-notification-json branch 3 times, most recently from 3052b4e to 27c8397 Compare August 15, 2025 05:57
@rustyrussell rustyrussell enabled auto-merge (rebase) August 15, 2025 05:58
@rustyrussell rustyrussell force-pushed the guilt/check-notification-json branch 2 times, most recently from 1a80751 to 06dba37 Compare August 15, 2025 07:42
@rustyrussell rustyrussell removed the Status::Ready for Review The work has been completed and is now awaiting evaluation or approval. label Aug 15, 2025
@rustyrussell
Copy link
Contributor Author

Trivial rebase...

@rustyrussell rustyrussell force-pushed the guilt/check-notification-json branch 5 times, most recently from 183e209 to 55db5c8 Compare August 16, 2025 23:24
rustyrussell and others added 12 commits August 17, 2025 09:38
Particularly important since we're going to update the format: this makes sure we don't break them!

Signed-off-by: Rusty Russell <[email protected]>
We haven't seen the "excessive queue length" backtrace since we fixed gossipd,
so it's safe to drop excess messages without worrying about losing gossip.

Signed-off-by: Rusty Russell <[email protected]>
… in channel_state_changed notification

We always prefer to omit fields rather than use 'null' (or unknown!).

Note that before this, the schema was broken, so we have to put a special
exemption in for that case.

Signed-off-by: Rusty Russell <[email protected]>
For older lightningd, we copy field into the raw dict, for newer we recreate the old
"payload" member.

We do fix up the custom_notification test which set params to a string instead of a dict:
that's just weird!

We also change the hacky parsing to proper dict extraction.

Signed-off-by: Rusty Russell <[email protected]>
Changelog-Changed: pyln-client: plugin notifications parameters now exposed directly, not wrapped in `params` object.
Rather than forcing them to wrap their parameters in a "payload"
sub-object, copy in params directly.  We include the "origin" field
one level up, if they care.

The next patch restores compatibility for the one place we currently use
them, which is the pay plugin.

Signed-off-by: Rusty Russell <[email protected]>
Changelog-Deprecated: pyln-client: plugin custom notifications origins and payload (use parameters directly)
…ame.

All the core notifications changed over to wrapping the notification
fields in an object with the name of the notification, but notifications
from plugins were missed.

Changelog-Added: Plugins: `channel_hint_update`, `pay_failure` and `pay_success` notifications now have objects of the same name containing the expected fields.
Changelog-Deprecated: Plugins: `channel_hint_update`, `pay_failure` and `pay_success` notification fields outside the same-named object.
Signed-off-by: Rusty Russell <[email protected]>
…ion schemas.

Note that we need a workaround for deprecated APIs where "channel_state_changed" output "null" which violated the schema.

Signed-off-by: Rusty Russell <[email protected]>
…ications.

Modern style for notifications is to put everything inside an object
of same name as the method.

For now this means duplication for backward compatibility.  ChatGPT
helped me do that.

Signed-off-by: Rusty Russell <[email protected]>
…t.json

This is done by tests/test_connection.py::test_websocket:

```
{
  "jsonrpc": "2.0",
  "method": "connect",
  "params": {
    "connect": {
      "id": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
      "direction": "in",
      "address": {
        "type": "websocket",
        "subtype": "ipv4",
        "address": "127.0.0.1",
        "port": 59412
      }
    }
  }
}
```

Signed-off-by: Rusty Russell <[email protected]>
@rustyrussell rustyrussell force-pushed the guilt/check-notification-json branch from 55db5c8 to 1c5e7cf Compare August 17, 2025 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Notifications and hooks schemas should be validated by test infrastructure
4 participants