diff --git a/ic-os/components/ic/generate-ic-config/generate-ic-config.sh b/ic-os/components/ic/generate-ic-config/generate-ic-config.sh index 3798cabef20..89acb7053a5 100755 --- a/ic-os/components/ic/generate-ic-config/generate-ic-config.sh +++ b/ic-os/components/ic/generate-ic-config/generate-ic-config.sh @@ -6,7 +6,7 @@ function usage() { cat <"${OUT_FILE}" diff --git a/ic-os/components/ic/generate-ic-config/ic.json5.template b/ic-os/components/ic/generate-ic-config/ic.json5.template index 43b662488d4..9bb90f72b34 100644 --- a/ic-os/components/ic/generate-ic-config/ic.json5.template +++ b/ic-os/components/ic/generate-ic-config/ic.json5.template @@ -505,6 +505,7 @@ table ip6 filter {\n\ }, registration: { + node_reward_type: "{{ node_reward_type }}", nns_url: "{{ nns_urls }}", nns_pub_key_pem: "/var/lib/ic/data/nns_public_key.pem", node_operator_pem: "/var/lib/ic/data/node_operator_private_key.pem" diff --git a/ic-os/components/setupos-scripts/check-hardware.sh b/ic-os/components/setupos-scripts/check-hardware.sh index 99f647018b0..8bfd16a30e6 100644 --- a/ic-os/components/setupos-scripts/check-hardware.sh +++ b/ic-os/components/setupos-scripts/check-hardware.sh @@ -277,7 +277,8 @@ function read_variables() { function validate_node_reward() { read_variables if [[ -z "$node_reward_type" ]]; then - log_and_halt_installation_on_error 1 "Configuration error: node_reward_type is not set" + echo "Node reward type is not set. Skipping validation." + return 0 fi if [[ ! "$node_reward_type" =~ ^type[0-9]+(\.[0-9])?$ ]]; then diff --git a/ic-os/docs/Configuration.adoc b/ic-os/docs/Configuration.adoc index 6167fbdcf9e..9f6ef0dcdd2 100644 --- a/ic-os/docs/Configuration.adoc +++ b/ic-os/docs/Configuration.adoc @@ -14,7 +14,7 @@ In production, configuration is propagated from a partition on the USB installer === SetupOS -> HostOS SetupOS validates, sanitizes, and copies all of its configuration files to the HostOS config partition: - config.ini # Data center-specific network settings + config.ini # Data center and node-specific network settings ssh_authorized_keys # SSH private keys node_operator_private_key.pem # Node Operator private key created in the Node Provider onboarding deployment.json # Deployment-specific configurations diff --git a/ic-os/setupos/README.adoc b/ic-os/setupos/README.adoc index 157782daac3..39eee13b533 100644 --- a/ic-os/setupos/README.adoc +++ b/ic-os/setupos/README.adoc @@ -63,8 +63,8 @@ The sequence of the scripts is defined in the main installation script, `setupos == Node Providers, Node Technicians, and Node Operators * *Node Provider*: An entity that purchases and owns the node hardware. Node Providers are rewarded for their node's useful work. -* *Node Technician*: These are the 'hired hands' or 'remote hands' employed by the Node Providers to maintain and manage the node. They do not necessarily own the hardware, but are responsible for its operation. Note that a single node will often have more than one Node Technician—the Node Technician can be thought of as the individual currently operating the node, so this role can cycle among several parties. -** Note: While it is possible, it is uncommon for a Node Provider to also act as their own Node Technician. +* *Node Technician*: Node Technicians are responsible for the physical installation, maintenance, and repair of the node. These are typically 'hired hands' or 'remote hands' employed by the Node Providers to maintain and manage the node. A single node may have more than one Node Technician — the Node Technician can be thought of as the individual currently operating the node, so this role can cycle among several parties. The information about the Node Technician is currently not stored in the NNS. +** Note: It is possible that a Node Provider and Node Technician are the same entity. * *Node Operator*: ** The term "Node Operator" refers not to a specific individual or group, but to: *** A specific record within the NNS registry—the *Node Operator record*, and to: diff --git a/ic-os/setupos/config/config.ini b/ic-os/setupos/config/config.ini index 14475fb8ee1..9d088d9959f 100644 --- a/ic-os/setupos/config/config.ini +++ b/ic-os/setupos/config/config.ini @@ -2,16 +2,6 @@ # # Update these settings according to your specific network and node requirements. -# ------------------------------ -# Node Reward Settings (required) -# ------------------------------ -# Update node_reward_type as per your node configuration. -# Node rewards setting should be of the form: -# node_reward_type=typeX or node_reward_type=typeX.Y -# Example: -# node_reward_type=type3.1 -node_reward_type= - # ------------------------------ # IPv6 Settings (required) # ------------------------------ @@ -23,6 +13,18 @@ ipv6_prefix=2a00:fb01:400:44 # Define the gateway address for your IPv6 network. Ensure it's within your network range: ipv6_gateway=2a00:fb01:400:44::1 +# ------------------------------ +# Node Reward Settings (Required for the ICP Mainnet) +# ----------------------------- +# +# Defines the node reward type that the operator expects for this node. +# The value must correspond to a community-approved type and operator configuration in the NNS. +# See https://wiki.internetcomputer.org/wiki/Node_Deployment_config.ini#node_reward_type_Documentation for more info. +# +# To find the rewardable node types for the node operator, run: +# ic-admin --nns-url https://ic0.app get-node-operator +# +# node_reward_type=type3.1 # ------------------------------ # IPv4 Settings (Optional) diff --git a/rs/config/src/registration.rs b/rs/config/src/registration.rs index f95d57147b9..11fe5ecb4e7 100644 --- a/rs/config/src/registration.rs +++ b/rs/config/src/registration.rs @@ -30,6 +30,10 @@ pub struct Config { /// If this Sec256k1 PEM is available, use it instead of the HSM. pub node_operator_pem: Option, + + /// Specifies the type of node rewards that the node operator expects to receive. + /// Examples include "type3.1" or "type1", corresponding to entries in the node rewards table in the NNS. + pub node_reward_type: Option, } // We allow for the operator to only specify some of the fields while the others @@ -57,6 +61,7 @@ impl Default for Config { nns_url: None, nns_pub_key_pem: None, node_operator_pem: None, + node_reward_type: None, } } } diff --git a/rs/ic_os/dev_test_tools/launch-single-vm/src/main.rs b/rs/ic_os/dev_test_tools/launch-single-vm/src/main.rs index 3fc52f3f1fb..87212f2d519 100644 --- a/rs/ic_os/dev_test_tools/launch-single-vm/src/main.rs +++ b/rs/ic_os/dev_test_tools/launch-single-vm/src/main.rs @@ -150,6 +150,7 @@ fn main() { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, )]); diff --git a/rs/orchestrator/src/registration.rs b/rs/orchestrator/src/registration.rs index afe9b88d497..75230b930fe 100644 --- a/rs/orchestrator/src/registration.rs +++ b/rs/orchestrator/src/registration.rs @@ -219,10 +219,17 @@ impl NodeRegistration { .expect("Invalid IPv4 configuration"), domain: process_domain_name(&self.log, &self.node_config.domain) .expect("Domain name is invalid"), - // Unused section follows - p2p_flow_endpoints: Default::default(), - prometheus_metrics_endpoint: Default::default(), - node_reward_type: None, + node_reward_type: if self.node_config.registration.node_reward_type.as_deref() + == Some("") + { + None + } else { + self.node_config.registration.node_reward_type.clone() + }, + + // The following fields are unused. + p2p_flow_endpoints: Default::default(), // unused field + prometheus_metrics_endpoint: Default::default(), // unused field } } diff --git a/rs/prep/src/bin/prep.rs b/rs/prep/src/bin/prep.rs index 7d200ed245a..1079d7c5632 100644 --- a/rs/prep/src/bin/prep.rs +++ b/rs/prep/src/bin/prep.rs @@ -509,6 +509,7 @@ mod test_flag_node_parser { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, }; diff --git a/rs/prep/src/node.rs b/rs/prep/src/node.rs index fa9bfaf53b8..3a92674cfa6 100644 --- a/rs/prep/src/node.rs +++ b/rs/prep/src/node.rs @@ -21,7 +21,9 @@ use ic_crypto_node_key_validation::ValidNodePublicKeys; use ic_interfaces_state_manager::{CertificationScope, StateHashError, StateManager}; use ic_protobuf::registry::{ crypto::v1::{PublicKey, X509PublicKeyCert}, - node::v1::{ConnectionEndpoint as pbConnectionEndpoint, NodeRecord as pbNodeRecord}, + node::v1::{ + ConnectionEndpoint as pbConnectionEndpoint, NodeRecord as pbNodeRecord, NodeRewardType, + }, }; use ic_registry_keys::{make_crypto_node_key, make_crypto_tls_cert_key, make_node_record_key}; use ic_registry_proto_data_provider::ProtoRegistryDataProvider; @@ -306,6 +308,10 @@ pub struct NodeConfiguration { /// The domain name of the node #[serde(skip_serializing, skip_deserializing)] pub domain: Option, + + /// The type of rewards that the node operator wants to receive for the node. + /// E.g. "type3.1" or "type1" or similar from the node reward table in the NNS. + pub node_reward_type: Option, } impl From for pbNodeRecord { @@ -324,6 +330,10 @@ impl From for pbNodeRecord { .map(|id| id.to_vec()) .unwrap_or_default(), domain: node_configuration.domain, + node_reward_type: node_configuration + .node_reward_type + .map(NodeRewardType::from) + .map(|t| t as i32), ..Default::default() } } @@ -440,6 +450,7 @@ mod node_configuration { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }; let got = pbNodeRecord::from(node_configuration); diff --git a/rs/prep/src/prep_state_directory.rs b/rs/prep/src/prep_state_directory.rs index a316b5d6c1c..ca86b81a83b 100755 --- a/rs/prep/src/prep_state_directory.rs +++ b/rs/prep/src/prep_state_directory.rs @@ -129,6 +129,7 @@ mod tests { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, ); diff --git a/rs/protobuf/src/registry/node.rs b/rs/protobuf/src/registry/node.rs index 96fb4072f7a..c850826d193 100644 --- a/rs/protobuf/src/registry/node.rs +++ b/rs/protobuf/src/registry/node.rs @@ -12,10 +12,30 @@ impl From for String { } NodeRewardType::Type0 => "type0".to_string(), NodeRewardType::Type1 => "type1".to_string(), + NodeRewardType::Type1dot1 => "type1.1".to_string(), NodeRewardType::Type2 => "type2".to_string(), NodeRewardType::Type3 => "type3".to_string(), NodeRewardType::Type3dot1 => "type3.1".to_string(), - NodeRewardType::Type1dot1 => "type1.1".to_string(), + } + } +} + +impl std::fmt::Display for NodeRewardType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", String::from(*self)) + } +} + +impl From for NodeRewardType { + fn from(value: String) -> Self { + match value.as_str() { + "type0" => NodeRewardType::Type0, + "type1" => NodeRewardType::Type1, + "type1.1" => NodeRewardType::Type1dot1, + "type2" => NodeRewardType::Type2, + "type3" => NodeRewardType::Type3, + "type3.1" => NodeRewardType::Type3dot1, + _ => NodeRewardType::Unspecified, } } } diff --git a/rs/registry/admin/src/main.rs b/rs/registry/admin/src/main.rs index f254b3d677b..2605bef4916 100644 --- a/rs/registry/admin/src/main.rs +++ b/rs/registry/admin/src/main.rs @@ -76,7 +76,7 @@ use ic_protobuf::registry::{ crypto::v1::{PublicKey, X509PublicKeyCert}, dc::v1::{AddOrRemoveDataCentersProposalPayload, DataCenterRecord}, firewall::v1::{FirewallConfig, FirewallRule, FirewallRuleSet}, - node::v1::NodeRecord, + node::v1::{NodeRecord, NodeRewardType}, node_operator::v1::{NodeOperatorRecord, RemoveNodeOperatorsPayload}, node_rewards::v2::{NodeRewardRate, UpdateNodeRewardsTableProposalPayload}, provisional_whitelist::v1::ProvisionalWhitelist as ProvisionalWhitelistProto, @@ -99,8 +99,8 @@ use ic_registry_keys::{ make_node_operator_record_key, make_node_record_key, make_provisional_whitelist_record_key, make_replica_version_key, make_routing_table_record_key, make_subnet_list_record_key, make_subnet_record_key, make_unassigned_nodes_config_record_key, FirewallRulesScope, - API_BOUNDARY_NODE_RECORD_KEY_PREFIX, NODE_OPERATOR_RECORD_KEY_PREFIX, NODE_REWARDS_TABLE_KEY, - ROOT_SUBNET_ID_KEY, + API_BOUNDARY_NODE_RECORD_KEY_PREFIX, NODE_OPERATOR_RECORD_KEY_PREFIX, NODE_RECORD_KEY_PREFIX, + NODE_REWARDS_TABLE_KEY, ROOT_SUBNET_ID_KEY, }; use ic_registry_local_store::{ Changelog, ChangelogEntry, KeyMutation, LocalStoreImpl, LocalStoreWriter, @@ -5090,6 +5090,48 @@ async fn print_and_get_last_value( record, as_json, ); + } else if key.starts_with(NODE_RECORD_KEY_PREFIX.as_bytes()) { + #[derive(Debug, Serialize)] + pub struct Node { + pub xnet: Option, + pub http: Option, + pub node_operator_id: PrincipalId, + pub chip_id: Option, + pub hostos_version_id: Option, + pub public_ipv4_config: Option, + pub domain: Option, + pub node_reward_type: Option, + } + let record = + NodeRecord::decode(&bytes[..]).expect("Error decoding value from registry."); + let record = Node { + xnet: record.xnet.map(|v| format!("[{}]:{}", v.ip_addr, v.port)), + http: record.http.map(|v| format!("[{}]:{}", v.ip_addr, v.port)), + node_operator_id: PrincipalId::try_from(record.node_operator_id) + .expect("Error decoding principal"), + chip_id: record.chip_id.map(hex::encode), + hostos_version_id: record.hostos_version_id, + public_ipv4_config: record.public_ipv4_config.map(|v| { + format!( + "ip_addr {} gw {:#?} prefix_length {}", + v.ip_addr, v.gateway_ip_addr, v.prefix_length + ) + }), + domain: record.domain, + node_reward_type: record.node_reward_type.map(|t: i32| { + NodeRewardType::try_from(t) + .expect("Invalid node_reward_type value") + .to_string() + }), + }; + print_value( + &std::str::from_utf8(&key) + .expect("key is not a str") + .to_string(), + version, + record, + as_json, + ); } else { let value = T::decode(&bytes[..]).expect("Error decoding value from registry."); print_value( diff --git a/rs/registry/regedit/src/tests.rs b/rs/registry/regedit/src/tests.rs index 13cdf4177a4..ec26e50c733 100644 --- a/rs/registry/regedit/src/tests.rs +++ b/rs/registry/regedit/src/tests.rs @@ -135,6 +135,7 @@ pub fn run_ic_prep() -> (TempDir, IcPrepStateDir) { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, ); diff --git a/rs/replica_tests/src/lib.rs b/rs/replica_tests/src/lib.rs index 5df1d565e45..49a3f2e3919 100644 --- a/rs/replica_tests/src/lib.rs +++ b/rs/replica_tests/src/lib.rs @@ -218,6 +218,7 @@ pub fn get_ic_config() -> IcConfig { node_operator_principal_id: None, secret_key_store: Some(node_sks), domain: None, + node_reward_type: None, }, ); diff --git a/rs/starter/src/main.rs b/rs/starter/src/main.rs index faf97c53b7e..c3a30475a92 100644 --- a/rs/starter/src/main.rs +++ b/rs/starter/src/main.rs @@ -101,6 +101,7 @@ fn main() -> Result<()> { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, ); diff --git a/rs/tests/driver/src/driver/bootstrap.rs b/rs/tests/driver/src/driver/bootstrap.rs index e634bb73fd3..cda693871e4 100644 --- a/rs/tests/driver/src/driver/bootstrap.rs +++ b/rs/tests/driver/src/driver/bootstrap.rs @@ -582,6 +582,7 @@ fn node_to_config(node: &Node) -> NodeConfiguration { node_operator_principal_id: None, secret_key_store: node.secret_key_store.clone(), domain: node.domain.clone(), + node_reward_type: None, } }