Skip to content

Commit 38b78ec

Browse files
valentinewallaceTheBlueMatt
authored andcommitted
Add test coverage for cc78b77
cc78b77 fixed an important downgrade bug, but neglected to add test coverage. Here we recitfy that by adding a few simple tests of common cases. Tests heavily tweaked by Matt Corallo <git@bluematt.me>.
1 parent eaf76f6 commit 38b78ec

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)