Skip to content

Commit 322e1f3

Browse files
Merge pull request #2987 from TheBlueMatt/2024-04-test-ser-bug
Add test coverage for cc78b77
2 parents bc4a5ea + 38b78ec commit 322e1f3

File tree

1 file changed

+126
-1
lines changed

1 file changed

+126
-1
lines changed

lightning/src/util/ser_macros.rs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ mod tests {
11481148

11491149
use crate::io::{self, Cursor};
11501150
use crate::ln::msgs::DecodeError;
1151-
use crate::util::ser::{Writeable, HighZeroBytesDroppedBigSize, VecWriter};
1151+
use crate::util::ser::{MaybeReadable, Readable, Writeable, HighZeroBytesDroppedBigSize, VecWriter};
11521152
use bitcoin::hashes::hex::FromHex;
11531153
use bitcoin::secp256k1::PublicKey;
11541154

@@ -1258,6 +1258,131 @@ mod tests {
12581258
} else { panic!(); }
12591259
}
12601260

1261+
/// A "V1" enum with only one variant
1262+
enum InnerEnumV1 {
1263+
StructVariantA {
1264+
field: u32,
1265+
},
1266+
}
1267+
1268+
impl_writeable_tlv_based_enum_upgradable!(InnerEnumV1,
1269+
(0, StructVariantA) => {
1270+
(0, field, required),
1271+
},
1272+
);
1273+
1274+
struct OuterStructOptionalEnumV1 {
1275+
inner_enum: Option<InnerEnumV1>,
1276+
other_field: u32,
1277+
}
1278+
1279+
impl_writeable_tlv_based!(OuterStructOptionalEnumV1, {
1280+
(0, inner_enum, upgradable_option),
1281+
(2, other_field, required),
1282+
});
1283+
1284+
/// An upgraded version of [`InnerEnumV1`] that added a second variant
1285+
enum InnerEnumV2 {
1286+
StructVariantA {
1287+
field: u32,
1288+
},
1289+
StructVariantB {
1290+
field2: u64,
1291+
}
1292+
}
1293+
1294+
impl_writeable_tlv_based_enum_upgradable!(InnerEnumV2,
1295+
(0, StructVariantA) => {
1296+
(0, field, required),
1297+
},
1298+
(1, StructVariantB) => {
1299+
(0, field2, required),
1300+
},
1301+
);
1302+
1303+
struct OuterStructOptionalEnumV2 {
1304+
inner_enum: Option<InnerEnumV2>,
1305+
other_field: u32,
1306+
}
1307+
1308+
impl_writeable_tlv_based!(OuterStructOptionalEnumV2, {
1309+
(0, inner_enum, upgradable_option),
1310+
(2, other_field, required),
1311+
});
1312+
1313+
#[test]
1314+
fn upgradable_enum_option() {
1315+
// Test downgrading from `OuterStructOptionalEnumV2` to `OuterStructOptionalEnumV1` and
1316+
// ensure we still read the `other_field` just fine.
1317+
let serialized_bytes = OuterStructOptionalEnumV2 {
1318+
inner_enum: Some(InnerEnumV2::StructVariantB { field2: 64 }),
1319+
other_field: 0x1bad1dea,
1320+
}.encode();
1321+
let mut s = Cursor::new(serialized_bytes);
1322+
1323+
let outer_struct: OuterStructOptionalEnumV1 = Readable::read(&mut s).unwrap();
1324+
assert!(outer_struct.inner_enum.is_none());
1325+
assert_eq!(outer_struct.other_field, 0x1bad1dea);
1326+
}
1327+
1328+
/// A struct that is read with an [`InnerEnumV1`] but is written with an [`InnerEnumV2`].
1329+
struct OuterStructRequiredEnum {
1330+
#[allow(unused)]
1331+
inner_enum: InnerEnumV1,
1332+
}
1333+
1334+
impl MaybeReadable for OuterStructRequiredEnum {
1335+
fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
1336+
let mut inner_enum = crate::util::ser::UpgradableRequired(None);
1337+
read_tlv_fields!(reader, {
1338+
(0, inner_enum, upgradable_required),
1339+
});
1340+
Ok(Some(Self {
1341+
inner_enum: inner_enum.0.unwrap(),
1342+
}))
1343+
}
1344+
}
1345+
1346+
impl Writeable for OuterStructRequiredEnum {
1347+
fn write<W: crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
1348+
write_tlv_fields!(writer, {
1349+
(0, InnerEnumV2::StructVariantB { field2: 0xdeadbeef }, required),
1350+
});
1351+
Ok(())
1352+
}
1353+
}
1354+
1355+
struct OuterOuterStruct {
1356+
outer_struct: Option<OuterStructRequiredEnum>,
1357+
other_field: u32,
1358+
}
1359+
1360+
impl_writeable_tlv_based!(OuterOuterStruct, {
1361+
(0, outer_struct, upgradable_option),
1362+
(2, other_field, required),
1363+
});
1364+
1365+
1366+
#[test]
1367+
fn upgradable_enum_required() {
1368+
// Test downgrading from an `OuterOuterStruct` (i.e. test downgrading an
1369+
// `upgradable_required` `InnerEnumV2` to an `InnerEnumV1`).
1370+
//
1371+
// Note that `OuterStructRequiredEnum` has a split write/read implementation that writes an
1372+
// `InnerEnumV2::StructVariantB` irrespective of the value of `inner_enum`.
1373+
1374+
let dummy_inner_enum = InnerEnumV1::StructVariantA { field: 42 };
1375+
let serialized_bytes = OuterOuterStruct {
1376+
outer_struct: Some(OuterStructRequiredEnum { inner_enum: dummy_inner_enum }),
1377+
other_field: 0x1bad1dea,
1378+
}.encode();
1379+
let mut s = Cursor::new(serialized_bytes);
1380+
1381+
let outer_outer_struct: OuterOuterStruct = Readable::read(&mut s).unwrap();
1382+
assert!(outer_outer_struct.outer_struct.is_none());
1383+
assert_eq!(outer_outer_struct.other_field, 0x1bad1dea);
1384+
}
1385+
12611386
// BOLT TLV test cases
12621387
fn tlv_reader_n1(s: &[u8]) -> Result<(Option<HighZeroBytesDroppedBigSize<u64>>, Option<u64>, Option<(PublicKey, u64, u64)>, Option<u16>), DecodeError> {
12631388
let mut s = Cursor::new(s);

0 commit comments

Comments
 (0)