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:
parent
99b42f210c
commit
852a2146ab
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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::{
|
||||||
|
blocktree_db::{
|
||||||
columns as cf, Column, Database, IteratorDirection, IteratorMode, LedgerColumn, WriteBatch,
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue