Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions ledger/src/blockstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3788,18 +3788,25 @@ impl Blockstore {
.expect("fetch from DuplicateSlots column family failed")
}

// `new_shred` is assumed to have slot and index equal to the given slot and index.
// Returns the existing shred if `new_shred` is not equal to the existing shred at the
// given slot and index as this implies the leader generated two different shreds with
// the same slot and index
pub fn is_shred_duplicate(&self, new_shred: &Shred) -> Option<Vec<u8>> {
let (slot, index, shred_type) = new_shred.id().unpack();
let existing_shred = match shred_type {
ShredType::Data => self.get_data_shred(slot, index as u64),
ShredType::Code => self.get_coding_shred(slot, index as u64),
/// Returns the shred already stored in blockstore if it has a different
/// payload than the given `shred` but the same (slot, index, shred-type).
/// This implies the leader generated two different shreds with the same
/// slot, index and shred-type.
/// The payload is modified so that it has the same retransmitter's
/// signature as the `shred` argument.
pub fn is_shred_duplicate(&self, shred: &Shred) -> Option<Vec<u8>> {
let (slot, index, shred_type) = shred.id().unpack();
let mut other = match shred_type {
ShredType::Data => self.get_data_shred(slot, u64::from(index)),
ShredType::Code => self.get_coding_shred(slot, u64::from(index)),
}
.expect("fetch from DuplicateSlots column family failed")?;
(existing_shred != *new_shred.payload()).then_some(existing_shred)
if let Ok(signature) = shred.retransmitter_signature() {
if let Err(err) = shred::layout::set_retransmitter_signature(&mut other, &signature) {
error!("set retransmitter signature failed: {err:?}");
}
}
(&other != shred.payload()).then_some(other)
}

pub fn has_duplicate_shreds_in_slot(&self, slot: Slot) -> bool {
Expand Down
29 changes: 29 additions & 0 deletions ledger/src/shred.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ impl Shred {
dispatch!(pub(crate) fn erasure_shard_as_slice(&self) -> Result<&[u8], Error>);
// Returns the shard index within the erasure coding set.
dispatch!(pub(crate) fn erasure_shard_index(&self) -> Result<usize, Error>);
dispatch!(pub(crate) fn retransmitter_signature(&self) -> Result<Signature, Error>);

dispatch!(pub fn into_payload(self) -> Vec<u8>);
dispatch!(pub fn merkle_root(&self) -> Result<Hash, Error>);
Expand Down Expand Up @@ -751,6 +752,34 @@ pub mod layout {
.map(Hash::new)
}

pub(crate) fn set_retransmitter_signature(
shred: &mut [u8],
signature: &Signature,
) -> Result<(), Error> {
let offset = match get_shred_variant(shred)? {
ShredVariant::LegacyCode | ShredVariant::LegacyData => Err(Error::InvalidShredVariant),
ShredVariant::MerkleCode {
proof_size,
chained,
resigned,
} => {
merkle::ShredCode::get_retransmitter_signature_offset(proof_size, chained, resigned)
}
ShredVariant::MerkleData {
proof_size,
chained,
resigned,
} => {
merkle::ShredData::get_retransmitter_signature_offset(proof_size, chained, resigned)
}
}?;
let Some(buffer) = shred.get_mut(offset..offset + SIZE_OF_SIGNATURE) else {
return Err(Error::InvalidPayloadSize(shred.len()));
};
buffer.copy_from_slice(signature.as_ref());
Ok(())
}

// Minimally corrupts the packet so that the signature no longer verifies.
#[cfg(test)]
pub(crate) fn corrupt_packet<R: Rng>(
Expand Down
66 changes: 66 additions & 0 deletions ledger/src/shred/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,39 @@ impl ShredData {
let node = get_merkle_node(shred, SIZE_OF_SIGNATURE..proof_offset).ok()?;
get_merkle_root(index, node, proof).ok()
}

pub(super) fn retransmitter_signature(&self) -> Result<Signature, Error> {
let offset = self.retransmitter_signature_offset()?;
self.payload
.get(offset..offset + SIZE_OF_SIGNATURE)
.map(|bytes| <[u8; SIZE_OF_SIGNATURE]>::try_from(bytes).unwrap())
.map(Signature::from)
.ok_or(Error::InvalidPayloadSize(self.payload.len()))
}

fn retransmitter_signature_offset(&self) -> Result<usize, Error> {
let ShredVariant::MerkleData {
proof_size,
chained,
resigned,
} = self.common_header.shred_variant
else {
return Err(Error::InvalidShredVariant);
};
Self::get_retransmitter_signature_offset(proof_size, chained, resigned)
}

pub(super) fn get_retransmitter_signature_offset(
proof_size: u8,
chained: bool,
resigned: bool,
) -> Result<usize, Error> {
if !resigned {
return Err(Error::InvalidShredVariant);
}
let proof_offset = Self::get_proof_offset(proof_size, chained, resigned)?;
Ok(proof_offset + usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY)
}
}

impl ShredCode {
Expand Down Expand Up @@ -524,6 +557,39 @@ impl ShredCode {
let node = get_merkle_node(shred, SIZE_OF_SIGNATURE..proof_offset).ok()?;
get_merkle_root(index, node, proof).ok()
}

pub(super) fn retransmitter_signature(&self) -> Result<Signature, Error> {
let offset = self.retransmitter_signature_offset()?;
self.payload
.get(offset..offset + SIZE_OF_SIGNATURE)
.map(|bytes| <[u8; SIZE_OF_SIGNATURE]>::try_from(bytes).unwrap())
.map(Signature::from)
.ok_or(Error::InvalidPayloadSize(self.payload.len()))
}

fn retransmitter_signature_offset(&self) -> Result<usize, Error> {
let ShredVariant::MerkleCode {
proof_size,
chained,
resigned,
} = self.common_header.shred_variant
else {
return Err(Error::InvalidShredVariant);
};
Self::get_retransmitter_signature_offset(proof_size, chained, resigned)
}

pub(super) fn get_retransmitter_signature_offset(
proof_size: u8,
chained: bool,
resigned: bool,
) -> Result<usize, Error> {
if !resigned {
return Err(Error::InvalidShredVariant);
}
let proof_offset = Self::get_proof_offset(proof_size, chained, resigned)?;
Ok(proof_offset + usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY)
}
}

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 @@ -106,6 +106,13 @@ impl ShredCode {
}
}
}

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

impl From<legacy::ShredCode> for ShredCode {
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 @@ -143,6 +143,13 @@ impl ShredData {
Self::Merkle(_) => panic!("Not Implemented!"),
}
}

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

impl From<legacy::ShredData> for ShredData {
Expand Down