@@ -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