-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
feat: string and message value renderers for textual #13510
feat: string and message value renderers for textual #13510
Conversation
Draft checkin to illustrate issue in |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #13510 +/- ##
========================================
Coverage 54.81% 54.81%
========================================
Files 652 654 +2
Lines 56023 56150 +127
========================================
+ Hits 30707 30780 +73
- Misses 22810 22847 +37
- Partials 2506 2523 +17
|
|
||
The encoding and decoding operations between a Protobuf transaction (whose definition can be found [here](https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/tx/v1beta1/tx.proto#L13)) and the string array MUST be bijective. | ||
### Invertible Rendering |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What motivates the change in nomenclature away from "bijective"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this draft PR is based off of the not-yet-committed #13434, so you're commenting on changes that will go away once this PR is rebased. You comments might be more appropriate for #13434.
But to quickly answer, "bijective" is not exactly correct. That would require that every valid Tx round-trip back to an equal Tx (yes) and also that every possible string round-trip back to an equal string (no). Having just the first condition makes Format()
an "injection" (i.e. one-to-one), but this was deemed to be too technical, hence the use of "invertible".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I move my comments to #13434, or is there enough visibility here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes that's preferable. Let's keep this PR as message value renderer only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm - #13434 (structured screens) is just awaiting final approval, so I don't want to derail that. I'm not seeing anything in these comments that would make me want to add any late changes. This discussion seems more appropriate for #12910 - but as you can see that ship sailed a while ago. @peterbourgon it would be most appropriate if you could open an issue for discussion and raise you concerns there. I'm going to change the target for this draft to we can see the actual content of this PR better.
} | ||
pass_check() | ||
``` | ||
Moreover, the renderer must provide 2 functions: one for formatting from Protobuf to string, and one for parsing string to Protobuf. These 2 functions are provided by the application developer. To satisfy point #1, these 2 functions MUST be bijective with each other. Bijectivity of these 2 functions will not be checked by the SDK at runtime. However, we strongly recommend the application developer to include a comprehensive suite in their app repo to test bijectivity, as to not introduce security bugs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the application fails to ensure bijectivity, are any guarantees asserted by Cosmos and/or Tendermint violated?
Note that the existence of an inverse function ensures that the | ||
rendered text contains the full information of the original transaction, | ||
not a hash or subset. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it claims this property, but doesn't really ensure it, right?
The correctness and security of `SIGN_MODE_TEXTUAL` is guaranteed by demonstrating an inverse function from the rendering to transaction protos. | ||
This means that it is impossible for a different protocol buffer message to render to the same text. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that inversability is not guaranteed or even verified by the SDK, right? It's just assumed to be true. So if an implementation happens to have a bug that violates this assumption, and isn't fully inversible in some circumstance, what happens?
When client software forms a transaction, the "raw" transaction (`TxRaw`) is serialized as a proto | ||
and a hash of the resulting byte sequence is computed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that protobuf serialization is not deterministic.
By default, repeated invocations of serialization methods on the same protocol buffer message instance may not produce the same byte output. That is, the default serialization is not deterministic.
Deterministic serialization only guarantees the same byte output for a particular binary. The byte output may change across different versions of the binary.
Bijectivity will be tested in two ways: | ||
Note that this inverse function does not need to perform correct | ||
parsing or error signaling for the whole domain of textual data. | ||
Merely that the range of valid transactions be invertible under |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merely that the range of valid transactions be invertible under | |
Merely, the range of valid transactions be invertible under |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still considering this. Where'd I put my Strunk & White?
|
||
- by providing a set of test fixtures between a transaction's Proto JSON representation and its TEXTUAL representation, and checking that encoding/decoding in both directions matches the fixtures, | ||
- by using property testing on the proto transaction itself, and testing that the composition of encoding and decoding yields the original transaction itself. | ||
Note that the existence of an inverse function ensures that the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the existence of an inverse function ensures that the | |
Note: the existence of an inverse function ensures that the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the original, but push back if you feel strongly.
26f3089
to
4898da0
Compare
@joeabbey Thanks for finding al those problems in the docs! Unfortunately, those aren't really part of this PR - the structured screens PR hadn't been merged yet, so I branched this off the end of it, and its contents were displayed here since there was no way to make it the target. In any case, I've created #13608 for minor documentation fixes and addressed your comments there. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work on this PR @JimLarson 👏
I left 2 small comments. I'll approve and put automerge once they are addressed
"screens": [ | ||
{"text": "Foo object"}, | ||
{"text": "Full name: subfield", "indent": 1}, | ||
{"text": "Left: Foo object", "indent": 1}, | ||
{"text": "Right: Foo object", "indent": 1}, | ||
{"text": "Nickname: junior", "indent": 2}, | ||
{"text": "Bar: Bar object", "indent": 1} | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an interesting test case. I'm wondering if the indentation is enough to clearly denote for humans the beginning and end of a struct. From a bijectivity view, yes indent
is enough, but from a human-readability one, it's almost as if we're expecting the next line to be Bar's fields.
When writing the spec initially, I was pondering on whether to add a End of Foo object
(like we do in repeated
) or not. Looking at this test case, I'm considering yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd weigh the utility of the footer screen against the disadvantage of additional clutter. I think it's important to optimize legibility for the common cases, and I doubt that present-but-empty messages will be common. This was mostly meant to explore the corner cases of the renderer than to represent an important test case.
{"text": "Bar id: quux", "indent": 3}, | ||
{"text": "Data: FFFE", "indent": 3} | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a couple of parsing error cases:
[]
[{"text": "Bar object"}]
[{"text": "Foo object", "indent": 1}]
[{"text": "Foo object"}, {"text": "Full name: deep", indent: 2}]
[{"text": "Foo object"}, {"text": "Leftover screen"}]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to not care about the limits of Parse()
. It must successfully invert any Screens produced by Format()
, but other than that it can really do whatever it wants.
case fd.Kind() == protoreflect.StringKind: | ||
return stringValueRenderer{}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need two StringKind cases?
why not incorporate in the above?
case fd.Kind() == protoreflect.StringKind:
if proto.GetExtension(fd.Options(), cosmos_proto.E_Scalar) == "" {
return stringValueRenderer{}, nil
} else {
scalar, ok := proto.GetExtension(fd.Options(), cosmos_proto.E_Scalar).(string)
...
Head branch was pushed to by a user without write access
60a3f1f
to
53f738c
Compare
Updated. @AmauryM please re-enable automerge. |
Head branch was pushed to by a user without write access
53f738c
to
4801283
Compare
This change adds value renderers for message and string types. I also added some message types to testpb with nested messages + snake-cased names and without repeated fields (which aren't implemented yet). Updates cosmos#12713
4801283
to
107ceac
Compare
Description
Closes: #12713
Refs: #12878
Sign mode textual value renderers for string and message.
Author Checklist
All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.
I have...
!
to the type prefix if API or client breaking changeCHANGELOG.md
Reviewers Checklist
All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.
I have...
!
in the type prefix if API or client breaking change