2022-06-07 15:41:03 -07:00
|
|
|
use {
|
|
|
|
crate::shred::{
|
|
|
|
common::impl_shred_common,
|
|
|
|
shred_code, shred_data,
|
|
|
|
traits::{Shred, ShredCode as ShredCodeTrait, ShredData as ShredDataTrait},
|
|
|
|
CodingShredHeader, DataShredHeader, Error, ShredCommonHeader, ShredFlags, ShredVariant,
|
|
|
|
SIZE_OF_CODING_SHRED_HEADERS, SIZE_OF_COMMON_SHRED_HEADER, SIZE_OF_DATA_SHRED_HEADERS,
|
|
|
|
SIZE_OF_SIGNATURE,
|
|
|
|
},
|
|
|
|
solana_perf::packet::deserialize_from_with_limit,
|
|
|
|
solana_sdk::{
|
|
|
|
clock::Slot,
|
|
|
|
hash::{hashv, Hash},
|
|
|
|
signature::Signature,
|
|
|
|
},
|
|
|
|
static_assertions::const_assert_eq,
|
|
|
|
std::{
|
|
|
|
io::{Cursor, Seek, SeekFrom},
|
|
|
|
iter::repeat_with,
|
|
|
|
ops::Range,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const_assert_eq!(SIZE_OF_MERKLE_ROOT, 20);
|
|
|
|
const SIZE_OF_MERKLE_ROOT: usize = std::mem::size_of::<MerkleRoot>();
|
|
|
|
const_assert_eq!(SIZE_OF_MERKLE_PROOF_ENTRY, 20);
|
|
|
|
const SIZE_OF_MERKLE_PROOF_ENTRY: usize = std::mem::size_of::<MerkleProofEntry>();
|
|
|
|
const_assert_eq!(ShredData::SIZE_OF_PAYLOAD, 1203);
|
|
|
|
|
|
|
|
// Defense against second preimage attack:
|
|
|
|
// https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack
|
|
|
|
const MERKLE_HASH_PREFIX_LEAF: &[u8] = &[0x00];
|
|
|
|
const MERKLE_HASH_PREFIX_NODE: &[u8] = &[0x01];
|
|
|
|
|
|
|
|
type MerkleRoot = MerkleProofEntry;
|
|
|
|
type MerkleProofEntry = [u8; 20];
|
|
|
|
|
|
|
|
// Layout: {common, data} headers | data buffer | merkle branch
|
|
|
|
// The slice past signature and before merkle branch is erasure coded.
|
|
|
|
// Same slice is hashed to generate merkle tree.
|
|
|
|
// The root of merkle tree is signed.
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct ShredData {
|
|
|
|
common_header: ShredCommonHeader,
|
|
|
|
data_header: DataShredHeader,
|
|
|
|
merkle_branch: MerkleBranch,
|
|
|
|
payload: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Layout: {common, coding} headers | erasure coded shard | merkle branch
|
|
|
|
// The slice past signature and before merkle branch is hashed to generate
|
|
|
|
// merkle tree. The root of merkle tree is signed.
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct ShredCode {
|
|
|
|
common_header: ShredCommonHeader,
|
|
|
|
coding_header: CodingShredHeader,
|
|
|
|
merkle_branch: MerkleBranch,
|
|
|
|
payload: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
struct MerkleBranch {
|
|
|
|
root: MerkleRoot,
|
|
|
|
proof: Vec<MerkleProofEntry>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShredData {
|
|
|
|
// proof_size is the number of proof entries in the merkle tree branch.
|
|
|
|
fn proof_size(&self) -> Result<u8, Error> {
|
|
|
|
match self.common_header.shred_variant {
|
|
|
|
ShredVariant::MerkleData(proof_size) => Ok(proof_size),
|
|
|
|
_ => Err(Error::InvalidShredVariant),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maximum size of ledger data that can be embedded in a data-shred.
|
|
|
|
// Also equal to:
|
2022-08-16 05:30:38 -07:00
|
|
|
// ShredCode::capacity(proof_size).unwrap()
|
2022-08-15 12:02:32 -07:00
|
|
|
// - ShredData::SIZE_OF_HEADERS
|
2022-06-07 15:41:03 -07:00
|
|
|
// + SIZE_OF_SIGNATURE
|
|
|
|
pub(super) fn capacity(proof_size: u8) -> Result<usize, Error> {
|
|
|
|
Self::SIZE_OF_PAYLOAD
|
|
|
|
.checked_sub(
|
2022-08-15 12:02:32 -07:00
|
|
|
Self::SIZE_OF_HEADERS
|
2022-06-07 15:41:03 -07:00
|
|
|
+ SIZE_OF_MERKLE_ROOT
|
|
|
|
+ usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY,
|
|
|
|
)
|
|
|
|
.ok_or(Error::InvalidProofSize(proof_size))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn get_signed_message_range(proof_size: u8) -> Option<Range<usize>> {
|
|
|
|
let data_buffer_size = Self::capacity(proof_size).ok()?;
|
2022-08-15 12:02:32 -07:00
|
|
|
let offset = Self::SIZE_OF_HEADERS + data_buffer_size;
|
2022-06-07 15:41:03 -07:00
|
|
|
Some(offset..offset + SIZE_OF_MERKLE_ROOT)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn merkle_tree_node(&self) -> Result<Hash, Error> {
|
|
|
|
let chunk = self.erasure_shard_as_slice()?;
|
|
|
|
Ok(hashv(&[MERKLE_HASH_PREFIX_LEAF, chunk]))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify_merkle_proof(&self) -> Result<bool, Error> {
|
|
|
|
let node = self.merkle_tree_node()?;
|
|
|
|
let index = self.erasure_shard_index()?;
|
|
|
|
Ok(verify_merkle_proof(index, node, &self.merkle_branch))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShredCode {
|
|
|
|
// proof_size is the number of proof entries in the merkle tree branch.
|
|
|
|
fn proof_size(&self) -> Result<u8, Error> {
|
|
|
|
match self.common_header.shred_variant {
|
|
|
|
ShredVariant::MerkleCode(proof_size) => Ok(proof_size),
|
|
|
|
_ => Err(Error::InvalidShredVariant),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-16 05:30:38 -07:00
|
|
|
// Size of buffer embedding erasure codes.
|
|
|
|
fn capacity(proof_size: u8) -> Result<usize, Error> {
|
2022-06-07 15:41:03 -07:00
|
|
|
// Merkle branch is generated and signed after coding shreds are
|
|
|
|
// generated. Coding shred headers cannot be erasure coded either.
|
|
|
|
Self::SIZE_OF_PAYLOAD
|
|
|
|
.checked_sub(
|
2022-08-15 12:02:32 -07:00
|
|
|
Self::SIZE_OF_HEADERS
|
2022-06-07 15:41:03 -07:00
|
|
|
+ SIZE_OF_MERKLE_ROOT
|
|
|
|
+ SIZE_OF_MERKLE_PROOF_ENTRY * usize::from(proof_size),
|
|
|
|
)
|
|
|
|
.ok_or(Error::InvalidProofSize(proof_size))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn merkle_tree_node(&self) -> Result<Hash, Error> {
|
|
|
|
let proof_size = self.proof_size()?;
|
2022-08-16 05:30:38 -07:00
|
|
|
let shard_size = Self::capacity(proof_size)?;
|
2022-06-07 15:41:03 -07:00
|
|
|
let chunk = self
|
|
|
|
.payload
|
2022-08-15 12:02:32 -07:00
|
|
|
.get(SIZE_OF_SIGNATURE..Self::SIZE_OF_HEADERS + shard_size)
|
2022-06-07 15:41:03 -07:00
|
|
|
.ok_or(Error::InvalidPayloadSize(self.payload.len()))?;
|
|
|
|
Ok(hashv(&[MERKLE_HASH_PREFIX_LEAF, chunk]))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify_merkle_proof(&self) -> Result<bool, Error> {
|
|
|
|
let node = self.merkle_tree_node()?;
|
|
|
|
let index = self.erasure_shard_index()?;
|
|
|
|
Ok(verify_merkle_proof(index, node, &self.merkle_branch))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn get_signed_message_range(proof_size: u8) -> Option<Range<usize>> {
|
2022-08-16 05:30:38 -07:00
|
|
|
let offset = Self::SIZE_OF_HEADERS + Self::capacity(proof_size).ok()?;
|
2022-06-07 15:41:03 -07:00
|
|
|
Some(offset..offset + SIZE_OF_MERKLE_ROOT)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn erasure_mismatch(&self, other: &ShredCode) -> bool {
|
|
|
|
shred_code::erasure_mismatch(self, other)
|
|
|
|
|| self.merkle_branch.root != other.merkle_branch.root
|
|
|
|
|| self.common_header.signature != other.common_header.signature
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Shred for ShredData {
|
|
|
|
impl_shred_common!();
|
|
|
|
|
|
|
|
// Also equal to:
|
2022-08-15 12:02:32 -07:00
|
|
|
// ShredData::SIZE_OF_HEADERS
|
2022-06-07 15:41:03 -07:00
|
|
|
// + ShredData::capacity(proof_size).unwrap()
|
|
|
|
// + SIZE_OF_MERKLE_ROOT
|
|
|
|
// + usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY
|
|
|
|
const SIZE_OF_PAYLOAD: usize =
|
2022-08-15 12:02:32 -07:00
|
|
|
ShredCode::SIZE_OF_PAYLOAD - ShredCode::SIZE_OF_HEADERS + SIZE_OF_SIGNATURE;
|
|
|
|
const SIZE_OF_HEADERS: usize = SIZE_OF_DATA_SHRED_HEADERS;
|
2022-06-07 15:41:03 -07:00
|
|
|
|
|
|
|
fn from_payload(mut payload: Vec<u8>) -> Result<Self, Error> {
|
|
|
|
if payload.len() < Self::SIZE_OF_PAYLOAD {
|
|
|
|
return Err(Error::InvalidPayloadSize(payload.len()));
|
|
|
|
}
|
|
|
|
payload.truncate(Self::SIZE_OF_PAYLOAD);
|
|
|
|
let mut cursor = Cursor::new(&payload[..]);
|
|
|
|
let common_header: ShredCommonHeader = deserialize_from_with_limit(&mut cursor)?;
|
|
|
|
let proof_size = match common_header.shred_variant {
|
|
|
|
ShredVariant::MerkleData(proof_size) => proof_size,
|
|
|
|
_ => return Err(Error::InvalidShredVariant),
|
|
|
|
};
|
|
|
|
let data_header = deserialize_from_with_limit(&mut cursor)?;
|
|
|
|
// Skip data buffer.
|
|
|
|
let data_buffer_size = Self::capacity(proof_size)?;
|
|
|
|
let data_buffer_size = i64::try_from(data_buffer_size).unwrap();
|
|
|
|
cursor.seek(SeekFrom::Current(data_buffer_size))?;
|
|
|
|
// Deserialize merkle branch.
|
|
|
|
let root = deserialize_from_with_limit(&mut cursor)?;
|
|
|
|
let proof = repeat_with(|| deserialize_from_with_limit(&mut cursor))
|
|
|
|
.take(usize::from(proof_size))
|
|
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
let merkle_branch = MerkleBranch { root, proof };
|
|
|
|
let shred = Self {
|
|
|
|
common_header,
|
|
|
|
data_header,
|
|
|
|
merkle_branch,
|
|
|
|
payload,
|
|
|
|
};
|
|
|
|
shred.sanitize().map(|_| shred)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn erasure_shard_index(&self) -> Result<usize, Error> {
|
|
|
|
shred_data::erasure_shard_index(self).ok_or_else(|| {
|
|
|
|
let headers = Box::new((self.common_header, self.data_header));
|
|
|
|
Error::InvalidErasureShardIndex(headers)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn erasure_shard(self) -> Result<Vec<u8>, Error> {
|
|
|
|
if self.payload.len() != Self::SIZE_OF_PAYLOAD {
|
|
|
|
return Err(Error::InvalidPayloadSize(self.payload.len()));
|
|
|
|
}
|
|
|
|
let proof_size = self.proof_size()?;
|
|
|
|
let data_buffer_size = Self::capacity(proof_size)?;
|
|
|
|
let mut shard = self.payload;
|
2022-08-15 12:02:32 -07:00
|
|
|
shard.truncate(Self::SIZE_OF_HEADERS + data_buffer_size);
|
2022-06-07 15:41:03 -07:00
|
|
|
shard.drain(0..SIZE_OF_SIGNATURE);
|
|
|
|
Ok(shard)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn erasure_shard_as_slice(&self) -> Result<&[u8], Error> {
|
|
|
|
if self.payload.len() != Self::SIZE_OF_PAYLOAD {
|
|
|
|
return Err(Error::InvalidPayloadSize(self.payload.len()));
|
|
|
|
}
|
|
|
|
let proof_size = self.proof_size()?;
|
|
|
|
let data_buffer_size = Self::capacity(proof_size)?;
|
|
|
|
self.payload
|
2022-08-15 12:02:32 -07:00
|
|
|
.get(SIZE_OF_SIGNATURE..Self::SIZE_OF_HEADERS + data_buffer_size)
|
2022-06-07 15:41:03 -07:00
|
|
|
.ok_or(Error::InvalidPayloadSize(self.payload.len()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sanitize(&self) -> Result<(), Error> {
|
|
|
|
match self.common_header.shred_variant {
|
|
|
|
ShredVariant::MerkleData(proof_size) => {
|
|
|
|
if self.merkle_branch.proof.len() != usize::from(proof_size) {
|
|
|
|
return Err(Error::InvalidProofSize(proof_size));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => return Err(Error::InvalidShredVariant),
|
|
|
|
}
|
|
|
|
if !self.verify_merkle_proof()? {
|
|
|
|
return Err(Error::InvalidMerkleProof);
|
|
|
|
}
|
|
|
|
shred_data::sanitize(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signed_message(&self) -> &[u8] {
|
|
|
|
self.merkle_branch.root.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Shred for ShredCode {
|
|
|
|
impl_shred_common!();
|
|
|
|
const SIZE_OF_PAYLOAD: usize = shred_code::ShredCode::SIZE_OF_PAYLOAD;
|
2022-08-15 12:02:32 -07:00
|
|
|
const SIZE_OF_HEADERS: usize = SIZE_OF_CODING_SHRED_HEADERS;
|
2022-06-07 15:41:03 -07:00
|
|
|
|
|
|
|
fn from_payload(mut payload: Vec<u8>) -> Result<Self, Error> {
|
|
|
|
let mut cursor = Cursor::new(&payload[..]);
|
|
|
|
let common_header: ShredCommonHeader = deserialize_from_with_limit(&mut cursor)?;
|
|
|
|
let proof_size = match common_header.shred_variant {
|
|
|
|
ShredVariant::MerkleCode(proof_size) => proof_size,
|
|
|
|
_ => return Err(Error::InvalidShredVariant),
|
|
|
|
};
|
|
|
|
let coding_header = deserialize_from_with_limit(&mut cursor)?;
|
|
|
|
// Skip erasure code shard.
|
2022-08-16 05:30:38 -07:00
|
|
|
let shard_size = Self::capacity(proof_size)?;
|
2022-06-07 15:41:03 -07:00
|
|
|
let shard_size = i64::try_from(shard_size).unwrap();
|
|
|
|
cursor.seek(SeekFrom::Current(shard_size))?;
|
|
|
|
// Deserialize merkle branch.
|
|
|
|
let root = deserialize_from_with_limit(&mut cursor)?;
|
|
|
|
let proof = repeat_with(|| deserialize_from_with_limit(&mut cursor))
|
|
|
|
.take(usize::from(proof_size))
|
|
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
let merkle_branch = MerkleBranch { root, proof };
|
|
|
|
// see: https://github.com/solana-labs/solana/pull/10109
|
|
|
|
payload.truncate(Self::SIZE_OF_PAYLOAD);
|
|
|
|
let shred = Self {
|
|
|
|
common_header,
|
|
|
|
coding_header,
|
|
|
|
merkle_branch,
|
|
|
|
payload,
|
|
|
|
};
|
|
|
|
shred.sanitize().map(|_| shred)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn erasure_shard_index(&self) -> Result<usize, Error> {
|
|
|
|
shred_code::erasure_shard_index(self).ok_or_else(|| {
|
|
|
|
let headers = Box::new((self.common_header, self.coding_header));
|
|
|
|
Error::InvalidErasureShardIndex(headers)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn erasure_shard(self) -> Result<Vec<u8>, Error> {
|
|
|
|
if self.payload.len() != Self::SIZE_OF_PAYLOAD {
|
|
|
|
return Err(Error::InvalidPayloadSize(self.payload.len()));
|
|
|
|
}
|
|
|
|
let proof_size = self.proof_size()?;
|
2022-08-16 05:30:38 -07:00
|
|
|
let shard_size = Self::capacity(proof_size)?;
|
2022-06-07 15:41:03 -07:00
|
|
|
let mut shard = self.payload;
|
2022-08-15 12:02:32 -07:00
|
|
|
shard.drain(..Self::SIZE_OF_HEADERS);
|
2022-06-07 15:41:03 -07:00
|
|
|
shard.truncate(shard_size);
|
|
|
|
Ok(shard)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn erasure_shard_as_slice(&self) -> Result<&[u8], Error> {
|
|
|
|
if self.payload.len() != Self::SIZE_OF_PAYLOAD {
|
|
|
|
return Err(Error::InvalidPayloadSize(self.payload.len()));
|
|
|
|
}
|
|
|
|
let proof_size = self.proof_size()?;
|
2022-08-16 05:30:38 -07:00
|
|
|
let shard_size = Self::capacity(proof_size)?;
|
2022-06-07 15:41:03 -07:00
|
|
|
self.payload
|
2022-08-15 12:02:32 -07:00
|
|
|
.get(Self::SIZE_OF_HEADERS..Self::SIZE_OF_HEADERS + shard_size)
|
2022-06-07 15:41:03 -07:00
|
|
|
.ok_or(Error::InvalidPayloadSize(self.payload.len()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sanitize(&self) -> Result<(), Error> {
|
|
|
|
match self.common_header.shred_variant {
|
|
|
|
ShredVariant::MerkleCode(proof_size) => {
|
|
|
|
if self.merkle_branch.proof.len() != usize::from(proof_size) {
|
|
|
|
return Err(Error::InvalidProofSize(proof_size));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => return Err(Error::InvalidShredVariant),
|
|
|
|
}
|
|
|
|
if !self.verify_merkle_proof()? {
|
|
|
|
return Err(Error::InvalidMerkleProof);
|
|
|
|
}
|
|
|
|
shred_code::sanitize(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signed_message(&self) -> &[u8] {
|
|
|
|
self.merkle_branch.root.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShredDataTrait for ShredData {
|
|
|
|
#[inline]
|
|
|
|
fn data_header(&self) -> &DataShredHeader {
|
|
|
|
&self.data_header
|
|
|
|
}
|
|
|
|
|
|
|
|
fn data(&self) -> Result<&[u8], Error> {
|
|
|
|
let proof_size = self.proof_size()?;
|
|
|
|
let data_buffer_size = Self::capacity(proof_size)?;
|
|
|
|
let size = usize::from(self.data_header.size);
|
|
|
|
if size > self.payload.len()
|
2022-08-15 12:02:32 -07:00
|
|
|
|| size < Self::SIZE_OF_HEADERS
|
|
|
|
|| size > Self::SIZE_OF_HEADERS + data_buffer_size
|
2022-06-07 15:41:03 -07:00
|
|
|
{
|
|
|
|
return Err(Error::InvalidDataSize {
|
|
|
|
size: self.data_header.size,
|
|
|
|
payload: self.payload.len(),
|
|
|
|
});
|
|
|
|
}
|
2022-08-15 12:02:32 -07:00
|
|
|
Ok(&self.payload[Self::SIZE_OF_HEADERS..size])
|
2022-06-07 15:41:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Only for tests.
|
|
|
|
fn set_last_in_slot(&mut self) {
|
|
|
|
self.data_header.flags |= ShredFlags::LAST_SHRED_IN_SLOT;
|
|
|
|
let buffer = &mut self.payload[SIZE_OF_COMMON_SHRED_HEADER..];
|
|
|
|
bincode::serialize_into(buffer, &self.data_header).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShredCodeTrait for ShredCode {
|
|
|
|
#[inline]
|
|
|
|
fn coding_header(&self) -> &CodingShredHeader {
|
|
|
|
&self.coding_header
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtains parent's hash by joining two sibiling nodes in merkle tree.
|
|
|
|
fn join_nodes<S: AsRef<[u8]>, T: AsRef<[u8]>>(node: S, other: T) -> Hash {
|
|
|
|
let node = &node.as_ref()[..SIZE_OF_MERKLE_PROOF_ENTRY];
|
|
|
|
let other = &other.as_ref()[..SIZE_OF_MERKLE_PROOF_ENTRY];
|
|
|
|
hashv(&[MERKLE_HASH_PREFIX_NODE, node, other])
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify_merkle_proof(index: usize, node: Hash, merkle_branch: &MerkleBranch) -> bool {
|
|
|
|
let proof = merkle_branch.proof.iter();
|
|
|
|
let (index, root) = proof.fold((index, node), |(index, node), other| {
|
|
|
|
let parent = if index % 2 == 0 {
|
|
|
|
join_nodes(node, other)
|
|
|
|
} else {
|
|
|
|
join_nodes(other, node)
|
|
|
|
};
|
|
|
|
(index >> 1, parent)
|
|
|
|
});
|
|
|
|
let root = &root.as_ref()[..SIZE_OF_MERKLE_ROOT];
|
|
|
|
(index, root) == (0usize, &merkle_branch.root[..])
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
fn make_merkle_tree(mut nodes: Vec<Hash>) -> Vec<Hash> {
|
|
|
|
let mut size = nodes.len();
|
|
|
|
while size > 1 {
|
|
|
|
let offset = nodes.len() - size;
|
|
|
|
for index in (offset..offset + size).step_by(2) {
|
|
|
|
let node = &nodes[index];
|
|
|
|
let other = &nodes[(index + 1).min(offset + size - 1)];
|
|
|
|
let parent = join_nodes(node, other);
|
|
|
|
nodes.push(parent);
|
|
|
|
}
|
|
|
|
size = nodes.len() - offset - size;
|
|
|
|
}
|
|
|
|
nodes
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
fn make_merkle_branch(
|
|
|
|
mut index: usize, // leaf index ~ shred's erasure shard index.
|
|
|
|
mut size: usize, // number of leaves ~ erasure batch size.
|
|
|
|
tree: &[Hash],
|
|
|
|
) -> Option<MerkleBranch> {
|
|
|
|
if index >= size {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let mut offset = 0;
|
|
|
|
let mut proof = Vec::<MerkleProofEntry>::new();
|
|
|
|
while size > 1 {
|
|
|
|
let node = tree.get(offset + (index ^ 1).min(size - 1))?;
|
|
|
|
let entry = &node.as_ref()[..SIZE_OF_MERKLE_PROOF_ENTRY];
|
|
|
|
proof.push(MerkleProofEntry::try_from(entry).unwrap());
|
|
|
|
offset += size;
|
|
|
|
size = (size + 1) >> 1;
|
|
|
|
index >>= 1;
|
|
|
|
}
|
|
|
|
if offset + 1 != tree.len() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let root = &tree.last()?.as_ref()[..SIZE_OF_MERKLE_ROOT];
|
|
|
|
let root = MerkleRoot::try_from(root).unwrap();
|
|
|
|
Some(MerkleBranch { root, proof })
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use {super::*, rand::Rng, std::iter::repeat_with};
|
|
|
|
|
|
|
|
// Total size of a data shred including headers and merkle branch.
|
|
|
|
fn shred_data_size_of_payload(proof_size: u8) -> usize {
|
2022-08-15 12:02:32 -07:00
|
|
|
ShredData::SIZE_OF_HEADERS
|
2022-06-07 15:41:03 -07:00
|
|
|
+ ShredData::capacity(proof_size).unwrap()
|
|
|
|
+ SIZE_OF_MERKLE_ROOT
|
|
|
|
+ usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merkle branch is generated and signed after coding shreds are generated.
|
|
|
|
// All payload excluding merkle branch and the signature are erasure coded.
|
|
|
|
// Therefore the data capacity is equal to erasure encoded shard size minus
|
|
|
|
// size of erasure encoded header.
|
|
|
|
fn shred_data_capacity(proof_size: u8) -> usize {
|
|
|
|
const SIZE_OF_ERASURE_ENCODED_HEADER: usize =
|
2022-08-15 12:02:32 -07:00
|
|
|
ShredData::SIZE_OF_HEADERS - SIZE_OF_SIGNATURE;
|
2022-08-16 05:30:38 -07:00
|
|
|
ShredCode::capacity(proof_size).unwrap() - SIZE_OF_ERASURE_ENCODED_HEADER
|
2022-06-07 15:41:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn shred_data_size_of_erasure_encoded_slice(proof_size: u8) -> usize {
|
|
|
|
ShredData::SIZE_OF_PAYLOAD
|
|
|
|
- SIZE_OF_SIGNATURE
|
|
|
|
- SIZE_OF_MERKLE_ROOT
|
|
|
|
- usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_shred_data_size_of_payload() {
|
|
|
|
for proof_size in 0..0x15 {
|
|
|
|
assert_eq!(
|
|
|
|
ShredData::SIZE_OF_PAYLOAD,
|
|
|
|
shred_data_size_of_payload(proof_size)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_shred_data_capacity() {
|
|
|
|
for proof_size in 0..0x15 {
|
|
|
|
assert_eq!(
|
|
|
|
ShredData::capacity(proof_size).unwrap(),
|
|
|
|
shred_data_capacity(proof_size)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-08-16 05:30:38 -07:00
|
|
|
fn test_shred_code_capacity() {
|
2022-06-07 15:41:03 -07:00
|
|
|
for proof_size in 0..0x15 {
|
|
|
|
assert_eq!(
|
2022-08-16 05:30:38 -07:00
|
|
|
ShredCode::capacity(proof_size).unwrap(),
|
2022-06-07 15:41:03 -07:00
|
|
|
shred_data_size_of_erasure_encoded_slice(proof_size),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_merkle_proof_entry_from_hash() {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let bytes: [u8; 32] = rng.gen();
|
|
|
|
let hash = Hash::from(bytes);
|
|
|
|
let entry = &hash.as_ref()[..SIZE_OF_MERKLE_PROOF_ENTRY];
|
|
|
|
let entry = MerkleProofEntry::try_from(entry).unwrap();
|
|
|
|
assert_eq!(entry, &bytes[..SIZE_OF_MERKLE_PROOF_ENTRY]);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_merkle_tree_round_trip(size: usize) {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let nodes = repeat_with(|| rng.gen::<[u8; 32]>()).map(Hash::from);
|
|
|
|
let nodes: Vec<_> = nodes.take(size).collect();
|
|
|
|
let tree = make_merkle_tree(nodes.clone());
|
|
|
|
for index in 0..size {
|
|
|
|
let branch = make_merkle_branch(index, size, &tree).unwrap();
|
|
|
|
let root = &tree.last().unwrap().as_ref()[..SIZE_OF_MERKLE_ROOT];
|
|
|
|
assert_eq!(&branch.root, root);
|
|
|
|
assert!(verify_merkle_proof(index, nodes[index], &branch));
|
|
|
|
for i in (0..size).filter(|&i| i != index) {
|
|
|
|
assert!(!verify_merkle_proof(i, nodes[i], &branch));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_merkle_tree_round_trip() {
|
|
|
|
for size in [1, 2, 3, 4, 5, 6, 7, 8, 9, 19, 37, 64, 79] {
|
|
|
|
run_merkle_tree_round_trip(size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|