Skip to content

Commit

Permalink
Merge branch 'de-randomize-dissolve-delay-daniel-wong' into 'master'
Browse files Browse the repository at this point in the history
NNS1-1889: De-randomize dissolve delay of SNS neurons created by decentralization sale.

 

See merge request dfinity-lab/public/ic!10380
  • Loading branch information
daniel-wong-dfinity-org committed Jan 31, 2023
2 parents 08807e6 + 1f55fb4 commit fcef362
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 253 deletions.
4 changes: 0 additions & 4 deletions rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions rs/nervous_system/common/BUILD.bazel
Expand Up @@ -20,8 +20,6 @@ DEPENDENCIES = [
"@crate_index//:bytes",
"@crate_index//:candid",
"@crate_index//:ic-metrics-encoder",
"@crate_index//:rand_0_8_4",
"@crate_index//:rand_chacha_0_3_1",
"@crate_index//:rust_decimal",
"@crate_index//:serde",
]
Expand Down
2 changes: 0 additions & 2 deletions rs/nervous_system/common/Cargo.toml
Expand Up @@ -27,8 +27,6 @@ ic-icrc1 = { path = "../../rosetta-api/icrc1" }
ic-ledger-core = { path = "../../rosetta-api/ledger_core" }
ic-metrics-encoder = "1"
icp-ledger = { path = "../../rosetta-api/icp_ledger" }
rand = "0.8.4"
rand_chacha = "0.3.1"
rust_decimal = "1.25"
serde = { version = "1.0", features = ["derive"] }

Expand Down
148 changes: 0 additions & 148 deletions rs/nervous_system/common/src/lib.rs
@@ -1,7 +1,5 @@
use candid::{CandidType, Deserialize};
use dfn_core::api::{call, time_nanos, CanisterId};
use rand::Rng;
use rand_chacha::ChaCha20Rng;
use rust_decimal::Decimal;
use serde::Serialize;

Expand Down Expand Up @@ -137,41 +135,6 @@ pub async fn get_canister_status(
.await
}

/// Generate a set of increasing dissolve delays where each new dissolve delay is randomized
/// within its interval.
///
/// Expressed another way, this method will create a vector of integers where the first element
/// is 0, the second element will be between [D, 2 * D), the third element will be between
/// [2 * D, 3 * D) and so on, where D = dissolve_delay_interval_seconds.
///
/// Preconditions:
/// - `interval_count` must be greater than 0
/// - `dissolve_delay_interval_seconds` must be greater than 0
pub fn generate_random_dissolve_delay_intervals(
interval_count: u64,
dissolve_delay_interval_seconds: u64,
random_number_generator: &mut ChaCha20Rng,
) -> Vec<u64> {
assert!(interval_count > 0);
assert!(dissolve_delay_interval_seconds > 0);
(0..interval_count)
.map(|interval_i| {
// The 0th interval should always be 0 so the dissolve delay of a neuron will
// result in liquid tokens
if interval_i == 0 {
return 0;
}

let random_offset_within_one_interval =
random_number_generator.gen_range(0..dissolve_delay_interval_seconds);

interval_i
.saturating_mul(dissolve_delay_interval_seconds)
.saturating_add(random_offset_within_one_interval)
})
.collect()
}

/// A more convenient (but explosive) way to do token math. Not suitable for
/// production use! Only for use in tests.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
Expand Down Expand Up @@ -334,117 +297,6 @@ pub fn serve_metrics(
}
}

#[cfg(test)]
mod test {
use super::*;
use proptest::prelude::proptest;
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;

const TEN_YEARS_IN_SECONDS: u64 = 315360000;

proptest! {
// Test that the generate_random_dissolve_delay_intervals method returns increasing intervals
// of dissolve delay
#[test]
fn test_generate_random_dissolve_delay_intervals(
interval_count in 1_u64..48,
dissolve_delay_interval_seconds in 1_u64..TEN_YEARS_IN_SECONDS,
rng_seed in 0_u64..u64::MAX,
) {
let mut rng = ChaCha20Rng::seed_from_u64(rng_seed);

let random_dissolve_delay_intervals = generate_random_dissolve_delay_intervals(
interval_count,
dissolve_delay_interval_seconds,
&mut rng
);

// The elements should be ordered and increasing
let mut sorted = random_dissolve_delay_intervals.clone();
sorted.sort_unstable();
assert_eq!(random_dissolve_delay_intervals, sorted);

// The first element should always be 0, and is a special case in the algorithm
let first_element = random_dissolve_delay_intervals[0];
assert_eq!(first_element, 0_u64);

random_dissolve_delay_intervals
.iter()
.enumerate()
.for_each(|(interval, dissolve_delay)| {
let interval_start = dissolve_delay_interval_seconds * interval as u64;
let interval_end = interval_start + dissolve_delay_interval_seconds;

let interval = interval_start..interval_end;
assert!(
interval.contains(dissolve_delay),
"Randomized dissolve delay {} not in expected interval {:?}",
dissolve_delay, interval
);
})
}
}

// Test that the edge case values of generate_random_dissolve_delay_intervals
#[test]
fn test_generate_random_dissolve_delay_intervals_with_edge_values() {
let mut rng = ChaCha20Rng::seed_from_u64(1_u64);

// The edge case values. These are the lowest possible values the
// algorithm accepts
let interval_count = 1;
let dissolve_delay_interval_seconds = 1;

let random_dissolve_delay_intervals = generate_random_dissolve_delay_intervals(
interval_count,
dissolve_delay_interval_seconds,
&mut rng,
);

// The elements should be ordered and increasing
let mut sorted = random_dissolve_delay_intervals.clone();
sorted.sort_unstable();
assert_eq!(random_dissolve_delay_intervals, sorted);

// The first element should always be 0, and is a special case in the algorithm
let first_element = random_dissolve_delay_intervals[0];
assert_eq!(first_element, 0_u64);

random_dissolve_delay_intervals
.iter()
.enumerate()
.for_each(|(interval, dissolve_delay)| {
let interval_step = dissolve_delay_interval_seconds * interval as u64;
let randomized_step = dissolve_delay - interval_step;
assert!(randomized_step <= dissolve_delay_interval_seconds);
})
}

// Test that saturating arithmetic prevents overflow
#[test]
fn test_generate_random_dissolve_delay_intervals_with_overflow() {
let mut rng = ChaCha20Rng::seed_from_u64(1_u64);

// The edge case values. These are the lowest possible values the
// algorithm accepts
let interval_count = 3;
let dissolve_delay_interval_seconds = u64::MAX;

let random_dissolve_delay_intervals = generate_random_dissolve_delay_intervals(
interval_count,
dissolve_delay_interval_seconds,
&mut rng,
);

// As the dissolve_delay_interval_seconds is u64::MAX the saturating arithmetic should
// result in dissolve_delays that do not overflow and remain at u64::MAX.
assert_eq!(random_dissolve_delay_intervals[0], 0);
assert_eq!(random_dissolve_delay_intervals[1], u64::MAX);
assert_eq!(random_dissolve_delay_intervals[2], u64::MAX);
}
}

/// Verifies that the url is within the allowed length, and begins with
/// `http://` or `https://`. In addition, it will return an error in case of a
/// possibly "dangerous" condition, such as the url containing a username or
Expand Down
2 changes: 0 additions & 2 deletions rs/sns/swap/BUILD.bazel
Expand Up @@ -41,8 +41,6 @@ DEPENDENCIES = [
"@crate_index//:lazy_static",
"@crate_index//:maplit",
"@crate_index//:prost",
"@crate_index//:rand_0_8_4",
"@crate_index//:rand_chacha_0_3_1",
"@crate_index//:rust_decimal",
"@crate_index//:serde",
"@crate_index//:strum",
Expand Down
2 changes: 0 additions & 2 deletions rs/sns/swap/Cargo.toml
Expand Up @@ -48,8 +48,6 @@ lazy_static = "1.4.0"
maplit = "1.0.2"
on_wire = { path = "../../rust_canisters/on_wire" }
prost = "0.11.0"
rand = "0.8"
rand_chacha = "0.3"
registry-canister = { path = "../../registry/canister" }
rust_decimal = "1.25"
serde = { version = "1.0", features = ["derive"] }
Expand Down
6 changes: 1 addition & 5 deletions rs/sns/swap/gen/ic_sns_swap.pb.v1.rs
Expand Up @@ -346,11 +346,7 @@ pub mod params {
/// evenly distributed across the `count` neurons.
#[prost(uint64, tag = "1")]
pub count: u64,
/// The interval in seconds that the dissolve delay of each neuron in the
/// basket will be increased by. The 0th neuron created will have its dissolve
/// delay set to 0, and each subsequent neuron will have a dissolve delay
/// calculated by:
/// `(i * dissolve_delay_interval_seconds) + rand(0..dissolve_delay_interval_seconds)`
/// The amount of additional time it takes for the next neuron to dissolve.
#[prost(uint64, tag = "2")]
pub dissolve_delay_interval_seconds: u64,
}
Expand Down
6 changes: 1 addition & 5 deletions rs/sns/swap/proto/ic_sns_swap/pb/v1/swap.proto
Expand Up @@ -323,11 +323,7 @@ message Params {
// evenly distributed across the `count` neurons.
uint64 count = 1;

// The interval in seconds that the dissolve delay of each neuron in the
// basket will be increased by. The 0th neuron created will have its dissolve
// delay set to 0, and each subsequent neuron will have a dissolve delay
// calculated by:
// `(i * dissolve_delay_interval_seconds) + rand(0..dissolve_delay_interval_seconds)`
// The amount of additional time it takes for the next neuron to dissolve.
uint64 dissolve_delay_interval_seconds = 2;
}

Expand Down

0 comments on commit fcef362

Please sign in to comment.