-
Notifications
You must be signed in to change notification settings - Fork 88
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
Fix compatibility de/serialization of Policies #878
Conversation
…ion' into backward_compatible_deserialization
Co-authored-by: Andrea Cerone <22031682+acerone85@users.noreply.github.com>
General comment on serialization and deserialization: You can still derive most of it. The main idea is that you can define your own structure PoliciesV1 and PoliciesV2, only for serialization/deserialization purposes, for which This is what I have got locally: pub struct Policies {
/// A bitmask that indicates what policies are set.
bits: PoliciesBits,
/// The array of policy values.
values: [Word; POLICIES_NUMBER],
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "Policies")]
pub struct PoliciesV1 {
bits: PoliciesBits,
values: [Word; 4],
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "Policies")]
pub struct PoliciesV2 {
bits: PoliciesBits,
values: Vec<Word>,
}
// This serde is manually implemented because of the `values` field format.
// Serialization of the `values` field :
// 1. Always write the 4 elements for the first 4 policies even if they are not set for
// backward compatibility.
// 2. For the remaining, write the value only if the policy is set.
impl serde::Serialize for Policies {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let Policies { bits, values } = self;
if self.bits.intersection(PoliciesBits::all())
== self.bits.intersection(
PoliciesBits::Maturity
.union(PoliciesBits::MaxFee)
.union(PoliciesBits::Tip)
.union(PoliciesBits::WitnessLimit),
)
{
PoliciesV1 {
bits: *bits,
values: values[..4].try_into().map_err(|_| {
serde::ser::Error::custom("The first 4 values should be present")
})?,
}
.serialize(serializer)
} else {
PoliciesV2 {
bits: *bits,
values: values.to_vec(),
}
.serialize(serializer)
}
}
}
impl<'de> serde::Deserialize<'de> for Policies {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let policies_v2 = PoliciesV2::deserialize(deserializer)?;
if policies_v2.bits.intersection(PoliciesBits::all())
== policies_v2.bits.intersection(
PoliciesBits::Maturity
.union(PoliciesBits::MaxFee)
.union(PoliciesBits::Tip)
.union(PoliciesBits::WitnessLimit),
)
{
let mut values: [Word; POLICIES_NUMBER] = [0; POLICIES_NUMBER];
values[..4].copy_from_slice(&policies_v2.values[..4]);
Ok(Policies {
bits: policies_v2.bits,
values,
})
} else {
let mut values: [Word; POLICIES_NUMBER] = [0; POLICIES_NUMBER];
values[..POLICIES_NUMBER]
.copy_from_slice(&policies_v2.values[..POLICIES_NUMBER]);
Ok(Policies {
bits: policies_v2.bits,
values,
})
}
}
} |
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.
LGTM. I have proposed a change but it can be implemented on top of your PR
Maybe it is possible to deserialize Maybe the approach with |
Yep. Alternatively you can try to deserialize as V2, and if either the deserialization fails or only the first 4 policy bits are set, you try to deserialize as V1. But I'm not 100% happy with that, as it requires deserializing twice in the worst case |
Issue
We want to be able to execute transactions that were created with transactions builders using versions before 0.59.0 in the next VM versions.
The problem is that with the new policy (#871) the array of policies values is now of length 5 and before it was length 4. On 0.59.0 the serde de/serialization is taking 5 "Words" to decode the policies values array which cause transactions from 0.58.0 to not be able to be deserialized correctly.
Fix
We want in this PR to change the serde de/serialization so that we ensure that previous format is still supported and new policies too.
We first de/serialize the
PoliciesBits
this allow us to know if we have only policies set in the first 4 (old) policies then we just the backward compatibility mode otherwise we use a new mode with a flexible length.Previous serde derive expanded (to compare with new implementation)
Checklist
Before requesting review
After merging, notify other teams
[Add or remove entries as needed]