2019-12-07 12:54:10 -07:00
|
|
|
use crate::{
|
|
|
|
|
account::Account,
|
2020-03-04 12:01:32 -07:00
|
|
|
declare_sysvar_id,
|
|
|
|
|
fee_calculator::FeeCalculator,
|
2019-12-07 12:54:10 -07:00
|
|
|
hash::{hash, Hash},
|
|
|
|
|
sysvar::Sysvar,
|
|
|
|
|
};
|
2020-03-04 12:01:32 -07:00
|
|
|
use std::{cmp::Ordering, collections::BinaryHeap, iter::FromIterator, ops::Deref};
|
2019-11-04 10:51:15 -07:00
|
|
|
|
|
|
|
|
const MAX_ENTRIES: usize = 32;
|
|
|
|
|
|
2020-03-04 12:01:32 -07:00
|
|
|
declare_sysvar_id!(
|
2019-11-04 12:31:24 -08:00
|
|
|
"SysvarRecentB1ockHashes11111111111111111111",
|
|
|
|
|
RecentBlockhashes
|
|
|
|
|
);
|
2019-11-04 10:51:15 -07:00
|
|
|
|
|
|
|
|
#[repr(C)]
|
2020-03-04 12:01:32 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Entry {
|
|
|
|
|
pub blockhash: Hash,
|
|
|
|
|
pub fee_calculator: FeeCalculator,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Entry {
|
|
|
|
|
pub fn new(blockhash: &Hash, fee_calculator: &FeeCalculator) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
blockhash: *blockhash,
|
|
|
|
|
fee_calculator: fee_calculator.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct IterItem<'a>(pub u64, pub &'a Hash, pub &'a FeeCalculator);
|
|
|
|
|
|
|
|
|
|
impl<'a> Eq for IterItem<'a> {}
|
|
|
|
|
|
|
|
|
|
impl<'a> PartialEq for IterItem<'a> {
|
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
|
self.0 == other.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Ord for IterItem<'a> {
|
|
|
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
|
|
|
self.0.cmp(&other.0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> PartialOrd for IterItem<'a> {
|
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
|
|
|
Some(self.cmp(other))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[repr(C)]
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
|
|
|
pub struct RecentBlockhashes(Vec<Entry>);
|
2019-11-04 10:51:15 -07:00
|
|
|
|
|
|
|
|
impl Default for RecentBlockhashes {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Vec::with_capacity(MAX_ENTRIES))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-04 12:01:32 -07:00
|
|
|
impl<'a> FromIterator<IterItem<'a>> for RecentBlockhashes {
|
2019-11-04 10:51:15 -07:00
|
|
|
fn from_iter<I>(iter: I) -> Self
|
|
|
|
|
where
|
2020-03-04 12:01:32 -07:00
|
|
|
I: IntoIterator<Item = IterItem<'a>>,
|
2019-11-04 10:51:15 -07:00
|
|
|
{
|
|
|
|
|
let mut new = Self::default();
|
|
|
|
|
for i in iter {
|
2020-03-04 12:01:32 -07:00
|
|
|
new.0.push(Entry::new(i.1, i.2))
|
2019-11-04 10:51:15 -07:00
|
|
|
}
|
|
|
|
|
new
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-23 10:51:22 +09:00
|
|
|
// This is cherry-picked from HEAD of rust-lang's master (ref1) because it's
|
|
|
|
|
// a nightly-only experimental API.
|
|
|
|
|
// (binary_heap_into_iter_sorted [rustc issue #59278])
|
|
|
|
|
// Remove this and use the standard API once BinaryHeap::into_iter_sorted (ref2)
|
|
|
|
|
// is stabilized.
|
|
|
|
|
// ref1: https://github.com/rust-lang/rust/blob/2f688ac602d50129388bb2a5519942049096cbff/src/liballoc/collections/binary_heap.rs#L1149
|
|
|
|
|
// ref2: https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html#into_iter_sorted.v
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct IntoIterSorted<T> {
|
|
|
|
|
inner: BinaryHeap<T>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Ord> Iterator for IntoIterSorted<T> {
|
|
|
|
|
type Item = T;
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn next(&mut self) -> Option<T> {
|
|
|
|
|
self.inner.pop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
|
let exact = self.inner.len();
|
|
|
|
|
(exact, Some(exact))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 12:31:24 -08:00
|
|
|
impl Sysvar for RecentBlockhashes {
|
2019-12-23 12:23:45 -08:00
|
|
|
fn size_of() -> usize {
|
|
|
|
|
// hard-coded so that we don't have to construct an empty
|
2020-03-04 12:01:32 -07:00
|
|
|
1288 // golden, update if MAX_ENTRIES changes
|
2019-11-04 10:51:15 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Deref for RecentBlockhashes {
|
2020-03-04 12:01:32 -07:00
|
|
|
type Target = Vec<Entry>;
|
2019-11-04 10:51:15 -07:00
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create_account(lamports: u64) -> Account {
|
2019-11-04 12:31:24 -08:00
|
|
|
RecentBlockhashes::default().create_account(lamports)
|
2019-11-04 10:51:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn update_account<'a, I>(account: &mut Account, recent_blockhash_iter: I) -> Option<()>
|
|
|
|
|
where
|
2020-03-04 12:01:32 -07:00
|
|
|
I: IntoIterator<Item = IterItem<'a>>,
|
2019-11-04 10:51:15 -07:00
|
|
|
{
|
|
|
|
|
let sorted = BinaryHeap::from_iter(recent_blockhash_iter);
|
2020-01-23 10:51:22 +09:00
|
|
|
let sorted_iter = IntoIterSorted { inner: sorted };
|
2020-03-04 12:01:32 -07:00
|
|
|
let recent_blockhash_iter = sorted_iter.take(MAX_ENTRIES);
|
2019-11-04 10:51:15 -07:00
|
|
|
let recent_blockhashes = RecentBlockhashes::from_iter(recent_blockhash_iter);
|
|
|
|
|
recent_blockhashes.to_account(account)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create_account_with_data<'a, I>(lamports: u64, recent_blockhash_iter: I) -> Account
|
|
|
|
|
where
|
2020-03-04 12:01:32 -07:00
|
|
|
I: IntoIterator<Item = IterItem<'a>>,
|
2019-11-04 10:51:15 -07:00
|
|
|
{
|
|
|
|
|
let mut account = create_account(lamports);
|
|
|
|
|
update_account(&mut account, recent_blockhash_iter).unwrap();
|
|
|
|
|
account
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-07 12:54:10 -07:00
|
|
|
pub fn create_test_recent_blockhashes(start: usize) -> RecentBlockhashes {
|
2020-03-04 12:01:32 -07:00
|
|
|
let blocks: Vec<_> = (start..start + MAX_ENTRIES)
|
2020-03-05 07:40:26 -07:00
|
|
|
.map(|i| {
|
|
|
|
|
(
|
|
|
|
|
i as u64,
|
|
|
|
|
hash(&bincode::serialize(&i).unwrap()),
|
|
|
|
|
FeeCalculator::new(i as u64 * 100),
|
|
|
|
|
)
|
|
|
|
|
})
|
2020-03-04 12:01:32 -07:00
|
|
|
.collect();
|
|
|
|
|
let bhq: Vec<_> = blocks
|
|
|
|
|
.iter()
|
2020-03-05 07:40:26 -07:00
|
|
|
.map(|(i, hash, fee_calc)| IterItem(*i, hash, fee_calc))
|
2019-12-07 12:54:10 -07:00
|
|
|
.collect();
|
2020-03-04 12:01:32 -07:00
|
|
|
RecentBlockhashes::from_iter(bhq.into_iter())
|
2019-12-07 12:54:10 -07:00
|
|
|
}
|
|
|
|
|
|
2019-11-04 10:51:15 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
2020-01-23 10:51:22 +09:00
|
|
|
use crate::hash::HASH_BYTES;
|
|
|
|
|
use rand::seq::SliceRandom;
|
|
|
|
|
use rand::thread_rng;
|
2019-11-04 10:51:15 -07:00
|
|
|
|
2019-12-23 12:23:45 -08:00
|
|
|
#[test]
|
|
|
|
|
fn test_size_of() {
|
2020-03-04 12:01:32 -07:00
|
|
|
let entry = Entry::new(&Hash::default(), &FeeCalculator::default());
|
2019-12-23 12:23:45 -08:00
|
|
|
assert_eq!(
|
2020-03-04 12:01:32 -07:00
|
|
|
bincode::serialized_size(&RecentBlockhashes(vec![entry; MAX_ENTRIES])).unwrap()
|
|
|
|
|
as usize,
|
2019-12-23 12:23:45 -08:00
|
|
|
RecentBlockhashes::size_of()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 10:51:15 -07:00
|
|
|
#[test]
|
|
|
|
|
fn test_create_account_empty() {
|
|
|
|
|
let account = create_account_with_data(42, vec![].into_iter());
|
|
|
|
|
let recent_blockhashes = RecentBlockhashes::from_account(&account).unwrap();
|
|
|
|
|
assert_eq!(recent_blockhashes, RecentBlockhashes::default());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_create_account_full() {
|
|
|
|
|
let def_hash = Hash::default();
|
2020-03-04 12:01:32 -07:00
|
|
|
let def_fees = FeeCalculator::default();
|
|
|
|
|
let account = create_account_with_data(
|
|
|
|
|
42,
|
|
|
|
|
vec![IterItem(0u64, &def_hash, &def_fees); MAX_ENTRIES].into_iter(),
|
|
|
|
|
);
|
2019-11-04 10:51:15 -07:00
|
|
|
let recent_blockhashes = RecentBlockhashes::from_account(&account).unwrap();
|
|
|
|
|
assert_eq!(recent_blockhashes.len(), MAX_ENTRIES);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_create_account_truncate() {
|
|
|
|
|
let def_hash = Hash::default();
|
2020-03-04 12:01:32 -07:00
|
|
|
let def_fees = FeeCalculator::default();
|
|
|
|
|
let account = create_account_with_data(
|
|
|
|
|
42,
|
|
|
|
|
vec![IterItem(0u64, &def_hash, &def_fees); MAX_ENTRIES + 1].into_iter(),
|
|
|
|
|
);
|
2019-11-04 10:51:15 -07:00
|
|
|
let recent_blockhashes = RecentBlockhashes::from_account(&account).unwrap();
|
|
|
|
|
assert_eq!(recent_blockhashes.len(), MAX_ENTRIES);
|
|
|
|
|
}
|
2020-01-23 10:51:22 +09:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_create_account_unsorted() {
|
2020-03-04 12:01:32 -07:00
|
|
|
let def_fees = FeeCalculator::default();
|
|
|
|
|
let mut unsorted_blocks: Vec<_> = (0..MAX_ENTRIES)
|
2020-01-23 10:51:22 +09:00
|
|
|
.map(|i| {
|
|
|
|
|
(i as u64, {
|
|
|
|
|
// create hash with visibly recognizable ordering
|
|
|
|
|
let mut h = [0; HASH_BYTES];
|
|
|
|
|
h[HASH_BYTES - 1] = i as u8;
|
|
|
|
|
Hash::new(&h)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
2020-03-04 12:01:32 -07:00
|
|
|
unsorted_blocks.shuffle(&mut thread_rng());
|
2020-01-23 10:51:22 +09:00
|
|
|
|
|
|
|
|
let account = create_account_with_data(
|
|
|
|
|
42,
|
2020-03-04 12:01:32 -07:00
|
|
|
unsorted_blocks
|
2020-01-23 10:51:22 +09:00
|
|
|
.iter()
|
2020-03-04 12:01:32 -07:00
|
|
|
.map(|(i, hash)| IterItem(*i, hash, &def_fees)),
|
2020-01-23 10:51:22 +09:00
|
|
|
);
|
|
|
|
|
let recent_blockhashes = RecentBlockhashes::from_account(&account).unwrap();
|
|
|
|
|
|
2020-03-04 12:01:32 -07:00
|
|
|
let mut unsorted_recent_blockhashes: Vec<_> = unsorted_blocks
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(i, hash)| IterItem(*i, hash, &def_fees))
|
|
|
|
|
.collect();
|
|
|
|
|
unsorted_recent_blockhashes.sort();
|
|
|
|
|
unsorted_recent_blockhashes.reverse();
|
|
|
|
|
let expected_recent_blockhashes: Vec<_> = (unsorted_recent_blockhashes
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|IterItem(_, b, f)| Entry::new(b, f)))
|
|
|
|
|
.collect();
|
2020-01-23 10:51:22 +09:00
|
|
|
|
|
|
|
|
assert_eq!(*recent_blockhashes, expected_recent_blockhashes);
|
|
|
|
|
}
|
2019-11-04 10:51:15 -07:00
|
|
|
}
|