-
Notifications
You must be signed in to change notification settings - Fork 155
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
Versioned serialization schemes for types #3014
Comments
My $0.02. We have a similar problem in
It's a bit clunky, but it works okay. You can also then take a mixed approach: if you need to decode something that isn't version-sensitive (an integer, say), you can just call I guess we could define a typeclass for this, but that does force you to pick an interface for all the decoders to share. And in fact I lied above: we don't just pass the protocol version, in some cases we also pass a predicate that indicates whether particular builtins are allowed. So we do make use of the variability. Something to consider, anyway. |
I second @michaelpj 's suggestion — for a slightly different reason. In cardano-wallet, we always have trouble finding the piece of code that is responsible for serializing/deserializing a specific type, because it's hard to figure out which instance of which type class is selected (this information is inferred from the code, but not written explicitly). So, usually we end up with "Hm, I think it's ok to use Put differently: I think that specifying the format explicitly on the value-level as opposed to implicitly on the type-level would help with whatever issues we have. 🤔 |
The reason that you probably cannot always trust the inferred serialization is because when we do find bugs, we cannot easily fix the bug, and have to work around it at an upstream type. My hope for a |
Yes and no. The trouble is that I'm often not sure which type I call |
Our types are often too polymorphic for that. Here's a good example that I stole from @lehins :
We need to rely on type classes (or something equivalent) in order to decode |
@HeinrichApfelmus This a classic argument type classes vs monomorphic function. Some people say you should embrace them, some say they are evil. You can make the same argument for almost any prominent type class in Haskell community. Be it To/FromJSON, Vector and MVector type class, any serialization library (binary, serialize, persist, etc), mtl vs transformers, etc. My argument is, type classes are strictly superior in most cases, because you can always specialize the type class function, but you can't make a specialized function more polymorphic.
So, to your trouble I can say, restrict the type and you'll know what you are interested in:
|
To expand on the example @JaredCorduan posted above, @michaelpj and @HeinrichApfelmus Thank you for your suggestions though. |
I'm not saying that the deserialisers need to be monomorphic! In fact, the Plutus ones are highly polymorphic. Just that having them be functions rather than typeclass methods avoids tying you to an upstream interface, which can be helpful. |
@michaelpj Do you mind sharing a link to a few modules in a Plutus repo where there examples of highly polymorphic deserializers. I'd like see some concrete examples of what you mean. |
Fair enough. 🤔 My trouble is about imposing some type constraint, whether that is done through I guess that the specific function from the ledger that made me nervous is |
I have no idea why |
Ah, I see the issue. We don't have any examples with type families, that does make things tricky because you need to pass around deserializers for the parts, either as explicit values or as typeclass dictionaries, and the typeclass dictionaries sure are more convenient. In |
The problem
When we encounter a problem with our deserializers, such as in #3003, #2965 and #2444, it can be very difficult to implement a fix. It is difficult because we can only fix such issues during a hard fork, and leading up to the hard fork we must maintain two serializations for the same type in order to not cause unintended network splitting (the problematic version must be used before the hard fork, and the fixed version is used afterwards). This can be especially tricky with the
FromCBOR
typeclass, since it is not always easy to search for where all the problematic uses are located.A solution
Create a new typeclass to replace
ToCBOR
andFromCBOR
which depends not only on the type but also on the protocol version. The first instances will all be implemented identically to our currentToCBOR
andFromCBOR
instances for all protocol versions up to the current one.Note that this change that will effect the downstream components. We will need to coordinate with consensus and node, and many other teams.
The text was updated successfully, but these errors were encountered: