diff --git a/ic-os/components/guestos/ic-replica.service b/ic-os/components/guestos/ic-replica.service index 7657e20e4e4b..67d96dbb7eb9 100644 --- a/ic-os/components/guestos/ic-replica.service +++ b/ic-os/components/guestos/ic-replica.service @@ -20,7 +20,7 @@ User=ic-replica Environment=RUST_BACKTRACE=1 Environment=RUST_MIN_STACK=8192000 -ExecStart=/opt/ic/bin/orchestrator --replica-binary-dir /var/lib/ic/data/images --cup-dir /var/lib/ic/data/cups --replica-config-file /run/ic-node/config/ic.json5 --ic-boundary-env-file /opt/ic/share/ic-boundary.env --ic-gateway-env-file /opt/ic/share/ic-gateway.env --enable-provisional-registration --ic-binary-directory /opt/ic/bin --orchestrator-data-directory /var/lib/ic/data/orchestrator --version-file /opt/ic/share/version.txt +ExecStart=/opt/ic/bin/orchestrator --replica-binary-dir /var/lib/ic/data/images --cup-dir /var/lib/ic/data/cups --replica-config-file /run/ic-node/config/ic.json5 --ic-boundary-env-file /opt/ic/share/ic-boundary.env --ic-gateway-env-file /opt/ic/share/ic-gateway.env --enable-provisional-registration --ic-binary-directory /opt/ic/bin --orchestrator-data-directory /var/lib/ic/data/orchestrator --replica-version-file /opt/ic/share/binary_version.txt --guestos-version-file /opt/ic/share/version.txt LimitNOFILE=16777216 Restart=always RestartSec=10 diff --git a/ic-os/components/monitoring/guestos/custom-metrics.sh b/ic-os/components/monitoring/guestos/custom-metrics.sh index e3954bb751e8..a9d069153c7f 100644 --- a/ic-os/components/monitoring/guestos/custom-metrics.sh +++ b/ic-os/components/monitoring/guestos/custom-metrics.sh @@ -10,6 +10,7 @@ source /opt/ic/bin/config.sh MICROCODE_FILE="/sys/devices/system/cpu/cpu0/microcode/version" GUESTOS_VERSION_FILE="/opt/ic/share/version.txt" +BINARY_VERSION_FILE="/opt/ic/share/binary_version.txt" STATE_ROOT_PATH="/var/lib/ic" function update_guestos_version_metric() { @@ -28,6 +29,22 @@ function update_guestos_version_metric() { "gauge" } +function update_binary_version_metric() { + if [ -r ${BINARY_VERSION_FILE} ]; then + BINARY_VERSION=$(cat ${BINARY_VERSION_FILE}) + BINARY_VERSION_OK=1 + else + BINARY_VERSION="unknown" + BINARY_VERSION_OK=0 + fi + write_log "Binary version ${BINARY_VERSION}" + write_metric_attr "binary_version" \ + "{version=\"${BINARY_VERSION}\"}" \ + "${BINARY_VERSION_OK}" \ + "Replica binary version string" \ + "gauge" +} + function update_guestos_boot_action_metric() { write_metric_attr "guestos_boot_action" \ "{successful_boot=\"true\"}" \ @@ -77,6 +94,7 @@ function update_tee_metrics() { function main() { update_guestos_version_metric + update_binary_version_metric update_guestos_boot_action_metric update_config_version_metric update_tee_metrics diff --git a/ic-os/components/monitoring/metrics-proxy/guestos/metrics-proxy.yaml b/ic-os/components/monitoring/metrics-proxy/guestos/metrics-proxy.yaml index 6545d6bad48a..866b022f1702 100644 --- a/ic-os/components/monitoring/metrics-proxy/guestos/metrics-proxy.yaml +++ b/ic-os/components/monitoring/metrics-proxy/guestos/metrics-proxy.yaml @@ -44,6 +44,10 @@ proxies: - regex: guestos_version actions: - keep + # Replica binary version metric. + - regex: binary_version + actions: + - keep # Clock synchronization status. - regex: node_timex_sync_status actions: diff --git a/ic-os/defs.bzl b/ic-os/defs.bzl index 9b6761bc5577..e1d7d36cce00 100644 --- a/ic-os/defs.bzl +++ b/ic-os/defs.bzl @@ -80,6 +80,30 @@ def icos_build( tags = ["manual"], ) + # A separate copy of the version file installed as binary_version.txt in + # the rootfs, holding the replica binary version. During a fast upgrade, + # the overlay ships a new version and the file will be mounted over. + # version.txt above is not mounted over so it can be used to read the base + # GuestOS version. + copy_file( + name = "copy_binary_version_txt", + src = ic_version, + out = "binary_version.txt", + allow_symlink = True, + visibility = ["//visibility:public"], + tags = ["manual"], + ) + + if upgrades: + native.genrule( + name = "test_binary_version_txt", + srcs = [":copy_binary_version_txt"], + outs = ["binary_version-test.txt"], + cmd = "sed -e 's/.*/&-test/' < $< > $@", + visibility = ["//visibility:public"], + tags = ["manual"], + ) + # -------------------- Build grub partition -------------------- build_grub_partition("partition-grub.tzst", grub_config = image_deps.get("grub_config", default = None), tags = ["manual"]) @@ -214,6 +238,7 @@ tar --create --file "$@" --numeric-owner -C "$$tmpdir/bootfs" . partition_root_hash = partition_root + "-hash" partition_boot_tzst = "partition-boot" + test_suffix + ".tzst" version_txt = "version" + test_suffix + ".txt" + binary_version_txt = "binary_version" + test_suffix + ".txt" boot_args = "boot" + test_suffix + "_args" launch_measurements = "launch-measurements" + test_suffix + ".json" @@ -226,7 +251,10 @@ tar --create --file "$@" --numeric-owner -C "$$tmpdir/bootfs" . strip_paths = PARTITION_ROOT_STRIP_PATHS, extra_files = { k: v - for k, v in (image_deps["rootfs"].items() + [(version_txt, "/opt/ic/share/version.txt:0644")]) + for k, v in (image_deps["rootfs"].items() + [ + (version_txt, "/opt/ic/share/version.txt:0644"), + (binary_version_txt, "/opt/ic/share/binary_version.txt:0644"), + ]) }, target_compatible_with = ["@platforms//os:linux"], tags = ["manual", "no-cache"], diff --git a/rs/boundary_node/ic_boundary/src/http/handlers.rs b/rs/boundary_node/ic_boundary/src/http/handlers.rs index fe04a1199ee4..4ea6d9db4fb1 100644 --- a/rs/boundary_node/ic_boundary/src/http/handlers.rs +++ b/rs/boundary_node/ic_boundary/src/http/handlers.rs @@ -206,6 +206,7 @@ pub async fn status( let status = HttpStatusResponse { root_key: rk.root_key().map(|x| x.into()), impl_version: None, + guestos_version: None, impl_hash: None, replica_health_status: Some(health), certified_height: None, diff --git a/rs/consensus/dkg/src/dkg_key_manager.rs b/rs/consensus/dkg/src/dkg_key_manager.rs index 662ddf9887cd..3946b4bf0bae 100644 --- a/rs/consensus/dkg/src/dkg_key_manager.rs +++ b/rs/consensus/dkg/src/dkg_key_manager.rs @@ -665,7 +665,7 @@ mod tests { use ic_registry_keys::make_catch_up_package_contents_key; use ic_test_utilities_logger::with_test_replica_logger; use ic_test_utilities_registry::SubnetRecordBuilder; - use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_replica_version}; + use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_platform_version}; use ic_types::{ NodeId, RegistryVersion, SubnetId, consensus::{ @@ -856,7 +856,7 @@ mod tests { node_id: local_node_id, // The local node always starts in the source subnet. subnet_id: source_subnet_id, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); diff --git a/rs/consensus/dkg/src/lib.rs b/rs/consensus/dkg/src/lib.rs index 0b66c460480b..7ae278d4fb85 100644 --- a/rs/consensus/dkg/src/lib.rs +++ b/rs/consensus/dkg/src/lib.rs @@ -88,14 +88,12 @@ impl DkgImpl { logger: ReplicaLogger, ) -> Self { let ReplicaConfig { - node_id, - subnet_id, - replica_version, + node_id, subnet_id, .. } = replica_config; Self { node_id, subnet_id, - replica_version, + replica_version: replica_config.replica_version().clone(), registry_client, state_reader, crypto, @@ -450,7 +448,7 @@ mod tests { use ic_test_utilities_logger::with_test_replica_logger; use ic_test_utilities_registry::{SubnetRecordBuilder, add_subnet_record}; use ic_test_utilities_state::get_initial_state; - use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_replica_version}; + use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_platform_version}; use ic_types::{ RegistryVersion, ReplicaVersion, batch::ValidationContext, @@ -835,7 +833,7 @@ mod tests { // Node Id = 1, who is a dealer node_id: node_test_id(1), subnet_id: subnet_test_id(0), - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .without_state_manager_expectations() .build(); @@ -1113,7 +1111,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id: node_test_id(1), subnet_id: subnet_test_id(0), - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); let Dependencies { @@ -1127,7 +1125,7 @@ mod tests { // This is not a dealer! node_id: node_test_id(0), subnet_id: subnet_test_id(0), - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); for state_manager in [&state_manager_1, &state_manager_2] { @@ -1575,7 +1573,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id: node_test_id(1), subnet_id: subnet_test_id(0), - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .without_state_manager_expectations() .build(); @@ -1584,7 +1582,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id: node_test_id(2), subnet_id: subnet_test_id(0), - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .without_state_manager_expectations() .build(); @@ -2167,7 +2165,7 @@ mod tests { // Node 2 is a non-dealer receiver node_id: node_test_id(2), subnet_id: subnet_test_id(0), - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .with_dkg_interval_length(dkg_interval_length) .without_state_manager_expectations() diff --git a/rs/consensus/dkg/src/payload_validator.rs b/rs/consensus/dkg/src/payload_validator.rs index e973f9d7bd6d..05f40fdc7b7c 100644 --- a/rs/consensus/dkg/src/payload_validator.rs +++ b/rs/consensus/dkg/src/payload_validator.rs @@ -274,7 +274,7 @@ mod tests { use ic_test_utilities_state::get_initial_state; use ic_test_utilities_types::ids::{ NODE_1, NODE_2, NODE_3, SUBNET_1, SUBNET_2, node_test_id, subnet_test_id, - test_replica_version, + test_platform_version, test_replica_version, }; use ic_types::{ Height, NodeId, RegistryVersion, @@ -809,7 +809,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id, subnet_id, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); state_manager diff --git a/rs/consensus/mocks/src/lib.rs b/rs/consensus/mocks/src/lib.rs index a62512700cd6..58194d60eb74 100644 --- a/rs/consensus/mocks/src/lib.rs +++ b/rs/consensus/mocks/src/lib.rs @@ -26,7 +26,7 @@ use ic_test_utilities_registry::{ use ic_test_utilities_time::FastForwardTimeSource; use ic_test_utilities_types::ids::{node_test_id, subnet_test_id}; use ic_types::{ - Height, RegistryVersion, ReplicaVersion, SubnetId, Time, + Height, PlatformVersion, RegistryVersion, ReplicaVersion, SubnetId, Time, batch::{BatchPayload, ValidationContext}, consensus::{Payload, block_maker::SubnetRecords}, replica_config::ReplicaConfig, @@ -174,13 +174,17 @@ impl DependenciesBuilder { // order when inserting them into the registry. subnet_records.sort_by_key(|(version, _, _)| *version); + let replica_version = ReplicaVersion::from_str(&subnet_records[0].2.replica_version_id) + .expect("Invalid replica_version_id"); Self { pool_config, replica_config: ReplicaConfig { node_id: node_test_id(0), subnet_id: subnet_records[0].1, - replica_version: ReplicaVersion::from_str(&subnet_records[0].2.replica_version_id) - .expect("Invalid replica_version_id"), + platform_version: PlatformVersion { + guestos_version: replica_version.clone(), + replica_version, + }, }, sorted_subnet_records: subnet_records, with_state_manager_expectations: true, @@ -283,7 +287,7 @@ impl DependenciesBuilder { let pool = TestConsensusPool::new( self.replica_config.node_id, self.replica_config.subnet_id, - self.replica_config.replica_version.clone(), + self.replica_config.replica_version().clone(), self.pool_config, time_source.clone(), registry.clone(), diff --git a/rs/consensus/src/consensus/batch_delivery.rs b/rs/consensus/src/consensus/batch_delivery.rs index d4259c549313..aec6ca1dc992 100644 --- a/rs/consensus/src/consensus/batch_delivery.rs +++ b/rs/consensus/src/consensus/batch_delivery.rs @@ -644,7 +644,9 @@ mod tests { use ic_management_canister_types_private::{SetupInitialDKGResponse, VetKdCurve, VetKdKeyId}; use ic_test_utilities::message_routing::FakeMessageRouting; use ic_test_utilities_registry::SubnetRecordBuilder; - use ic_test_utilities_types::ids::{subnet_test_id, test_replica_version}; + use ic_test_utilities_types::ids::{ + subnet_test_id, test_platform_version, test_replica_version, + }; use ic_types::{ PrincipalId, RegistryVersion, SubnetId, batch::{BatchPayload, ValidationContext}, @@ -901,7 +903,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); diff --git a/rs/consensus/src/consensus/block_maker.rs b/rs/consensus/src/consensus/block_maker.rs index 2a701041c101..44fd2d87424c 100644 --- a/rs/consensus/src/consensus/block_maker.rs +++ b/rs/consensus/src/consensus/block_maker.rs @@ -379,7 +379,7 @@ impl BlockMaker { self.registry_client.as_ref(), self.replica_config.subnet_id, pool, - &self.replica_config.replica_version, + self.replica_config.replica_version(), &self.log, )? { // Don't propose any block if the replica is halted. @@ -445,7 +445,7 @@ impl BlockMaker { height, rank, context, - self.replica_config.replica_version.clone(), + self.replica_config.replica_version().clone(), ); let hashed_block = hashed::Hashed::new(ic_types::crypto::crypto_hash, block); let metadata = BlockMetadata::from_block(&hashed_block, self.replica_config.subnet_id); @@ -744,7 +744,9 @@ mod tests { use ic_registry_keys::make_catch_up_package_contents_key; use ic_test_utilities_consensus::fake::FromParent; use ic_test_utilities_registry::{SubnetRecordBuilder, add_subnet_record}; - use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_replica_version}; + use ic_test_utilities_types::ids::{ + node_test_id, subnet_test_id, test_platform_version, test_replica_version, + }; use ic_types::{ consensus::{ CatchUpContent, CatchUpPackage, HasHeight, HasVersion, HashedRandomBeacon, dkg, @@ -799,7 +801,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id: node_test_id(1), subnet_id, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); @@ -869,7 +871,7 @@ mod tests { next_height, Rank(4), expected_context.clone(), - replica_config.replica_version.clone(), + replica_config.replica_version().clone(), ); payload_builder @@ -892,7 +894,7 @@ mod tests { }) .unwrap(), subnet_id: replica_config.subnet_id, - replica_version: replica_config.replica_version, + platform_version: replica_config.platform_version, }; let block_maker = BlockMaker::new( @@ -1019,7 +1021,7 @@ mod tests { }) .unwrap(), subnet_id: replica_config.subnet_id, - replica_version: replica_config.replica_version, + platform_version: replica_config.platform_version, }; let block_maker = BlockMaker::new( @@ -1223,7 +1225,7 @@ mod tests { let proposal = proposal.unwrap(); let block = proposal.content.as_ref(); // The block still uses the old version, not the new version. - assert_eq!(block.version(), &replica_config.replica_version); + assert_eq!(block.version(), replica_config.replica_version()); // registry version 10 becomes effective. assert_eq!( PoolReader::new(&pool).registry_version(proposal.height()), diff --git a/rs/consensus/src/consensus/catchup_package_maker.rs b/rs/consensus/src/consensus/catchup_package_maker.rs index 8ffa4c1ef355..23e552cf14c9 100644 --- a/rs/consensus/src/consensus/catchup_package_maker.rs +++ b/rs/consensus/src/consensus/catchup_package_maker.rs @@ -197,7 +197,7 @@ impl CatchUpPackageMaker { self.membership.registry_client.as_ref(), self.membership.subnet_id, pool, - &self.replica_config.replica_version, + self.replica_config.replica_version(), &self.log, ) == Some(true) }; @@ -516,7 +516,7 @@ mod tests { }; use ic_test_utilities_logger::with_test_replica_logger; use ic_test_utilities_registry::{SubnetRecordBuilder, insert_initial_dkg_transcript}; - use ic_test_utilities_types::ids::{subnet_test_id, test_replica_version}; + use ic_test_utilities_types::ids::{subnet_test_id, test_platform_version}; use ic_types::{ CryptoHashOfState, Height, NodeId, RegistryVersion, consensus::{ @@ -1100,7 +1100,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); // Manually insert DKG transcripts at the splitting version to simulate what the @@ -1311,7 +1311,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id: NODE_5, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); diff --git a/rs/consensus/src/consensus/finalizer.rs b/rs/consensus/src/consensus/finalizer.rs index e52992a89b82..a76adc827c3d 100644 --- a/rs/consensus/src/consensus/finalizer.rs +++ b/rs/consensus/src/consensus/finalizer.rs @@ -235,7 +235,7 @@ impl Finalizer { self.pick_block_to_finality_sign(pool, height)? .get_hash() .clone(), - self.replica_config.replica_version.clone(), + self.replica_config.replica_version().clone(), ); let signature = self .crypto diff --git a/rs/consensus/src/consensus/malicious_consensus.rs b/rs/consensus/src/consensus/malicious_consensus.rs index 71543a9cfaa5..a9c7f2ab8ea1 100644 --- a/rs/consensus/src/consensus/malicious_consensus.rs +++ b/rs/consensus/src/consensus/malicious_consensus.rs @@ -281,7 +281,7 @@ impl ConsensusImpl { let content = FinalizationContent::new( block.height, ic_types::crypto::crypto_hash(block), - self.replica_config.replica_version.clone(), + self.replica_config.replica_version().clone(), ); let signature = self .finalizer diff --git a/rs/consensus/src/consensus/notary.rs b/rs/consensus/src/consensus/notary.rs index 25f586d26c68..29e58a0d2db6 100644 --- a/rs/consensus/src/consensus/notary.rs +++ b/rs/consensus/src/consensus/notary.rs @@ -133,7 +133,7 @@ impl Notary { &self.log, height, rank, - &self.replica_config.replica_version, + self.replica_config.replica_version(), )?; let now_relative = self.time_source.get_relative_time(); @@ -188,7 +188,7 @@ impl Notary { let content = NotarizationContent::new( block.height(), block.get_hash().clone(), - self.replica_config.replica_version.clone(), + self.replica_config.replica_version().clone(), ); match self .crypto @@ -421,7 +421,7 @@ mod tests { } = DependenciesBuilder::new(pool_config, 1) .with_dkg_interval_length(dkg_interval_length) .build(); - let replica_version = replica_config.replica_version.clone(); + let replica_version = replica_config.replica_version().clone(); state_manager .get_mut() .expect_latest_certified_height() @@ -618,7 +618,7 @@ mod tests { } = DependenciesBuilder::new(pool_config, 1) .with_dkg_interval_length(dkg_interval_length) .build(); - let replica_version = replica_config.replica_version.clone(); + let replica_version = replica_config.replica_version().clone(); state_manager .get_mut() .expect_latest_certified_height() @@ -729,7 +729,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ), NotaryDelay::ReachedMaxNotarizationCertificationGap { .. } @@ -755,7 +755,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ), NotaryDelay::CanNotarizeAfter(Duration::from_secs(0)) @@ -783,7 +783,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ), NotaryDelay::ReachedMaxNotarizationCUPGap { .. } @@ -825,7 +825,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ); assert_eq!( @@ -841,7 +841,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ); assert_eq!( @@ -857,7 +857,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ); assert_eq!( @@ -874,7 +874,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ); assert_eq!( @@ -893,7 +893,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ); assert_eq!( @@ -963,7 +963,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ); assert_eq!( @@ -985,7 +985,7 @@ mod tests { state_manager.as_ref(), membership.as_ref(), Rank(0), - &replica_config.replica_version, + replica_config.replica_version(), &logger, ); assert_eq!( diff --git a/rs/consensus/src/consensus/random_beacon_maker.rs b/rs/consensus/src/consensus/random_beacon_maker.rs index 5e5fbdc51e82..f26dba511adb 100644 --- a/rs/consensus/src/consensus/random_beacon_maker.rs +++ b/rs/consensus/src/consensus/random_beacon_maker.rs @@ -74,7 +74,7 @@ impl RandomBeaconMaker { let content = RandomBeaconContent::new( next_height, ic_types::crypto::crypto_hash(&beacon), - self.replica_config.replica_version.clone(), + self.replica_config.replica_version().clone(), ); // One might wonder whether it is appropriate to use the // dkg_id from the start_block at h to generate the diff --git a/rs/consensus/src/consensus/random_tape_maker.rs b/rs/consensus/src/consensus/random_tape_maker.rs index ed1a526d3e02..34a0b2e55753 100644 --- a/rs/consensus/src/consensus/random_tape_maker.rs +++ b/rs/consensus/src/consensus/random_tape_maker.rs @@ -130,7 +130,7 @@ impl RandomTapeMaker { height: Height, pool: &PoolReader<'_>, ) -> Option { - let content = RandomTapeContent::new(height, self.replica_config.replica_version.clone()); + let content = RandomTapeContent::new(height, self.replica_config.replica_version().clone()); if let Some(dkg_id) = active_low_threshold_nidkg_id(pool.as_cache(), height) { match self diff --git a/rs/consensus/src/consensus/share_aggregator.rs b/rs/consensus/src/consensus/share_aggregator.rs index 161a9fba136c..2e3e95440d66 100644 --- a/rs/consensus/src/consensus/share_aggregator.rs +++ b/rs/consensus/src/consensus/share_aggregator.rs @@ -271,7 +271,7 @@ mod tests { use ic_test_utilities_consensus::fake::{FakeContentSigner, FakeSigner}; use ic_test_utilities_logger::with_test_replica_logger; use ic_test_utilities_registry::{SubnetRecordBuilder, insert_initial_dkg_transcript}; - use ic_test_utilities_types::ids::{node_test_id, test_replica_version}; + use ic_test_utilities_types::ids::{node_test_id, test_platform_version}; use ic_types::{ CryptoHashOfState, NodeId, RegistryVersion, SubnetId, consensus::{ @@ -540,7 +540,7 @@ mod tests { .with_replica_config(ReplicaConfig { node_id: NODE_1, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); // Manually insert DKG transcripts at the splitting version to simulate what the @@ -591,7 +591,7 @@ mod tests { ReplicaConfig { node_id, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }, membership.clone(), crypto.clone(), diff --git a/rs/consensus/src/consensus/validator.rs b/rs/consensus/src/consensus/validator.rs index 00a5828f9b51..27407fa01355 100644 --- a/rs/consensus/src/consensus/validator.rs +++ b/rs/consensus/src/consensus/validator.rs @@ -867,7 +867,7 @@ impl Validator { artifact: &S, ) -> ValidationResult { let version = artifact.version(); - let expected_version = &self.replica_config.replica_version; + let expected_version = self.replica_config.replica_version(); if version != expected_version { return Err(InvalidArtifactReason::ReplicaVersionMismatch.into()); } @@ -988,7 +988,7 @@ impl Validator { T: NotaryIssued + HasVersion, { let version = notary_issued.content.version(); - let expected_version = &self.replica_config.replica_version; + let expected_version = self.replica_config.replica_version(); if version != expected_version { return Some(ChangeAction::RemoveFromUnvalidated( notary_issued.into_message(), @@ -1298,7 +1298,7 @@ impl Validator { self.registry_client.as_ref(), self.replica_config.subnet_id, pool_reader, - &self.replica_config.replica_version, + self.replica_config.replica_version(), &self.log, ) else { return Err(ValidationFailure::FailedToGetConsensusStatus.into()); @@ -2204,7 +2204,7 @@ pub mod test { }; use ic_test_utilities_time::FastForwardTimeSource; use ic_test_utilities_types::{ - ids::{node_test_id, subnet_test_id, test_replica_version}, + ids::{node_test_id, subnet_test_id, test_platform_version}, messages::SignedIngressBuilder, }; use ic_types::{ @@ -2823,7 +2823,7 @@ pub mod test { // validated let tape_1 = RandomTape::fake(RandomTapeContent::new( Height::from(1), - replica_config.replica_version.clone(), + replica_config.replica_version().clone(), )); pool.insert_validated(tape_1); @@ -2854,7 +2854,7 @@ pub mod test { // Insert random tape at height 4, check if it is ignored let content = - RandomTapeContent::new(Height::from(4), replica_config.replica_version.clone()); + RandomTapeContent::new(Height::from(4), replica_config.replica_version().clone()); let signature = ThresholdSignature::fake(); let tape_4 = RandomTape { content, signature }; pool.insert_unvalidated(tape_4.clone()); @@ -2877,7 +2877,8 @@ pub mod test { pool.apply(changeset); // Set expected batch height to height 4, check if tape_3 is ignored - let content = RandomTapeContent::new(Height::from(3), replica_config.replica_version); + let content = + RandomTapeContent::new(Height::from(3), replica_config.replica_version().clone()); let signature = ThresholdSignature::fake(); let tape_3 = RandomTape { content, signature }; pool.insert_unvalidated(tape_3); @@ -3117,7 +3118,7 @@ pub mod test { registry.as_ref(), replica_config.subnet_id, &PoolReader::new(&pool), - &replica_config.replica_version, + replica_config.replica_version(), &no_op_logger() ), Some(Status::Halting | Status::Halted) @@ -3710,7 +3711,7 @@ pub mod test { registry.as_ref(), replica_config.subnet_id, &PoolReader::new(&pool), - &replica_config.replica_version, + replica_config.replica_version(), &no_op_logger(), ), Some(Status::Halting | Status::Halted) @@ -4243,10 +4244,10 @@ pub mod test { certified_height: Height::from(42), time: ic_types::time::UNIX_EPOCH, }, - replica_config.replica_version.clone(), + replica_config.replica_version().clone(), ); let fake_beacon = RandomBeacon::fake(RandomBeaconContent { - version: replica_config.replica_version, + version: replica_config.replica_version().clone(), height: cup_height, parent: CryptoHashOf::from(CryptoHash(vec![])), }); @@ -4573,7 +4574,7 @@ pub mod test { let content = NotarizationContent::new( block.height(), ic_types::crypto::crypto_hash(block.as_ref()), - replica_config.replica_version, + replica_config.replica_version().clone(), ); let mut notarization = Notarization::fake(content); notarization.signature.signers = @@ -4814,7 +4815,7 @@ pub mod test { let mut notarization = Notarization::fake(NotarizationContent::new( block.height(), block.content.get_hash().clone(), - replica_config.replica_version, + replica_config.replica_version().clone(), )); notarization.signature.signers = vec![node_test_id(1), node_test_id(2), node_test_id(3)]; @@ -5270,7 +5271,7 @@ pub mod test { let content = NotarizationContent::new( block.height(), block.content.get_hash().clone(), - replica_config.replica_version.clone(), + replica_config.replica_version().clone(), ); let mut notarization = Notarization::fake(content); let random_beacon = PoolReader::new(&pool).get_random_beacon_tip(); @@ -5437,7 +5438,7 @@ pub mod test { .with_replica_config(ReplicaConfig { node_id: validator_node_id, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); // Manually insert DKG transcripts at the splitting version to simulate what the @@ -5467,7 +5468,7 @@ pub mod test { ReplicaConfig { node_id: cup_share_node_id, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }, membership, crypto, @@ -5696,7 +5697,7 @@ pub mod test { .with_replica_config(ReplicaConfig { node_id: validator_node_id, subnet_id: SOURCE_SUBNET_ID, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .build(); diff --git a/rs/consensus/tests/framework/test_runner.rs b/rs/consensus/tests/framework/test_runner.rs index 7b6971b6c5f5..6073123869b6 100644 --- a/rs/consensus/tests/framework/test_runner.rs +++ b/rs/consensus/tests/framework/test_runner.rs @@ -9,7 +9,7 @@ use ic_management_canister_types_private::MasterPublicKeyId; use ic_registry_client_fake::FakeRegistryClient; use ic_registry_proto_data_provider::ProtoRegistryDataProvider; use ic_test_utilities_time::FastForwardTimeSource; -use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_replica_version}; +use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_platform_version}; use ic_types::{Height, batch::BatchContent, crypto::CryptoHash, replica_config::ReplicaConfig}; use rand_chacha::{ChaChaRng, rand_core::SeedableRng}; use std::{cell::RefCell, rc::Rc, sync::Arc}; @@ -85,7 +85,7 @@ impl TestRunner { .map(|(index, _)| ReplicaConfig { node_id: node_test_id(index as u64), subnet_id, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }) .collect(); let node_ids: Vec<_> = replica_configs diff --git a/rs/consensus/tests/framework/types.rs b/rs/consensus/tests/framework/types.rs index 3ce2ce56061e..fd7785799dfe 100644 --- a/rs/consensus/tests/framework/types.rs +++ b/rs/consensus/tests/framework/types.rs @@ -207,7 +207,7 @@ impl ConsensusDependencies { let consensus_pool = Arc::new(RwLock::new(ConsensusPoolImpl::new( replica_config.node_id, replica_config.subnet_id, - &replica_config.replica_version, + replica_config.replica_version(), cup.into(), pool_config.clone(), metrics_registry.clone(), diff --git a/rs/consensus/tests/payload.rs b/rs/consensus/tests/payload.rs index c0b1528badaf..117063f0830c 100644 --- a/rs/consensus/tests/payload.rs +++ b/rs/consensus/tests/payload.rs @@ -31,7 +31,7 @@ use ic_test_utilities_types::{ messages::SignedIngressBuilder, }; use ic_types::{ - CryptoHashOfState, Height, batch::BatchContent, crypto::CryptoHash, + CryptoHashOfState, Height, PlatformVersion, batch::BatchContent, crypto::CryptoHash, malicious_flags::MaliciousFlags, replica_config::ReplicaConfig, }; use std::{ @@ -111,7 +111,10 @@ fn consensus_produces_expected_batches() { let replica_config = ReplicaConfig { node_id, subnet_id, - replica_version, + platform_version: PlatformVersion { + guestos_version: replica_version.clone(), + replica_version, + }, }; let fake_crypto = CryptoReturningOk::default(); let fake_crypto = Arc::new(fake_crypto); @@ -131,7 +134,7 @@ fn consensus_produces_expected_batches() { 1, SubnetRecordBuilder::from(&[node_id]) .with_dkg_interval_length(DKG_INTERVAL_LENGTH) - .with_replica_version(replica_config.replica_version.as_ref()) + .with_replica_version(replica_config.replica_version().as_ref()) .build(), )], ); @@ -153,7 +156,7 @@ fn consensus_produces_expected_batches() { let consensus_pool = Arc::new(RwLock::new(consensus_pool::ConsensusPoolImpl::new( node_id, subnet_id, - &replica_config.replica_version, + replica_config.replica_version(), make_genesis(summary).into(), pool_config.clone(), MetricsRegistry::new(), diff --git a/rs/determinism_test/src/setup.rs b/rs/determinism_test/src/setup.rs index c390809612d6..93fc49e2a8a9 100644 --- a/rs/determinism_test/src/setup.rs +++ b/rs/determinism_test/src/setup.rs @@ -22,7 +22,7 @@ use ic_test_utilities_consensus::fake::FakeVerifier; use ic_test_utilities_registry::{ SubnetRecordBuilder, add_subnet_record, insert_initial_dkg_transcript, }; -use ic_test_utilities_types::ids::{subnet_test_id, test_replica_version}; +use ic_test_utilities_types::ids::{subnet_test_id, test_platform_version}; use ic_types::{ CanisterId, NodeId, PrincipalId, RegistryVersion, SubnetId, malicious_flags::MaliciousFlags, replica_config::ReplicaConfig, @@ -101,7 +101,7 @@ pub(crate) fn setup() -> ( let replica_config = ReplicaConfig { node_id: NodeId::from(PrincipalId::new_node_test_id(27)), subnet_id, - replica_version: test_replica_version(), + platform_version: test_platform_version(), }; let metrics_registry = MetricsRegistry::new(); diff --git a/rs/http_endpoints/public/src/dashboard.rs b/rs/http_endpoints/public/src/dashboard.rs index 73ffac242242..ca92f6d2f546 100644 --- a/rs/http_endpoints/public/src/dashboard.rs +++ b/rs/http_endpoints/public/src/dashboard.rs @@ -14,7 +14,7 @@ use ic_config::http_handler::Config; use ic_interfaces_state_manager::StateReader; use ic_registry_subnet_type::SubnetType; use ic_replicated_state::ReplicatedState; -use ic_types::{Height, ReplicaVersion}; +use ic_types::{Height, PlatformVersion}; #[derive(Template)] #[template(path = "dashboard.html", escape = "html")] @@ -28,7 +28,7 @@ struct Dashboard<'a> { &'a ic_replicated_state::CanisterState, &'a ic_replicated_state::CanisterPriority, )>, - replica_version: ic_types::ReplicaVersion, + platform_version: PlatformVersion, } #[derive(Clone)] @@ -36,7 +36,7 @@ pub(crate) struct DashboardService { config: Config, subnet_type: SubnetType, state_reader: Arc>, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, } impl DashboardService { @@ -48,13 +48,13 @@ impl DashboardService { config: Config, subnet_type: SubnetType, state_reader: Arc>, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, ) -> Router { let state = DashboardService { config, subnet_type, state_reader, - replica_version, + platform_version, }; Router::new().route( DashboardService::route(), @@ -68,7 +68,7 @@ async fn dashboard( config, subnet_type, state_reader, - replica_version, + platform_version, }): State, ) -> impl IntoResponse { let labeled_state = @@ -99,7 +99,7 @@ async fn dashboard( height: labeled_state.height(), replicated_state: labeled_state.get_ref(), canisters: &canisters, - replica_version, + platform_version, }; match dashboard.render() { diff --git a/rs/http_endpoints/public/src/lib.rs b/rs/http_endpoints/public/src/lib.rs index d542bfbe112b..50f81a1d1d93 100644 --- a/rs/http_endpoints/public/src/lib.rs +++ b/rs/http_endpoints/public/src/lib.rs @@ -78,7 +78,7 @@ use ic_registry_subnet_type::SubnetType; use ic_replicated_state::ReplicatedState; use ic_tracing::ReloadHandles; use ic_types::{ - Height, NodeId, ReplicaVersion, SubnetId, + Height, NodeId, PlatformVersion, SubnetId, artifact::UnvalidatedArtifactMutation, malicious_flags::MaliciousFlags, messages::{MessageId, QueryResponseHash, ReplicaHealthStatus, SignedIngress}, @@ -265,7 +265,7 @@ pub fn start_server( ingress_verifier: Arc, node_id: NodeId, subnet_id: SubnetId, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, nns_subnet_id: SubnetId, log: ReplicaLogger, consensus_pool_cache: Arc, @@ -386,13 +386,13 @@ pub fn start_server( Arc::clone(®istry_client), Arc::clone(&health_status), state_reader.clone(), - replica_version.clone(), + platform_version.clone(), ); let dashboard_router = DashboardService::new_router( config.clone(), subnet_type, state_reader.clone(), - replica_version, + platform_version, ); let catchup_router = CatchUpPackageService::new_router(consensus_pool_cache.clone()); diff --git a/rs/http_endpoints/public/src/status.rs b/rs/http_endpoints/public/src/status.rs index 08ac72dccdd9..4943018c1633 100644 --- a/rs/http_endpoints/public/src/status.rs +++ b/rs/http_endpoints/public/src/status.rs @@ -9,7 +9,7 @@ use ic_interfaces_state_manager::StateReader; use ic_logger::{ReplicaLogger, warn}; use ic_replicated_state::ReplicatedState; use ic_types::{ - ReplicaVersion, SubnetId, + PlatformVersion, SubnetId, messages::{Blob, HttpStatusResponse, ReplicaHealthStatus}, replica_version::REPLICA_BINARY_HASH, }; @@ -22,7 +22,7 @@ pub(crate) struct StatusService { registry_client: Arc, replica_health_status: Arc>, state_reader: Arc>, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, } impl StatusService { @@ -38,7 +38,7 @@ impl StatusService { registry_client: Arc, replica_health_status: Arc>, state_reader: Arc>, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, ) -> Router { let state = Self { log, @@ -46,7 +46,7 @@ impl StatusService { registry_client, replica_health_status, state_reader, - replica_version, + platform_version, }; Router::new().route_service( StatusService::route(), @@ -78,7 +78,8 @@ pub(crate) async fn status(State(state): State) -> CborSubnet Settings & Parameters - + + + + + diff --git a/rs/http_endpoints/public/tests/common/mod.rs b/rs/http_endpoints/public/tests/common/mod.rs index 59a75925929a..fdeb127acc10 100644 --- a/rs/http_endpoints/public/tests/common/mod.rs +++ b/rs/http_endpoints/public/tests/common/mod.rs @@ -46,9 +46,9 @@ use ic_registry_subnet_type::SubnetType; use ic_replicated_state::{ CanisterQueues, NetworkTopology, RefundPool, ReplicatedState, SystemMetadata, }; -use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_replica_version}; +use ic_test_utilities_types::ids::{node_test_id, subnet_test_id, test_platform_version}; use ic_types::{ - CanisterId, CryptoHashOfPartialState, Height, PrincipalId, RegistryVersion, + CanisterId, CryptoHashOfPartialState, Height, PlatformVersion, PrincipalId, RegistryVersion, artifact::UnvalidatedArtifactMutation, batch::RawQueryStats, consensus::certification::{Certification, CertificationContent}, @@ -447,6 +447,7 @@ pub struct HttpEndpointBuilder { certified_height: Option, ingress_pool_throttler: Arc>, ingress_channel_capacity: usize, + platform_version: Option, } impl HttpEndpointBuilder { @@ -463,9 +464,15 @@ impl HttpEndpointBuilder { tls_config: Arc::new(MockTlsConfig::new()), certified_height: None, ingress_channel_capacity: MAX_P2P_IO_CHANNEL_SIZE, + platform_version: None, } } + pub fn with_platform_version(mut self, platform_version: PlatformVersion) -> Self { + self.platform_version = Some(platform_version); + self + } + pub fn with_state_manager( mut self, state_manager: impl StateReader + 'static, @@ -542,7 +549,7 @@ impl HttpEndpointBuilder { let (terminal_state_ingress_messages_tx, terminal_state_ingress_messages_rx) = channel(100); let node_id = node_test_id(1); - let replica_version = test_replica_version(); + let platform_version = self.platform_version.unwrap_or_else(test_platform_version); let sig_verifier = Arc::new(temp_crypto_component_with_fake_registry(node_test_id(0))); let crypto = Arc::new(CryptoReturningOk::default()); @@ -564,7 +571,7 @@ impl HttpEndpointBuilder { sig_verifier, node_id, subnet_id, - replica_version, + platform_version, nns_subnet_id, log, self.consensus_cache, diff --git a/rs/http_endpoints/public/tests/test.rs b/rs/http_endpoints/public/tests/test.rs index 57ec5487db34..23bd33bbc296 100644 --- a/rs/http_endpoints/public/tests/test.rs +++ b/rs/http_endpoints/public/tests/test.rs @@ -65,6 +65,7 @@ use ic_types::{ signature::ThresholdSignature, time::current_time, }; +use ic_types::{PlatformVersion, ReplicaVersion}; use prost::Message; use reqwest::header::CONTENT_TYPE; use rstest::rstest; @@ -102,6 +103,9 @@ fn test_healthy_behind() { ..Default::default() }; + let guestos_version = ReplicaVersion::try_from("guestos-version-under-test").unwrap(); + let replica_version = ReplicaVersion::try_from("replica-version-under-test").unwrap(); + // We use this atomic to make sure that the health transition is from healthy -> certified_state_behind let healthy = Arc::new(AtomicBool::new(false)); let healthy_c = healthy.clone(); @@ -136,6 +140,10 @@ fn test_healthy_behind() { HttpEndpointBuilder::new(rt.handle().clone(), config) .with_registry_client(mock_registry_client) .with_consensus_cache(mock_consensus_cache) + .with_platform_version(PlatformVersion { + guestos_version: guestos_version.clone(), + replica_version: replica_version.clone(), + }) .run(); rt.block_on(async { @@ -170,6 +178,15 @@ fn test_healthy_behind() { replica_health_status, &CBOR::Text("certified_state_behind".to_string()) ); + + assert_eq!( + replica_status.get(&CBOR::Text("impl_version".to_string())), + Some(&CBOR::Text(replica_version.to_string())) + ); + assert_eq!( + replica_status.get(&CBOR::Text("guestos_version".to_string())), + Some(&CBOR::Text(guestos_version.to_string())) + ); }) } diff --git a/rs/https_outcalls/consensus/src/pool_manager.rs b/rs/https_outcalls/consensus/src/pool_manager.rs index 742f4a24a379..7965f589023b 100644 --- a/rs/https_outcalls/consensus/src/pool_manager.rs +++ b/rs/https_outcalls/consensus/src/pool_manager.rs @@ -403,7 +403,7 @@ impl CanisterHttpPoolManagerImpl { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: response.content.is_reject(), - replica_version: self.replica_config.replica_version.clone(), + replica_version: self.replica_config.replica_version().clone(), }, payment_receipt, }; @@ -491,7 +491,7 @@ impl CanisterHttpPoolManagerImpl { let share = &artifact.share; // Reject shares from different replica versions - if share.content.replica_version() != &self.replica_config.replica_version { + if share.content.replica_version() != self.replica_config.replica_version() { self.metrics .observe_pool_manager_event("share_dropped_unknown_version"); return Some(CanisterHttpChangeAction::RemoveUnvalidated(share.clone())); @@ -864,7 +864,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![])), content_size: 0, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -960,7 +960,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![])), content_size: 0, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1180,7 +1180,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![])), content_size: 0, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1308,7 +1308,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1497,7 +1497,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1594,7 +1594,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1730,7 +1730,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1794,7 +1794,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1896,7 +1896,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: true, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -1984,7 +1984,7 @@ pub mod test { content_hash: dishonest_hash, content_size: dishonest_response.content.count_bytes() as u32, is_reject: true, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -2088,7 +2088,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![0xAB; 32])), content_size: limit as u32 + 1, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -2312,7 +2312,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: true, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -2389,7 +2389,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![])), content_size: 0, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -2796,7 +2796,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![])), content_size: 0, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -2962,7 +2962,7 @@ pub mod test { content_hash: crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -3060,7 +3060,7 @@ pub mod test { content_hash: crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -3322,7 +3322,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -3384,7 +3384,7 @@ pub mod test { content_hash: ic_types::crypto::crypto_hash(&response), content_size: response.content.count_bytes() as u32, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt::default(), }; @@ -3559,7 +3559,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![])), content_size: 0, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt { spent: Cycles::new(200), @@ -3666,7 +3666,7 @@ pub mod test { content_hash: CryptoHashOf::new(CryptoHash(vec![])), content_size: 0, is_reject: false, - replica_version: replica_config.replica_version.clone(), + replica_version: replica_config.replica_version().clone(), }, payment_receipt: CanisterHttpPaymentReceipt { spent: Cycles::new(200), diff --git a/rs/orchestrator/src/args.rs b/rs/orchestrator/src/args.rs index 43e35f980dc5..f5aaab8f2490 100644 --- a/rs/orchestrator/src/args.rs +++ b/rs/orchestrator/src/args.rs @@ -51,9 +51,13 @@ pub struct OrchestratorArgs { #[clap(long)] pub(crate) enable_provisional_registration: bool, - /// The path to the version file. + /// The path to the replica version file. #[clap(long)] - pub(crate) version_file: PathBuf, + pub(crate) replica_version_file: PathBuf, + + /// The path to the GuestOS version file. + #[clap(long)] + pub(crate) guestos_version_file: PathBuf, /// Print the replica's current node ID. #[clap(long)] diff --git a/rs/orchestrator/src/metrics.rs b/rs/orchestrator/src/metrics.rs index 66dfccaab41d..68e2e59af441 100644 --- a/rs/orchestrator/src/metrics.rs +++ b/rs/orchestrator/src/metrics.rs @@ -68,7 +68,7 @@ impl OrchestratorMetrics { orchestrator_info: metrics_registry.int_gauge_vec( "ic_orchestrator_info", "version info for the internet computer orchestrator running.", - &["ic_active_version"], + &["ic_active_version", "ic_guestos_version"], ), key_rotation_status: metrics_registry.int_gauge_vec( "orchestrator_key_rotation_status", diff --git a/rs/orchestrator/src/orchestrator.rs b/rs/orchestrator/src/orchestrator.rs index 92bc4a60a08d..c649c22dad48 100644 --- a/rs/orchestrator/src/orchestrator.rs +++ b/rs/orchestrator/src/orchestrator.rs @@ -32,7 +32,7 @@ use ic_logger::{ReplicaLogger, error, info, warn}; use ic_metrics::MetricsRegistry; use ic_registry_replicator::RegistryReplicator; use ic_sys::utility_command::UtilityCommand; -use ic_types::{ReplicaVersion, SubnetId, hostos_version::HostosVersion}; +use ic_types::{PlatformVersion, ReplicaVersion, SubnetId, hostos_version::HostosVersion}; use std::{ collections::HashMap, convert::TryFrom, @@ -134,11 +134,20 @@ impl Orchestrator { .unwrap()?; let metrics_registry = MetricsRegistry::global(); - let replica_version = load_version_from_file(&logger, &args.version_file) + let replica_version = load_version_from_file(&logger, &args.replica_version_file) .map_err(|()| OrchestratorInstantiationError::VersionFileError)?; + let guestos_version = load_version_from_file(&logger, &args.guestos_version_file) + .map_err(|()| OrchestratorInstantiationError::VersionFileError)?; + let platform_version = PlatformVersion { + guestos_version: guestos_version.clone(), + replica_version: replica_version.clone(), + }; info!( logger, - "Orchestrator started: version={}, config={:?}", replica_version, config + "Orchestrator started: replica_version={}, guestos_version={}, config={:?}", + replica_version, + guestos_version, + config ); UtilityCommand::notify_host( format!("node-id {node_id}: starting with version {replica_version}").as_str(), @@ -230,7 +239,7 @@ impl Orchestrator { metrics .orchestrator_info - .with_label_values(&[replica_version.as_ref()]) + .with_label_values(&[replica_version.as_ref(), guestos_version.as_ref()]) .set(1); let mut registration = NodeRegistration::new( @@ -300,7 +309,7 @@ impl Orchestrator { manageboot_runner, cup_provider, Arc::clone(&subnet_assignment), - replica_version.clone(), + platform_version, args.replica_config_file.clone(), node_id, Arc::clone(®istry_replicator) as _, @@ -352,7 +361,9 @@ impl Orchestrator { let boundary_node = BoundaryNodeManager::new( Arc::clone(®istry), ic_boundary_manager, - replica_version.clone(), + // Boundary nodes don't run fast upgrades, the corresponding binary is not + // overlaid. + guestos_version.clone(), node_id, logger.clone(), ); diff --git a/rs/orchestrator/src/processes.rs b/rs/orchestrator/src/processes.rs index 0aa2ef2579a6..f4ea41ccd0be 100644 --- a/rs/orchestrator/src/processes.rs +++ b/rs/orchestrator/src/processes.rs @@ -7,7 +7,7 @@ use crate::{ use ic_config::crypto::CryptoConfig; use ic_logger::{ReplicaLogger, info}; use ic_protobuf::registry::subnet::v1::SubnetType; -use ic_types::{RegistryVersion, ReplicaVersion, SubnetId}; +use ic_types::{PlatformVersion, RegistryVersion, ReplicaVersion, SubnetId}; use nix::unistd::Pid; use std::{collections::HashMap, ffi::OsString, path::PathBuf, sync::Arc}; @@ -24,7 +24,7 @@ pub(crate) struct ReplicaProcessConfig { pub(crate) struct ReplicaProcess { ic_binary_dir: PathBuf, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, cup_path: PathBuf, replica_config_file: PathBuf, subnet_id: SubnetId, @@ -34,15 +34,15 @@ impl Process for ReplicaProcess { const NAME: &'static str = "replica"; type Version = ReplicaVersion; type Config = ReplicaProcessConfig; - type Args = (ReplicaVersion, SubnetId); + type Args = (PlatformVersion, SubnetId); fn build( config: &Self::Config, - (replica_version, subnet_id): Self::Args, + (platform_version, subnet_id): Self::Args, ) -> OrchestratorResult { Ok(Self { ic_binary_dir: config.ic_binary_dir.clone(), - replica_version, + platform_version, cup_path: config.cup_path.clone(), replica_config_file: config.replica_config_file.clone(), subnet_id, @@ -50,15 +50,17 @@ impl Process for ReplicaProcess { } fn get_version(&self) -> &Self::Version { - &self.replica_version + &self.platform_version.replica_version } fn get_binary(&self) -> PathBuf { self.ic_binary_dir.join(Self::NAME) } fn get_args(&self) -> Vec { vec![ + OsString::from("--guestos-version"), + self.platform_version.guestos_version.to_string().into(), OsString::from("--replica-version"), - self.replica_version.to_string().into(), + self.platform_version.replica_version.to_string().into(), OsString::from("--config-file"), self.replica_config_file.clone().into(), OsString::from("--catch-up-package"), @@ -454,14 +456,14 @@ impl MultipleProcessesManager { /// starts ic-gateway. pub(crate) fn start_all( &mut self, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, subnet_id: SubnetId, registry_version: RegistryVersion, ) -> OrchestratorResult<()> { let mut result = Ok(()); result = result.and( self.replica_manager - .ensure_running((replica_version.clone(), subnet_id)), + .ensure_running((platform_version.clone(), subnet_id)), ); // Cloud-engine nodes run ic-gateway as a sidecar, but only once the @@ -477,7 +479,10 @@ impl MultipleProcessesManager { result = result.and(self.ic_gateway_manager.stop()); } Some(SubnetType::CloudEngine) => { - result = result.and(self.ic_gateway_manager.ensure_running(replica_version)); + result = result.and( + self.ic_gateway_manager + .ensure_running(platform_version.replica_version), + ); } } } diff --git a/rs/orchestrator/src/upgrade.rs b/rs/orchestrator/src/upgrade.rs index c1db8e02ed75..4bfce64bae43 100644 --- a/rs/orchestrator/src/upgrade.rs +++ b/rs/orchestrator/src/upgrade.rs @@ -23,7 +23,7 @@ use ic_registry_client_helpers::subnet::SubnetRegistry; use ic_registry_local_store::{LocalStore, LocalStoreImpl}; use ic_registry_replicator::RegistryReplicator; use ic_types::{ - Height, NodeId, RegistryVersion, ReplicaVersion, SubnetId, + Height, NodeId, PlatformVersion, RegistryVersion, ReplicaVersion, SubnetId, consensus::{CatchUpPackage, HasHeight}, crypto::{ canister_threshold_sig::MasterPublicKey, @@ -93,7 +93,7 @@ pub(crate) struct Upgrade { manageboot_runner: Box, cup_provider: CatchUpPackageProvider, subnet_assignment: Arc>, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, replica_config_file: PathBuf, pub image_path: PathBuf, registry_replicator: Arc, @@ -115,7 +115,7 @@ impl Upgrade { manageboot_runner: Box, cup_provider: CatchUpPackageProvider, subnet_assignment: Arc>, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, replica_config_file: PathBuf, node_id: NodeId, registry_replicator: Arc, @@ -134,7 +134,7 @@ impl Upgrade { cup_provider, subnet_assignment, node_id, - replica_version, + platform_version, replica_config_file, image_path: release_content_dir.join("image.bin"), registry_replicator, @@ -339,7 +339,7 @@ impl Upgrade { let new_replica_version = self .registry .get_replica_version(subnet_id, cup_registry_version)?; - if new_replica_version != self.replica_version { + if new_replica_version != *self.replica_version() { self.ensure_upgrade_should_be_executed( subnet_id, latest_registry_version, @@ -350,7 +350,7 @@ impl Upgrade { self.logger, "Starting version upgrade at CUP registry version {}: {} -> {}", cup_registry_version, - self.replica_version, + self.replica_version(), new_replica_version ); // Only downloads the new image if it doesn't already exists locally, i.e. it @@ -369,11 +369,7 @@ impl Upgrade { self.stop_replica_if_new_recovery_cup(&latest_cup, old_cup_height); // This will start new child processes if any of them is not running - self.ensure_children_are_running( - self.replica_version.clone(), - subnet_id, - latest_registry_version, - )?; + self.ensure_children_are_running(subnet_id, latest_registry_version)?; // This will trigger an image download if one is already scheduled but we did // not arrive at the corresponding CUP yet. @@ -476,12 +472,12 @@ impl Upgrade { let expected_replica_version = self .registry .get_replica_version(subnet_id, registry_version)?; - if expected_replica_version != self.replica_version { + if expected_replica_version != *self.replica_version() { info!( self.logger, "Replica version upgrade detected at registry version {}: {} -> {}", registry_version, - self.replica_version, + self.replica_version(), expected_replica_version ); self.prepare_upgrade(&expected_replica_version).await? @@ -504,14 +500,14 @@ impl Upgrade { err => Err(err), })?; - if self.replica_version == replica_version { + if *self.replica_version() == replica_version { return Ok(OrchestratorControlFlow::Unassigned); } info!( self.logger, "Replica upgrade on unassigned node detected: old version {}, new version {}", - self.replica_version, + self.replica_version(), replica_version ); @@ -607,16 +603,19 @@ impl Upgrade { /// Start all child processes appropriate for this node. fn ensure_children_are_running( &self, - replica_version: ReplicaVersion, subnet_id: SubnetId, registry_version: RegistryVersion, ) -> OrchestratorResult<()> { self.processes_manager.write().unwrap().start_all( - replica_version, + self.platform_version.clone(), subnet_id, registry_version, ) } + + fn replica_version(&self) -> &ReplicaVersion { + &self.platform_version.replica_version + } } #[async_trait] @@ -1090,6 +1089,7 @@ fn report_master_public_key_changed_metric( } #[cfg(test)] +#[allow(clippy::too_many_arguments)] mod tests { use crate::catch_up_package_provider::LocalCUPReader; use crate::catch_up_package_provider::tests::mock_tls_config; @@ -1479,11 +1479,11 @@ mod tests { let UpgradeTestScenario { node_id, subnet_type, - current_replica_version, has_local_cup, initial_subnet_assignment, .. } = test_scenario.clone(); + let platform_version = test_scenario.platform_version(); let registry_client = Arc::new(FakeRegistryClient::new(data_provider)); registry_client.update_to_latest_version(); @@ -1539,7 +1539,7 @@ mod tests { .start( ReplicaProcess::build( &replica_process_config, - (current_replica_version.clone(), SUBNET_1), + (platform_version.clone(), SUBNET_1), ) .unwrap(), ) @@ -1549,7 +1549,7 @@ mod tests { .start( IcGatewayProcess::build( &ic_gateway_process_config, - current_replica_version.clone(), + platform_version.replica_version.clone(), ) .unwrap(), ) @@ -1608,7 +1608,7 @@ mod tests { manageboot_runner, cup_provider, subnet_assignment, - current_replica_version, + platform_version, replica_config_file, node_id, Arc::new(registry_replicator), @@ -1696,6 +1696,9 @@ mod tests { subnet_type: SubnetType, // Current replica version of the running orchestrator current_replica_version: ReplicaVersion, + // GuestOS version of the running orchestrator, if different from the + // replica version (e.g. mid fast upgrade); defaults to the replica version. + guestos_version: Option, // Whether the node is assigned to a subnet (<=> presence of local CUP) // `Some` includes some parameters for the local CUP. // `None` means no local CUP, i.e. unassigned. @@ -1724,6 +1727,16 @@ mod tests { } impl UpgradeTestScenario { + fn platform_version(&self) -> PlatformVersion { + PlatformVersion { + guestos_version: self + .guestos_version + .clone() + .unwrap_or_else(|| self.current_replica_version.clone()), + replica_version: self.current_replica_version.clone(), + } + } + // Returns the CUP with the highest height among local and registry CUPs, if any. fn highest_cup(&self) -> Option<&CUPScenario> { match (&self.has_local_cup, &self.has_registry_cup) { @@ -2731,6 +2744,7 @@ mod tests { #[values(NODE_1)] node_id: NodeId, #[values(SubnetType::Application, SubnetType::CloudEngine)] subnet_type: SubnetType, #[values(ReplicaVersion::from_str("replica_version_0.1").unwrap())] current_replica_version: ReplicaVersion, + #[values(None)] guestos_version: Option, #[values( None, Some(CUPScenario { @@ -2808,6 +2822,7 @@ mod tests { node_id, subnet_type, current_replica_version, + guestos_version, has_local_cup, has_registry_cup, initial_subnet_assignment, @@ -2855,6 +2870,7 @@ mod tests { node_id: NODE_1, subnet_type: SubnetType::System, current_replica_version: ReplicaVersion::from_str("replica_version_0.1").unwrap(), + guestos_version: None, has_local_cup: Some(CUPScenario { height: Height::from(100), // Set as the NNS subnet in `setup_registry` @@ -2898,6 +2914,7 @@ mod tests { node_id: NODE_1, subnet_type: SubnetType::Application, current_replica_version: ReplicaVersion::from_str("replica_version_0.1").unwrap(), + guestos_version: None, has_local_cup: Some(CUPScenario { height: Height::from(100), subnet_id: SUBNET_1, diff --git a/rs/recovery/src/replay_helper.rs b/rs/recovery/src/replay_helper.rs index a5bc08abc94f..2dbf0dadf405 100644 --- a/rs/recovery/src/replay_helper.rs +++ b/rs/recovery/src/replay_helper.rs @@ -34,6 +34,7 @@ pub async fn replay( data_root: Some(data_root), skip_prompts, replica_version: None, + guestos_version: None, }; // Since replay output needs to be persisted anyway in case the recovery process // is restarted, we avoid declaring a return value and moving out of the diff --git a/rs/replay/src/cmd.rs b/rs/replay/src/cmd.rs index 8b7ab77bec5b..e9dcab0822db 100644 --- a/rs/replay/src/cmd.rs +++ b/rs/replay/src/cmd.rs @@ -46,6 +46,10 @@ pub struct ReplayToolArgs { /// Whether or not to skip prompts for user input. pub skip_prompts: bool, + #[clap(long)] + /// The GuestOS version to report; defaults to the replica version. + pub guestos_version: Option, + /// The replica version under which the extra messages of the subcommand are /// executed. Only needed if no consensus pool is available, otherwise the version is taken from /// its finalized tip. diff --git a/rs/replay/src/lib.rs b/rs/replay/src/lib.rs index 68e489fb3371..6cd6e68830cb 100644 --- a/rs/replay/src/lib.rs +++ b/rs/replay/src/lib.rs @@ -22,7 +22,7 @@ use crate::{ use ic_config::{Config, ConfigSource}; use ic_nns_constants::GOVERNANCE_CANISTER_ID; use ic_protobuf::{registry::subnet::v1::InitialNiDkgTranscriptRecord, types::v1 as pb}; -use ic_types::ReplicaVersion; +use ic_types::{PlatformVersion, ReplicaVersion}; use prost::Message; use std::{cell::RefCell, convert::TryFrom, rc::Rc}; @@ -62,6 +62,7 @@ mod validator; /// })), /// skip_prompts: true, /// replica_version: None, +/// guestos_version: None, /// }; /// // Once the arguments are set well, the local store and spool directories are populated; /// // replay function could be called as follows: @@ -112,10 +113,17 @@ pub fn replay(args: ReplayToolArgs) -> ReplayResult { if let Some(SubCommand::RestoreFromBackup(cmd)) = subcmd { let _enter_guard = rt.enter(); + let replica_version = ReplicaVersion::try_from(cmd.replica_version.as_str()) + .expect("Couldn't parse the replica version"); + let platform_version = PlatformVersion { + guestos_version: args + .guestos_version + .unwrap_or_else(|| replica_version.clone()), + replica_version, + }; let mut player = Player::new_for_backup( cfg, - ReplicaVersion::try_from(cmd.replica_version.as_str()) - .expect("Couldn't parse the replica version"), + platform_version, &cmd.backup_spool_path, &cmd.registry_local_store_path, subnet_id, @@ -128,7 +136,7 @@ pub fn replay(args: ReplayToolArgs) -> ReplayResult { { let _enter_guard = rt.enter(); - let player = Player::new(cfg, subnet_id, args.replica_version) + let player = Player::new(cfg, subnet_id, args.replica_version, args.guestos_version) .with_replay_target_height(target_height); if let Some(SubCommand::GetRecoveryCup(cmd)) = subcmd { diff --git a/rs/replay/src/player.rs b/rs/replay/src/player.rs index fd7ad9eacf7e..dc0630d7c1d2 100644 --- a/rs/replay/src/player.rs +++ b/rs/replay/src/player.rs @@ -53,8 +53,8 @@ use ic_registry_transport::{ use ic_replicated_state::metrics::ReplicatedStateInvariants; use ic_state_manager::StateManagerImpl; use ic_types::{ - CryptoHashOfPartialState, CryptoHashOfState, Height, NodeId, PrincipalId, Randomness, - RegistryVersion, ReplicaVersion, SubnetId, Time, UserId, + CryptoHashOfPartialState, CryptoHashOfState, Height, NodeId, PlatformVersion, PrincipalId, + Randomness, RegistryVersion, ReplicaVersion, SubnetId, Time, UserId, batch::{Batch, BatchContent, BatchMessages, BlockmakerMetrics}, consensus::{ CatchUpContentProtobufBytes, CatchUpPackage, HasHeight, HasVersion, @@ -146,7 +146,7 @@ impl Player { /// restoring states from backups. pub(crate) fn new_for_backup( mut cfg: Config, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, backup_spool_path: &Path, registry_local_store_path: &Path, subnet_id: SubnetId, @@ -176,7 +176,7 @@ impl Player { let artifact_pool_config = ArtifactPoolConfig::from(cfg.artifact_pool.clone()); let backup_dir = backup_spool_path .join(subnet_id.to_string()) - .join(replica_version.as_ref()); + .join(platform_version.replica_version.as_ref()); // Extract the genesis CUP and instantiate a new pool. let cup_file = backup::cup_file_name(&backup_dir, Height::from(start_height)); let initial_cup_proto = backup::read_cup_proto_file(&cup_file) @@ -185,7 +185,7 @@ impl Player { let pool = ConsensusPoolImpl::new( NodeId::from(PrincipalId::new_anonymous()), subnet_id, - &replica_version, + &platform_version.replica_version, // Note: it's important to pass the original proto which came from the command line (as // opposed to, for example, a proto which was first deserialized and then serialized // again). Since the proto file could have been produced and signed by nodes running a @@ -207,7 +207,7 @@ impl Player { subnet_id, Some(pool), Some(backup_dir), - replica_version, + platform_version, log, _async_log_guard, ); @@ -221,6 +221,7 @@ impl Player { cfg: Config, subnet_id: SubnetId, replica_version: Option, + guestos_version: Option, ) -> Self { let (log, _async_log_guard) = new_replica_logger_from_config(&cfg.logger); let metrics_registry = MetricsRegistry::new(); @@ -258,13 +259,18 @@ impl Player { }) }; + let platform_version = PlatformVersion { + guestos_version: guestos_version.unwrap_or_else(|| replica_version.clone()), + replica_version, + }; + Player::new_with_params( cfg, registry, subnet_id, consensus_pool, None, - replica_version, + platform_version, log, _async_log_guard, ) @@ -277,7 +283,7 @@ impl Player { subnet_id: SubnetId, consensus_pool: Option, backup_dir: Option, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, log: ReplicaLogger, _async_log_guard: AsyncGuard, ) -> Self { @@ -349,7 +355,7 @@ impl Player { ReplayValidator::new( cfg, subnet_id, - replica_version.clone(), + platform_version.clone(), crypto.clone(), crypto.clone(), verifier, @@ -383,7 +389,7 @@ impl Player { registry, local_store_path, subnet_id, - replica_version, + replica_version: platform_version.replica_version, backup_dir, log, _async_log_guard, diff --git a/rs/replay/src/validator.rs b/rs/replay/src/validator.rs index 57245c7b2e6e..5060d2f79ed8 100644 --- a/rs/replay/src/validator.rs +++ b/rs/replay/src/validator.rs @@ -27,7 +27,7 @@ use ic_metrics::MetricsRegistry; use ic_protobuf::types::v1 as pb; use ic_replicated_state::ReplicatedState; use ic_types::{ - Height, NodeId, PrincipalId, ReplicaVersion, SubnetId, + Height, NodeId, PlatformVersion, PrincipalId, SubnetId, artifact::ConsensusMessageId, consensus::{ Block, ConsensusMessage, ConsensusMessageHash, ConsensusMessageHashable, HasBlockHash, @@ -103,7 +103,7 @@ impl ReplayValidator { pub fn new( cfg: Config, subnet_id: SubnetId, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, consensus_crypto: Arc, certification_crypto: Arc, verifier: Arc, @@ -129,7 +129,7 @@ impl ReplayValidator { let replica_cfg = ReplicaConfig { node_id, subnet_id, - replica_version, + platform_version, }; let thread_pool = ThreadPoolBuilder::new() .num_threads(MAX_VALIDATION_THREADS) @@ -230,7 +230,7 @@ impl ReplayValidator { let mut pool = ConsensusPoolImpl::new( self.replica_cfg.node_id, self.replica_cfg.subnet_id, - &self.replica_cfg.replica_version, + self.replica_cfg.replica_version(), cup, artifact_pool_config, MetricsRegistry::new(), diff --git a/rs/replica/bin/replica/main.rs b/rs/replica/bin/replica/main.rs index 1ced58c0d718..03e721a2dae2 100644 --- a/rs/replica/bin/replica/main.rs +++ b/rs/replica/bin/replica/main.rs @@ -12,7 +12,7 @@ use ic_tracing::ReloadHandles; use ic_tracing_jaeger_exporter::jaeger_exporter; use ic_tracing_logging_layer::logging_layer; use ic_types::{ - PrincipalId, ReplicaVersion, SubnetId, consensus::CatchUpPackage, + PlatformVersion, PrincipalId, ReplicaVersion, SubnetId, consensus::CatchUpPackage, replica_version::REPLICA_BINARY_HASH, }; use nix::unistd::{Pid, setpgid}; @@ -187,15 +187,28 @@ fn main() -> io::Result<()> { |_| ReplicaVersion::try_from(UNKNOWN_REPLICA_VERSION).unwrap(), |args| args.replica_version.clone(), ); + let guestos_version = replica_args.as_ref().map_or_else( + |_| ReplicaVersion::try_from(UNKNOWN_REPLICA_VERSION).unwrap(), + |args| args.guestos_version.clone(), + ); + let platform_version = PlatformVersion { + guestos_version, + replica_version, + }; // Report replica version metric { let g = metrics_registry.int_gauge_vec( "ic_replica_info", "version info for the internet computer replica running.", - &["ic_active_version", "ic_replica_binary_hash"], + &[ + "ic_active_version", + "ic_guestos_version", + "ic_replica_binary_hash", + ], ); g.with_label_values(&[ - replica_version.as_ref(), + platform_version.replica_version.as_ref(), + platform_version.guestos_version.as_ref(), &get_replica_binary_hash() .map(|x| x.1) .unwrap_or_else(|_| "na".to_string()), @@ -288,7 +301,7 @@ fn main() -> io::Result<()> { config.clone(), node_id, subnet_id, - replica_version, + platform_version, registry, crypto, cup_proto, diff --git a/rs/replica/setup_ic_network/src/lib.rs b/rs/replica/setup_ic_network/src/lib.rs index b51214b1243c..c3b70d63791d 100644 --- a/rs/replica/setup_ic_network/src/lib.rs +++ b/rs/replica/setup_ic_network/src/lib.rs @@ -47,7 +47,7 @@ use ic_registry_subnet_type::SubnetType; use ic_replicated_state::ReplicatedState; use ic_state_manager::state_sync::types::StateSyncMessage; use ic_types::{ - NodeId, ReplicaVersion, SubnetId, + NodeId, PlatformVersion, SubnetId, artifact::UnvalidatedArtifactMutation, canister_http::{ CanisterHttpPaymentReceipt, CanisterHttpRequest, CanisterHttpResponse, @@ -336,7 +336,7 @@ pub fn setup_consensus_and_p2p( node_id: NodeId, subnet_id: SubnetId, subnet_type: SubnetType, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, tls_config: Arc, state_manager: Arc>, state_sync_client: Arc>, @@ -440,7 +440,7 @@ pub fn setup_consensus_and_p2p( node_id, subnet_id, subnet_type, - replica_version, + platform_version, artifact_pools, channels, Arc::clone(&consensus_crypto) as Arc<_>, @@ -472,7 +472,7 @@ fn start_consensus( node_id: NodeId, subnet_id: SubnetId, subnet_type: SubnetType, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, artifact_pools: ArtifactPools, abortable_broadcast_channels: AbortableBroadcastChannels, // ConsensusCrypto is an extension of the Crypto trait and we can @@ -547,7 +547,7 @@ fn start_consensus( let replica_config = ReplicaConfig { node_id, subnet_id, - replica_version, + platform_version, }; let dkg_key_manager = Arc::new(Mutex::new(ic_consensus_dkg::DkgKeyManager::new( metrics_registry.clone(), diff --git a/rs/replica/src/args.rs b/rs/replica/src/args.rs index 963190c26a67..f710bcf3c19d 100644 --- a/rs/replica/src/args.rs +++ b/rs/replica/src/args.rs @@ -26,6 +26,10 @@ pub struct ReplicaArgs { #[clap(long)] pub catch_up_package: Option, + /// The version of the GuestOS the node booted from + #[clap(long)] + pub guestos_version: ReplicaVersion, + /// The version of the Replica being run #[clap(long)] pub replica_version: ReplicaVersion, diff --git a/rs/replica/src/setup_ic_stack.rs b/rs/replica/src/setup_ic_stack.rs index 60a5a221cd5b..582b04b72cc7 100644 --- a/rs/replica/src/setup_ic_stack.rs +++ b/rs/replica/src/setup_ic_stack.rs @@ -28,7 +28,7 @@ use ic_replicated_state::{ReplicatedState, metrics::ReplicatedStateInvariants}; use ic_state_manager::{StateManagerImpl, state_sync::StateSync}; use ic_tracing::ReloadHandles; use ic_types::{ - Height, NodeId, ReplicaVersion, SubnetId, + Height, NodeId, PlatformVersion, SubnetId, artifact::UnvalidatedArtifactMutation, consensus::{CatchUpPackage, HasHeight}, messages::SignedIngress, @@ -67,7 +67,7 @@ pub fn construct_ic_stack( config: Config, node_id: NodeId, subnet_id: SubnetId, - replica_version: ReplicaVersion, + platform_version: PlatformVersion, registry: Arc, crypto: Arc, catch_up_package: Option, @@ -138,13 +138,13 @@ pub fn construct_ic_stack( create_consensus_pool_dir(&config); ensure_persistent_pool_replica_version_compatibility( artifact_pool_config.persistent_pool_db_path(), - &replica_version, + &platform_version.replica_version, ); let consensus_pool = Arc::new(RwLock::new(ConsensusPoolImpl::new( node_id, subnet_id, - &replica_version, + &platform_version.replica_version, // Note: it's important to pass the original proto which came from the command line (as // opposed to, for example, a proto which was first deserialized and then serialized // again). Since the proto file could have been produced and signed by nodes running a @@ -320,7 +320,7 @@ pub fn construct_ic_stack( node_id, subnet_id, subnet_type, - replica_version.clone(), + platform_version.clone(), Arc::clone(&crypto) as Arc<_>, Arc::clone(&state_manager) as Arc<_>, Arc::new(state_sync) as Arc<_>, @@ -358,7 +358,7 @@ pub fn construct_ic_stack( Arc::clone(&crypto) as Arc<_>, node_id, subnet_id, - replica_version, + platform_version, root_subnet_id, log.clone(), consensus_pool_cache, diff --git a/rs/replica_tests/src/lib.rs b/rs/replica_tests/src/lib.rs index 42bf2bf82316..3d59b2242200 100644 --- a/rs/replica_tests/src/lib.rs +++ b/rs/replica_tests/src/lib.rs @@ -35,7 +35,7 @@ use ic_test_utilities_types::{ messages::SignedIngressBuilder, }; use ic_types::{ - CanisterId, Height, NodeId, Time, + CanisterId, Height, NodeId, PlatformVersion, Time, artifact::UnvalidatedArtifactMutation, ingress::{IngressState, IngressStatus, WasmResult}, messages::{Query, QuerySource, SignedIngress}, @@ -352,7 +352,10 @@ where config.clone(), temp_node, subnet_id, - replica_version, + PlatformVersion { + guestos_version: replica_version.clone(), + replica_version, + }, registry.clone(), crypto, None, diff --git a/rs/types/types/src/lib.rs b/rs/types/types/src/lib.rs index 4ccc1afb8019..ee5e4e2b36c3 100644 --- a/rs/types/types/src/lib.rs +++ b/rs/types/types/src/lib.rs @@ -91,7 +91,7 @@ pub use crate::canister_log::{ CanisterLog, DEFAULT_AGGREGATE_LOG_MEMORY_LIMIT, MAX_AGGREGATE_LOG_MEMORY_LIMIT, MAX_DELTA_LOG_MEMORY_LIMIT, MIN_AGGREGATE_LOG_MEMORY_LIMIT, }; -pub use crate::replica_version::ReplicaVersion; +pub use crate::replica_version::{PlatformVersion, ReplicaVersion}; pub use crate::time::Time; pub use ic_base_types::{ CanisterId, CanisterIdBlobParseError, NodeId, NodeTag, NumBytes, NumOsPages, PrincipalId, diff --git a/rs/types/types/src/messages/http.rs b/rs/types/types/src/messages/http.rs index b7282931704d..c71228a5c3f5 100644 --- a/rs/types/types/src/messages/http.rs +++ b/rs/types/types/src/messages/http.rs @@ -903,6 +903,11 @@ pub struct HttpStatusResponse { pub root_key: Option, #[serde(skip_serializing_if = "Option::is_none")] pub impl_version: Option, + /// The GuestOS version the node booted from (from version.txt). Distinct + /// from `impl_version` (the replica binary version) when a fast-upgrade + /// overlay is active. + #[serde(skip_serializing_if = "Option::is_none")] + pub guestos_version: Option, #[serde(skip_serializing_if = "Option::is_none")] pub replica_health_status: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/rs/types/types/src/messages/http/tests.rs b/rs/types/types/src/messages/http/tests.rs index 08edf9971448..86f8d9cd7e74 100644 --- a/rs/types/types/src/messages/http/tests.rs +++ b/rs/types/types/src/messages/http/tests.rs @@ -845,6 +845,7 @@ mod cbor_serialization { root_key: None, impl_version: Some("0.0".to_string()), impl_hash: None, + guestos_version: None, replica_health_status: Some(ReplicaHealthStatus::Starting), certified_height: None, }, @@ -862,6 +863,7 @@ mod cbor_serialization { root_key: Some(Blob(vec![1, 2, 3])), impl_version: Some("0.0".to_string()), impl_hash: None, + guestos_version: None, replica_health_status: Some(ReplicaHealthStatus::Healthy), certified_height: None, }, @@ -880,6 +882,7 @@ mod cbor_serialization { root_key: Some(Blob(vec![1, 2, 3])), impl_version: Some("0.0".to_string()), impl_hash: None, + guestos_version: None, replica_health_status: None, certified_height: None, }, @@ -897,6 +900,7 @@ mod cbor_serialization { root_key: Some(Blob(vec![1, 2, 3])), impl_version: Some("0.0".to_string()), impl_hash: None, + guestos_version: None, replica_health_status: Some(ReplicaHealthStatus::Healthy), certified_height: Some(AmountOf::new(1)), }, diff --git a/rs/types/types/src/replica_config.rs b/rs/types/types/src/replica_config.rs index d12f7dc28161..ce809b58c08e 100644 --- a/rs/types/types/src/replica_config.rs +++ b/rs/types/types/src/replica_config.rs @@ -1,5 +1,5 @@ //! Defines the [`ReplicaConfig`]. -use crate::{NodeId, ReplicaVersion, SubnetId}; +use crate::{NodeId, PlatformVersion, ReplicaVersion, SubnetId}; use serde::{Deserialize, Serialize}; pub const NODE_INDEX_DEFAULT: u64 = 0; @@ -10,5 +10,15 @@ pub const SUBNET_ID_DEFAULT: u64 = 0; pub struct ReplicaConfig { pub node_id: NodeId, pub subnet_id: SubnetId, - pub replica_version: ReplicaVersion, + pub platform_version: PlatformVersion, +} + +impl ReplicaConfig { + pub fn replica_version(&self) -> &ReplicaVersion { + &self.platform_version.replica_version + } + + pub fn guestos_version(&self) -> &ReplicaVersion { + &self.platform_version.guestos_version + } } diff --git a/rs/types/types/src/replica_version.rs b/rs/types/types/src/replica_version.rs index f32f5b6a9412..7af15673a808 100644 --- a/rs/types/types/src/replica_version.rs +++ b/rs/types/types/src/replica_version.rs @@ -102,3 +102,18 @@ mod test { assert!(ReplicaVersion::from_str("?+").is_err()); } } + +/// The node's platform versions: the GuestOS version the node booted from and +/// the replica binary version. +/// +/// Under normal conditions the two are the same. During a GuestOS fast +/// upgrade, binaries from the target (new) GuestOS are hot-swapped in the +/// running (old) GuestOS, so the replica version is ahead of the GuestOS +/// version until the node reboots into the target GuestOS. +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] +pub struct PlatformVersion { + /// The GuestOS version the node booted from. + pub guestos_version: ReplicaVersion, + /// The replica binary version, possibly hot-swapped by a fast upgrade. + pub replica_version: ReplicaVersion, +} diff --git a/rs/types/types_test_utils/src/ids.rs b/rs/types/types_test_utils/src/ids.rs index 01889e56f7b3..481cf9a2f1a5 100644 --- a/rs/types/types_test_utils/src/ids.rs +++ b/rs/types/types_test_utils/src/ids.rs @@ -1,5 +1,5 @@ use ic_types::{ - CanisterId, NodeId, PrincipalId, ReplicaVersion, SubnetId, UserId, + CanisterId, NodeId, PlatformVersion, PrincipalId, ReplicaVersion, SubnetId, UserId, messages::{CallContextId, EXPECTED_MESSAGE_ID_LENGTH, MessageId}, }; use std::str::FromStr; @@ -155,7 +155,18 @@ pub fn node_test_id(i: u64) -> NodeId { } pub fn test_replica_version() -> ReplicaVersion { - ReplicaVersion::from_str("cafebabe0000ffff0000ffff0000ffff0000ffff").unwrap() + ReplicaVersion::from_str("cafebabecafebabecafebabecafebabecafebabe").unwrap() +} + +pub fn test_guestos_version() -> ReplicaVersion { + ReplicaVersion::from_str("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef").unwrap() +} + +pub fn test_platform_version() -> PlatformVersion { + PlatformVersion { + guestos_version: test_guestos_version(), + replica_version: test_replica_version(), + } } /// Converts a [`NodeId`] to a [`u64`].
Replica Version{{ self.replica_version.to_string() }}{{ self.platform_version.replica_version.to_string() }}
GuestOS Version{{ self.platform_version.guestos_version.to_string() }}
Subnet Type