Add bounded timestamp-estimation method
This commit is contained in:
parent
0049ab69fb
commit
80db6c0980
|
@ -33,7 +33,9 @@ use solana_sdk::{
|
|||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signature, Signer},
|
||||
stake_weighted_timestamp::{calculate_stake_weighted_timestamp, TIMESTAMP_SLOT_RANGE},
|
||||
stake_weighted_timestamp::{
|
||||
calculate_stake_weighted_timestamp, EstimateType, TIMESTAMP_SLOT_RANGE,
|
||||
},
|
||||
timing::timestamp,
|
||||
transaction::Transaction,
|
||||
};
|
||||
|
@ -1638,9 +1640,15 @@ impl Blockstore {
|
|||
}
|
||||
|
||||
let mut calculate_timestamp = Measure::start("calculate_timestamp");
|
||||
let stake_weighted_timestamp =
|
||||
calculate_stake_weighted_timestamp(&unique_timestamps, stakes, slot, slot_duration)
|
||||
.ok_or(BlockstoreError::EmptyEpochStakes)?;
|
||||
let stake_weighted_timestamp = calculate_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
stakes,
|
||||
slot,
|
||||
slot_duration,
|
||||
EstimateType::Unbounded,
|
||||
None,
|
||||
)
|
||||
.ok_or(BlockstoreError::EmptyEpochStakes)?;
|
||||
calculate_timestamp.stop();
|
||||
datapoint_info!(
|
||||
"blockstore-get-block-time",
|
||||
|
|
|
@ -59,7 +59,9 @@ use solana_sdk::{
|
|||
signature::{Keypair, Signature},
|
||||
slot_hashes::SlotHashes,
|
||||
slot_history::SlotHistory,
|
||||
stake_weighted_timestamp::{calculate_stake_weighted_timestamp, TIMESTAMP_SLOT_RANGE},
|
||||
stake_weighted_timestamp::{
|
||||
calculate_stake_weighted_timestamp, EstimateType, TIMESTAMP_SLOT_RANGE,
|
||||
},
|
||||
system_transaction,
|
||||
sysvar::{self},
|
||||
timing::years_as_slots,
|
||||
|
@ -1079,7 +1081,9 @@ impl Bank {
|
|||
.feature_set
|
||||
.is_active(&feature_set::timestamp_correction::id())
|
||||
{
|
||||
if let Some(timestamp_estimate) = self.get_timestamp_estimate() {
|
||||
if let Some(timestamp_estimate) =
|
||||
self.get_timestamp_estimate(EstimateType::Unbounded, None)
|
||||
{
|
||||
if timestamp_estimate > unix_timestamp {
|
||||
datapoint_info!(
|
||||
"bank-timestamp-correction",
|
||||
|
@ -1440,7 +1444,11 @@ impl Bank {
|
|||
self.update_recent_blockhashes_locked(&blockhash_queue);
|
||||
}
|
||||
|
||||
fn get_timestamp_estimate(&self) -> Option<UnixTimestamp> {
|
||||
fn get_timestamp_estimate(
|
||||
&self,
|
||||
estimate_type: EstimateType,
|
||||
epoch_start_timestamp: Option<(Slot, UnixTimestamp)>,
|
||||
) -> Option<UnixTimestamp> {
|
||||
let mut get_timestamp_estimate_time = Measure::start("get_timestamp_estimate");
|
||||
let recent_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = self
|
||||
.vote_accounts()
|
||||
|
@ -1467,6 +1475,8 @@ impl Bank {
|
|||
stakes,
|
||||
self.slot(),
|
||||
slot_duration,
|
||||
estimate_type,
|
||||
epoch_start_timestamp,
|
||||
);
|
||||
get_timestamp_estimate_time.stop();
|
||||
datapoint_info!(
|
||||
|
@ -9661,7 +9671,10 @@ mod tests {
|
|||
vec![10_000; 2],
|
||||
);
|
||||
let mut bank = Bank::new(&genesis_config);
|
||||
assert_eq!(bank.get_timestamp_estimate(), Some(0));
|
||||
assert_eq!(
|
||||
bank.get_timestamp_estimate(EstimateType::Unbounded, None),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
let recent_timestamp: UnixTimestamp = bank.unix_timestamp_from_genesis();
|
||||
update_vote_account_timestamp(
|
||||
|
@ -9682,7 +9695,7 @@ mod tests {
|
|||
&validator_vote_keypairs1.vote_keypair.pubkey(),
|
||||
);
|
||||
assert_eq!(
|
||||
bank.get_timestamp_estimate(),
|
||||
bank.get_timestamp_estimate(EstimateType::Unbounded, None),
|
||||
Some(recent_timestamp + additional_secs / 2)
|
||||
);
|
||||
|
||||
|
@ -9691,14 +9704,17 @@ mod tests {
|
|||
}
|
||||
let adjustment = (bank.ns_per_slot as u64 * bank.slot()) / 1_000_000_000;
|
||||
assert_eq!(
|
||||
bank.get_timestamp_estimate(),
|
||||
bank.get_timestamp_estimate(EstimateType::Unbounded, None),
|
||||
Some(recent_timestamp + adjustment as i64 + additional_secs / 2)
|
||||
);
|
||||
|
||||
for _ in 0..7 {
|
||||
bank = new_from_parent(&Arc::new(bank));
|
||||
}
|
||||
assert_eq!(bank.get_timestamp_estimate(), None);
|
||||
assert_eq!(
|
||||
bank.get_timestamp_estimate(EstimateType::Unbounded, None),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5,15 +5,49 @@ use solana_sdk::{
|
|||
clock::{Slot, UnixTimestamp},
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub const TIMESTAMP_SLOT_RANGE: usize = 16;
|
||||
const MAX_ALLOWABLE_DRIFT_PERCENTAGE: u32 = 25;
|
||||
|
||||
pub enum EstimateType {
|
||||
Bounded,
|
||||
Unbounded, // Deprecated. Remove in the Solana v1.6.0 timeframe
|
||||
}
|
||||
|
||||
pub fn calculate_stake_weighted_timestamp(
|
||||
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
||||
stakes: &HashMap<Pubkey, (u64, Account)>,
|
||||
slot: Slot,
|
||||
slot_duration: Duration,
|
||||
estimate_type: EstimateType,
|
||||
epoch_start_timestamp: Option<(Slot, UnixTimestamp)>,
|
||||
) -> Option<UnixTimestamp> {
|
||||
match estimate_type {
|
||||
EstimateType::Bounded => calculate_bounded_stake_weighted_timestamp(
|
||||
unique_timestamps,
|
||||
stakes,
|
||||
slot,
|
||||
slot_duration,
|
||||
epoch_start_timestamp,
|
||||
),
|
||||
EstimateType::Unbounded => calculate_unbounded_stake_weighted_timestamp(
|
||||
unique_timestamps,
|
||||
stakes,
|
||||
slot,
|
||||
slot_duration,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_unbounded_stake_weighted_timestamp(
|
||||
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
||||
stakes: &HashMap<Pubkey, (u64, Account)>,
|
||||
slot: Slot,
|
||||
slot_duration: Duration,
|
||||
) -> Option<UnixTimestamp> {
|
||||
let (stake_weighted_timestamps_sum, total_stake) = unique_timestamps
|
||||
.iter()
|
||||
|
@ -36,6 +70,58 @@ pub fn calculate_stake_weighted_timestamp(
|
|||
}
|
||||
}
|
||||
|
||||
fn calculate_bounded_stake_weighted_timestamp(
|
||||
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
||||
stakes: &HashMap<Pubkey, (u64, Account)>,
|
||||
slot: Slot,
|
||||
slot_duration: Duration,
|
||||
epoch_start_timestamp: Option<(Slot, UnixTimestamp)>,
|
||||
) -> Option<UnixTimestamp> {
|
||||
let mut stake_per_timestamp: BTreeMap<UnixTimestamp, u128> = BTreeMap::new();
|
||||
let mut total_stake = 0;
|
||||
for (vote_pubkey, (timestamp_slot, timestamp)) in unique_timestamps.iter() {
|
||||
let offset = slot.saturating_sub(*timestamp_slot) as u32 * slot_duration;
|
||||
let estimate = timestamp + offset.as_secs() as i64;
|
||||
let stake = stakes
|
||||
.get(&vote_pubkey)
|
||||
.map(|(stake, _account)| stake)
|
||||
.unwrap_or(&0);
|
||||
stake_per_timestamp
|
||||
.entry(estimate)
|
||||
.and_modify(|stake_sum| *stake_sum += *stake as u128)
|
||||
.or_insert(*stake as u128);
|
||||
total_stake += *stake as u128;
|
||||
}
|
||||
if total_stake == 0 {
|
||||
return None;
|
||||
}
|
||||
let mut stake_accumulator = 0;
|
||||
let mut estimate = 0;
|
||||
// Populate `estimate` with stake-weighted median timestamp
|
||||
for (timestamp, stake) in stake_per_timestamp.into_iter() {
|
||||
stake_accumulator += stake;
|
||||
if stake_accumulator > total_stake / 2 {
|
||||
estimate = timestamp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Bound estimate by `MAX_ALLOWABLE_DRIFT_PERCENTAGE` since the start of the epoch
|
||||
if let Some((epoch_start_slot, epoch_start_timestamp)) = epoch_start_timestamp {
|
||||
let poh_estimate_offset = slot.saturating_sub(epoch_start_slot) as u32 * slot_duration;
|
||||
let estimate_offset =
|
||||
Duration::from_secs(estimate.saturating_sub(epoch_start_timestamp) as u64);
|
||||
let delta = if estimate_offset > poh_estimate_offset {
|
||||
estimate_offset - poh_estimate_offset
|
||||
} else {
|
||||
poh_estimate_offset - estimate_offset
|
||||
};
|
||||
if delta > poh_estimate_offset * MAX_ALLOWABLE_DRIFT_PERCENTAGE / 100 {
|
||||
estimate = epoch_start_timestamp + poh_estimate_offset.as_secs() as i64;
|
||||
}
|
||||
}
|
||||
Some(estimate)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
@ -95,7 +181,7 @@ pub mod tests {
|
|||
.cloned()
|
||||
.collect();
|
||||
assert_eq!(
|
||||
calculate_stake_weighted_timestamp(
|
||||
calculate_unbounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
|
@ -138,7 +224,7 @@ pub mod tests {
|
|||
.cloned()
|
||||
.collect();
|
||||
assert_eq!(
|
||||
calculate_stake_weighted_timestamp(
|
||||
calculate_unbounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
|
@ -147,4 +233,335 @@ pub mod tests {
|
|||
Some(recent_timestamp + expected_offset as i64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_bounded_stake_weighted_timestamp_uses_median() {
|
||||
let recent_timestamp: UnixTimestamp = 1_578_909_061;
|
||||
let slot = 5;
|
||||
let slot_duration = Duration::from_millis(400);
|
||||
let pubkey0 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey2 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey3 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey4 = solana_sdk::pubkey::new_rand();
|
||||
|
||||
// Test low-staked outlier(s)
|
||||
let stakes: HashMap<Pubkey, (u64, Account)> = [
|
||||
(
|
||||
pubkey0,
|
||||
(sol_to_lamports(1.0), Account::new(1, 0, &Pubkey::default())),
|
||||
),
|
||||
(
|
||||
pubkey1,
|
||||
(sol_to_lamports(1.0), Account::new(1, 0, &Pubkey::default())),
|
||||
),
|
||||
(
|
||||
pubkey2,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
(
|
||||
pubkey3,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
(
|
||||
pubkey4,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (5, 0)),
|
||||
(pubkey1, (5, recent_timestamp)),
|
||||
(pubkey2, (5, recent_timestamp)),
|
||||
(pubkey3, (5, recent_timestamp)),
|
||||
(pubkey4, (5, recent_timestamp)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let unbounded = calculate_unbounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bounded - unbounded, 527); // timestamp w/ 0.00003% of the stake can shift the timestamp backward 8min
|
||||
assert_eq!(bounded, recent_timestamp); // low-staked outlier cannot affect bounded timestamp
|
||||
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (5, recent_timestamp)),
|
||||
(pubkey1, (5, i64::MAX)),
|
||||
(pubkey2, (5, recent_timestamp)),
|
||||
(pubkey3, (5, recent_timestamp)),
|
||||
(pubkey4, (5, recent_timestamp)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let unbounded = calculate_unbounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(unbounded - bounded, 3074455295455); // timestamp w/ 0.00003% of the stake can shift the timestamp forward 97k years!
|
||||
assert_eq!(bounded, recent_timestamp); // low-staked outlier cannot affect bounded timestamp
|
||||
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (5, 0)),
|
||||
(pubkey1, (5, i64::MAX)),
|
||||
(pubkey2, (5, recent_timestamp)),
|
||||
(pubkey3, (5, recent_timestamp)),
|
||||
(pubkey4, (5, recent_timestamp)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bounded, recent_timestamp); // multiple low-staked outliers cannot affect bounded timestamp if they don't shift the median
|
||||
|
||||
// Test higher-staked outlier(s)
|
||||
let stakes: HashMap<Pubkey, (u64, Account)> = [
|
||||
(
|
||||
pubkey0,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0), // 1/3 stake
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
(
|
||||
pubkey1,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
(
|
||||
pubkey2,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (5, 0)),
|
||||
(pubkey1, (5, i64::MAX)),
|
||||
(pubkey2, (5, recent_timestamp)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bounded, recent_timestamp); // outlier(s) cannot affect bounded timestamp if they don't shift the median
|
||||
|
||||
let stakes: HashMap<Pubkey, (u64, Account)> = [
|
||||
(
|
||||
pubkey0,
|
||||
(
|
||||
sol_to_lamports(1_000_001.0), // 1/3 stake
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
(
|
||||
pubkey1,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> =
|
||||
[(pubkey0, (5, 0)), (pubkey1, (5, recent_timestamp))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(recent_timestamp - bounded, 1578909061); // outliers > 1/2 of available stake can affect timestamp
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_bounded_stake_weighted_timestamp_poh() {
|
||||
let epoch_start_timestamp: UnixTimestamp = 1_578_909_061;
|
||||
let slot = 20;
|
||||
let slot_duration = Duration::from_millis(400);
|
||||
let poh_offset = (slot * slot_duration).as_secs();
|
||||
let acceptable_delta = (MAX_ALLOWABLE_DRIFT_PERCENTAGE * poh_offset as u32 / 100) as i64;
|
||||
let poh_estimate = epoch_start_timestamp + poh_offset as i64;
|
||||
let pubkey0 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey2 = solana_sdk::pubkey::new_rand();
|
||||
|
||||
let stakes: HashMap<Pubkey, (u64, Account)> = [
|
||||
(
|
||||
pubkey0,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
(
|
||||
pubkey1,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
(
|
||||
pubkey2,
|
||||
(
|
||||
sol_to_lamports(1_000_000.0),
|
||||
Account::new(1, 0, &Pubkey::default()),
|
||||
),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// Test when stake-weighted median is too high
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (slot as u64, poh_estimate + acceptable_delta + 1)),
|
||||
(pubkey1, (slot as u64, poh_estimate + acceptable_delta + 1)),
|
||||
(pubkey2, (slot as u64, poh_estimate + acceptable_delta + 1)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
Some((0, epoch_start_timestamp)),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bounded, poh_estimate);
|
||||
|
||||
// Test when stake-weighted median is too low
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (slot as u64, poh_estimate - acceptable_delta - 1)),
|
||||
(pubkey1, (slot as u64, poh_estimate - acceptable_delta - 1)),
|
||||
(pubkey2, (slot as u64, poh_estimate - acceptable_delta - 1)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
Some((0, epoch_start_timestamp)),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bounded, poh_estimate);
|
||||
|
||||
// Test stake-weighted median within bounds
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (slot as u64, poh_estimate + acceptable_delta)),
|
||||
(pubkey1, (slot as u64, poh_estimate + acceptable_delta)),
|
||||
(pubkey2, (slot as u64, poh_estimate + acceptable_delta)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
Some((0, epoch_start_timestamp)),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bounded, poh_estimate + acceptable_delta);
|
||||
|
||||
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = [
|
||||
(pubkey0, (slot as u64, poh_estimate - acceptable_delta)),
|
||||
(pubkey1, (slot as u64, poh_estimate - acceptable_delta)),
|
||||
(pubkey2, (slot as u64, poh_estimate - acceptable_delta)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let bounded = calculate_bounded_stake_weighted_timestamp(
|
||||
&unique_timestamps,
|
||||
&stakes,
|
||||
slot as Slot,
|
||||
slot_duration,
|
||||
Some((0, epoch_start_timestamp)),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bounded, poh_estimate - acceptable_delta);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue