Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 8732a2c

Browse files
committed
adds a new shred variant embedding merkle tree hashes of the erasure batch
Coding shreds can only be signed once erasure codings are already generated. Therefore coding shreds recovered from erasure codings lack slot leader's signature and so cannot be retransmitted to the rest of the cluster. shred/merkle.rs implements a new shred variant where we generate merkle tree for each erasure encoded batch and each shred includes: * root of the merkle tree (which is 32-byte Hash). * slot leader's signature of the root of the merkle tree. * merkle tree nodes along the branch the shred belongs to, where hashes are trimmed to 20 bytes during tree construction. This schema results in the same signature for all shreds within an erasure batch. When recovering shreds from erasure codes, we can reconstruct merkle tree for the batch and for each recovered shred also recover respective merkle tree branch; then snap the slot leader's signature from any of the shreds received from turbine and retransmit all recovered code or data shreds. Backward compatibility is achieved by encoding shred variant at byte 65 of payload (previously shred-type at this position): * 0b0101_1010 indicates a legacy coding shred, which is also equal to ShredType::Code for backward compatibility. * 0b1010_0101 indicates a legacy data shred, which is also equal to ShredType::Data for backward compatibility. * 0b0100_???? indicates a merkle coding shred with merkle branch size indicated by the last 4 bits. * 0b1000_???? indicates a merkle data shred with merkle branch size indicated by the last 4 bits. Merkle root and branch are encoded at the end of the shred payload.
1 parent 5dbf7d8 commit 8732a2c

File tree

6 files changed

+666
-13
lines changed

6 files changed

+666
-13
lines changed

ledger/src/shred.rs

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ use {
7777

7878
mod common;
7979
mod legacy;
80+
mod merkle;
8081
mod shred_code;
8182
mod shred_data;
8283
mod stats;
@@ -129,6 +130,8 @@ pub enum Error {
129130
InvalidDataSize { size: u16, payload: usize },
130131
#[error("Invalid erasure shard index: {0:?}")]
131132
InvalidErasureShardIndex(/*headers:*/ Box<dyn Debug>),
133+
#[error("Invalid merkle proof")]
134+
InvalidMerkleProof,
132135
#[error("Invalid num coding shreds: {0}")]
133136
InvalidNumCodingShreds(u16),
134137
#[error("Invalid parent_offset: {parent_offset}, slot: {slot}")]
@@ -137,12 +140,16 @@ pub enum Error {
137140
InvalidParentSlot { slot: Slot, parent_slot: Slot },
138141
#[error("Invalid payload size: {0}")]
139142
InvalidPayloadSize(/*payload size:*/ usize),
143+
#[error("Invalid proof size: {0}")]
144+
InvalidProofSize(/*proof_size:*/ u8),
140145
#[error("Invalid shred flags: {0}")]
141146
InvalidShredFlags(u8),
142147
#[error("Invalid shred type")]
143148
InvalidShredType,
144149
#[error("Invalid shred variant")]
145150
InvalidShredVariant,
151+
#[error(transparent)]
152+
IoError(#[from] std::io::Error),
146153
}
147154

148155
#[repr(u8)]
@@ -171,6 +178,9 @@ pub enum ShredType {
171178
enum ShredVariant {
172179
LegacyCode, // 0b0101_1010
173180
LegacyData, // 0b1010_0101
181+
// proof_size is the number of proof entries in the merkle tree branch.
182+
MerkleCode(/*proof_size:*/ u8), // 0b0100_????
183+
MerkleData(/*proof_size:*/ u8), // 0b1000_????
174184
}
175185

176186
/// A common header that is present in data and code shred headers
@@ -325,6 +335,14 @@ impl Shred {
325335
let shred = legacy::ShredData::from_payload(shred)?;
326336
Self::from(ShredData::from(shred))
327337
}
338+
ShredVariant::MerkleCode(_) => {
339+
let shred = merkle::ShredCode::from_payload(shred)?;
340+
Self::from(ShredCode::from(shred))
341+
}
342+
ShredVariant::MerkleData(_) => {
343+
let shred = merkle::ShredData::from_payload(shred)?;
344+
Self::from(ShredData::from(shred))
345+
}
328346
})
329347
}
330348

@@ -557,6 +575,12 @@ pub mod layout {
557575
pub(crate) fn get_signed_message_range(shred: &[u8]) -> Option<Range<usize>> {
558576
let range = match get_shred_variant(shred).ok()? {
559577
ShredVariant::LegacyCode | ShredVariant::LegacyData => legacy::SIGNED_MESSAGE_RANGE,
578+
ShredVariant::MerkleCode(proof_size) => {
579+
merkle::ShredCode::get_signed_message_range(proof_size)?
580+
}
581+
ShredVariant::MerkleData(proof_size) => {
582+
merkle::ShredData::get_signed_message_range(proof_size)?
583+
}
560584
};
561585
(shred.len() <= range.end).then(|| range)
562586
}
@@ -593,6 +617,8 @@ impl From<ShredVariant> for ShredType {
593617
match shred_variant {
594618
ShredVariant::LegacyCode => ShredType::Code,
595619
ShredVariant::LegacyData => ShredType::Data,
620+
ShredVariant::MerkleCode(_) => ShredType::Code,
621+
ShredVariant::MerkleData(_) => ShredType::Data,
596622
}
597623
}
598624
}
@@ -602,6 +628,8 @@ impl From<ShredVariant> for u8 {
602628
match shred_variant {
603629
ShredVariant::LegacyCode => u8::from(ShredType::Code),
604630
ShredVariant::LegacyData => u8::from(ShredType::Data),
631+
ShredVariant::MerkleCode(proof_size) => proof_size | 0x40,
632+
ShredVariant::MerkleData(proof_size) => proof_size | 0x80,
605633
}
606634
}
607635
}
@@ -614,7 +642,11 @@ impl TryFrom<u8> for ShredVariant {
614642
} else if shred_variant == u8::from(ShredType::Data) {
615643
Ok(ShredVariant::LegacyData)
616644
} else {
617-
Err(Error::InvalidShredVariant)
645+
match shred_variant & 0xF0 {
646+
0x40 => Ok(ShredVariant::MerkleCode(shred_variant & 0x0F)),
647+
0x80 => Ok(ShredVariant::MerkleData(shred_variant & 0x0F)),
648+
_ => Err(Error::InvalidShredVariant),
649+
}
618650
}
619651
}
620652
}
@@ -673,7 +705,7 @@ pub fn max_entries_per_n_shred(
673705
num_shreds: u64,
674706
shred_data_size: Option<usize>,
675707
) -> u64 {
676-
let data_buffer_size = ShredData::capacity().unwrap();
708+
let data_buffer_size = ShredData::capacity(/*merkle_proof_size:*/ None).unwrap();
677709
let shred_data_size = shred_data_size.unwrap_or(data_buffer_size) as u64;
678710
let vec_size = bincode::serialized_size(&vec![entry]).unwrap();
679711
let entry_size = bincode::serialized_size(entry).unwrap();
@@ -786,7 +818,7 @@ mod tests {
786818
);
787819
assert_eq!(
788820
SIZE_OF_SHRED_VARIANT,
789-
bincode::serialized_size(&ShredVariant::LegacyCode).unwrap() as usize
821+
bincode::serialized_size(&ShredVariant::MerkleCode(15)).unwrap() as usize
790822
);
791823
assert_eq!(
792824
SIZE_OF_SHRED_SLOT,
@@ -988,6 +1020,74 @@ mod tests {
9881020
bincode::deserialize::<ShredVariant>(&[0b1010_0101]),
9891021
Ok(ShredVariant::LegacyData)
9901022
);
1023+
// Merkle coding shred.
1024+
assert_eq!(u8::from(ShredVariant::MerkleCode(5)), 0b0100_0101);
1025+
assert_eq!(
1026+
ShredType::from(ShredVariant::MerkleCode(5)),
1027+
ShredType::Code
1028+
);
1029+
assert_matches!(
1030+
ShredVariant::try_from(0b0100_0101),
1031+
Ok(ShredVariant::MerkleCode(5))
1032+
);
1033+
let buf = bincode::serialize(&ShredVariant::MerkleCode(5)).unwrap();
1034+
assert_eq!(buf, vec![0b0100_0101]);
1035+
assert_matches!(
1036+
bincode::deserialize::<ShredVariant>(&[0b0100_0101]),
1037+
Ok(ShredVariant::MerkleCode(5))
1038+
);
1039+
for proof_size in 0..=15u8 {
1040+
let byte = proof_size | 0b0100_0000;
1041+
assert_eq!(u8::from(ShredVariant::MerkleCode(proof_size)), byte);
1042+
assert_eq!(
1043+
ShredType::from(ShredVariant::MerkleCode(proof_size)),
1044+
ShredType::Code
1045+
);
1046+
assert_eq!(
1047+
ShredVariant::try_from(byte).unwrap(),
1048+
ShredVariant::MerkleCode(proof_size)
1049+
);
1050+
let buf = bincode::serialize(&ShredVariant::MerkleCode(proof_size)).unwrap();
1051+
assert_eq!(buf, vec![byte]);
1052+
assert_eq!(
1053+
bincode::deserialize::<ShredVariant>(&[byte]).unwrap(),
1054+
ShredVariant::MerkleCode(proof_size)
1055+
);
1056+
}
1057+
// Merkle data shred.
1058+
assert_eq!(u8::from(ShredVariant::MerkleData(10)), 0b1000_1010);
1059+
assert_eq!(
1060+
ShredType::from(ShredVariant::MerkleData(10)),
1061+
ShredType::Data
1062+
);
1063+
assert_matches!(
1064+
ShredVariant::try_from(0b1000_1010),
1065+
Ok(ShredVariant::MerkleData(10))
1066+
);
1067+
let buf = bincode::serialize(&ShredVariant::MerkleData(10)).unwrap();
1068+
assert_eq!(buf, vec![0b1000_1010]);
1069+
assert_matches!(
1070+
bincode::deserialize::<ShredVariant>(&[0b1000_1010]),
1071+
Ok(ShredVariant::MerkleData(10))
1072+
);
1073+
for proof_size in 0..=15u8 {
1074+
let byte = proof_size | 0b1000_0000;
1075+
assert_eq!(u8::from(ShredVariant::MerkleData(proof_size)), byte);
1076+
assert_eq!(
1077+
ShredType::from(ShredVariant::MerkleData(proof_size)),
1078+
ShredType::Data
1079+
);
1080+
assert_eq!(
1081+
ShredVariant::try_from(byte).unwrap(),
1082+
ShredVariant::MerkleData(proof_size)
1083+
);
1084+
let buf = bincode::serialize(&ShredVariant::MerkleData(proof_size)).unwrap();
1085+
assert_eq!(buf, vec![byte]);
1086+
assert_eq!(
1087+
bincode::deserialize::<ShredVariant>(&[byte]).unwrap(),
1088+
ShredVariant::MerkleData(proof_size)
1089+
);
1090+
}
9911091
}
9921092

9931093
#[test]

ledger/src/shred/common.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ macro_rules! dispatch {
44
$vis fn $name(&self $(, $arg:$ty)?) $(-> $out)? {
55
match self {
66
Self::Legacy(shred) => shred.$name($($arg, )?),
7+
Self::Merkle(shred) => shred.$name($($arg, )?),
78
}
89
}
910
};
@@ -12,6 +13,7 @@ macro_rules! dispatch {
1213
$vis fn $name(self $(, $arg:$ty)?) $(-> $out)? {
1314
match self {
1415
Self::Legacy(shred) => shred.$name($($arg, )?),
16+
Self::Merkle(shred) => shred.$name($($arg, )?),
1517
}
1618
}
1719
};
@@ -20,6 +22,7 @@ macro_rules! dispatch {
2022
$vis fn $name(&mut self $(, $arg:$ty)?) $(-> $out)? {
2123
match self {
2224
Self::Legacy(shred) => shred.$name($($arg, )?),
25+
Self::Merkle(shred) => shred.$name($($arg, )?),
2326
}
2427
}
2528
}

0 commit comments

Comments
 (0)