Skip to content

msggen: Add UnionField support to future oneOf fields for oneOf schemas#8965

Open
4xvgal wants to merge 1 commit into
ElementsProject:masterfrom
4xvgal:feat/msggen-unionfield-support
Open

msggen: Add UnionField support to future oneOf fields for oneOf schemas#8965
4xvgal wants to merge 1 commit into
ElementsProject:masterfrom
4xvgal:feat/msggen-unionfield-support

Conversation

@4xvgal
Copy link
Copy Markdown

@4xvgal 4xvgal commented Mar 22, 2026

Summary

msggen could not handle oneOf fields in JSON schemas. Every oneOf field was manually overridden to a single type, which dropped some variants. This PR adds UnionField support to all generators so that future oneOf fields are automatically handled without needing manual overrides. Existing overrides are preserved to avoid breaking changes.

For example, invoice.exposeprivatechannels accepts true, ["1x1x3"], or "1x1x3" in JSON-RPC, but cln-rpc only generated Option<Vec<ShortChannelId>>, making it impossible to pass true through the typed API.

Changes

model.py:

  • Fix UnionField.from_js() constructor bug (missing added/deprecated args)
  • Add top-level oneOf detection in CompositeField.from_js()
  • Existing overrides are preserved

Generators (6 files)- ready for future oneOf fields :

  • rust.py: Generate #[serde(untagged)] enums
  • proto.py: Generate protobuf oneof blocks with array wrapper messages
  • convert.py / unconvert.py: Generate bidirectional From impls
  • grpc2py.py: Generate WhichOneof()-based conversion
  • notification.py: Automatically supported via rust.py's gen_field

Traversal:

  • patch.py, checks.py: Add recursion into UnionField variants

Tests

  • cargo test -p cln-rpc — passed
  • cargo test -p cln-grpc --features server — passed (4/4)
  • msggen generate — runs without errors

Related

Resolves #8961, #8964

@4xvgal 4xvgal marked this pull request as ready for review March 22, 2026 15:29
@4xvgal 4xvgal requested a review from cdecker as a code owner March 22, 2026 15:29
Comment thread cln-grpc/proto/node.proto
optional uint64 generation = 2;
optional bytes hex = 3;
optional string string = 4;
repeated string key = 5;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a backwards incompatible change. Never change the field numbering in protobuf.

Copy link
Copy Markdown
Author

@4xvgal 4xvgal Mar 22, 2026

Choose a reason for hiding this comment

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

Fixed - the first variant of each oneof now reuses the original field number.
new numbers are only allocated for additional variants.

@4xvgal 4xvgal requested a review from cdecker March 23, 2026 02:08
Comment thread cln-grpc/proto/node.proto Outdated
optional uint64 generation = 4;
repeated string key = 5;
oneof key {
DatastoreRequestarr_stringWrapper key_arr_string = 5;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Pretty sure this is also not backwards compatible. Also, what is really gained by having "repeated string" and "string"? I think it would be best to keep "repeated string" here.

Comment thread cln-grpc/proto/node.proto Outdated
string label = 2;
oneof label {
string label_string = 2;
sint64 label_int = 4;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If you use this and send a label_int = 4 to an old parser, it will just ignore the field i think. This makes it kind of a breaking change as well imo.

Copy link
Copy Markdown
Author

@4xvgal 4xvgal Apr 14, 2026

Choose a reason for hiding this comment

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

Thanks for reviewing.

I'll look around more and fix it and Re-open when it ready.
I was in hurry when found & open it.

@daywalker90
Copy link
Copy Markdown
Collaborator

Unfortunately i think it's either breaking changes or only use the new generator code on new union fields. @cdecker what do you think?

@cdecker
Copy link
Copy Markdown
Member

cdecker commented Apr 14, 2026

It is for sure breaking, and I'm unsure if we want to lean into generation more than truncating the hierarchy with manually managed stubs. I'd like to see the output of the pre and post change generated files as I fear it may be changing a lot.

But there is a discussion to be had on the future direction of the generated bindings. Personally I am tempted to go more for a uniffi / protobuf style of nested messages that translate into binding classes directly rather than the current heavily nested system (generating names like ListPeerChannelsChannelsHtlcEntry which is just ludicrous).

So three options:

  • Lean into the mix of manual and generated
  • Lean into generated only
  • Rework from scratch, making the json-schema little more than yet another generated file, but coming up with a source of truth that is better suited for generating bindings from it

Maybe it's a good time to discuss this ☺️

@4xvgal
Copy link
Copy Markdown
Author

4xvgal commented Apr 16, 2026

Let me check if I understand correctly

  1. keep current approach, Json schema as source of truth with manual overrides where oneOf desen't map clean

  2. my pr direction, still JSON Schema as source of truth, but lean into full generation (remove overrides). But trade-off is breaking changes for existing users of cln-rpc (grpc)

  3. Replace Json schema with a new Protobuf or unifii as the source of truth, allow shared type def and avoid name explosion problem

@daywalker90
Copy link
Copy Markdown
Collaborator

For this PR i'd like to stick to the current overrides and only use the UnionField generation for future oneOf fields. I'll research abit more what a complete rework would look like for CLN and come back to you. But for now let's not do major breaking changes.

What do you think?

@4xvgal
Copy link
Copy Markdown
Author

4xvgal commented Apr 16, 2026

For this PR i'd like to stick to the current overrides and only use the UnionField generation for future oneOf fields. I'll research abit more what a complete rework would look like for CLN and come back to you. But for now let's not do major breaking changes.

What do you think?

The reason I opened this PR was the missing type info from msgen. In my case, cashu-developement-kit needs to get private channel IDs from CLN, and right now it uses call_raw to bypass cln-rpc's typed API limitation. It works, but it's not a clean approach.

That said, sticking to the current overrides and using UnionField generation only for future fields sounds resonable to me. I don't want to break anything right now.

@4xvgal 4xvgal changed the title msggen: Add UnionField support to all generators for oneOf schemas msggen: Add UnionField support to future oneOf fields for oneOf schemas Apr 17, 2026
@4xvgal
Copy link
Copy Markdown
Author

4xvgal commented Apr 17, 2026

I've updated the PR to preserve existing overrides while keeping the UnionField logic for future oneOf fileds. Also updated the title and body to reflect this. Should i squash the commits into one?

@daywalker90 daywalker90 force-pushed the feat/msggen-unionfield-support branch from d1837e8 to 4bf490e Compare May 11, 2026 20:35
…ogic

Changelog-None

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

msggen: Invoice.exposeprivatechannels override drops boolean variant from oneOf

3 participants