use {
crate::shred::{Shred, ShredType},
bitflags::bitflags,
serde::{Deserialize, Deserializer, Serialize, Serializer},
solana_sdk::{
clock::{Slot, UnixTimestamp},
hash::Hash,
},
std::{
collections::BTreeSet,
ops::{Range, RangeBounds},
},
};
bitflags! {
#[derive(Deserialize, Serialize)]
pub struct ConnectedFlags:u8 {
const CONNECTED = 0b0000_0001;
const PARENT_CONNECTED = 0b1000_0000;
}
}
impl Default for ConnectedFlags {
fn default() -> Self {
ConnectedFlags::empty()
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
pub struct SlotMeta {
pub slot: Slot,
pub consumed: u64,
pub received: u64,
pub first_shred_timestamp: u64,
#[serde(with = "serde_compat")]
pub last_index: Option<u64>,
#[serde(with = "serde_compat")]
pub parent_slot: Option<Slot>,
pub next_slots: Vec<Slot>,
pub connected_flags: ConnectedFlags,
pub completed_data_indexes: BTreeSet<u32>,
}
mod serde_compat {
use super::*;
pub(super) fn serialize<S>(val: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
val.unwrap_or(u64::MAX).serialize(serializer)
}
pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
let val = u64::deserialize(deserializer)?;
Ok((val != u64::MAX).then(|| val))
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct Index {
pub slot: Slot,
data: ShredIndex,
coding: ShredIndex,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct ShredIndex {
index: BTreeSet<u64>,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct ErasureMeta {
set_index: u64,
first_coding_index: u64,
#[serde(rename = "size")]
__unused_size: usize,
config: ErasureConfig,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub(crate) struct ErasureConfig {
num_data: usize,
num_coding: usize,
}
#[derive(Deserialize, Serialize)]
pub struct DuplicateSlotProof {
#[serde(with = "serde_bytes")]
pub shred1: Vec<u8>,
#[serde(with = "serde_bytes")]
pub shred2: Vec<u8>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum ErasureMetaStatus {
CanRecover,
DataFull,
StillNeed(usize),
}
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub enum FrozenHashVersioned {
Current(FrozenHashStatus),
}
impl FrozenHashVersioned {
pub fn frozen_hash(&self) -> Hash {
match self {
FrozenHashVersioned::Current(frozen_hash_status) => frozen_hash_status.frozen_hash,
}
}
pub fn is_duplicate_confirmed(&self) -> bool {
match self {
FrozenHashVersioned::Current(frozen_hash_status) => {
frozen_hash_status.is_duplicate_confirmed
}
}
}
}
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct FrozenHashStatus {
pub frozen_hash: Hash,
pub is_duplicate_confirmed: bool,
}
impl Index {
pub(crate) fn new(slot: Slot) -> Self {
Index {
slot,
data: ShredIndex::default(),
coding: ShredIndex::default(),
}
}
pub fn data(&self) -> &ShredIndex {
&self.data
}
pub fn coding(&self) -> &ShredIndex {
&self.coding
}
pub(crate) fn data_mut(&mut self) -> &mut ShredIndex {
&mut self.data
}
pub(crate) fn coding_mut(&mut self) -> &mut ShredIndex {
&mut self.coding
}
}
impl ShredIndex {
pub fn num_shreds(&self) -> usize {
self.index.len()
}
pub(crate) fn range<R>(&self, bounds: R) -> impl Iterator<Item = &u64>
where
R: RangeBounds<u64>,
{
self.index.range(bounds)
}
pub(crate) fn contains(&self, index: u64) -> bool {
self.index.contains(&index)
}
pub(crate) fn insert(&mut self, index: u64) {
self.index.insert(index);
}
}
impl SlotMeta {
pub fn is_full(&self) -> bool {
if self
.last_index
.map(|ix| self.consumed > ix + 1)
.unwrap_or_default()
{
datapoint_error!(
"blockstore_error",
(
"error",
format!(
"Observed a slot meta with consumed: {} > meta.last_index + 1: {:?}",
self.consumed,
self.last_index.map(|ix| ix + 1),
),
String
)
);
}
Some(self.consumed) == self.last_index.map(|ix| ix + 1)
}
pub fn is_connected(&self) -> bool {
self.connected_flags.contains(ConnectedFlags::CONNECTED)
}
pub fn set_connected(&mut self) {
self.connected_flags.set(ConnectedFlags::CONNECTED, true);
}
pub fn unset_parent(&mut self) {
self.parent_slot = None;
}
pub fn clear_unconfirmed_slot(&mut self) {
let old = std::mem::replace(self, SlotMeta::new_orphan(self.slot));
self.next_slots = old.next_slots;
}
pub(crate) fn new(slot: Slot, parent_slot: Option<Slot>) -> Self {
let connected_flags = if slot == 0 {
ConnectedFlags::CONNECTED
} else {
ConnectedFlags::default()
};
SlotMeta {
slot,
parent_slot,
connected_flags,
..SlotMeta::default()
}
}
pub(crate) fn new_orphan(slot: Slot) -> Self {
Self::new(slot, None)
}
}
impl ErasureMeta {
pub(crate) fn from_coding_shred(shred: &Shred) -> Option<Self> {
match shred.shred_type() {
ShredType::Data => None,
ShredType::Code => {
let config = ErasureConfig {
num_data: usize::from(shred.num_data_shreds().ok()?),
num_coding: usize::from(shred.num_coding_shreds().ok()?),
};
let first_coding_index = u64::from(shred.first_coding_index()?);
let erasure_meta = ErasureMeta {
set_index: u64::from(shred.fec_set_index()),
config,
first_coding_index,
__unused_size: 0,
};
Some(erasure_meta)
}
}
}
pub(crate) fn check_coding_shred(&self, shred: &Shred) -> bool {
let mut other = match Self::from_coding_shred(shred) {
Some(erasure_meta) => erasure_meta,
None => return false,
};
other.__unused_size = self.__unused_size;
self == &other
}
pub(crate) fn config(&self) -> ErasureConfig {
self.config
}
pub(crate) fn data_shreds_indices(&self) -> Range<u64> {
let num_data = self.config.num_data as u64;
self.set_index..self.set_index + num_data
}
pub(crate) fn coding_shreds_indices(&self) -> Range<u64> {
let num_coding = self.config.num_coding as u64;
self.first_coding_index..self.first_coding_index + num_coding
}
pub(crate) fn status(&self, index: &Index) -> ErasureMetaStatus {
use ErasureMetaStatus::*;
let num_coding = index.coding().range(self.coding_shreds_indices()).count();
let num_data = index.data().range(self.data_shreds_indices()).count();
let (data_missing, num_needed) = (
self.config.num_data.saturating_sub(num_data),
self.config.num_data.saturating_sub(num_data + num_coding),
);
if data_missing == 0 {
DataFull
} else if num_needed == 0 {
CanRecover
} else {
StillNeed(num_needed)
}
}
}
impl DuplicateSlotProof {
pub(crate) fn new(shred1: Vec<u8>, shred2: Vec<u8>) -> Self {
DuplicateSlotProof { shred1, shred2 }
}
}
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct TransactionStatusIndexMeta {
pub max_slot: Slot,
pub frozen: bool,
}
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct AddressSignatureMeta {
pub writeable: bool,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct PerfSample {
pub num_transactions: u64,
pub num_slots: u64,
pub sample_period_secs: u16,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct ProgramCost {
pub cost: u64,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct OptimisticSlotMetaV0 {
pub hash: Hash,
pub timestamp: UnixTimestamp,
}
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub enum OptimisticSlotMetaVersioned {
V0(OptimisticSlotMetaV0),
}
impl OptimisticSlotMetaVersioned {
pub fn new(hash: Hash, timestamp: UnixTimestamp) -> Self {
OptimisticSlotMetaVersioned::V0(OptimisticSlotMetaV0 { hash, timestamp })
}
pub fn hash(&self) -> Hash {
match self {
OptimisticSlotMetaVersioned::V0(meta) => meta.hash,
}
}
pub fn timestamp(&self) -> UnixTimestamp {
match self {
OptimisticSlotMetaVersioned::V0(meta) => meta.timestamp,
}
}
}
#[cfg(test)]
mod test {
use {
super::*,
rand::{seq::SliceRandom, thread_rng},
};
#[test]
fn test_erasure_meta_status() {
use ErasureMetaStatus::*;
let set_index = 0;
let erasure_config = ErasureConfig {
num_data: 8,
num_coding: 16,
};
let e_meta = ErasureMeta {
set_index,
first_coding_index: set_index,
config: erasure_config,
__unused_size: 0,
};
let mut rng = thread_rng();
let mut index = Index::new(0);
let data_indexes = 0..erasure_config.num_data as u64;
let coding_indexes = 0..erasure_config.num_coding as u64;
assert_eq!(e_meta.status(&index), StillNeed(erasure_config.num_data));
for ix in data_indexes.clone() {
index.data_mut().insert(ix);
}
assert_eq!(e_meta.status(&index), DataFull);
for ix in coding_indexes.clone() {
index.coding_mut().insert(ix);
}
for &idx in data_indexes
.clone()
.collect::<Vec<_>>()
.choose_multiple(&mut rng, erasure_config.num_data)
{
index.data_mut().index.remove(&idx);
assert_eq!(e_meta.status(&index), CanRecover);
}
for ix in data_indexes {
index.data_mut().insert(ix);
}
for &idx in coding_indexes
.collect::<Vec<_>>()
.choose_multiple(&mut rng, erasure_config.num_coding)
{
index.coding_mut().index.remove(&idx);
assert_eq!(e_meta.status(&index), DataFull);
}
}
#[test]
fn test_connected_flags_compatibility() {
#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct WithBool {
slot: Slot,
connected: bool,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct WithFlags {
slot: Slot,
connected: ConnectedFlags,
}
let slot = 3;
let mut with_bool = WithBool {
slot,
connected: false,
};
let mut with_flags = WithFlags {
slot,
connected: ConnectedFlags::default(),
};
assert_eq!(
bincode::serialized_size(&with_bool).unwrap(),
bincode::serialized_size(&with_flags).unwrap()
);
assert_eq!(
bincode::serialize(&with_bool).unwrap(),
bincode::serialize(&with_flags).unwrap()
);
with_bool.connected = true;
assert_ne!(
bincode::serialize(&with_bool).unwrap(),
bincode::serialize(&with_flags).unwrap()
);
with_flags.connected.set(ConnectedFlags::CONNECTED, true);
assert_eq!(
bincode::serialize(&with_bool).unwrap(),
bincode::serialize(&with_flags).unwrap()
);
assert_eq!(
with_flags,
bincode::deserialize::<WithFlags>(&bincode::serialize(&with_bool).unwrap()).unwrap()
);
assert_eq!(
with_bool,
bincode::deserialize::<WithBool>(&bincode::serialize(&with_flags).unwrap()).unwrap()
);
with_flags
.connected
.set(ConnectedFlags::PARENT_CONNECTED, true);
assert!(
bincode::deserialize::<WithBool>(&bincode::serialize(&with_flags).unwrap()).is_err()
);
}
#[test]
fn test_clear_unconfirmed_slot() {
let mut slot_meta = SlotMeta::new_orphan(5);
slot_meta.consumed = 5;
slot_meta.received = 5;
slot_meta.next_slots = vec![6, 7];
slot_meta.clear_unconfirmed_slot();
let mut expected = SlotMeta::new_orphan(5);
expected.next_slots = vec![6, 7];
assert_eq!(slot_meta, expected);
}
}