Add special handling for snapshot root slot in get_confirmed_block (#7430)
* Add special handling for snapshot root slot * Improve test
This commit is contained in:
parent
46a4ea8f67
commit
1d0ba0d1f2
|
@ -3566,6 +3566,7 @@ dependencies = [
|
||||||
name = "solana-ledger"
|
name = "solana-ledger"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct RpcConfirmedBlock {
|
||||||
pub transactions: Vec<(Transaction, Option<RpcTransactionStatus>)>,
|
pub transactions: Vec<(Transaction, Option<RpcTransactionStatus>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct RpcTransactionStatus {
|
pub struct RpcTransactionStatus {
|
||||||
pub status: Result<()>,
|
pub status: Result<()>,
|
||||||
pub fee: u64,
|
pub fee: u64,
|
||||||
|
|
|
@ -55,6 +55,7 @@ default-features = false
|
||||||
features = ["lz4"]
|
features = ["lz4"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
assert_matches = "1.3.0"
|
||||||
matches = "0.1.6"
|
matches = "0.1.6"
|
||||||
solana-budget-program = { path = "../programs/budget", version = "0.22.0" }
|
solana-budget-program = { path = "../programs/budget", version = "0.22.0" }
|
||||||
|
|
||||||
|
|
|
@ -1182,24 +1182,30 @@ impl Blocktree {
|
||||||
.expect("Rooted slot must exist in SlotMeta");
|
.expect("Rooted slot must exist in SlotMeta");
|
||||||
|
|
||||||
let slot_entries = self.get_slot_entries(slot, 0, None)?;
|
let slot_entries = self.get_slot_entries(slot, 0, None)?;
|
||||||
let slot_transaction_iterator = slot_entries
|
if !slot_entries.is_empty() {
|
||||||
.iter()
|
let slot_transaction_iterator = slot_entries
|
||||||
.cloned()
|
.iter()
|
||||||
.flat_map(|entry| entry.transactions);
|
.cloned()
|
||||||
let parent_slot_entries = self.get_slot_entries(slot_meta.parent_slot, 0, None)?;
|
.flat_map(|entry| entry.transactions);
|
||||||
|
let parent_slot_entries = self.get_slot_entries(slot_meta.parent_slot, 0, None)?;
|
||||||
|
let previous_blockhash = if !parent_slot_entries.is_empty() {
|
||||||
|
get_last_hash(parent_slot_entries.iter()).unwrap()
|
||||||
|
} else {
|
||||||
|
Hash::default()
|
||||||
|
};
|
||||||
|
|
||||||
let block = RpcConfirmedBlock {
|
let block = RpcConfirmedBlock {
|
||||||
previous_blockhash: get_last_hash(parent_slot_entries.iter())
|
previous_blockhash,
|
||||||
.expect("Rooted parent slot must have blockhash"),
|
blockhash: get_last_hash(slot_entries.iter())
|
||||||
blockhash: get_last_hash(slot_entries.iter())
|
.unwrap_or_else(|| panic!("Rooted slot {:?} must have blockhash", slot)),
|
||||||
.expect("Rooted slot must have blockhash"),
|
parent_slot: slot_meta.parent_slot,
|
||||||
parent_slot: slot_meta.parent_slot,
|
transactions: self
|
||||||
transactions: self.map_transactions_to_statuses(slot, slot_transaction_iterator),
|
.map_transactions_to_statuses(slot, slot_transaction_iterator),
|
||||||
};
|
};
|
||||||
Ok(block)
|
return Ok(block);
|
||||||
} else {
|
}
|
||||||
Err(BlocktreeError::SlotNotRooted)
|
|
||||||
}
|
}
|
||||||
|
Err(BlocktreeError::SlotNotRooted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_transactions_to_statuses<'a>(
|
fn map_transactions_to_statuses<'a>(
|
||||||
|
@ -2137,11 +2143,13 @@ pub mod tests {
|
||||||
leader_schedule::{FixedSchedule, LeaderSchedule},
|
leader_schedule::{FixedSchedule, LeaderSchedule},
|
||||||
shred::{max_ticks_per_n_shreds, DataShredHeader},
|
shred::{max_ticks_per_n_shreds, DataShredHeader},
|
||||||
};
|
};
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
use bincode::serialize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
hash::{self, Hash},
|
hash::{self, hash, Hash},
|
||||||
instruction::CompiledInstruction,
|
instruction::CompiledInstruction,
|
||||||
packet::PACKET_DATA_SIZE,
|
packet::PACKET_DATA_SIZE,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -2153,7 +2161,7 @@ pub mod tests {
|
||||||
// used for tests only
|
// used for tests only
|
||||||
fn make_slot_entries_with_transactions(num_entries: u64) -> Vec<Entry> {
|
fn make_slot_entries_with_transactions(num_entries: u64) -> Vec<Entry> {
|
||||||
let mut entries: Vec<Entry> = Vec::new();
|
let mut entries: Vec<Entry> = Vec::new();
|
||||||
for _ in 0..num_entries {
|
for x in 0..num_entries {
|
||||||
let transaction = Transaction::new_with_compiled_instructions(
|
let transaction = Transaction::new_with_compiled_instructions(
|
||||||
&[&Keypair::new()],
|
&[&Keypair::new()],
|
||||||
&[Pubkey::new_rand()],
|
&[Pubkey::new_rand()],
|
||||||
|
@ -2162,7 +2170,7 @@ pub mod tests {
|
||||||
vec![CompiledInstruction::new(1, &(), vec![0])],
|
vec![CompiledInstruction::new(1, &(), vec![0])],
|
||||||
);
|
);
|
||||||
entries.push(next_entry_mut(&mut Hash::default(), 0, vec![transaction]));
|
entries.push(next_entry_mut(&mut Hash::default(), 0, vec![transaction]));
|
||||||
let mut tick = create_ticks(1, 0, Hash::default());
|
let mut tick = create_ticks(1, 0, hash(&serialize(&x).unwrap()));
|
||||||
entries.append(&mut tick);
|
entries.append(&mut tick);
|
||||||
}
|
}
|
||||||
entries
|
entries
|
||||||
|
@ -4208,13 +4216,22 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_confirmed_block() {
|
fn test_get_confirmed_block() {
|
||||||
let slot = 0;
|
let slot = 10;
|
||||||
let entries = make_slot_entries_with_transactions(100);
|
let entries = make_slot_entries_with_transactions(100);
|
||||||
let shreds = entries_to_test_shreds(entries.clone(), slot, 0, true, 0);
|
let blockhash = get_last_hash(entries.iter()).unwrap();
|
||||||
|
let shreds = entries_to_test_shreds(entries.clone(), slot, slot - 1, true, 0);
|
||||||
|
let more_shreds = entries_to_test_shreds(entries.clone(), slot + 1, slot, true, 0);
|
||||||
let ledger_path = get_tmp_ledger_path!();
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
let ledger = Blocktree::open(&ledger_path).unwrap();
|
let ledger = Blocktree::open(&ledger_path).unwrap();
|
||||||
ledger.insert_shreds(shreds, None, false).unwrap();
|
ledger.insert_shreds(shreds, None, false).unwrap();
|
||||||
ledger.set_roots(&[0]).unwrap();
|
ledger.insert_shreds(more_shreds, None, false).unwrap();
|
||||||
|
ledger.set_roots(&[slot - 1, slot, slot + 1]).unwrap();
|
||||||
|
|
||||||
|
let mut parent_meta = SlotMeta::default();
|
||||||
|
parent_meta.parent_slot = std::u64::MAX;
|
||||||
|
ledger
|
||||||
|
.put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let expected_transactions: Vec<(Transaction, Option<RpcTransactionStatus>)> = entries
|
let expected_transactions: Vec<(Transaction, Option<RpcTransactionStatus>)> = entries
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -4233,6 +4250,16 @@ pub mod tests {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
ledger
|
||||||
|
.transaction_status_cf
|
||||||
|
.put(
|
||||||
|
(slot + 1, signature),
|
||||||
|
&RpcTransactionStatus {
|
||||||
|
status: Ok(()),
|
||||||
|
fee: 42,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
(
|
(
|
||||||
transaction,
|
transaction,
|
||||||
Some(RpcTransactionStatus {
|
Some(RpcTransactionStatus {
|
||||||
|
@ -4243,17 +4270,33 @@ pub mod tests {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let confirmed_block = ledger.get_confirmed_block(0).unwrap();
|
// Even if marked as root, a slot that is empty of entries should return an error
|
||||||
|
let confirmed_block_err = ledger.get_confirmed_block(slot - 1).unwrap_err();
|
||||||
|
assert_matches!(confirmed_block_err, BlocktreeError::SlotNotRooted);
|
||||||
|
|
||||||
|
let confirmed_block = ledger.get_confirmed_block(slot).unwrap();
|
||||||
|
assert_eq!(confirmed_block.transactions.len(), 100);
|
||||||
|
|
||||||
|
let mut expected_block = RpcConfirmedBlock::default();
|
||||||
|
expected_block.transactions = expected_transactions.clone();
|
||||||
|
expected_block.parent_slot = slot - 1;
|
||||||
|
expected_block.blockhash = blockhash;
|
||||||
|
// The previous_blockhash of `expected_block` is default because its parent slot is a
|
||||||
|
// root, but empty of entries. This is special handling for snapshot root slots.
|
||||||
|
assert_eq!(confirmed_block, expected_block);
|
||||||
|
|
||||||
|
let confirmed_block = ledger.get_confirmed_block(slot + 1).unwrap();
|
||||||
assert_eq!(confirmed_block.transactions.len(), 100);
|
assert_eq!(confirmed_block.transactions.len(), 100);
|
||||||
|
|
||||||
let mut expected_block = RpcConfirmedBlock::default();
|
let mut expected_block = RpcConfirmedBlock::default();
|
||||||
expected_block.transactions = expected_transactions;
|
expected_block.transactions = expected_transactions;
|
||||||
// The blockhash and previous_blockhash of `expected_block` are default only because
|
expected_block.parent_slot = slot;
|
||||||
// `make_slot_entries_with_transactions` sets all entry hashes to default
|
expected_block.previous_blockhash = blockhash;
|
||||||
|
expected_block.blockhash = blockhash;
|
||||||
assert_eq!(confirmed_block, expected_block);
|
assert_eq!(confirmed_block, expected_block);
|
||||||
|
|
||||||
let not_root = ledger.get_confirmed_block(1);
|
let not_root = ledger.get_confirmed_block(slot + 2).unwrap_err();
|
||||||
assert!(not_root.is_err());
|
assert_matches!(not_root, BlocktreeError::SlotNotRooted);
|
||||||
|
|
||||||
drop(ledger);
|
drop(ledger);
|
||||||
Blocktree::destroy(&ledger_path).expect("Expected successful database destruction");
|
Blocktree::destroy(&ledger_path).expect("Expected successful database destruction");
|
||||||
|
|
Loading…
Reference in New Issue