getBlockTime: Fix RootedSlotIterator lowest root (#7681)

* Determine lowest_nonzero_root for purged blocktrees, and clean up slot offset math

* Filter duplicate timestamp votes

* Refactor deduping code
This commit is contained in:
Tyera Eulberg 2020-01-05 23:38:27 -07:00 committed by GitHub
parent bc71e1b612
commit e75a64a8a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 33 additions and 33 deletions

View File

@ -1247,30 +1247,25 @@ impl Blocktree {
slot_duration: Duration,
stakes: &HashMap<Pubkey, (u64, Account)>,
) -> Option<UnixTimestamp> {
let mut total_stake = 0;
let stake_weighted_timestamps_sum: u64 = self
let unique_timestamps: HashMap<Pubkey, (Slot, UnixTimestamp)> = self
.get_timestamp_slots(slot, TIMESTAMP_SLOT_INTERVAL, TIMESTAMP_SLOT_RANGE)
.iter()
.flat_map(|timestamp_slot| {
.into_iter()
.flat_map(|query_slot| self.get_block_timestamps(query_slot).unwrap_or_default())
.collect();
let (stake_weighted_timestamps_sum, total_stake) = unique_timestamps
.into_iter()
.filter_map(|(vote_pubkey, (timestamp_slot, timestamp))| {
let offset = (slot - timestamp_slot) as u32 * slot_duration;
if let Ok(timestamps) = self.get_block_timestamps(*timestamp_slot) {
timestamps
.iter()
.filter_map(|(vote_pubkey, timestamp)| {
stakes.get(vote_pubkey).map(|(stake, _account)| {
total_stake += stake;
(*timestamp as u64 + offset.as_secs()) * stake
})
})
.collect()
} else {
vec![]
}
stakes
.get(&vote_pubkey)
.map(|(stake, _account)| ((timestamp as u64 + offset.as_secs()) * stake, stake))
})
.sum();
.fold((0, 0), |(timestamps, stakes), (timestamp, stake)| {
(timestamps + timestamp, stakes + stake)
});
if total_stake > 0 {
let mean_timestamp: u64 = stake_weighted_timestamps_sum / total_stake;
Some(mean_timestamp as i64)
Some((stake_weighted_timestamps_sum / total_stake) as i64)
} else {
None
}
@ -1282,10 +1277,12 @@ impl Blocktree {
timestamp_interval: u64,
timestamp_sample_range: usize,
) -> Vec<Slot> {
let rooted_slots = RootedSlotIterator::new(0, &self);
if !self.is_root(slot) || rooted_slots.is_err() {
let root_iterator = self.db.iter::<cf::Root>(IteratorMode::Start);
if !self.is_root(slot) || root_iterator.is_err() {
return vec![];
}
let lowest_nonzero_root = root_iterator.unwrap().map(|(slot, _)| slot).nth(1).unwrap();
let rooted_slots = RootedSlotIterator::new(lowest_nonzero_root, &self);
let slots: Vec<Slot> = rooted_slots
.unwrap()
.map(|(iter_slot, _)| iter_slot)
@ -1377,14 +1374,14 @@ impl Blocktree {
self.transaction_status_cf.put(index, status)
}
fn get_block_timestamps(&self, slot: Slot) -> Result<Vec<(Pubkey, UnixTimestamp)>> {
fn get_block_timestamps(&self, slot: Slot) -> Result<Vec<(Pubkey, (Slot, UnixTimestamp))>> {
let slot_entries = self.get_slot_entries(slot, 0, None)?;
Ok(slot_entries
.iter()
.cloned()
.flat_map(|entry| entry.transactions)
.flat_map(|transaction| {
let mut timestamps: Vec<(Pubkey, UnixTimestamp)> = Vec::new();
let mut timestamps: Vec<(Pubkey, (Slot, UnixTimestamp))> = Vec::new();
for instruction in transaction.message.instructions {
let program_id = instruction.program_id(&transaction.message.account_keys);
if program_id == &solana_vote_program::id() {
@ -1392,9 +1389,12 @@ impl Blocktree {
limited_deserialize(&instruction.data)
{
if let Some(timestamp) = vote.timestamp {
let vote_pubkey = transaction.message.account_keys
[instruction.accounts[0] as usize];
timestamps.push((vote_pubkey, timestamp));
let timestamp_slot = vote.slots.iter().max();
if let Some(timestamp_slot) = timestamp_slot {
let vote_pubkey = transaction.message.account_keys
[instruction.accounts[0] as usize];
timestamps.push((vote_pubkey, (*timestamp_slot, timestamp)));
}
}
}
}
@ -4495,11 +4495,11 @@ pub mod tests {
assert_eq!(
blocktree.get_timestamp_slots(2, timestamp_interval, timestamp_sample_range),
vec![0, 1, 2]
vec![1, 2]
);
assert_eq!(
blocktree.get_timestamp_slots(3, timestamp_interval, timestamp_sample_range),
vec![0, 1, 2, 3]
vec![1, 2, 3]
);
drop(blocktree);
@ -4535,11 +4535,11 @@ pub mod tests {
assert_eq!(
blocktree.get_timestamp_slots(2, timestamp_interval, timestamp_sample_range),
vec![0, 1, 2]
vec![1, 2]
);
assert_eq!(
blocktree.get_timestamp_slots(8, timestamp_interval, timestamp_sample_range),
vec![0, 1, 2, 3, 4]
vec![1, 2, 3, 4, 5]
);
assert_eq!(
blocktree.get_timestamp_slots(13, timestamp_interval, timestamp_sample_range),
@ -4659,14 +4659,14 @@ pub mod tests {
fn test_get_block_timestamps() {
let vote_keypairs: Vec<Keypair> = (0..6).map(|_| Keypair::new()).collect();
let base_timestamp = 1576183541;
let mut expected_timestamps: Vec<(Pubkey, UnixTimestamp)> = Vec::new();
let mut expected_timestamps: Vec<(Pubkey, (Slot, UnixTimestamp))> = Vec::new();
// Populate slot 1 with vote transactions, some of which have timestamps
let mut vote_entries: Vec<Entry> = Vec::new();
for (i, keypair) in vote_keypairs.iter().enumerate() {
let timestamp = if i % 2 == 0 {
let unique_timestamp = base_timestamp + i as i64;
expected_timestamps.push((keypair.pubkey(), unique_timestamp));
expected_timestamps.push((keypair.pubkey(), (1, unique_timestamp)));
Some(unique_timestamp)
} else {
None