Skip to content

Commit 15e7742

Browse files
authored
feat: custom Encode/Decode for backward compatibility of Candiates Data (#938)
1 parent 19236c1 commit 15e7742

File tree

2 files changed

+99
-14
lines changed

2 files changed

+99
-14
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ match this change.
3131
* `select_authorities` in `authority-selection-inherents` crate now returns a `BoundedVec` of `CommitteeMembers`. Projects that used the old version no longer
3232
need to transform the data in their own runtime code
3333
* Updates Rust dependency to v1.88, changes WASM target to 'wasm32v1-none'
34+
* Backward compatible Encode and Decode for legacy chains that operate with AURA and Grandpa keys as session keys.
3435

3536
## Added
3637

toolkit/sidechain/domain/src/lib.rs

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ use byte_string_derive::byte_string;
2222
use core::{
2323
fmt::{Display, Formatter},
2424
ops::Deref,
25+
u32,
2526
};
2627
use crypto::blake2b;
2728
use derive_more::{From, Into};
2829
use num_derive::*;
29-
use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen, WrapperTypeEncode};
30+
use parity_scale_codec::{
31+
Compact, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen, WrapperTypeEncode,
32+
decode_vec_with_len,
33+
};
3034
use plutus_datum_derive::*;
3135
use scale_info::TypeInfo;
3236
use sp_core::{
@@ -1197,19 +1201,7 @@ impl CandidateKey {
11971201
}
11981202
}
11991203

1200-
#[derive(
1201-
Debug,
1202-
Clone,
1203-
PartialEq,
1204-
Eq,
1205-
Decode,
1206-
DecodeWithMemTracking,
1207-
Encode,
1208-
TypeInfo,
1209-
PartialOrd,
1210-
Ord,
1211-
Hash,
1212-
)]
1204+
#[derive(Debug, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash)]
12131205
#[cfg_attr(feature = "serde", derive(Serialize))]
12141206
/// Bytes of CandidateKeys that come from Cardano or other input.
12151207
pub struct CandidateKeys(pub Vec<CandidateKey>);
@@ -1239,6 +1231,57 @@ impl From<Vec<([u8; 4], Vec<u8>)>> for CandidateKeys {
12391231
}
12401232
}
12411233

1234+
/// Backward compatible [Encode] for [CandidateKeys].
1235+
/// After node update InherentData would be encoded with this version but runtime would still know [PermissionedCandidateData] and [CandidateRegistration]
1236+
/// that have [AuraPublicKey] and [GrandpaPublicKey] fields instead of [CandidateKeys] field.
1237+
/// To support existing chains, when keys are AURA and Grandpa, [CandidateKeys] is encoded exactly like in previous version.
1238+
/// If other key set is used in the place where AURA vector lenght is encoded, Compact(u32::MAX) is encoded to enable discrimination between the types that
1239+
/// has to be decoded.
1240+
/// The represtation is either:
1241+
/// Legacy: AuraKeySize, AuraKeyBytes, GrandpaKeySize, GrandpaKeyBytes
1242+
/// Generic: u32::MAX, CandidateKeysVector
1243+
/// AuraKeySize cannot be u32::MAX (won't fit on Cardano), so when it is encountered, we know that the generic representation is used.
1244+
impl Encode for CandidateKeys {
1245+
fn size_hint(&self) -> usize {
1246+
if self.has_only_aura_and_grandpa_keys() {
1247+
Encode::size_hint(&AuraPublicKey(self.find_or_empty(AURA)))
1248+
.saturating_add(Encode::size_hint(&GrandpaPublicKey(self.find_or_empty(GRANDPA))))
1249+
} else {
1250+
Encode::size_hint(&Compact(u32::MAX)).saturating_add(Encode::size_hint(&self.0))
1251+
}
1252+
}
1253+
1254+
fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, dest: &mut T) {
1255+
if self.has_only_aura_and_grandpa_keys() {
1256+
Encode::encode_to(&AuraPublicKey(self.find_or_empty(AURA)), dest);
1257+
Encode::encode_to(&GrandpaPublicKey(self.find_or_empty(GRANDPA)), dest)
1258+
} else {
1259+
// Compact(u32::MAX) is used to signal that a vector of CandidateKey should be decoded
1260+
// It has to be this type, be it is the item that AuraPublicKey::decode expects.
1261+
Encode::encode_to(&Compact(u32::MAX), dest);
1262+
Encode::encode_to(&self.0, dest)
1263+
}
1264+
}
1265+
}
1266+
1267+
/// Custom backward compatibile Decode. See comment on the Encode implementation.
1268+
impl Decode for CandidateKeys {
1269+
fn decode<I: parity_scale_codec::Input>(
1270+
input: &mut I,
1271+
) -> Result<Self, parity_scale_codec::Error> {
1272+
// See Encode instance
1273+
let marker_or_aura_size: u32 = <Compact<u32>>::decode(input)?.0;
1274+
if marker_or_aura_size == u32::MAX {
1275+
let keys = Vec::<CandidateKey>::decode(input)?;
1276+
Ok(Self(keys))
1277+
} else {
1278+
let aura_bytes: Vec<u8> = decode_vec_with_len(input, marker_or_aura_size as usize)?;
1279+
let grandpa = GrandpaPublicKey::decode(input)?;
1280+
Ok(Self(vec![AuraPublicKey(aura_bytes).into(), grandpa.into()]))
1281+
}
1282+
}
1283+
}
1284+
12421285
#[derive(
12431286
Debug,
12441287
Clone,
@@ -1378,6 +1421,7 @@ mod tests {
13781421
use super::*;
13791422
use core::str::FromStr;
13801423
use hex_literal::hex;
1424+
use parity_scale_codec::{Decode, Encode};
13811425

13821426
#[test]
13831427
fn main_chain_address_string_serialize_deserialize_round_trip() {
@@ -1449,6 +1493,46 @@ mod tests {
14491493

14501494
assert!(signature.verify(&pubkey, &signed_data).is_ok())
14511495
}
1496+
1497+
#[derive(Decode, Encode, PartialEq, Eq, Debug)]
1498+
struct TestCandidateKeys {
1499+
field_before: Option<u32>,
1500+
keys: CandidateKeys,
1501+
// ensures that Decode of CandidateKeys does not consume the whole input
1502+
field_after: Option<u32>,
1503+
}
1504+
1505+
#[test]
1506+
fn encode_decode_round_trip_for_generic_candidate_keys() {
1507+
let keys = TestCandidateKeys {
1508+
field_before: Some(42),
1509+
keys: CandidateKeys(vec![
1510+
CandidateKey { id: *b"abcd", bytes: [7u8; 32].to_vec() },
1511+
CandidateKey { id: *b"efgh", bytes: [9u8; 32].to_vec() },
1512+
]),
1513+
field_after: Some(15),
1514+
};
1515+
let bytes: Vec<u8> = Encode::encode(&keys);
1516+
let mut bytes: &[u8] = &bytes;
1517+
let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1518+
assert_eq!(keys, decoded)
1519+
}
1520+
1521+
#[test]
1522+
fn encode_decode_round_trip_for_aura_and_grandpa_candidate_keys() {
1523+
let keys = TestCandidateKeys {
1524+
field_before: Some(42),
1525+
keys: CandidateKeys(vec![
1526+
AuraPublicKey([7u8; 32].to_vec()).into(),
1527+
GrandpaPublicKey([9u8; 32].to_vec()).into(),
1528+
]),
1529+
field_after: Some(15),
1530+
};
1531+
let bytes: Vec<u8> = Encode::encode(&keys);
1532+
let mut bytes: &[u8] = &bytes;
1533+
let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1534+
assert_eq!(keys, decoded)
1535+
}
14521536
}
14531537

14541538
#[derive(

0 commit comments

Comments
 (0)