Skip to content

Commit

Permalink
blockstore: send duplicate proofs for chained merkle root conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
AshwinSekar committed Mar 6, 2024
1 parent c161351 commit 2e5fd6f
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 11 deletions.
23 changes: 23 additions & 0 deletions core/src/window_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ fn run_check_duplicate(
shred_slot,
&root_bank,
);
let chained_merkle_conflict_duplicate_proofs = cluster_nodes::check_feature_activation(
&feature_set::chained_merkle_conflict_duplicate_proofs::id(),
shred_slot,
&root_bank,
);
let (shred1, shred2) = match shred {
PossibleDuplicateShred::LastIndexConflict(shred, conflict)
| PossibleDuplicateShred::ErasureConflict(shred, conflict) => {
Expand Down Expand Up @@ -196,6 +201,24 @@ fn run_check_duplicate(
return Ok(());
}
}
PossibleDuplicateShred::ChainedMerkleRootConflict(shred, conflict) => {
if chained_merkle_conflict_duplicate_proofs {
// Although this proof can be immediately stored on detection, we wait until
// here in order to check the feature flag, as storage in blockstore can
// preclude the detection of other duplicate proofs in this slot
if blockstore.has_duplicate_shreds_in_slot(shred_slot) {
return Ok(());
}
blockstore.store_duplicate_slot(
shred_slot,
conflict.clone(),
shred.clone().into_payload(),
)?;
(shred, conflict)
} else {
return Ok(());
}
}
PossibleDuplicateShred::Exists(shred) => {
// Unlike the other cases we have to wait until here to decide to handle the duplicate and store
// in blockstore. This is because the duplicate could have been part of the same insert batch,
Expand Down
249 changes: 240 additions & 9 deletions ledger/src/blockstore.rs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions ledger/src/blockstore_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,12 @@ impl ErasureMeta {
self.first_coding_index..self.first_coding_index + num_coding
}

pub(crate) fn next_set_index(&self) -> u32 {
u32::try_from(self.set_index)
.unwrap()
.saturating_add(self.config.num_data as u32)
}

pub(crate) fn status(&self, index: &Index) -> ErasureMetaStatus {
use ErasureMetaStatus::*;

Expand Down
23 changes: 22 additions & 1 deletion ledger/src/shred.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,14 @@ impl ShredId {
}

/// Tuple which identifies erasure coding set that the shred belongs to.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) struct ErasureSetId(Slot, /*fec_set_index:*/ u32);

impl ErasureSetId {
pub(crate) fn new(slot: Slot, fec_set_index: u32) -> Self {
Self(slot, fec_set_index)
}

pub(crate) fn slot(&self) -> Slot {
self.0
}
Expand Down Expand Up @@ -352,6 +356,7 @@ impl Shred {
dispatch!(pub(crate) fn erasure_shard_index(&self) -> Result<usize, Error>);

dispatch!(pub fn into_payload(self) -> Vec<u8>);
dispatch!(pub fn chained_merkle_root(&self) -> Result<Hash, Error>);
dispatch!(pub fn merkle_root(&self) -> Result<Hash, Error>);
dispatch!(pub fn payload(&self) -> &Vec<u8>);
dispatch!(pub fn sanitize(&self) -> Result<(), Error>);
Expand Down Expand Up @@ -726,6 +731,22 @@ pub mod layout {
}
}

pub fn get_chained_merkle_root(shred: &[u8]) -> Option<Hash> {
match get_shred_variant(shred).ok()? {
ShredVariant::LegacyCode | ShredVariant::LegacyData => None,
ShredVariant::MerkleCode {
proof_size,
chained,
resigned,
} => merkle::ShredCode::get_chained_merkle_root(shred, proof_size, chained, resigned),
ShredVariant::MerkleData {
proof_size,
chained,
resigned,
} => merkle::ShredData::get_chained_merkle_root(shred, proof_size, chained, resigned),
}
}

// Minimally corrupts the packet so that the signature no longer verifies.
#[cfg(test)]
pub(crate) fn corrupt_packet<R: Rng>(
Expand Down
68 changes: 67 additions & 1 deletion ledger/src/shred/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,18 @@ impl ShredData {
Ok(Self::SIZE_OF_HEADERS + Self::capacity(proof_size, /*chained:*/ true, resigned)?)
}

fn get_chained_merkle_root_offset(proof_size: u8, resigned: bool) -> Result<usize, Error> {
Ok(Self::SIZE_OF_HEADERS + Self::capacity(proof_size, /*chained:*/ true, resigned)?)
}

pub(super) fn chained_merkle_root(&self) -> Result<Hash, Error> {
let offset = self.chained_merkle_root_offset()?;
self.payload
.get(offset..offset + SIZE_OF_MERKLE_ROOT)
.map(Hash::new)
.ok_or(Error::InvalidPayloadSize(self.payload.len()))
}

fn set_chained_merkle_root(&mut self, chained_merkle_root: &Hash) -> Result<(), Error> {
let offset = self.chained_merkle_root_offset()?;
let Some(buffer) = self.payload.get_mut(offset..offset + SIZE_OF_MERKLE_ROOT) else {
Expand Down Expand Up @@ -307,6 +319,31 @@ impl ShredData {
let node = get_merkle_node(shred, SIZE_OF_SIGNATURE..proof_offset).ok()?;
get_merkle_root(index, node, proof).ok()
}

pub(super) fn get_chained_merkle_root(
shred: &[u8],
proof_size: u8,
chained: bool,
resigned: bool,
) -> Option<Hash> {
debug_assert_eq!(
shred::layout::get_shred_variant(shred).unwrap(),
ShredVariant::MerkleData {
proof_size,
chained,
resigned
}
);

if !chained {
return None;
}

let offset = Self::get_chained_merkle_root_offset(proof_size, resigned).ok()?;
shred
.get(offset..offset + SIZE_OF_MERKLE_ROOT)
.map(Hash::new)
}
}

impl ShredCode {
Expand Down Expand Up @@ -364,7 +401,11 @@ impl ShredCode {
Ok(Self::SIZE_OF_HEADERS + Self::capacity(proof_size, /*chained:*/ true, resigned)?)
}

fn chained_merkle_root(&self) -> Result<Hash, Error> {
fn get_chained_merkle_root_offset(proof_size: u8, resigned: bool) -> Result<usize, Error> {
Ok(Self::SIZE_OF_HEADERS + Self::capacity(proof_size, /*chained:*/ true, resigned)?)
}

pub(super) fn chained_merkle_root(&self) -> Result<Hash, Error> {
let offset = self.chained_merkle_root_offset()?;
self.payload
.get(offset..offset + SIZE_OF_MERKLE_ROOT)
Expand Down Expand Up @@ -487,6 +528,31 @@ impl ShredCode {
let node = get_merkle_node(shred, SIZE_OF_SIGNATURE..proof_offset).ok()?;
get_merkle_root(index, node, proof).ok()
}

pub(super) fn get_chained_merkle_root(
shred: &[u8],
proof_size: u8,
chained: bool,
resigned: bool,
) -> Option<Hash> {
debug_assert_eq!(
shred::layout::get_shred_variant(shred).unwrap(),
ShredVariant::MerkleCode {
proof_size,
chained,
resigned
}
);

if !chained {
return None;
}

let offset = Self::get_chained_merkle_root_offset(proof_size, resigned).ok()?;
shred
.get(offset..offset + SIZE_OF_MERKLE_ROOT)
.map(Hash::new)
}
}

impl<'a> ShredTrait<'a> for ShredData {
Expand Down
7 changes: 7 additions & 0 deletions ledger/src/shred/shred_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ impl ShredCode {
}
}

pub(super) fn chained_merkle_root(&self) -> Result<Hash, Error> {
match self {
Self::Legacy(_) => Err(Error::InvalidShredType),
Self::Merkle(shred) => shred.chained_merkle_root(),
}
}

pub(super) fn merkle_root(&self) -> Result<Hash, Error> {
match self {
Self::Legacy(_) => Err(Error::InvalidShredType),
Expand Down
7 changes: 7 additions & 0 deletions ledger/src/shred/shred_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ impl ShredData {
}
}

pub(super) fn chained_merkle_root(&self) -> Result<Hash, Error> {
match self {
Self::Legacy(_) => Err(Error::InvalidShredType),
Self::Merkle(shred) => shred.chained_merkle_root(),
}
}

pub(super) fn merkle_root(&self) -> Result<Hash, Error> {
match self {
Self::Legacy(_) => Err(Error::InvalidShredType),
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/feature_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,10 @@ pub mod enable_gossip_duplicate_proof_ingestion {
solana_sdk::declare_id!("FNKCMBzYUdjhHyPdsKG2LSmdzH8TCHXn3ytj8RNBS4nG");
}

pub mod chained_merkle_conflict_duplicate_proofs {
solana_sdk::declare_id!("chaie9S2zVfuxJKNRGkyTDokLwWxx6kD2ZLsqQHaDD8");
}

pub mod enable_chained_merkle_shreds {
solana_sdk::declare_id!("7uZBkJXJ1HkuP6R3MJfZs7mLwymBcDbKdqbF51ZWLier");
}
Expand Down Expand Up @@ -965,6 +969,7 @@ lazy_static! {
(enable_gossip_duplicate_proof_ingestion::id(), "enable gossip duplicate proof ingestion #32963"),
(enable_chained_merkle_shreds::id(), "Enable chained Merkle shreds #34916"),
(remove_rounding_in_fee_calculation::id(), "Removing unwanted rounding in fee calculation #34982"),
(chained_merkle_conflict_duplicate_proofs::id(), "generate duplicate proofs for chained merkle root conflicts #102"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
Expand Down

0 comments on commit 2e5fd6f

Please sign in to comment.