Added discard remaining input option when deserializing #13
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Abstract
When trying to write objects with shared logic, developers might need to define generic objects. This is especially true in programming languages that don’t support interfaces such as Move.
These generic objects are easy to serialize, since the compiler or runtime always knows the exact object type at the time of calling the serialization function. However, this doesn’t apply to deserialization, where we might want to decode the common fields of a generic object without needing to know its generic parameters.
Let’s take an example using the Move language. A developer could declare the following object:
This object is serialized in BCS as <borrowed bytes><fees bytes><T bytes>
Let's assume we want to develop a backend around this that checks what account borrowed the most and the one having the highest pending fees. We only have to retrieve each PledgedLoan for all the accounts and deserialize them into the following Rust struct for each address:
This is impossible to do in the current implementation of this crate because the deserializer checks that all the bytes input has been used, which is not the case because we don't need the locked field.
Safety concerns
Removing these checks directly is not good practice. Indeed, we can imagine fields of a struct having an incorrect
Deserializeimplementation, causing them to consume more or fewer bytes than theirSerializecounterpart. In the best case, this would make deserialization fail, in the worst case, it could succeed with incorrect or undefined behavior.Solution implemented
I added a boolean field,
discard_remaining_input, to theDeserializerstruct. When true, it skips the check in theendmethod. This field is always set to false in all existing methods, so the current behavior of the crate remains unchanged.Next, I added two unsafe functions:
new_discarding_remaining_input: likenew, but setsdiscard_remaining_inputto true.from_bytes_discarding_remaining_input: likefrom_bytes, but usesnew_discarding_remaining_inputinstead of new.I marked them
unsafeto force developers to understand the risks, since misuse can have severe consequences.