Skip to content

Commit

Permalink
feat(decentralization): Add distance from the target topology as busi…
Browse files Browse the repository at this point in the history
…ness rules (#817)
  • Loading branch information
sasa-tomic authored Aug 28, 2024
1 parent 38af8b9 commit 3752142
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 9 deletions.
35 changes: 30 additions & 5 deletions rs/decentralization/src/nakamoto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,17 @@ impl NakamotoScore {
let only_counter = counters.iter().map(|(_feat, cnt)| *cnt).collect::<Vec<_>>();
// But for deeper understanding (logging and debugging) we also keep track of
// all strings and their counts
let value_counts = counters.into_iter().sorted_by_key(|(_feat, cnt)| -(*cnt as isize)).collect::<Vec<_>>();
let value_counts = counters
.into_iter()
.sorted_unstable_by(|(feat1, cnt1), (feat2, cnt2)| {
let cmp1 = cnt2.partial_cmp(cnt1).unwrap();
if cmp1 == Ordering::Equal {
feat1.partial_cmp(feat2).unwrap_or(Ordering::Equal)
} else {
cmp1
}
})
.collect::<Vec<_>>();

(value.0.clone(), Self::nakamoto(&only_counter), value_counts)
});
Expand Down Expand Up @@ -696,8 +706,11 @@ mod tests {
assert_eq!(
subnet_initial.check_business_rules().unwrap(),
(
1000,
vec!["NodeFeature 'country' controls 9 of nodes, which is > 8 (2/3 of all) nodes".to_string()]
1070,
vec![
"Country US controls 9 of nodes, which is higher than target of 2 for the subnet. Applying penalty of 70.".to_string(),
"NodeFeature country controls 9 of nodes, which is > 8 (2/3 of all) nodes".to_string()
]
)
);
let nodes_available = new_test_nodes_with_overrides("spare", 13, 3, 0, (&NodeFeature::Country, &["US", "RO", "JP"]));
Expand Down Expand Up @@ -753,7 +766,13 @@ mod tests {
);
assert_eq!(
subnet_initial.check_business_rules().unwrap(),
(10000, vec!["A single Node Provider can halt the subnet".to_string()])
(
10020,
vec![
"node_provider NP2 controls 3 of nodes, which is higher than target of 1 for the subnet. Applying penalty of 20.".to_string(),
"A single Node Provider can halt the subnet".to_string()
]
)
);
let nodes_available = new_test_nodes_with_overrides("spare", 7, 2, 0, (&NodeFeature::NodeProvider, &["NP6", "NP7"]));
let health_of_nodes = nodes_available.iter().map(|n| (n.id, HealthStatus::Healthy)).collect::<BTreeMap<_, _>>();
Expand Down Expand Up @@ -800,7 +819,13 @@ mod tests {
1,
(&NodeFeature::NodeProvider, &["NP1", "NP2", "NP2", "NP3", "NP4", "NP4", "NP5"]),
);
assert_eq!(subnet_initial.check_business_rules().unwrap(), (0, vec![]));
assert_eq!(
subnet_initial.check_business_rules().unwrap(),
(
10,
vec!["node_provider NP2 controls 2 of nodes, which is higher than target of 1 for the subnet. Applying penalty of 10.".to_string()]
)
);

// There are 2 spare nodes, but both are DFINITY
let nodes_available = new_test_nodes_with_overrides("spare", 7, 2, 2, (&NodeFeature::NodeProvider, &["NP6", "NP7"]));
Expand Down
56 changes: 52 additions & 4 deletions rs/decentralization/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,17 +320,65 @@ impl DecentralizedSubnet {
Some((country_dominant, country_nodes_count)) => {
let controlled_nodes_max = nodes.len() / 3;
if country_nodes_count > controlled_nodes_max {
let penalty = (country_nodes_count - controlled_nodes_max) * 1000;
checks.push(format!(
"Country '{}' controls {} of nodes, which is > {} (1/3 - 1) of subnet nodes",
country_dominant, country_nodes_count, controlled_nodes_max
"Country {} controls {} of nodes, which is > {} (1/3 - 1) of subnet nodes. Applying penalty of {}.",
country_dominant, country_nodes_count, controlled_nodes_max, penalty
));
penalties += (country_nodes_count - controlled_nodes_max) * 1000;
penalties += penalty;
}
}
_ => return Err(anyhow::anyhow!("Incomplete data for {}", feature)),
}
}

// As per the adopted target topology
// https://dashboard.internetcomputer.org/proposal/132136
let max_nodes_per_np_and_dc = 1;
for feature in &[NodeFeature::NodeProvider, NodeFeature::DataCenter, NodeFeature::DataCenterOwner] {
match nakamoto_scores.feature_value_counts_max(feature) {
Some((name, value)) => {
if value > max_nodes_per_np_and_dc {
let penalty = (value - max_nodes_per_np_and_dc) * 10;
checks.push(format!(
"{} {} controls {} of nodes, which is higher than target of {} for the subnet. Applying penalty of {}.",
feature, name, value, max_nodes_per_np_and_dc, penalty
));
penalties += penalty;
}
}
_ => return Err(anyhow::anyhow!("Incomplete data for {}", feature)),
}
}

// As per the adopted target topology
// https://dashboard.internetcomputer.org/proposal/132136
let max_nodes_per_country = match subnet_id_str.as_str() {
"tdb26-jop6k-aogll-7ltgs-eruif-6kk7m-qpktf-gdiqx-mxtrf-vb5e6-eqe"
| "x33ed-h457x-bsgyx-oqxqf-6pzwv-wkhzr-rm2j3-npodi-purzm-n66cg-gae"
| "pzp6e-ekpqk-3c5x7-2h6so-njoeq-mt45d-h3h6c-q3mxf-vpeq5-fk5o7-yae"
| "uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe" => 3,
_ => 2,
};
match nakamoto_scores.feature_value_counts_max(&NodeFeature::Country) {
Some((name, value)) => {
if value > max_nodes_per_country {
let penalty = (value - max_nodes_per_country) * 10;
checks.push(format!(
"Country {} controls {} of nodes, which is higher than target of {} for the subnet. Applying penalty of {}.",
name, value, max_nodes_per_country, penalty
));
penalties += penalty;
}
}
_ => {
return Err(anyhow::anyhow!(
"Incomplete data for Node Feature Country in subnet {}",
subnet_id.to_string()
))
}
}

if is_european_subnet {
// European subnet should only take European nodes.
let continent_counts = nakamoto_scores.feature_value_counts(&NodeFeature::Continent);
Expand Down Expand Up @@ -387,7 +435,7 @@ impl DecentralizedSubnet {

if score == 1.0 && controlled_nodes > nodes.len() * 2 / 3 && !european_subnet_continent_penalty {
checks.push(format!(
"NodeFeature '{}' controls {} of nodes, which is > {} (2/3 of all) nodes",
"NodeFeature {} controls {} of nodes, which is > {} (2/3 of all) nodes",
feature,
controlled_nodes,
nodes.len() * 2 / 3
Expand Down

0 comments on commit 3752142

Please sign in to comment.