Add Blocktree api to get transactions by slot (#6966)

* Add blocktree method to get confirmed-block txs

* Clean up use statements

* Add test, and fmt

* Plumb new blocktree method into getConfirmedBlock
This commit is contained in:
Tyera Eulberg 2019-11-14 16:34:39 -07:00 committed by GitHub
parent 99b42f210c
commit 852a2146ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 70 deletions

View File

@ -4,7 +4,6 @@ use crate::{
cluster_info::ClusterInfo, cluster_info::ClusterInfo,
commitment::{BlockCommitment, BlockCommitmentCache}, commitment::{BlockCommitment, BlockCommitmentCache},
contact_info::ContactInfo, contact_info::ContactInfo,
gen_keys::GenKeys,
packet::PACKET_DATA_SIZE, packet::PACKET_DATA_SIZE,
storage_stage::StorageState, storage_stage::StorageState,
validator::ValidatorExit, validator::ValidatorExit,
@ -29,8 +28,7 @@ use solana_sdk::{
inflation::Inflation, inflation::Inflation,
instruction::InstructionError, instruction::InstructionError,
pubkey::Pubkey, pubkey::Pubkey,
signature::{KeypairUtil, Signature}, signature::Signature,
system_transaction,
transaction::{self, Transaction, TransactionError}, transaction::{self, Transaction, TransactionError},
}; };
use solana_vote_api::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}; use solana_vote_api::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
@ -305,14 +303,36 @@ impl JsonRpcRequestProcessor {
} }
// The `get_confirmed_block` method is not fully implemented. It currenlty returns a batch of // The `get_confirmed_block` method is not fully implemented. It currenlty returns a batch of
// test transaction tuples (Transaction, transaction::Result) to demonstrate message format and // transaction tuples (Transaction, transaction::Result), where the Transaction is a legitimate
// TransactionErrors. Transaction count == slot, and transaction keys are derived // transaction, but the Result is mocked to demonstrate Ok results and TransactionErrors.
// deterministically to allow testers to track the pubkeys across slots.
pub fn get_confirmed_block( pub fn get_confirmed_block(
&self, &self,
slot: Slot, slot: Slot,
) -> Result<Vec<(Transaction, transaction::Result<()>)>> { ) -> Result<Vec<(Transaction, transaction::Result<()>)>> {
Ok(make_test_transactions(slot)) let transactions = self
.blocktree
.get_confirmed_block_transactions(slot)
.unwrap_or_else(|_| vec![]);
Ok(transactions
.iter()
.enumerate()
.map(|(i, transaction)| {
let status = if i % 3 == 0 {
Ok(())
} else if i % 3 == 1 {
Err(TransactionError::InstructionError(
0,
InstructionError::InsufficientFunds,
))
} else {
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(3),
))
};
(transaction.clone(), status)
})
.collect())
} }
} }
@ -979,35 +999,6 @@ impl RpcSol for RpcSolImpl {
} }
} }
fn make_test_transactions(count: u64) -> Vec<(Transaction, transaction::Result<()>)> {
let seed = [42u8; 32];
let keys = GenKeys::new(seed).gen_n_keypairs(count + 1);
let mut transactions: Vec<(Transaction, transaction::Result<()>)> = Vec::new();
for x in 0..count {
let tx = system_transaction::transfer(
&keys[x as usize],
&keys[(x + 1) as usize].pubkey(),
123,
Hash::default(),
);
let status = if x % 3 == 0 {
Ok(())
} else if x % 3 == 1 {
Err(TransactionError::InstructionError(
0,
InstructionError::InsufficientFunds,
))
} else {
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(3),
))
};
transactions.push((tx, status))
}
transactions
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
@ -1021,7 +1012,7 @@ pub mod tests {
fee_calculator::DEFAULT_BURN_PERCENT, fee_calculator::DEFAULT_BURN_PERCENT,
hash::{hash, Hash}, hash::{hash, Hash},
instruction::InstructionError, instruction::InstructionError,
signature::Keypair, signature::{Keypair, KeypairUtil},
system_transaction, system_transaction,
transaction::TransactionError, transaction::TransactionError,
}; };

View File

@ -1,38 +1,50 @@
//! The `blocktree` module provides functions for parallel verification of the //! The `blocktree` module provides functions for parallel verification of the
//! Proof of History ledger as well as iterative read, append write, and random //! Proof of History ledger as well as iterative read, append write, and random
//! access read to a persistent file-based ledger. //! access read to a persistent file-based ledger.
use crate::blocktree_db::{ use crate::{
columns as cf, Column, Database, IteratorDirection, IteratorMode, LedgerColumn, WriteBatch, blocktree_db::{
columns as cf, Column, Database, IteratorDirection, IteratorMode, LedgerColumn, WriteBatch,
},
blocktree_meta::*,
entry::{create_ticks, Entry},
erasure::ErasureConfig,
leader_schedule_cache::LeaderScheduleCache,
shred::{Shred, Shredder},
};
pub use crate::{
blocktree_db::{BlocktreeError, Result},
blocktree_meta::SlotMeta,
}; };
pub use crate::blocktree_db::{BlocktreeError, Result};
pub use crate::blocktree_meta::SlotMeta;
use crate::blocktree_meta::*;
use crate::entry::{create_ticks, Entry};
use crate::erasure::ErasureConfig;
use crate::leader_schedule_cache::LeaderScheduleCache;
use crate::shred::{Shred, Shredder};
use bincode::deserialize; use bincode::deserialize;
use log::*; use log::*;
use rayon::iter::IntoParallelRefIterator; use rayon::{
use rayon::iter::ParallelIterator; iter::{IntoParallelRefIterator, ParallelIterator},
use rayon::ThreadPool; ThreadPool,
};
use rocksdb::DBRawIterator; use rocksdb::DBRawIterator;
use solana_measure::measure::Measure; use solana_measure::measure::Measure;
use solana_metrics::{datapoint_debug, datapoint_error}; use solana_metrics::{datapoint_debug, datapoint_error};
use solana_rayon_threadlimit::get_thread_count; use solana_rayon_threadlimit::get_thread_count;
use solana_sdk::clock::{Slot, DEFAULT_TICKS_PER_SECOND}; use solana_sdk::{
use solana_sdk::genesis_config::GenesisConfig; clock::{Slot, DEFAULT_TICKS_PER_SECOND},
use solana_sdk::hash::Hash; genesis_config::GenesisConfig,
use solana_sdk::signature::{Keypair, KeypairUtil}; hash::Hash,
use solana_sdk::timing::timestamp; signature::{Keypair, KeypairUtil},
use std::cell::RefCell; timing::timestamp,
use std::cmp; transaction::Transaction,
use std::collections::HashMap; };
use std::fs; use std::{
use std::path::{Path, PathBuf}; cell::RefCell,
use std::rc::Rc; cmp,
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError}; collections::HashMap,
use std::sync::{Arc, Mutex, RwLock}; fs,
path::{Path, PathBuf},
rc::Rc,
sync::{
mpsc::{sync_channel, Receiver, SyncSender, TrySendError},
Arc, Mutex, RwLock,
},
};
pub const BLOCKTREE_DIRECTORY: &str = "rocksdb"; pub const BLOCKTREE_DIRECTORY: &str = "rocksdb";
@ -1104,6 +1116,19 @@ impl Blocktree {
} }
} }
pub fn get_confirmed_block_transactions(&self, slot: Slot) -> Result<Vec<Transaction>> {
if self.is_root(slot) {
Ok(self
.get_slot_entries(slot, 0, None)?
.iter()
.cloned()
.flat_map(|entry| entry.transactions)
.collect())
} else {
Err(BlocktreeError::SlotNotRooted)
}
}
/// Returns the entry vector for the slot starting with `shred_start_index` /// Returns the entry vector for the slot starting with `shred_start_index`
pub fn get_slot_entries( pub fn get_slot_entries(
&self, &self,
@ -1951,15 +1976,40 @@ fn adjust_ulimit_nofile() {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo}; use crate::{
use crate::shred::{max_ticks_per_n_shreds, DataShredHeader}; entry::next_entry_mut,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
shred::{max_ticks_per_n_shreds, DataShredHeader},
};
use itertools::Itertools; use itertools::Itertools;
use rand::seq::SliceRandom; use rand::{seq::SliceRandom, thread_rng};
use rand::thread_rng; use solana_sdk::{
use solana_sdk::hash::Hash; hash::Hash, instruction::CompiledInstruction, packet::PACKET_DATA_SIZE, pubkey::Pubkey,
use solana_sdk::packet::PACKET_DATA_SIZE; };
use std::iter::FromIterator; use std::{iter::FromIterator, time::Duration};
use std::time::Duration;
// used for tests only
fn make_slot_entries_with_transactions(
slot: Slot,
parent_slot: Slot,
num_entries: u64,
) -> (Vec<Shred>, Vec<Entry>) {
let mut entries: Vec<Entry> = Vec::new();
for _ in 0..num_entries {
let transaction = Transaction::new_with_compiled_instructions(
&[&Keypair::new()],
&[Pubkey::new_rand()],
Hash::default(),
vec![Pubkey::new_rand()],
vec![CompiledInstruction::new(1, &(), vec![0])],
);
entries.push(next_entry_mut(&mut Hash::default(), 0, vec![transaction]));
let mut tick = create_ticks(1, 0, Hash::default());
entries.append(&mut tick);
}
let shreds = entries_to_test_shreds(entries.clone(), slot, parent_slot, true);
(shreds, entries)
}
#[test] #[test]
fn test_create_new_ledger() { fn test_create_new_ledger() {
@ -3994,4 +4044,31 @@ pub mod tests {
assert!(blocktree.get_data_shred(1, 0).unwrap().is_some()); assert!(blocktree.get_data_shred(1, 0).unwrap().is_some());
} }
} }
#[test]
fn test_get_confirmed_block_transactions() {
let slot = 0;
let (shreds, entries) = make_slot_entries_with_transactions(slot, 0, 100);
let ledger_path = get_tmp_ledger_path!();
let ledger = Blocktree::open(&ledger_path).unwrap();
ledger.insert_shreds(shreds, None, false).unwrap();
ledger.set_roots(&[0]).unwrap();
let transactions = ledger.get_confirmed_block_transactions(0).unwrap();
assert_eq!(transactions.len(), 100);
let expected_transactions: Vec<Transaction> = entries
.iter()
.cloned()
.filter(|entry| !entry.is_tick())
.flat_map(|entry| entry.transactions)
.collect();
assert_eq!(transactions, expected_transactions);
let not_root = ledger.get_confirmed_block_transactions(1);
assert!(not_root.is_err());
drop(ledger);
Blocktree::destroy(&ledger_path).expect("Expected successful database destruction");
}
} }