use {
crate::{
blockstore::error::Result,
blockstore_meta::{self},
},
bincode::Options as BincodeOptions,
serde::{de::DeserializeOwned, Serialize},
solana_clock::{Slot, UnixTimestamp},
solana_pubkey::{Pubkey, PUBKEY_BYTES},
solana_signature::{Signature, SIGNATURE_BYTES},
solana_storage_proto::convert::generated,
};
pub(crate) const DEPRECATED_PROGRAM_COSTS_COLUMN_NAME: &str = "program_costs";
pub mod columns {
#[cfg(doc)]
use super::{blockstore_meta, generated, Pubkey, Signature, Slot, SlotColumn, UnixTimestamp};
#[derive(Debug)]
pub struct SlotMeta;
#[derive(Debug)]
pub struct Orphans;
#[derive(Debug)]
pub struct DeadSlots;
#[derive(Debug)]
pub struct DuplicateSlots;
#[derive(Debug)]
pub struct ErasureMeta;
#[derive(Debug)]
pub struct BankHash;
#[derive(Debug)]
pub struct Root;
#[derive(Debug)]
pub struct Index;
#[derive(Debug)]
pub struct ShredData;
#[derive(Debug)]
pub struct ShredCode;
#[derive(Debug)]
pub struct TransactionStatus;
#[derive(Debug)]
pub struct AddressSignatures;
#[derive(Debug)]
pub struct TransactionMemos;
#[derive(Debug)]
pub struct TransactionStatusIndex;
#[derive(Debug)]
pub struct Rewards;
#[derive(Debug)]
pub struct Blocktime;
#[derive(Debug)]
pub struct PerfSamples;
#[derive(Debug)]
pub struct BlockHeight;
#[derive(Debug)]
pub struct OptimisticSlots;
#[derive(Debug)]
pub struct MerkleRootMeta;
}
macro_rules! convert_column_index_to_key_bytes {
($key:ident, $($range:expr => $bytes:expr),* $(,)?) => {{
let mut key = [0u8; std::mem::size_of::<Self::$key>()];
debug_assert_eq!(0 $(+$bytes.len())*, key.len());
$(key[$range].copy_from_slice($bytes);)*
key
}};
}
macro_rules! convert_column_key_bytes_to_index {
($k:ident, $($a:literal..$b:literal => $f:expr),* $(,)?) => {{
($($f(<[u8; $b-$a]>::try_from(&$k[$a..$b]).unwrap())),*)
}};
}
pub trait Column {
type Index;
type Key: AsRef<[u8]>;
fn key(index: &Self::Index) -> Self::Key;
fn index(key: &[u8]) -> Self::Index;
fn as_index(slot: Slot) -> Self::Index;
fn slot(index: Self::Index) -> Slot;
}
pub trait ColumnName {
const NAME: &'static str;
}
pub trait TypedColumn: Column {
type Type: Serialize + DeserializeOwned;
fn deserialize(data: &[u8]) -> Result<Self::Type> {
Ok(bincode::deserialize(data)?)
}
fn serialize(data: &Self::Type) -> Result<Vec<u8>> {
Ok(bincode::serialize(data)?)
}
}
pub trait ProtobufColumn: Column {
type Type: prost::Message + Default;
}
pub trait SlotColumn<Index = Slot> {}
pub enum IndexError {
UnpackError,
}
pub trait ColumnIndexDeprecation: Column {
const CURRENT_INDEX_LEN: usize;
type DeprecatedIndex;
type DeprecatedKey: AsRef<[u8]>;
fn deprecated_key(index: Self::DeprecatedIndex) -> Self::DeprecatedKey;
fn try_deprecated_index(key: &[u8]) -> std::result::Result<Self::DeprecatedIndex, IndexError>;
fn try_current_index(key: &[u8]) -> std::result::Result<Self::Index, IndexError>;
fn convert_index(deprecated_index: Self::DeprecatedIndex) -> Self::Index;
fn index(key: &[u8]) -> Self::Index {
if let Ok(index) = Self::try_current_index(key) {
index
} else if let Ok(index) = Self::try_deprecated_index(key) {
Self::convert_index(index)
} else {
Self::as_index(0)
}
}
}
impl TypedColumn for columns::AddressSignatures {
type Type = blockstore_meta::AddressSignatureMeta;
}
impl TypedColumn for columns::TransactionMemos {
type Type = String;
}
impl TypedColumn for columns::TransactionStatusIndex {
type Type = blockstore_meta::TransactionStatusIndexMeta;
}
impl<T: SlotColumn> Column for T {
type Index = Slot;
type Key = [u8; std::mem::size_of::<Slot>()];
#[inline]
fn key(slot: &Self::Index) -> Self::Key {
slot.to_be_bytes()
}
fn index(key: &[u8]) -> Self::Index {
convert_column_key_bytes_to_index!(key, 0..8 => Slot::from_be_bytes)
}
fn slot(index: Self::Index) -> Slot {
index
}
fn as_index(slot: Slot) -> u64 {
slot
}
}
impl Column for columns::TransactionStatus {
type Index = (Signature, Slot);
type Key = [u8; SIGNATURE_BYTES + std::mem::size_of::<Slot>()];
#[inline]
fn key((signature, slot): &Self::Index) -> Self::Key {
convert_column_index_to_key_bytes!(Key,
..64 => signature.as_ref(),
64.. => &slot.to_be_bytes(),
)
}
fn index(key: &[u8]) -> (Signature, Slot) {
<columns::TransactionStatus as ColumnIndexDeprecation>::index(key)
}
fn slot(index: Self::Index) -> Slot {
index.1
}
fn as_index(_index: u64) -> Self::Index {
(Signature::default(), 0)
}
}
impl ColumnName for columns::TransactionStatus {
const NAME: &'static str = "transaction_status";
}
impl ProtobufColumn for columns::TransactionStatus {
type Type = generated::TransactionStatusMeta;
}
impl ColumnIndexDeprecation for columns::TransactionStatus {
const CURRENT_INDEX_LEN: usize = 72;
type DeprecatedIndex = (u64, Signature, Slot);
type DeprecatedKey = [u8; 80];
fn deprecated_key((index, signature, slot): Self::DeprecatedIndex) -> Self::DeprecatedKey {
convert_column_index_to_key_bytes!(DeprecatedKey,
..8 => &index.to_be_bytes(),
8..72 => signature.as_ref(),
72.. => &slot.to_be_bytes(),
)
}
fn try_deprecated_index(key: &[u8]) -> std::result::Result<Self::DeprecatedIndex, IndexError> {
if key.len() != std::mem::size_of::<Self::DeprecatedKey>() {
return Err(IndexError::UnpackError);
}
Ok(convert_column_key_bytes_to_index!(key,
0..8 => u64::from_be_bytes, 8..72 => Signature::from,
72..80 => Slot::from_be_bytes,
))
}
fn try_current_index(key: &[u8]) -> std::result::Result<Self::Index, IndexError> {
if key.len() != Self::CURRENT_INDEX_LEN {
return Err(IndexError::UnpackError);
}
Ok(convert_column_key_bytes_to_index!(key,
0..64 => Signature::from,
64..72 => Slot::from_be_bytes,
))
}
fn convert_index(deprecated_index: Self::DeprecatedIndex) -> Self::Index {
let (_primary_index, signature, slot) = deprecated_index;
(signature, slot)
}
}
impl Column for columns::AddressSignatures {
type Index = (Pubkey, Slot, u32, Signature);
type Key = [u8; PUBKEY_BYTES
+ std::mem::size_of::<Slot>()
+ std::mem::size_of::<u32>()
+ SIGNATURE_BYTES];
#[inline]
fn key((pubkey, slot, transaction_index, signature): &Self::Index) -> Self::Key {
convert_column_index_to_key_bytes!(Key,
..32 => pubkey.as_ref(),
32..40 => &slot.to_be_bytes(),
40..44 => &transaction_index.to_be_bytes(),
44.. => signature.as_ref(),
)
}
fn index(key: &[u8]) -> Self::Index {
<columns::AddressSignatures as ColumnIndexDeprecation>::index(key)
}
fn slot(index: Self::Index) -> Slot {
index.1
}
fn as_index(_index: u64) -> Self::Index {
(Pubkey::default(), 0, 0, Signature::default())
}
}
impl ColumnName for columns::AddressSignatures {
const NAME: &'static str = "address_signatures";
}
impl ColumnIndexDeprecation for columns::AddressSignatures {
const CURRENT_INDEX_LEN: usize = 108;
type DeprecatedIndex = (u64, Pubkey, Slot, Signature);
type DeprecatedKey = [u8; 112];
fn deprecated_key(
(primary_index, pubkey, slot, signature): Self::DeprecatedIndex,
) -> Self::DeprecatedKey {
convert_column_index_to_key_bytes!(DeprecatedKey,
..8 => &primary_index.to_be_bytes(),
8..40 => pubkey.as_ref(),
40..48 => &slot.to_be_bytes(),
48.. => signature.as_ref(),
)
}
fn try_deprecated_index(key: &[u8]) -> std::result::Result<Self::DeprecatedIndex, IndexError> {
if key.len() != std::mem::size_of::<Self::DeprecatedKey>() {
return Err(IndexError::UnpackError);
}
Ok(convert_column_key_bytes_to_index!(key,
0..8 => u64::from_be_bytes, 8..40 => Pubkey::from,
40..48 => Slot::from_be_bytes,
48..112 => Signature::from,
))
}
fn try_current_index(key: &[u8]) -> std::result::Result<Self::Index, IndexError> {
if key.len() != Self::CURRENT_INDEX_LEN {
return Err(IndexError::UnpackError);
}
Ok(convert_column_key_bytes_to_index!(key,
0..32 => Pubkey::from,
32..40 => Slot::from_be_bytes,
40..44 => u32::from_be_bytes, 44..108 => Signature::from,
))
}
fn convert_index(deprecated_index: Self::DeprecatedIndex) -> Self::Index {
let (_primary_index, pubkey, slot, signature) = deprecated_index;
(pubkey, slot, 0, signature)
}
}
impl Column for columns::TransactionMemos {
type Index = (Signature, Slot);
type Key = [u8; SIGNATURE_BYTES + std::mem::size_of::<Slot>()];
#[inline]
fn key((signature, slot): &Self::Index) -> Self::Key {
convert_column_index_to_key_bytes!(Key,
..64 => signature.as_ref(),
64.. => &slot.to_be_bytes(),
)
}
fn index(key: &[u8]) -> Self::Index {
<columns::TransactionMemos as ColumnIndexDeprecation>::index(key)
}
fn slot(index: Self::Index) -> Slot {
index.1
}
fn as_index(index: u64) -> Self::Index {
(Signature::default(), index)
}
}
impl ColumnName for columns::TransactionMemos {
const NAME: &'static str = "transaction_memos";
}
impl ColumnIndexDeprecation for columns::TransactionMemos {
const CURRENT_INDEX_LEN: usize = 72;
type DeprecatedIndex = Signature;
type DeprecatedKey = [u8; 64];
fn deprecated_key(signature: Self::DeprecatedIndex) -> Self::DeprecatedKey {
Self::DeprecatedKey::from(signature)
}
fn try_deprecated_index(key: &[u8]) -> std::result::Result<Self::DeprecatedIndex, IndexError> {
Signature::try_from(&key[..64]).map_err(|_| IndexError::UnpackError)
}
fn try_current_index(key: &[u8]) -> std::result::Result<Self::Index, IndexError> {
if key.len() != Self::CURRENT_INDEX_LEN {
return Err(IndexError::UnpackError);
}
Ok(convert_column_key_bytes_to_index!(key,
0..64 => Signature::from,
64..72 => Slot::from_be_bytes,
))
}
fn convert_index(deprecated_index: Self::DeprecatedIndex) -> Self::Index {
(deprecated_index, 0)
}
}
impl Column for columns::TransactionStatusIndex {
type Index = u64;
type Key = [u8; std::mem::size_of::<u64>()];
#[inline]
fn key(index: &Self::Index) -> Self::Key {
index.to_be_bytes()
}
fn index(key: &[u8]) -> Self::Index {
convert_column_key_bytes_to_index!(key, 0..8 => u64::from_be_bytes)
}
fn slot(_index: Self::Index) -> Slot {
unimplemented!()
}
fn as_index(slot: u64) -> u64 {
slot
}
}
impl ColumnName for columns::TransactionStatusIndex {
const NAME: &'static str = "transaction_status_index";
}
impl SlotColumn for columns::Rewards {}
impl ColumnName for columns::Rewards {
const NAME: &'static str = "rewards";
}
impl ProtobufColumn for columns::Rewards {
type Type = generated::Rewards;
}
impl SlotColumn for columns::Blocktime {}
impl ColumnName for columns::Blocktime {
const NAME: &'static str = "blocktime";
}
impl TypedColumn for columns::Blocktime {
type Type = UnixTimestamp;
}
impl SlotColumn for columns::PerfSamples {}
impl ColumnName for columns::PerfSamples {
const NAME: &'static str = "perf_samples";
}
impl SlotColumn for columns::BlockHeight {}
impl ColumnName for columns::BlockHeight {
const NAME: &'static str = "block_height";
}
impl TypedColumn for columns::BlockHeight {
type Type = u64;
}
impl Column for columns::ShredCode {
type Index = (Slot, u64);
type Key = <columns::ShredData as Column>::Key;
#[inline]
fn key(index: &Self::Index) -> Self::Key {
<columns::ShredData as Column>::key(index)
}
fn index(key: &[u8]) -> Self::Index {
columns::ShredData::index(key)
}
fn slot(index: Self::Index) -> Slot {
index.0
}
fn as_index(slot: Slot) -> Self::Index {
(slot, 0)
}
}
impl ColumnName for columns::ShredCode {
const NAME: &'static str = "code_shred";
}
impl Column for columns::ShredData {
type Index = (Slot, u64);
type Key = [u8; std::mem::size_of::<Slot>() + std::mem::size_of::<u64>()];
#[inline]
fn key((slot, index): &Self::Index) -> Self::Key {
convert_column_index_to_key_bytes!(Key,
..8 => &slot.to_be_bytes(),
8.. => &index.to_be_bytes(),
)
}
fn index(key: &[u8]) -> Self::Index {
convert_column_key_bytes_to_index!(key,
0..8 => Slot::from_be_bytes,
8..16 => u64::from_be_bytes, )
}
fn slot(index: Self::Index) -> Slot {
index.0
}
fn as_index(slot: Slot) -> Self::Index {
(slot, 0)
}
}
impl ColumnName for columns::ShredData {
const NAME: &'static str = "data_shred";
}
impl SlotColumn for columns::Index {}
impl ColumnName for columns::Index {
const NAME: &'static str = "index";
}
impl TypedColumn for columns::Index {
type Type = blockstore_meta::Index;
fn deserialize(data: &[u8]) -> Result<Self::Type> {
let config = bincode::DefaultOptions::new()
.with_fixint_encoding()
.reject_trailing_bytes();
let index: bincode::Result<blockstore_meta::Index> = config.deserialize(data);
match index {
Ok(index) => Ok(index),
Err(_) => {
let index: blockstore_meta::IndexFallback = config.deserialize(data)?;
Ok(index.into())
}
}
}
}
impl SlotColumn for columns::DeadSlots {}
impl ColumnName for columns::DeadSlots {
const NAME: &'static str = "dead_slots";
}
impl TypedColumn for columns::DeadSlots {
type Type = bool;
}
impl SlotColumn for columns::DuplicateSlots {}
impl ColumnName for columns::DuplicateSlots {
const NAME: &'static str = "duplicate_slots";
}
impl TypedColumn for columns::DuplicateSlots {
type Type = blockstore_meta::DuplicateSlotProof;
}
impl SlotColumn for columns::Orphans {}
impl ColumnName for columns::Orphans {
const NAME: &'static str = "orphans";
}
impl TypedColumn for columns::Orphans {
type Type = bool;
}
impl SlotColumn for columns::BankHash {}
impl ColumnName for columns::BankHash {
const NAME: &'static str = "bank_hashes";
}
impl TypedColumn for columns::BankHash {
type Type = blockstore_meta::FrozenHashVersioned;
}
impl SlotColumn for columns::Root {}
impl ColumnName for columns::Root {
const NAME: &'static str = "root";
}
impl TypedColumn for columns::Root {
type Type = bool;
}
impl SlotColumn for columns::SlotMeta {}
impl ColumnName for columns::SlotMeta {
const NAME: &'static str = "meta";
}
impl TypedColumn for columns::SlotMeta {
type Type = blockstore_meta::SlotMeta;
fn deserialize(data: &[u8]) -> Result<Self::Type> {
let config = bincode::DefaultOptions::new()
.with_fixint_encoding()
.reject_trailing_bytes();
let index: bincode::Result<blockstore_meta::SlotMeta> = config.deserialize(data);
match index {
Ok(index) => Ok(index),
Err(_) => {
let index: blockstore_meta::SlotMetaFallback = config.deserialize(data)?;
Ok(index.into())
}
}
}
}
impl Column for columns::ErasureMeta {
type Index = (Slot, u64);
type Key = [u8; std::mem::size_of::<Slot>() + std::mem::size_of::<u64>()];
#[inline]
fn key((slot, fec_set_index): &Self::Index) -> Self::Key {
convert_column_index_to_key_bytes!(Key,
..8 => &slot.to_be_bytes(),
8.. => &fec_set_index.to_be_bytes(),
)
}
fn index(key: &[u8]) -> Self::Index {
convert_column_key_bytes_to_index!(key,
0..8 => Slot::from_be_bytes,
8..16 => u64::from_be_bytes, )
}
fn slot(index: Self::Index) -> Slot {
index.0
}
fn as_index(slot: Slot) -> Self::Index {
(slot, 0)
}
}
impl ColumnName for columns::ErasureMeta {
const NAME: &'static str = "erasure_meta";
}
impl TypedColumn for columns::ErasureMeta {
type Type = blockstore_meta::ErasureMeta;
}
impl SlotColumn for columns::OptimisticSlots {}
impl ColumnName for columns::OptimisticSlots {
const NAME: &'static str = "optimistic_slots";
}
impl TypedColumn for columns::OptimisticSlots {
type Type = blockstore_meta::OptimisticSlotMetaVersioned;
}
impl Column for columns::MerkleRootMeta {
type Index = (Slot, u32);
type Key = [u8; std::mem::size_of::<Slot>() + std::mem::size_of::<u32>()];
#[inline]
fn key((slot, fec_set_index): &Self::Index) -> Self::Key {
convert_column_index_to_key_bytes!(Key,
..8 => &slot.to_be_bytes(),
8.. => &fec_set_index.to_be_bytes(),
)
}
fn index(key: &[u8]) -> Self::Index {
convert_column_key_bytes_to_index!(key,
0..8 => Slot::from_be_bytes,
8..12 => u32::from_be_bytes, )
}
fn slot((slot, _fec_set_index): Self::Index) -> Slot {
slot
}
fn as_index(slot: Slot) -> Self::Index {
(slot, 0)
}
}
impl ColumnName for columns::MerkleRootMeta {
const NAME: &'static str = "merkle_root_meta";
}
impl TypedColumn for columns::MerkleRootMeta {
type Type = blockstore_meta::MerkleRootMeta;
}