Add ConfirmedBlock struct, and rework Blocktree apis to include block… (#7004)

* Add RpcConfirmedBlock struct, and rework Blocktree apis to include blockhash info and dummy tx statuses

* Remove unused lifetime
This commit is contained in:
Tyera Eulberg 2019-11-17 20:17:15 -07:00 committed by GitHub
parent b66c03667c
commit 3bc8d78801
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 54 deletions

1
Cargo.lock generated
View File

@ -3573,6 +3573,7 @@ dependencies = [
"serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-budget-api 0.21.0",
"solana-client 0.21.0",
"solana-genesis-programs 0.21.0",
"solana-logger 0.21.0",
"solana-measure 0.21.0",

View File

@ -3,6 +3,8 @@ use serde_json::{json, Value};
use solana_sdk::{
clock::{Epoch, Slot},
commitment_config::CommitmentConfig,
hash::Hash,
transaction::{Result, Transaction},
};
use std::{error, fmt, io, net::SocketAddr};
@ -20,6 +22,13 @@ pub struct Response<T> {
pub value: T,
}
#[derive(Debug, Default, PartialEq, Serialize)]
pub struct RpcConfirmedBlock {
pub previous_blockhash: Hash,
pub blockhash: Hash,
pub transactions: Vec<(Transaction, Result<()>)>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RpcContactInfo {
/// Pubkey of the node as a base-58 string

View File

@ -12,8 +12,8 @@ use bincode::serialize;
use jsonrpc_core::{Error, Metadata, Result};
use jsonrpc_derive::rpc;
use solana_client::rpc_request::{
Response, RpcContactInfo, RpcEpochInfo, RpcResponseContext, RpcVersionInfo, RpcVoteAccountInfo,
RpcVoteAccountStatus,
Response, RpcConfirmedBlock, RpcContactInfo, RpcEpochInfo, RpcResponseContext, RpcVersionInfo,
RpcVoteAccountInfo, RpcVoteAccountStatus,
};
use solana_drone::drone::request_airdrop_transaction;
use solana_ledger::{bank_forks::BankForks, blocktree::Blocktree};
@ -26,10 +26,9 @@ use solana_sdk::{
fee_calculator::FeeCalculator,
hash::Hash,
inflation::Inflation,
instruction::InstructionError,
pubkey::Pubkey,
signature::Signature,
transaction::{self, Transaction, TransactionError},
transaction::{self, Transaction},
};
use solana_vote_api::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
use std::{
@ -302,37 +301,13 @@ impl JsonRpcRequestProcessor {
}
}
// The `get_confirmed_block` method is not fully implemented. It currenlty returns a batch of
// 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<()>)>> {
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())
// The `get_confirmed_block` method is not fully implemented. It currenlty returns a partially
// complete RpcConfirmedBlock. The `blockhash` and `previous_blockhash` fields are legitimate
// data, while the `transactions` field contains transaction tuples (Transaction,
// transaction::Result), where the Transaction is a legitimate transaction, but the Result is
// always `Ok()`.
pub fn get_confirmed_block(&self, slot: Slot) -> Result<Option<RpcConfirmedBlock>> {
Ok(self.blocktree.get_confirmed_block(slot).ok())
}
}
@ -537,12 +512,12 @@ pub trait RpcSol {
#[rpc(meta, name = "setLogFilter")]
fn set_log_filter(&self, _meta: Self::Metadata, filter: String) -> Result<()>;
#[rpc(meta, name = "getConfirmedBlock")]
#[rpc(meta, name = "getRpcConfirmedBlock")]
fn get_confirmed_block(
&self,
meta: Self::Metadata,
slot: Slot,
) -> Result<Vec<(Transaction, transaction::Result<()>)>>;
) -> Result<Option<RpcConfirmedBlock>>;
}
pub struct RpcSolImpl;
@ -991,7 +966,7 @@ impl RpcSol for RpcSolImpl {
&self,
meta: Self::Metadata,
slot: Slot,
) -> Result<Vec<(Transaction, transaction::Result<()>)>> {
) -> Result<Option<RpcConfirmedBlock>> {
meta.request_processor
.read()
.unwrap()

View File

@ -28,6 +28,7 @@ rayon = "1.2.0"
reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0.1-3", features = ["simd-accel"] }
serde = "1.0.102"
serde_derive = "1.0.102"
solana-client = { path = "../client", version = "0.21.0" }
solana-genesis-programs = { path = "../genesis-programs", version = "0.21.0" }
solana-logger = { path = "../logger", version = "0.21.0" }
solana-measure = { path = "../measure", version = "0.21.0" }

View File

@ -22,6 +22,7 @@ use rayon::{
ThreadPool,
};
use rocksdb::DBRawIterator;
use solana_client::rpc_request::RpcConfirmedBlock;
use solana_measure::measure::Measure;
use solana_metrics::{datapoint_debug, datapoint_error};
use solana_rayon_threadlimit::get_thread_count;
@ -31,7 +32,7 @@ use solana_sdk::{
hash::Hash,
signature::{Keypair, KeypairUtil},
timing::timestamp,
transaction::Transaction,
transaction::{self, Transaction},
};
use std::{
cell::RefCell,
@ -1123,19 +1124,42 @@ impl Blocktree {
}
}
pub fn get_confirmed_block_transactions(&self, slot: Slot) -> Result<Vec<Transaction>> {
pub fn get_confirmed_block(&self, slot: Slot) -> Result<RpcConfirmedBlock> {
if self.is_root(slot) {
Ok(self
.get_slot_entries(slot, 0, None)?
let slot_meta_cf = self.db.column::<cf::SlotMeta>();
let slot_meta = slot_meta_cf
.get(slot)?
.expect("Rooted slot must exist in SlotMeta");
let slot_entries = self.get_slot_entries(slot, 0, None)?;
let slot_transaction_iterator = slot_entries
.iter()
.cloned()
.flat_map(|entry| entry.transactions)
.collect())
.flat_map(|entry| entry.transactions);
let parent_slot_entries = self.get_slot_entries(slot_meta.parent_slot, 0, None)?;
let block = RpcConfirmedBlock {
previous_blockhash: get_last_hash(parent_slot_entries.iter())
.expect("Rooted parent slot must have blockhash"),
blockhash: get_last_hash(slot_entries.iter())
.expect("Rooted slot must have blockhash"),
transactions: self.map_transactions_to_statuses(slot_transaction_iterator),
};
Ok(block)
} else {
Err(BlocktreeError::SlotNotRooted)
}
}
// The `map_transactions_to_statuses` method is not fully implemented (depends on persistent
// status store). It currently returns status Ok(()) for all transactions.
fn map_transactions_to_statuses<'a>(
&self,
iterator: impl Iterator<Item = Transaction> + 'a,
) -> Vec<(Transaction, transaction::Result<()>)> {
iterator.map(|transaction| (transaction, Ok(()))).collect()
}
/// Returns the entry vector for the slot starting with `shred_start_index`
pub fn get_slot_entries(
&self,
@ -1485,6 +1509,10 @@ fn get_slot_meta_entry<'a>(
})
}
fn get_last_hash<'a>(iterator: impl Iterator<Item = &'a Entry> + 'a) -> Option<Hash> {
iterator.last().map(|entry| entry.hash)
}
fn is_valid_write_to_slot_0(slot_to_write: u64, parent_slot: Slot, last_root: u64) -> bool {
slot_to_write == 0 && last_root == 0 && parent_slot == 0
}
@ -1984,15 +2012,19 @@ fn adjust_ulimit_nofile() {
pub mod tests {
use super::*;
use crate::{
entry::next_entry_mut,
entry::{next_entry, next_entry_mut},
genesis_utils::{create_genesis_config, GenesisConfigInfo},
shred::{max_ticks_per_n_shreds, DataShredHeader},
};
use itertools::Itertools;
use rand::{seq::SliceRandom, thread_rng};
use solana_sdk::{
hash::Hash, instruction::CompiledInstruction, packet::PACKET_DATA_SIZE, pubkey::Pubkey,
signature::Signature, transaction::TransactionError,
hash::{self, Hash},
instruction::CompiledInstruction,
packet::PACKET_DATA_SIZE,
pubkey::Pubkey,
signature::Signature,
transaction::TransactionError,
};
use std::{iter::FromIterator, time::Duration};
@ -4054,7 +4086,7 @@ pub mod tests {
}
#[test]
fn test_get_confirmed_block_transactions() {
fn test_get_confirmed_block() {
let slot = 0;
let (shreds, entries) = make_slot_entries_with_transactions(slot, 0, 100);
@ -4063,17 +4095,24 @@ pub mod tests {
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
let confirmed_block = ledger.get_confirmed_block(0).unwrap();
assert_eq!(confirmed_block.transactions.len(), 100);
let expected_transactions: Vec<(Transaction, transaction::Result<()>)> = entries
.iter()
.cloned()
.filter(|entry| !entry.is_tick())
.flat_map(|entry| entry.transactions)
.map(|transaction| (transaction, Ok(())))
.collect();
assert_eq!(transactions, expected_transactions);
let not_root = ledger.get_confirmed_block_transactions(1);
let mut expected_block = RpcConfirmedBlock::default();
expected_block.transactions = expected_transactions;
// The blockhash and previous_blockhash of `expected_block` are default only because
// `make_slot_entries_with_transactions` sets all entry hashes to default
assert_eq!(confirmed_block, expected_block);
let not_root = ledger.get_confirmed_block(1);
assert!(not_root.is_err());
drop(ledger);
@ -4134,4 +4173,20 @@ pub mod tests {
}
Blocktree::destroy(&blocktree_path).expect("Expected successful database destruction");
}
#[test]
fn test_get_last_hash() {
let mut entries: Vec<Entry> = vec![];
let empty_entries_iterator = entries.iter();
assert!(get_last_hash(empty_entries_iterator).is_none());
let mut prev_hash = hash::hash(&[42u8]);
for _ in 0..10 {
let entry = next_entry(&prev_hash, 1, vec![]);
prev_hash = entry.hash;
entries.push(entry);
}
let entries_iterator = entries.iter();
assert_eq!(get_last_hash(entries_iterator).unwrap(), entries[9].hash);
}
}

View File

@ -275,7 +275,7 @@ impl Column for columns::TransactionStatus {
key
}
fn index<'a>(key: &[u8]) -> (Slot, Signature) {
fn index(key: &[u8]) -> (Slot, Signature) {
let slot = BigEndian::read_u64(&key[..8]);
let index = Signature::new(&key[8..72]);
(slot, index)