Skip to content

Commit

Permalink
removes the merkle root from shreds binary
Browse files Browse the repository at this point in the history
solana-labs#29445
makes it unnecessary to embed merkle roots into shreds binary. This
commit removes the merkle root from shreds binary.

This adds 20 bytes to shreds capacity to store more data.
Additionally since we no longer need to truncate the merkle root, the
signature would be on the full 32 bytes of hash as opposed to the
truncated one.

Also signature verification would now effectively verify merkle proof as
well, so we no longer need to verify merkle proof in the sanitize
implementation.
  • Loading branch information
behzadnouri committed Jan 4, 2023
1 parent 9db2565 commit 6c1626f
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 290 deletions.
52 changes: 27 additions & 25 deletions ledger/src/shred.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
//! So, given a) - c), we must restrict data shred's payload length such that the entire coding
//! payload can fit into one coding shred / packet.
pub(crate) use self::merkle::{MerkleRoot, SIZE_OF_MERKLE_ROOT};
#[cfg(test)]
pub(crate) use self::shred_code::MAX_CODE_SHREDS_PER_SLOT;
use {
Expand Down Expand Up @@ -193,7 +192,7 @@ pub enum ShredType {
enum ShredVariant {
LegacyCode, // 0b0101_1010
LegacyData, // 0b1010_0101
// proof_size is the number of proof entries in the merkle tree branch.
// proof_size is the number of merkle proof entries.
MerkleCode(/*proof_size:*/ u8), // 0b0100_????
MerkleData(/*proof_size:*/ u8), // 0b1000_????
}
Expand Down Expand Up @@ -231,16 +230,17 @@ pub enum Shred {
ShredData(ShredData),
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum SignedData<'a> {
Chunk(&'a [u8]), // Chunk of payload past signature.
MerkleRoot(MerkleRoot),
MerkleRoot(Hash),
}

impl<'a> AsRef<[u8]> for SignedData<'a> {
fn as_ref(&self) -> &[u8] {
match self {
Self::Chunk(chunk) => chunk,
Self::MerkleRoot(root) => root,
Self::MerkleRoot(root) => root.as_ref(),
}
}
}
Expand Down Expand Up @@ -656,16 +656,17 @@ pub mod layout {

// Returns offsets within the shred payload which is signed.
pub(crate) fn get_signed_data_offsets(shred: &[u8]) -> Option<Range<usize>> {
let offsets = match get_shred_variant(shred).ok()? {
ShredVariant::LegacyCode | ShredVariant::LegacyData => legacy::SIGNED_MESSAGE_OFFSETS,
ShredVariant::MerkleCode(proof_size) => {
merkle::ShredCode::get_signed_data_offsets(proof_size)?
}
ShredVariant::MerkleData(proof_size) => {
merkle::ShredData::get_signed_data_offsets(proof_size)?
match get_shred_variant(shred).ok()? {
ShredVariant::LegacyCode | ShredVariant::LegacyData => {
let offsets = self::legacy::SIGNED_MESSAGE_OFFSETS;
(offsets.end <= shred.len()).then_some(offsets)
}
};
(offsets.end <= shred.len()).then_some(offsets)
// Merkle shreds sign merkle tree root which can be recovered from
// the merkle proof embedded in the payload but itself is not
// stored the payload.
ShredVariant::MerkleCode(_) => None,
ShredVariant::MerkleData(_) => None,
}
}

pub(crate) fn get_reference_tick(shred: &[u8]) -> Result<u8, Error> {
Expand All @@ -679,7 +680,7 @@ pub mod layout {
Ok(flags & ShredFlags::SHRED_TICK_REFERENCE_MASK.bits())
}

pub(crate) fn get_merkle_root(shred: &[u8]) -> Option<MerkleRoot> {
pub(crate) fn get_merkle_root(shred: &[u8]) -> Option<Hash> {
match get_shred_variant(shred).ok()? {
ShredVariant::LegacyCode | ShredVariant::LegacyData => None,
ShredVariant::MerkleCode(proof_size) => {
Expand Down Expand Up @@ -716,15 +717,15 @@ pub mod layout {
modify_packet(rng, packet, 0..SIGNATURE_BYTES);
} else {
// Corrupt one byte within the signed data offsets.
let size = shred.len();
let offsets = get_signed_data_offsets(shred).unwrap();
modify_packet(rng, packet, offsets);
if let Some(proof_size) = merkle_proof_size {
// Also need to corrupt the merkle proof.
// Proof entries are each 20 bytes at the end of shreds.
let offset = usize::from(proof_size) * 20;
modify_packet(rng, packet, size - offset..size);
}
let offsets = merkle_proof_size
.map(|merkle_proof_size| {
// Need to corrupt the merkle proof.
// Proof entries are each 20 bytes at the end of shreds.
let offset = usize::from(merkle_proof_size) * 20;
shred.len() - offset..shred.len()
})
.or_else(|| get_signed_data_offsets(shred));
modify_packet(rng, packet, offsets.unwrap());
}
// Assert that the signature no longer verifies.
let shred = get_shred(packet).unwrap();
Expand All @@ -734,8 +735,9 @@ pub mod layout {
let pubkey = keypairs[&slot].pubkey();
let data = get_signed_data(shred).unwrap();
assert!(!signature.verify(pubkey.as_ref(), data.as_ref()));
let offsets = get_signed_data_offsets(shred).unwrap();
assert!(!signature.verify(pubkey.as_ref(), &shred[offsets]));
if let Some(offsets) = get_signed_data_offsets(shred) {
assert!(!signature.verify(pubkey.as_ref(), &shred[offsets]));
}
} else {
// Slot may have been corrupted and no longer mapping to a keypair.
let pubkey = keypairs.get(&slot).map(Keypair::pubkey).unwrap_or_default();
Expand Down
Loading

0 comments on commit 6c1626f

Please sign in to comment.