This commit is contained in:
Anatoly Yakovenko 2019-01-28 12:42:23 -08:00 committed by Grimes
parent 57f82934f2
commit 3e1a926aa6
7 changed files with 4 additions and 413 deletions

View File

@ -1,6 +1,5 @@
use crate::bank::BankError;
use crate::bank::Result;
use crate::checkpoint::Checkpoint;
use crate::counter::Counter;
use crate::status_deque::{StatusDeque, StatusDequeError};
use bincode::serialize;
@ -11,7 +10,6 @@ use solana_sdk::hash::{hash, Hash};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::transaction::Transaction;
use std::collections::BTreeMap;
use std::collections::VecDeque;
use std::sync::atomic::AtomicUsize;
use std::sync::{Mutex, RwLock};
@ -35,9 +33,6 @@ pub struct AccountsDB {
/// Mapping of known public keys/IDs to accounts
pub accounts: HashMap<Pubkey, Account>,
/// list of prior states
checkpoints: VecDeque<(HashMap<Pubkey, Account>, u64)>,
/// The number of transactions the bank has processed without error since the
/// start of the ledger.
transaction_count: u64,
@ -55,7 +50,6 @@ impl Default for AccountsDB {
fn default() -> Self {
Self {
accounts: HashMap::new(),
checkpoints: VecDeque::new(),
transaction_count: 0,
}
}
@ -91,24 +85,12 @@ impl AccountsDB {
if let Some(account) = self.accounts.get(pubkey) {
return Some(account);
}
for (accounts, _) in &self.checkpoints {
if let Some(account) = accounts.get(pubkey) {
return Some(account);
}
}
None
}
pub fn store(&mut self, pubkey: &Pubkey, account: &Account) {
if account.tokens == 0 {
if self.checkpoints.is_empty() {
// purge if balance is 0 and no checkpoints
self.accounts.remove(pubkey);
} else {
// store default account if balance is 0 and there's a checkpoint
self.accounts.insert(pubkey.clone(), Account::default());
}
self.accounts.remove(pubkey);
} else {
self.accounts.insert(pubkey.clone(), account.clone());
}
@ -266,15 +248,6 @@ impl AccountsDB {
pub fn transaction_count(&self) -> u64 {
self.transaction_count
}
pub fn checkpoint_and_copy(&mut self) -> AccountsDB {
self.checkpoint();
let (accounts, tx_count) = self.checkpoints.front().unwrap();
let mut copy = AccountsDB::default();
copy.accounts = accounts.clone();
copy.transaction_count = *tx_count;
copy
}
}
impl Accounts {
@ -392,66 +365,6 @@ impl Accounts {
pub fn transaction_count(&self) -> u64 {
self.accounts_db.read().unwrap().transaction_count()
}
pub fn checkpoint(&self) {
self.accounts_db.write().unwrap().checkpoint()
}
pub fn rollback(&self) {
self.accounts_db.write().unwrap().rollback()
}
pub fn purge(&self, depth: usize) {
self.accounts_db.write().unwrap().purge(depth)
}
pub fn depth(&self) -> usize {
self.accounts_db.read().unwrap().depth()
}
pub fn checkpoint_and_copy(&self) -> Accounts {
let db = self.accounts_db.write().unwrap().checkpoint_and_copy();
let mut copy = Accounts::default();
copy.accounts_db = RwLock::new(db);
copy
}
}
impl Checkpoint for AccountsDB {
fn checkpoint(&mut self) {
let accounts = self.accounts.clone();
self.checkpoints
.push_front((accounts, self.transaction_count()));
}
fn rollback(&mut self) {
let (accounts, transaction_count) = self.checkpoints.pop_front().unwrap();
self.accounts = accounts;
self.transaction_count = transaction_count;
}
fn purge(&mut self, depth: usize) {
fn merge(into: &mut HashMap<Pubkey, Account>, purge: &mut HashMap<Pubkey, Account>) {
purge.retain(|pubkey, _| !into.contains_key(pubkey));
into.extend(purge.drain());
into.retain(|_, account| account.tokens != 0);
}
while self.depth() > depth {
let (mut purge, _) = self.checkpoints.pop_back().unwrap();
if let Some((into, _)) = self.checkpoints.back_mut() {
merge(into, &mut purge);
continue;
}
merge(&mut self.accounts, &mut purge);
}
}
fn depth(&self) -> usize {
self.checkpoints.len()
}
}
#[cfg(test)]

View File

@ -4,7 +4,6 @@
//! already been signed and verified.
use crate::accounts::{Accounts, ErrorCounters, InstructionAccounts, InstructionLoaders};
use crate::checkpoint::Checkpoint;
use crate::counter::Counter;
use crate::entry::Entry;
use crate::entry::EntrySlice;
@ -138,48 +137,6 @@ impl Bank {
*sub = subscriptions
}
/// Checkpoint this bank and return a copy of it
pub fn checkpoint_and_copy(&self) -> Bank {
let last_ids_cp = self.last_ids.write().unwrap().checkpoint_and_copy();
let accounts = self.accounts.checkpoint_and_copy();
let mut copy = Bank::default();
copy.accounts = accounts;
copy.last_ids = RwLock::new(last_ids_cp);
copy.leader_scheduler =
Arc::new(RwLock::new(self.leader_scheduler.read().unwrap().clone()));
copy.confirmation_time = AtomicUsize::new(self.confirmation_time.load(Ordering::Relaxed));
copy
}
pub fn checkpoint(&self) {
self.accounts.checkpoint();
self.last_ids.write().unwrap().checkpoint();
}
pub fn purge(&self, depth: usize) {
self.accounts.purge(depth);
self.last_ids.write().unwrap().purge(depth);
}
pub fn rollback(&self) {
let rolled_back_pubkeys: Vec<Pubkey> = self.accounts.keys();
self.accounts.rollback();
rolled_back_pubkeys.iter().for_each(|pubkey| {
if let Some(account) = self.accounts.load_slow(&pubkey) {
self.subscriptions
.read()
.unwrap()
.check_account(&pubkey, &account)
}
});
self.last_ids.write().unwrap().rollback();
}
pub fn checkpoint_depth(&self) -> usize {
self.accounts.depth()
}
fn process_genesis_block(&self, genesis_block: &GenesisBlock) {
assert!(genesis_block.mint_id != Pubkey::default());
assert!(genesis_block.tokens >= genesis_block.bootstrap_leader_tokens);
@ -869,8 +826,6 @@ mod tests {
use super::*;
use crate::entry::{next_entries, next_entry, Entry};
use crate::gen_keys::GenKeys;
use crate::status_deque;
use crate::status_deque::StatusDequeError;
use bincode::serialize;
use hashbrown::HashSet;
use solana_sdk::hash::hash;
@ -1577,199 +1532,6 @@ mod tests {
assert!(ids.into_iter().all(move |id| unique.insert(id)));
}
#[test]
fn test_bank_purge() {
let (genesis_block, alice) = GenesisBlock::new(10_000);
let bank = Bank::new(&genesis_block);
let bob = Keypair::new();
let charlie = Keypair::new();
// bob should have 500
bank.transfer(500, &alice, bob.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
bank.transfer(500, &alice, charlie.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
bank.checkpoint();
bank.checkpoint();
assert_eq!(bank.checkpoint_depth(), 2);
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
assert_eq!(bank.get_balance(&alice.pubkey()), 9_000);
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.transaction_count(), 2);
// transfer money back, so bob has zero
bank.transfer(500, &bob, alice.pubkey(), genesis_block.last_id())
.unwrap();
// this has to be stored as zero in the top accounts hashmap ;)
assert!(bank.accounts.load_slow(&bob.pubkey()).is_some());
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
// double-checks
assert_eq!(bank.get_balance(&alice.pubkey()), 9_500);
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.transaction_count(), 3);
bank.purge(1);
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
// double-checks
assert_eq!(bank.get_balance(&alice.pubkey()), 9_500);
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.transaction_count(), 3);
assert_eq!(bank.checkpoint_depth(), 1);
bank.purge(0);
// bob should still have 0, alice should have 10_000
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
assert!(bank.accounts.load_slow(&bob.pubkey()).is_none());
// double-checks
assert_eq!(bank.get_balance(&alice.pubkey()), 9_500);
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.transaction_count(), 3);
assert_eq!(bank.checkpoint_depth(), 0);
}
#[test]
fn test_bank_checkpoint_zero_balance() {
let (genesis_block, alice) = GenesisBlock::new(1_000);
let bank = Bank::new(&genesis_block);
let bob = Keypair::new();
let charlie = Keypair::new();
// bob should have 500
bank.transfer(500, &alice, bob.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
assert_eq!(bank.checkpoint_depth(), 0);
let account = bank.get_account(&alice.pubkey()).unwrap();
let default_account = Account::default();
assert_eq!(account.userdata, default_account.userdata);
assert_eq!(account.owner, default_account.owner);
assert_eq!(account.executable, default_account.executable);
assert_eq!(account.loader, default_account.loader);
bank.checkpoint();
assert_eq!(bank.checkpoint_depth(), 1);
// charlie should have 500, alice should have 0
bank.transfer(500, &alice, charlie.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.get_balance(&alice.pubkey()), 0);
let account = bank.get_account(&alice.pubkey()).unwrap();
assert_eq!(account.tokens, default_account.tokens);
assert_eq!(account.userdata, default_account.userdata);
assert_eq!(account.owner, default_account.owner);
assert_eq!(account.executable, default_account.executable);
assert_eq!(account.loader, default_account.loader);
}
fn reserve_signature_with_last_id_test(
bank: &Bank,
sig: &Signature,
last_id: &Hash,
) -> status_deque::Result<()> {
let mut last_ids = bank.last_ids.write().unwrap();
last_ids.reserve_signature_with_last_id(last_id, sig)
}
#[test]
fn test_bank_checkpoint_rollback() {
let (genesis_block, alice) = GenesisBlock::new(10_000);
let bank = Bank::new(&genesis_block);
let bob = Keypair::new();
let charlie = Keypair::new();
// bob should have 500
bank.transfer(500, &alice, bob.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
bank.transfer(500, &alice, charlie.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.checkpoint_depth(), 0);
bank.checkpoint();
bank.checkpoint();
assert_eq!(bank.checkpoint_depth(), 2);
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.transaction_count(), 2);
// transfer money back, so bob has zero
bank.transfer(500, &bob, alice.pubkey(), genesis_block.last_id())
.unwrap();
// this has to be stored as zero in the top accounts hashmap ;)
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.transaction_count(), 3);
bank.rollback();
// bob should have 500 again
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.transaction_count(), 2);
assert_eq!(bank.checkpoint_depth(), 1);
let signature = Signature::default();
for i in 0..MAX_ENTRY_IDS + 1 {
let last_id = hash(&serialize(&i).unwrap()); // Unique hash
bank.register_tick(&last_id);
}
assert_eq!(bank.tick_height(), MAX_ENTRY_IDS as u64 + 2);
assert_eq!(
reserve_signature_with_last_id_test(&bank, &signature, &genesis_block.last_id()),
Err(StatusDequeError::LastIdNotFound)
);
bank.rollback();
assert_eq!(bank.tick_height(), 1);
assert_eq!(
reserve_signature_with_last_id_test(&bank, &signature, &genesis_block.last_id()),
Ok(())
);
bank.checkpoint();
assert_eq!(
reserve_signature_with_last_id_test(&bank, &signature, &genesis_block.last_id()),
Err(StatusDequeError::DuplicateSignature)
);
}
#[test]
fn test_bank_checkpoint_and_copy() {
let (genesis_block, alice) = GenesisBlock::new(10_000);
let bank = Bank::new(&genesis_block);
let bob = Keypair::new();
let charlie = Keypair::new();
// bob should have 500
bank.transfer(500, &alice, bob.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
bank.transfer(500, &alice, charlie.pubkey(), genesis_block.last_id())
.unwrap();
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(bank.checkpoint_depth(), 0);
let cp_bank = bank.checkpoint_and_copy();
assert_eq!(cp_bank.get_balance(&bob.pubkey()), 500);
assert_eq!(cp_bank.get_balance(&charlie.pubkey()), 500);
assert_eq!(cp_bank.checkpoint_depth(), 0);
assert_eq!(bank.checkpoint_depth(), 1);
}
#[test]
#[should_panic]
fn test_bank_rollback_panic() {
let (genesis_block, _) = GenesisBlock::new(10_000);
let bank = Bank::new(&genesis_block);
bank.rollback();
}
#[test]
fn test_bank_record_transactions() {
let (genesis_block, mint_keypair) = GenesisBlock::new(10_000);

View File

@ -213,7 +213,7 @@ impl BroadcastService {
if let Err(e) = broadcast.run(&broadcast_table, receiver, sock, leader_scheduler) {
match e {
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => {
return BroadcastServiceReturnType::ChannelDisconnected
return BroadcastServiceReturnType::ChannelDisconnected;
}
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
Error::ClusterInfoError(ClusterInfoError::NoPeers) => (), // TODO: Why are the unit-tests throwing hundreds of these?

View File

@ -1,14 +0,0 @@
pub trait Checkpoint {
/// add a checkpoint to this data at current state
fn checkpoint(&mut self);
/// rollback to previous state, panics if no prior checkpoint
fn rollback(&mut self);
/// cull checkpoints to depth, that is depth of zero means
/// no checkpoints, only current state
fn purge(&mut self, depth: usize);
/// returns the number of checkpoints
fn depth(&self) -> usize;
}

View File

@ -278,7 +278,7 @@ impl Fullnode {
};
let tpu = Tpu::new(
&Arc::new(bank.checkpoint_and_copy()),
&bank,
Default::default(),
node.sockets
.tpu
@ -360,7 +360,7 @@ impl Fullnode {
let (to_validator_sender, to_validator_receiver) = channel();
self.role_notifiers.1 = to_validator_receiver;
self.node_services.tpu.switch_to_leader(
&Arc::new(self.bank.checkpoint_and_copy()),
&self.bank,
Default::default(),
self.tpu_sockets
.iter()

View File

@ -19,7 +19,6 @@ pub mod broadcast_service;
pub mod chacha;
#[cfg(all(feature = "chacha", feature = "cuda"))]
pub mod chacha_cuda;
pub mod checkpoint;
pub mod client;
pub mod crds;
pub mod crds_gossip;

View File

@ -1,10 +1,8 @@
use crate::checkpoint::Checkpoint;
use crate::poh_service::NUM_TICKS_PER_SECOND;
use hashbrown::HashMap;
use solana_sdk::hash::Hash;
use solana_sdk::signature::Signature;
use solana_sdk::timing::timestamp;
use std::collections::VecDeque;
use std::result;
/// The number of most recent `last_id` values that the bank will track the signatures
@ -67,8 +65,6 @@ pub struct StatusDeque<T> {
/// reject transactions with signatures it's seen before and to reject
/// transactions that are too old (nth is too small)
entries: StatusEntryMap<T>,
checkpoints: VecDeque<(u64, Option<Hash>, StatusEntryMap<T>)>,
}
impl<T> Default for StatusDeque<T> {
@ -77,32 +73,10 @@ impl<T> Default for StatusDeque<T> {
tick_height: 0,
last_id: None,
entries: HashMap::new(),
checkpoints: VecDeque::new(),
}
}
}
impl<T: Clone> Checkpoint for StatusDeque<T> {
fn checkpoint(&mut self) {
self.checkpoints
.push_front((self.tick_height, self.last_id, self.entries.clone()));
}
fn rollback(&mut self) {
let (tick_height, last_id, entries) = self.checkpoints.pop_front().unwrap();
self.tick_height = tick_height;
self.last_id = last_id;
self.entries = entries;
}
fn purge(&mut self, depth: usize) {
while self.depth() > depth {
self.checkpoints.pop_back().unwrap();
}
}
fn depth(&self) -> usize {
self.checkpoints.len()
}
}
impl<T: Clone> StatusDeque<T> {
pub fn update_signature_status_with_last_id(
&mut self,
@ -116,15 +90,6 @@ impl<T: Clone> StatusDeque<T> {
.insert(*signature, Status::Complete(result.clone()));
}
}
pub fn checkpoint_and_copy(&mut self) -> StatusDeque<T> {
self.checkpoint();
let (tick_height, last_id, entries) = self.checkpoints.front().unwrap().clone();
let mut copy = StatusDeque::default();
copy.tick_height = tick_height;
copy.last_id = last_id;
copy.entries = entries;
copy
}
pub fn reserve_signature_with_last_id(
&mut self,
last_id: &Hash,
@ -264,23 +229,6 @@ mod tests {
);
}
#[test]
fn test_duplicate_transaction_signature_checkpoint() {
let sig = Default::default();
let last_id = Default::default();
let mut status_deque: StatusDeque<()> = StatusDeque::default();
status_deque.register_tick(&last_id);
assert_eq!(
status_deque.reserve_signature_with_last_id(&last_id, &sig),
Ok(())
);
status_deque.checkpoint();
assert_eq!(
status_deque.reserve_signature_with_last_id(&last_id, &sig),
Err(StatusDequeError::DuplicateSignature)
);
}
#[test]
fn test_clear_signatures() {
let signature = Signature::default();
@ -297,23 +245,6 @@ mod tests {
);
}
#[test]
fn test_clear_signatures_checkpoint() {
let signature = Signature::default();
let last_id = Default::default();
let mut status_deque: StatusDeque<()> = StatusDeque::default();
status_deque.register_tick(&last_id);
status_deque
.reserve_signature_with_last_id(&last_id, &signature)
.unwrap();
status_deque.checkpoint();
status_deque.clear_signatures();
assert_eq!(
status_deque.reserve_signature_with_last_id(&last_id, &signature),
Ok(())
);
}
#[test]
fn test_get_signature_status() {
let signature = Signature::default();