@@ -1148,7 +1148,7 @@ mod tests {
1148
1148
1149
1149
use crate :: io:: { self , Cursor } ;
1150
1150
use crate :: ln:: msgs:: DecodeError ;
1151
- use crate :: util:: ser:: { Writeable , HighZeroBytesDroppedBigSize , VecWriter } ;
1151
+ use crate :: util:: ser:: { MaybeReadable , Readable , Writeable , HighZeroBytesDroppedBigSize , VecWriter } ;
1152
1152
use bitcoin:: hashes:: hex:: FromHex ;
1153
1153
use bitcoin:: secp256k1:: PublicKey ;
1154
1154
@@ -1258,6 +1258,131 @@ mod tests {
1258
1258
} else { panic ! ( ) ; }
1259
1259
}
1260
1260
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
+
1261
1386
// BOLT TLV test cases
1262
1387
fn tlv_reader_n1 ( s : & [ u8 ] ) -> Result < ( Option < HighZeroBytesDroppedBigSize < u64 > > , Option < u64 > , Option < ( PublicKey , u64 , u64 ) > , Option < u16 > ) , DecodeError > {
1263
1388
let mut s = Cursor :: new ( s) ;
0 commit comments