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,
commitment::{BlockCommitment, BlockCommitmentCache},
contact_info::ContactInfo,
gen_keys::GenKeys,
packet::PACKET_DATA_SIZE,
storage_stage::StorageState,
validator::ValidatorExit,
@ -29,8 +28,7 @@ use solana_sdk::{
inflation::Inflation,
instruction::InstructionError,
pubkey::Pubkey,
signature::{KeypairUtil, Signature},
system_transaction,
signature::Signature,
transaction::{self, Transaction, TransactionError},
};
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
// test transaction tuples (Transaction, transaction::Result) to demonstrate message format and
// TransactionErrors. Transaction count == slot, and transaction keys are derived
// deterministically to allow testers to track the pubkeys across slots.
// transaction tuples (Transaction, transaction::Result), where the Transaction is a legitimate
// transaction, but the Result is mocked to demonstrate Ok results and TransactionErrors.
pub fn get_confirmed_block(
&self,
slot: Slot,
) -> 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)]
pub mod tests {
use super::*;
@ -1021,7 +1012,7 @@ pub mod tests {
fee_calculator::DEFAULT_BURN_PERCENT,
hash::{hash, Hash},
instruction::InstructionError,
signature::Keypair,
signature::{Keypair, KeypairUtil},
system_transaction,
transaction::TransactionError,
};

View File

@ -1,38 +1,50 @@
//! The `blocktree` module provides functions for parallel verification of the
//! Proof of History ledger as well as iterative read, append write, and random
//! access read to a persistent file-based ledger.
use crate::blocktree_db::{
columns as cf, Column, Database, IteratorDirection, IteratorMode, LedgerColumn, WriteBatch,
use crate::{
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 log::*;
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use rayon::ThreadPool;
use rayon::{
iter::{IntoParallelRefIterator, ParallelIterator},
ThreadPool,
};
use rocksdb::DBRawIterator;
use solana_measure::measure::Measure;
use solana_metrics::{datapoint_debug, datapoint_error};
use solana_rayon_threadlimit::get_thread_count;
use solana_sdk::clock::{Slot, DEFAULT_TICKS_PER_SECOND};
use solana_sdk::genesis_config::GenesisConfig;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::timing::timestamp;
use std::cell::RefCell;
use std::cmp;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError};
use std::sync::{Arc, Mutex, RwLock};
use solana_sdk::{
clock::{Slot, DEFAULT_TICKS_PER_SECOND},
genesis_config::GenesisConfig,
hash::Hash,
signature::{Keypair, KeypairUtil},
timing::timestamp,
transaction::Transaction,
};
use std::{
cell::RefCell,
cmp,
collections::HashMap,
fs,
path::{Path, PathBuf},
rc::Rc,
sync::{
mpsc::{sync_channel, Receiver, SyncSender, TrySendError},
Arc, Mutex, RwLock,
},
};
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`
pub fn get_slot_entries(
&self,
@ -1951,15 +1976,40 @@ fn adjust_ulimit_nofile() {
#[cfg(test)]
pub mod tests {
use super::*;
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use crate::shred::{max_ticks_per_n_shreds, DataShredHeader};
use crate::{
entry::next_entry_mut,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
shred::{max_ticks_per_n_shreds, DataShredHeader},
};
use itertools::Itertools;
use rand::seq::SliceRandom;
use rand::thread_rng;
use solana_sdk::hash::Hash;
use solana_sdk::packet::PACKET_DATA_SIZE;
use std::iter::FromIterator;
use std::time::Duration;
use rand::{seq::SliceRandom, thread_rng};
use solana_sdk::{
hash::Hash, instruction::CompiledInstruction, packet::PACKET_DATA_SIZE, pubkey::Pubkey,
};
use std::{iter::FromIterator, 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]
fn test_create_new_ledger() {
@ -3994,4 +4044,31 @@ pub mod tests {
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");
}
}