Make sure banking stage is recording with the same bank that it read (#3447)

* make sure banking stage is recording with the same bank that it read with
This commit is contained in:
anatoly yakovenko
2019-03-22 14:17:39 -07:00
committed by GitHub
parent 60dfb35924
commit 52f6c33ff9
3 changed files with 74 additions and 17 deletions

View File

@ -217,6 +217,7 @@ impl BankingStage {
} }
fn record_transactions( fn record_transactions(
bank_slot: u64,
txs: &[Transaction], txs: &[Transaction],
results: &[bank::Result<()>], results: &[bank::Result<()>],
poh: &Arc<Mutex<PohRecorder>>, poh: &Arc<Mutex<PohRecorder>>,
@ -241,7 +242,9 @@ impl BankingStage {
if !processed_transactions.is_empty() { if !processed_transactions.is_empty() {
let hash = Transaction::hash(&processed_transactions); let hash = Transaction::hash(&processed_transactions);
// record and unlock will unlock all the successfull transactions // record and unlock will unlock all the successfull transactions
poh.lock().unwrap().record(hash, processed_transactions)?; poh.lock()
.unwrap()
.record(bank_slot, hash, processed_transactions)?;
} }
Ok(()) Ok(())
} }
@ -268,7 +271,7 @@ impl BankingStage {
let record_time = { let record_time = {
let now = Instant::now(); let now = Instant::now();
Self::record_transactions(txs, &results, poh)?; Self::record_transactions(bank.slot(), txs, &results, poh)?;
now.elapsed() now.elapsed()
}; };
@ -714,7 +717,8 @@ mod tests {
]; ];
let mut results = vec![Ok(()), Ok(())]; let mut results = vec![Ok(()), Ok(())];
BankingStage::record_transactions(&transactions, &results, &poh_recorder).unwrap(); BankingStage::record_transactions(bank.slot(), &transactions, &results, &poh_recorder)
.unwrap();
let (_, entries) = entry_receiver.recv().unwrap(); let (_, entries) = entry_receiver.recv().unwrap();
assert_eq!(entries[0].0.transactions.len(), transactions.len()); assert_eq!(entries[0].0.transactions.len(), transactions.len());
@ -723,13 +727,15 @@ mod tests {
1, 1,
InstructionError::new_result_with_negative_lamports(), InstructionError::new_result_with_negative_lamports(),
)); ));
BankingStage::record_transactions(&transactions, &results, &poh_recorder).unwrap(); BankingStage::record_transactions(bank.slot(), &transactions, &results, &poh_recorder)
.unwrap();
let (_, entries) = entry_receiver.recv().unwrap(); let (_, entries) = entry_receiver.recv().unwrap();
assert_eq!(entries[0].0.transactions.len(), transactions.len()); assert_eq!(entries[0].0.transactions.len(), transactions.len());
// Other TransactionErrors should not be recorded // Other TransactionErrors should not be recorded
results[0] = Err(TransactionError::AccountNotFound); results[0] = Err(TransactionError::AccountNotFound);
BankingStage::record_transactions(&transactions, &results, &poh_recorder).unwrap(); BankingStage::record_transactions(bank.slot(), &transactions, &results, &poh_recorder)
.unwrap();
let (_, entries) = entry_receiver.recv().unwrap(); let (_, entries) = entry_receiver.recv().unwrap();
assert_eq!(entries[0].0.transactions.len(), transactions.len() - 1); assert_eq!(entries[0].0.transactions.len(), transactions.len() - 1);
} }

View File

@ -257,9 +257,9 @@ impl PohRecorder {
let _ = self.flush_cache(true); let _ = self.flush_cache(true);
} }
pub fn record(&mut self, mixin: Hash, txs: Vec<Transaction>) -> Result<()> { pub fn record(&mut self, bank_slot: u64, mixin: Hash, txs: Vec<Transaction>) -> Result<()> {
self.flush_cache(false)?; self.flush_cache(false)?;
self.record_and_send_txs(mixin, txs) self.record_and_send_txs(bank_slot, mixin, txs)
} }
/// A recorder to synchronize PoH with the following data structures /// A recorder to synchronize PoH with the following data structures
@ -299,11 +299,19 @@ impl PohRecorder {
) )
} }
fn record_and_send_txs(&mut self, mixin: Hash, txs: Vec<Transaction>) -> Result<()> { fn record_and_send_txs(
&mut self,
bank_slot: u64,
mixin: Hash,
txs: Vec<Transaction>,
) -> Result<()> {
let working_bank = self let working_bank = self
.working_bank .working_bank
.as_ref() .as_ref()
.ok_or(Error::PohRecorderError(PohRecorderError::MaxHeightReached))?; .ok_or(Error::PohRecorderError(PohRecorderError::MaxHeightReached))?;
if bank_slot != working_bank.bank.slot() {
return Err(Error::PohRecorderError(PohRecorderError::MaxHeightReached));
}
let poh_entry = self.poh.record(mixin); let poh_entry = self.poh.record(mixin);
assert!(!txs.is_empty(), "Entries without transactions are used to track real-time passing in the ledger and can only be generated with PohRecorder::tick function"); assert!(!txs.is_empty(), "Entries without transactions are used to track real-time passing in the ledger and can only be generated with PohRecorder::tick function");
let recorded_entry = Entry { let recorded_entry = Entry {
@ -506,7 +514,7 @@ mod tests {
); );
let working_bank = WorkingBank { let working_bank = WorkingBank {
bank, bank: bank.clone(),
min_tick_height: 2, min_tick_height: 2,
max_tick_height: 3, max_tick_height: 3,
}; };
@ -514,10 +522,43 @@ mod tests {
poh_recorder.tick(); poh_recorder.tick();
let tx = test_tx(); let tx = test_tx();
let h1 = hash(b"hello world!"); let h1 = hash(b"hello world!");
assert!(poh_recorder.record(h1, vec![tx.clone()]).is_err()); assert!(poh_recorder
.record(bank.slot(), h1, vec![tx.clone()])
.is_err());
assert!(entry_receiver.try_recv().is_err()); assert!(entry_receiver.try_recv().is_err());
} }
#[test]
fn test_poh_recorder_record_bad_slot() {
let (genesis_block, _mint_keypair) = GenesisBlock::new(2);
let bank = Arc::new(Bank::new(&genesis_block));
let prev_hash = bank.last_blockhash();
let (mut poh_recorder, _entry_receiver) = PohRecorder::new(
0,
prev_hash,
0,
Some(4),
bank.ticks_per_slot(),
&Pubkey::default(),
);
let working_bank = WorkingBank {
bank: bank.clone(),
min_tick_height: 1,
max_tick_height: 2,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.len(), 1);
assert_eq!(poh_recorder.poh.tick_height, 1);
let tx = test_tx();
let h1 = hash(b"hello world!");
assert_matches!(
poh_recorder.record(bank.slot() + 1, h1, vec![tx.clone()]),
Err(Error::PohRecorderError(PohRecorderError::MaxHeightReached))
);
}
#[test] #[test]
fn test_poh_recorder_record_at_min_passes() { fn test_poh_recorder_record_at_min_passes() {
let (genesis_block, _mint_keypair) = GenesisBlock::new(2); let (genesis_block, _mint_keypair) = GenesisBlock::new(2);
@ -533,7 +574,7 @@ mod tests {
); );
let working_bank = WorkingBank { let working_bank = WorkingBank {
bank, bank: bank.clone(),
min_tick_height: 1, min_tick_height: 1,
max_tick_height: 2, max_tick_height: 2,
}; };
@ -543,7 +584,9 @@ mod tests {
assert_eq!(poh_recorder.poh.tick_height, 1); assert_eq!(poh_recorder.poh.tick_height, 1);
let tx = test_tx(); let tx = test_tx();
let h1 = hash(b"hello world!"); let h1 = hash(b"hello world!");
assert!(poh_recorder.record(h1, vec![tx.clone()]).is_ok()); assert!(poh_recorder
.record(bank.slot(), h1, vec![tx.clone()])
.is_ok());
assert_eq!(poh_recorder.tick_cache.len(), 0); assert_eq!(poh_recorder.tick_cache.len(), 0);
//tick in the cache + entry //tick in the cache + entry
@ -569,7 +612,7 @@ mod tests {
); );
let working_bank = WorkingBank { let working_bank = WorkingBank {
bank, bank: bank.clone(),
min_tick_height: 1, min_tick_height: 1,
max_tick_height: 2, max_tick_height: 2,
}; };
@ -579,7 +622,9 @@ mod tests {
assert_eq!(poh_recorder.poh.tick_height, 2); assert_eq!(poh_recorder.poh.tick_height, 2);
let tx = test_tx(); let tx = test_tx();
let h1 = hash(b"hello world!"); let h1 = hash(b"hello world!");
assert!(poh_recorder.record(h1, vec![tx.clone()]).is_err()); assert!(poh_recorder
.record(bank.slot(), h1, vec![tx.clone()])
.is_err());
let (_bank, e) = entry_receiver.recv().expect("recv 1"); let (_bank, e) = entry_receiver.recv().expect("recv 1");
assert_eq!(e.len(), 2); assert_eq!(e.len(), 2);
@ -745,7 +790,7 @@ mod tests {
let end_slot = 3; let end_slot = 3;
let max_tick_height = (end_slot + 1) * ticks_per_slot - 1; let max_tick_height = (end_slot + 1) * ticks_per_slot - 1;
let working_bank = WorkingBank { let working_bank = WorkingBank {
bank, bank: bank.clone(),
min_tick_height: 1, min_tick_height: 1,
max_tick_height, max_tick_height,
}; };
@ -757,7 +802,9 @@ mod tests {
let tx = test_tx(); let tx = test_tx();
let h1 = hash(b"hello world!"); let h1 = hash(b"hello world!");
assert!(poh_recorder.record(h1, vec![tx.clone()]).is_err()); assert!(poh_recorder
.record(bank.slot(), h1, vec![tx.clone()])
.is_err());
assert!(poh_recorder.working_bank.is_none()); assert!(poh_recorder.working_bank.is_none());
// Make sure the starting slot is updated // Make sure the starting slot is updated
assert_eq!(poh_recorder.start_slot(), end_slot); assert_eq!(poh_recorder.start_slot(), end_slot);

View File

@ -138,7 +138,11 @@ mod tests {
// send some data // send some data
let h1 = hash(b"hello world!"); let h1 = hash(b"hello world!");
let tx = test_tx(); let tx = test_tx();
poh_recorder.lock().unwrap().record(h1, vec![tx]).unwrap(); poh_recorder
.lock()
.unwrap()
.record(bank.slot(), h1, vec![tx])
.unwrap();
if exit.load(Ordering::Relaxed) { if exit.load(Ordering::Relaxed) {
break Ok(()); break Ok(());