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,
|
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
|
|
||||||
})
|
})
|
||||||
})
|
.fold((0, 0), |(timestamps, stakes), (timestamp, stake)| {
|
||||||
.collect()
|
(timestamps + timestamp, stakes + stake)
|
||||||
} else {
|
});
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.sum();
|
|
||||||
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 timestamp_slot = vote.slots.iter().max();
|
||||||
|
if let Some(timestamp_slot) = timestamp_slot {
|
||||||
let vote_pubkey = transaction.message.account_keys
|
let vote_pubkey = transaction.message.account_keys
|
||||||
[instruction.accounts[0] as usize];
|
[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!(
|
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
|
||||||
|
|
Loading…
Reference in New Issue