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