Skip to content
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

Update ADR 019 to reflect usage of Any #6081

Merged
merged 34 commits into from
Apr 29, 2020
Merged

Conversation

aaronc
Copy link
Member

@aaronc aaronc commented Apr 27, 2020

This PR updates ADR 019 to reflect the usage of Any over oneof as discussed in #6030.

ADR 020 will be updated in a second PR after discussion in #6078 proceeds towards a solution.


For contributor use:

  • Targeted PR against correct branch (see CONTRIBUTING.md)
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the module structure standards.
  • Wrote unit and integration tests
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/)
  • Added relevant godoc comments.
  • Added a relevant changelog entry to the Unreleased section in CHANGELOG.md
  • Re-reviewed Files changed in the Github PR explorer

For admin use:

  • Added appropriate labels to PR (ex. WIP, R4R, docs, etc)
  • Reviewers assigned
  • Squashed all commits, uses message "Merge pull request #XYZ: [title]" (coding standards)

@alexanderbez alexanderbez added T: ADR An issue or PR relating to an architectural decision record R4R labels Apr 27, 2020
@codecov
Copy link

codecov bot commented Apr 27, 2020

Codecov Report

Merging #6081 into master will decrease coverage by 0.20%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##           master    #6081      +/-   ##
==========================================
- Coverage   54.78%   54.57%   -0.21%     
==========================================
  Files         437      436       -1     
  Lines       26436    26296     -140     
==========================================
- Hits        14482    14352     -130     
+ Misses      10958    10952       -6     
+ Partials      996      992       -4     

docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved

Note that `InterfaceContext` usage does not deviate from standard protobuf
Copy link
Contributor

Choose a reason for hiding this comment

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

How and where is this type used? It seems it's used in a concrete codec type. I'd note that here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Does this addition explain things clearly enough: 0470e47 ?

docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
@aaronc aaronc mentioned this pull request Apr 27, 2020
11 tasks
aaronc and others added 3 commits April 27, 2020 12:32
Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
Copy link
Contributor

@alexanderbez alexanderbez left a comment

Choose a reason for hiding this comment

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

conceptACK. I do not hold strong opinions on if this is truly the best approach or not, I just simply don't have enough context and experience from the client-side POV. That being said, the design looks reasonable to me. Finally, I would like to note, something feels "off" to me about the UnpackInterfacesMsg. Maybe once I see it in practice I'll be able to understand its purpose and use better 👍

Also, I'd prefer to see more 👀 on this (as much as possible) and more approvals before proceeding.

@aaronc
Copy link
Member Author

aaronc commented Apr 27, 2020

Finally, I would like to note, something feels "off" to me about the UnpackInterfacesMsg. Maybe once I see it in practice I'll be able to understand its purpose and use better 👍

It's poorly named for sure, just couldn't think of something better yet. It could be generalized to a "post-unmarshal" hook - maybe something like PostUnmarshal(context.Context) error. I don't know if that would help...

Copy link
Member

@webmaster128 webmaster128 left a comment

Choose a reason for hiding this comment

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

👍 makes sense as far as I can see

docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
@webmaster128 webmaster128 mentioned this pull request Apr 27, 2020
4 tasks
Copy link
Contributor

@alpe alpe left a comment

Choose a reason for hiding this comment

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

Thanks for writing this up! 💐
I have added some questions (q) and personal preferences (pp) mainly on the provided code examples. The concept of an interface registry is interesting although it would be great to not need it.

// interface passed in as iface
// Ex:
// ctx.RegisterInterface("cosmos_sdk.Msg", (*sdk.Msg)(nil))
RegisterInterface(protoName string, iface interface{})
Copy link
Contributor

Choose a reason for hiding this comment

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

q: Why do you need to register a name to an interface?
pp: this feels over complicated and may create some confusion for example which module/ code is responsible to register an interface. Would there be sanity checks if all interfaces have an implementation for example, failures on duplicates or multiple names, ... must register interface be called before register implementation? It would be easier to drop this functionality.

Copy link
Member Author

Choose a reason for hiding this comment

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

q: Why do you need to register a name to an interface?

This is so that we can communicate which types satisfy which interface to clients. It is a usability thing. Since interfaces do not have a proto message name, we are giving them one here rather than making the client UX dependent on the golang name.

pp: this feels over complicated and may create some confusion for example which module/ code is responsible to register an interface. Would there be sanity checks if all interfaces have an implementation for example, failures on duplicates or multiple names, ... must register interface be called before register implementation? It would be easier to drop this functionality.

There should be failures on duplicates but it would not need to be called before registering implementations.

Yes it would be easier to drop this and dropping it would also diminish client UX IMHO.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not depending on the Golang name is a useful feature when integrating message protos across an application with many pieces outside of the blockchain written in other languages. It simplifies the ability to release proto bindings from a single shared reference project.

docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
about all the required types. Note, the use of `interface_type` allows us to avoid a significant
amount of code boilerplate when implementing the `Codec`.
The module manager will include a method to call `RegisterInterfaceTypes` on
every module that implements in order to populate the `InterfaceRegistry`.
Copy link
Contributor

Choose a reason for hiding this comment

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

q: Are there modules that do not register any new type for IO or persistence? Just curious if it is worth to make it optional

Copy link
Member Author

Choose a reason for hiding this comment

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

All modules would need it to register their sdk.Msg types, should it not be optional?

GetSubmitter() sdk.AccAddress
To implement the `UnpackInterfaces` phase of deserialization which unpacks
interfaces wrapped in `Any` before they're needed, we create an interface
that `sdk.Msg`s and other types can implement:
Copy link
Contributor

Choose a reason for hiding this comment

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

pp: The UnpackInterfaces adds some complexity and may be easily forgotten or done wrong.
I am wondering if it would be easier to have a reflection based approach that recursively scans for fields of types Any in the decoded protobuf. There needs to be a cast to the concrete interface or type later anyway.

Just learned about DynamicAny type in Gogo. That feels like something that goes into the same direction.

Copy link
Member Author

Choose a reason for hiding this comment

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

This would be possible via reflection if we didn't care about making sure that the packed type was registered for the requested interface. I think there is a subtle difference but my preference is to be more explicit about which types satisfy which interfaces. For instance, say we have two interfaces Msg and PubKey and some type MsgX happens to satisfy both because of either programmer error or pure maliciousness, would we want to allow MsgX to be used as a PubKey? I would prefer a system that only allowed things that were properly registered.

We could implement all of the UnpackInterfaces methods in a way that checks for the right type via a gogo proto plugin. It's not that hard and I've done it before. I was just trying to avoid needing write a new plugin for this. But I can if it's helpful.

DynamicAny uses the global registry I'm trying to avoid.

Copy link
Contributor

@iramiller iramiller Apr 28, 2020

Choose a reason for hiding this comment

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

I would prefer a system that only allowed things that were properly registered.

I also prefer this ... portions of the code base that depend on understanding the message format should fail quickly ... other areas that can delegate processing should be able to pass off the message without "opening that box".

docs/architecture/adr-019-protobuf-state-encoding.md Outdated Show resolved Hide resolved
@iramiller
Copy link
Contributor

I've been following this development in various places (referenced issues and PRs as linked above). The implementation specified in this ADR looks very workable from my perspective as a client/module developer building on the SDK.

Copy link
Contributor

@alpe alpe left a comment

Choose a reason for hiding this comment

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

Thanks for the answers and updates! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T: ADR An issue or PR relating to an architectural decision record
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants