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:
parent
bc71e1b612
commit
e75a64a8a2
|
@ -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
|
||||
stakes
|
||||
.get(&vote_pubkey)
|
||||
.map(|(stake, _account)| ((timestamp as u64 + offset.as_secs()) * stake, stake))
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.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 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));
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue