@@ -13,6 +13,7 @@ use ic_consensus_utils::{
1313 get_subnet_record,
1414 membership:: Membership ,
1515 pool_reader:: { PoolReader , UnexpectedChainLength } ,
16+ subnet_splitting,
1617} ;
1718use ic_interfaces:: {
1819 consensus:: PayloadBuilder , dkg:: DkgPool , idkg:: IDkgPool , time_source:: TimeSource ,
@@ -217,7 +218,16 @@ impl BlockMaker {
217218
218219 // The stable registry version to be agreed on in this block. If this is a summary
219220 // block, this version will be the new membership version of the next dkg interval.
220- let stable_registry_version = self . get_stable_registry_version ( parent. as_ref ( ) ) ?;
221+ let stable_registry_version = self . get_stable_registry_version (
222+ parent. as_ref ( ) ,
223+ last_summary_block. context . registry_version ,
224+ last_summary_block
225+ . payload
226+ . as_ref ( )
227+ . as_summary ( )
228+ . dkg
229+ . get_next_start_height ( ) ,
230+ ) ?;
221231 // Get the subnet records that are relevant to making a block
222232 let subnet_records =
223233 subnet_records_for_registry_version ( self , registry_version, stable_registry_version) ?;
@@ -521,20 +531,59 @@ impl BlockMaker {
521531 }
522532 }
523533
524- // Returns the registry version received from the NNS some specified amount of
525- // time ago. If the parent's context references higher version which is already
526- // available locally, we use that version.
527- pub ( crate ) fn get_stable_registry_version ( & self , parent : & Block ) -> Option < RegistryVersion > {
534+ /// Returns the registry version received from the NNS some specified amount of
535+ /// time ago. If the parent's context references higher version which is already
536+ /// available locally, we use that version.
537+ pub ( crate ) fn get_stable_registry_version (
538+ & self ,
539+ parent : & Block ,
540+ last_summary_block_registry_version : RegistryVersion ,
541+ next_summary_block_height : Height ,
542+ ) -> Option < RegistryVersion > {
528543 let parents_version = parent. context . registry_version ;
544+ let parents_height = parent. height ( ) ;
529545 let latest_version = self . registry_client . get_latest_version ( ) ;
530546 // Check if there is a stable version that we can bump up to.
531547 for v in ( parents_version. get ( ) ..=latest_version. get ( ) ) . rev ( ) {
532548 let version = RegistryVersion :: from ( v) ;
549+
550+ // Don't consider a registry version if it's too fresh.
533551 let version_timestamp = self . registry_client . get_version_timestamp ( version) ?;
534- if version_timestamp + self . stable_registry_version_age <= current_time ( ) {
535- return Some ( version) ;
552+ if version_timestamp + self . stable_registry_version_age > current_time ( ) {
553+ continue ;
554+ }
555+
556+ let subnet_splitting_status = subnet_splitting:: get_status (
557+ self . registry_client . as_ref ( ) ,
558+ self . replica_config . subnet_id ,
559+ last_summary_block_registry_version,
560+ version,
561+ )
562+ . inspect_err ( |err| {
563+ warn ! (
564+ self . log,
565+ "Failed to get subnet splitting status at registry version {version}: {err}"
566+ )
567+ } )
568+ . ok ( ) ?;
569+
570+ match subnet_splitting_status {
571+ subnet_splitting:: Status :: NotScheduled => return Some ( version) ,
572+ subnet_splitting:: Status :: Scheduled { scheduled_at, .. } => {
573+ info ! (
574+ every_n_seconds => 30 ,
575+ self . log,
576+ "Subnet splitting scheduled at registry version {scheduled_at} \
577+ and height {next_summary_block_height}. Freezing registry version."
578+ ) ;
579+
580+ if parents_height. increment ( ) == next_summary_block_height {
581+ return Some ( scheduled_at) ;
582+ }
583+ }
536584 }
537585 }
586+
538587 // If parent's version is locally available, return that.
539588 if parents_version <= latest_version {
540589 return Some ( parents_version) ;
@@ -669,6 +718,10 @@ mod tests {
669718 use ic_interfaces:: consensus_pool:: ConsensusPool ;
670719 use ic_logger:: replica_logger:: no_op_logger;
671720 use ic_metrics:: MetricsRegistry ;
721+ use ic_protobuf:: registry:: subnet:: v1:: {
722+ CatchUpPackageContents , SubnetSplittingArgs , catch_up_package_contents:: CupType ,
723+ } ;
724+ use ic_registry_keys:: make_catch_up_package_contents_key;
672725 use ic_test_utilities_consensus:: fake:: FromParent ;
673726 use ic_test_utilities_registry:: { SubnetRecordBuilder , add_subnet_record} ;
674727 use ic_test_utilities_types:: ids:: { node_test_id, subnet_test_id} ;
@@ -680,6 +733,8 @@ mod tests {
680733 signature:: ThresholdSignature ,
681734 * ,
682735 } ;
736+ use ic_types_test_utils:: ids:: NODE_1 ;
737+ use ic_types_test_utils:: ids:: { SUBNET_0 , SUBNET_1 } ;
683738 use rstest:: rstest;
684739 use std:: sync:: Arc ;
685740
@@ -1230,25 +1285,33 @@ mod tests {
12301285 block_maker. stable_registry_version_age =
12311286 current_time ( ) . saturating_duration_since ( v3_timestamp) ;
12321287 assert_eq ! (
1233- block_maker. get_stable_registry_version( & parent) . unwrap( ) ,
1288+ block_maker
1289+ . get_stable_registry_version( & parent, RegistryVersion :: new( 1 ) , Height :: new( 100 ) )
1290+ . unwrap( ) ,
12341291 RegistryVersion :: from( 3 )
12351292 ) ;
12361293 block_maker. stable_registry_version_age =
12371294 current_time ( ) . saturating_duration_since ( v2_timestamp) ;
12381295 assert_eq ! (
1239- block_maker. get_stable_registry_version( & parent) . unwrap( ) ,
1296+ block_maker
1297+ . get_stable_registry_version( & parent, RegistryVersion :: new( 1 ) , Height :: new( 100 ) )
1298+ . unwrap( ) ,
12401299 RegistryVersion :: from( 2 )
12411300 ) ;
12421301 block_maker. stable_registry_version_age =
12431302 current_time ( ) . saturating_duration_since ( v1_timestamp) ;
12441303 assert_eq ! (
1245- block_maker. get_stable_registry_version( & parent) . unwrap( ) ,
1304+ block_maker
1305+ . get_stable_registry_version( & parent, RegistryVersion :: new( 1 ) , Height :: new( 100 ) )
1306+ . unwrap( ) ,
12461307 RegistryVersion :: from( 1 )
12471308 ) ;
12481309 // Now let's test if parent's version is used
12491310 parent. context . registry_version = RegistryVersion :: from ( 2 ) ;
12501311 assert_eq ! (
1251- block_maker. get_stable_registry_version( & parent) . unwrap( ) ,
1312+ block_maker
1313+ . get_stable_registry_version( & parent, RegistryVersion :: new( 1 ) , Height :: new( 100 ) )
1314+ . unwrap( ) ,
12521315 RegistryVersion :: from( 2 )
12531316 ) ;
12541317 } )
@@ -1366,4 +1429,141 @@ mod tests {
13661429 )
13671430 } )
13681431 }
1432+
1433+ mod subnet_splitting {
1434+ use super :: * ;
1435+
1436+ const MAX_REGISTRY_VERSION : u64 = 4 ;
1437+
1438+ #[ derive( Debug ) ]
1439+ struct TestCase {
1440+ splitting_registry_version : Option < RegistryVersion > ,
1441+ last_summary_block_registry_version : RegistryVersion ,
1442+ next_summary_block_height : Height ,
1443+ parent_height : Height ,
1444+ expected_stable_registry_version : RegistryVersion ,
1445+ }
1446+
1447+ #[ rstest]
1448+ #[ case:: no_splitting( TestCase {
1449+ splitting_registry_version: None ,
1450+ last_summary_block_registry_version: RegistryVersion :: new( 1 ) ,
1451+ next_summary_block_height: Height :: new( 4 ) ,
1452+ parent_height: Height :: new( 1 ) ,
1453+ expected_stable_registry_version: RegistryVersion :: new( MAX_REGISTRY_VERSION ) ,
1454+ } ) ]
1455+ #[ case:: version_frozen_before_splitting( TestCase {
1456+ splitting_registry_version: Some ( RegistryVersion :: new( MAX_REGISTRY_VERSION - 1 ) ) ,
1457+ last_summary_block_registry_version: RegistryVersion :: new( 1 ) ,
1458+ next_summary_block_height: Height :: new( 4 ) ,
1459+ parent_height: Height :: new( 1 ) ,
1460+ expected_stable_registry_version: RegistryVersion :: new( MAX_REGISTRY_VERSION - 2 ) ,
1461+ } ) ]
1462+ #[ case:: version_frozen_before_splitting( TestCase {
1463+ splitting_registry_version: Some ( RegistryVersion :: new( MAX_REGISTRY_VERSION - 2 ) ) ,
1464+ last_summary_block_registry_version: RegistryVersion :: new( 1 ) ,
1465+ next_summary_block_height: Height :: new( 4 ) ,
1466+ parent_height: Height :: new( 1 ) ,
1467+ expected_stable_registry_version: RegistryVersion :: new( MAX_REGISTRY_VERSION - 3 ) ,
1468+ } ) ]
1469+ #[ case:: exact_version_during_splitting( TestCase {
1470+ splitting_registry_version: Some ( RegistryVersion :: new( MAX_REGISTRY_VERSION - 1 ) ) ,
1471+ last_summary_block_registry_version: RegistryVersion :: new( 1 ) ,
1472+ next_summary_block_height: Height :: new( 4 ) ,
1473+ parent_height: Height :: new( 3 ) ,
1474+ expected_stable_registry_version: RegistryVersion :: new( MAX_REGISTRY_VERSION - 1 ) ,
1475+ } ) ]
1476+ fn test_stable_registry_version_with_subnet_splitting ( #[ case] test_case : TestCase ) {
1477+ const SOURCE_SUBNET_ID : SubnetId = SUBNET_0 ;
1478+ const DESTINATION_SUBNET_ID : SubnetId = SUBNET_1 ;
1479+ ic_test_utilities:: artifact_pool_config:: with_test_pool_config ( |pool_config| {
1480+ let record = SubnetRecordBuilder :: from ( & [ NODE_1 ] )
1481+ . with_dkg_interval_length ( 4 )
1482+ . build ( ) ;
1483+ let Dependencies {
1484+ registry,
1485+ crypto,
1486+ pool,
1487+ time_source,
1488+ replica_config,
1489+ state_manager,
1490+ registry_data_provider,
1491+ dkg_pool,
1492+ idkg_pool,
1493+ ..
1494+ } = dependencies_with_subnet_params (
1495+ pool_config,
1496+ SOURCE_SUBNET_ID ,
1497+ vec ! [ ( 1 , record. clone( ) ) ] ,
1498+ ) ;
1499+
1500+ let mut payload_builder = MockPayloadBuilder :: new ( ) ;
1501+ payload_builder
1502+ . expect_get_payload ( )
1503+ . return_const ( BatchPayload :: default ( ) ) ;
1504+ let membership = Arc :: new ( Membership :: new (
1505+ pool. get_cache ( ) ,
1506+ registry. clone ( ) ,
1507+ replica_config. subnet_id ,
1508+ ) ) ;
1509+
1510+ let block_maker = BlockMaker :: new (
1511+ Arc :: clone ( & time_source) as Arc < _ > ,
1512+ replica_config,
1513+ Arc :: clone ( & registry) as Arc < dyn RegistryClient > ,
1514+ membership,
1515+ crypto,
1516+ Arc :: new ( payload_builder) ,
1517+ dkg_pool,
1518+ idkg_pool,
1519+ state_manager,
1520+ Duration :: from_millis ( 0 ) ,
1521+ MetricsRegistry :: new ( ) ,
1522+ no_op_logger ( ) ,
1523+ ) ;
1524+
1525+ for version in 2 ..=MAX_REGISTRY_VERSION {
1526+ add_subnet_record (
1527+ & registry_data_provider,
1528+ version,
1529+ SOURCE_SUBNET_ID ,
1530+ record. clone ( ) ,
1531+ ) ;
1532+ }
1533+ if let Some ( splitting_registry_version) = test_case. splitting_registry_version {
1534+ registry_data_provider
1535+ . add (
1536+ & make_catch_up_package_contents_key ( SOURCE_SUBNET_ID ) ,
1537+ splitting_registry_version,
1538+ Some ( CatchUpPackageContents {
1539+ cup_type : Some ( CupType :: SubnetSplitting ( SubnetSplittingArgs {
1540+ destination_subnet_id : Some ( subnet_id_into_protobuf (
1541+ DESTINATION_SUBNET_ID ,
1542+ ) ) ,
1543+ } ) ) ,
1544+ ..Default :: default ( )
1545+ } ) ,
1546+ )
1547+ . unwrap ( ) ;
1548+ }
1549+
1550+ registry. update_to_latest_version ( ) ;
1551+ let mut parent = pool. get_cache ( ) . finalized_block ( ) ;
1552+ parent. height = test_case. parent_height ;
1553+ parent. context . registry_version = RegistryVersion :: from ( 1 ) ;
1554+
1555+ std:: thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
1556+ assert_eq ! (
1557+ block_maker
1558+ . get_stable_registry_version(
1559+ & parent,
1560+ test_case. last_summary_block_registry_version,
1561+ test_case. next_summary_block_height,
1562+ )
1563+ . unwrap( ) ,
1564+ test_case. expected_stable_registry_version,
1565+ ) ;
1566+ } )
1567+ }
1568+ }
13691569}
0 commit comments