pub trait Scheme:
Debug
+ Clone
+ Send
+ Sync
+ 'static {
type Commitment: Digest;
type StrongShard: Clone + Debug + Eq + Codec<Cfg = CodecConfig> + Send + Sync + 'static;
type WeakShard: Clone + Debug + Eq + Codec<Cfg = CodecConfig> + Send + Sync + 'static;
type CheckingData: Clone + Send + Sync;
type CheckedShard: Clone + Send + Sync;
type Error: Debug + Send;
// Required methods
fn encode(
config: &Config,
data: impl Buf,
strategy: &impl Strategy,
) -> Result<(Self::Commitment, Vec<Self::StrongShard>), Self::Error>;
fn weaken(
config: &Config,
commitment: &Self::Commitment,
index: u16,
shard: Self::StrongShard,
) -> Result<(Self::CheckingData, Self::CheckedShard, Self::WeakShard), Self::Error>;
fn check(
config: &Config,
commitment: &Self::Commitment,
checking_data: &Self::CheckingData,
index: u16,
weak_shard: Self::WeakShard,
) -> Result<Self::CheckedShard, Self::Error>;
fn decode(
config: &Config,
commitment: &Self::Commitment,
checking_data: Self::CheckingData,
shards: &[Self::CheckedShard],
strategy: &impl Strategy,
) -> Result<Vec<u8>, Self::Error>;
}Expand description
A scheme for encoding data into pieces, and recovering the data from those pieces.
§Example
use commonware_coding::{Config, ReedSolomon, Scheme as _};
use commonware_cryptography::Sha256;
use commonware_parallel::Sequential;
use commonware_utils::NZU16;
const STRATEGY: Sequential = Sequential;
type RS = ReedSolomon<Sha256>;
let config = Config {
minimum_shards: NZU16!(2),
extra_shards: NZU16!(1),
};
let data = b"Hello!";
// Turn the data into shards, and a commitment to those shards.
let (commitment, shards) =
RS::encode(&config, data.as_slice(), &STRATEGY).unwrap();
// Each person produces weak shards, their own checked shard, and checking data
// to check other peoples weak shards.
let (mut checking_data_w_shard, weak_shards): (Vec<_>, Vec<_>) = shards
.into_iter()
.enumerate()
.map(|(i, shard)| {
let (checking_data, checked_shard, weak_shard) = RS::weaken(&config, &commitment, i as u16, shard).unwrap();
((checking_data, checked_shard), weak_shard)
})
.collect();
// Let's pretend that the last item is "ours"
let (checking_data, checked_shard) = checking_data_w_shard.pop().unwrap();
// We can use this checking_data to check the other shards.
let mut checked_shards = Vec::new();
checked_shards.push(checked_shard);
for (i, weak_shard) in weak_shards.into_iter().enumerate().skip(1) {
checked_shards.push(RS::check(&config, &commitment, &checking_data, i as u16, weak_shard).unwrap())
}
let data2 = RS::decode(&config, &commitment, checking_data, &checked_shards[..2], &STRATEGY).unwrap();
assert_eq!(&data[..], &data2[..]);
// Decoding works with different shards, with a guarantee to get the same result.
let data3 = RS::decode(&config, &commitment, checking_data, &checked_shards[1..], &STRATEGY).unwrap();
assert_eq!(&data[..], &data3[..]);§Guarantees
Here are additional properties that implementors of this trait need to consider, and that users of this trait can rely on.
§Weaken vs Check
Scheme::weaken and Scheme::check should agree, even for malicious encoders.
It should not be possible for parties A and B to call weaken successfully,
but then have either of them fail on the other’s shard when calling check.
In other words, if an honest party considers their shard to be correctly formed, then other honest parties which have successfully constructed their checking data will also agree with the shard being correct.
A violation of this property would be, for example, if a malicious payload could convince two parties that they both have valid shards, but then the checking data they produce from the malicious payload reports issues with those shards.
Required Associated Types§
Sourcetype Commitment: Digest
type Commitment: Digest
A commitment attesting to the shards of data.
Sourcetype StrongShard: Clone + Debug + Eq + Codec<Cfg = CodecConfig> + Send + Sync + 'static
type StrongShard: Clone + Debug + Eq + Codec<Cfg = CodecConfig> + Send + Sync + 'static
A strong shard of data, to be received by a participant.
Sourcetype WeakShard: Clone + Debug + Eq + Codec<Cfg = CodecConfig> + Send + Sync + 'static
type WeakShard: Clone + Debug + Eq + Codec<Cfg = CodecConfig> + Send + Sync + 'static
A weak shard shared with other participants, to aid them in reconstruction.
In most cases, this will be the same as StrongShard, but some schemes might
have extra information in StrongShard that may not be necessary to reconstruct
the data.
Sourcetype CheckingData: Clone + Send + Sync
type CheckingData: Clone + Send + Sync
Data which can assist in checking shards.
Sourcetype CheckedShard: Clone + Send + Sync
type CheckedShard: Clone + Send + Sync
A shard that has been checked for inclusion in the commitment.
This allows excluding Scheme::WeakShards which are invalid, and shouldn’t be considered as progress towards meeting the minimum number of shards.
Required Methods§
Sourcefn encode(
config: &Config,
data: impl Buf,
strategy: &impl Strategy,
) -> Result<(Self::Commitment, Vec<Self::StrongShard>), Self::Error>
fn encode( config: &Config, data: impl Buf, strategy: &impl Strategy, ) -> Result<(Self::Commitment, Vec<Self::StrongShard>), Self::Error>
Encode a piece of data, returning a commitment, along with shards, and proofs.
Each shard and proof is intended for exactly one participant. The number of shards returned
should equal config.minimum_shards + config.extra_shards.
Sourcefn weaken(
config: &Config,
commitment: &Self::Commitment,
index: u16,
shard: Self::StrongShard,
) -> Result<(Self::CheckingData, Self::CheckedShard, Self::WeakShard), Self::Error>
fn weaken( config: &Config, commitment: &Self::Commitment, index: u16, shard: Self::StrongShard, ) -> Result<(Self::CheckingData, Self::CheckedShard, Self::WeakShard), Self::Error>
Take your own shard, check it, and produce a Scheme::WeakShard to forward to others.
This takes in an index, which is the index you expect the shard to be.
This will produce a Scheme::CheckedShard which counts towards the minimum number of shards you need to reconstruct the data, in Scheme::decode.
You also get Scheme::CheckingData, which has information you can use to check the shards you receive from others.
Sourcefn check(
config: &Config,
commitment: &Self::Commitment,
checking_data: &Self::CheckingData,
index: u16,
weak_shard: Self::WeakShard,
) -> Result<Self::CheckedShard, Self::Error>
fn check( config: &Config, commitment: &Self::Commitment, checking_data: &Self::CheckingData, index: u16, weak_shard: Self::WeakShard, ) -> Result<Self::CheckedShard, Self::Error>
Check the integrity of a weak shard, producing a checked shard.
This requires the Scheme::CheckingData produced by Scheme::weaken.
This takes in an index, to make sure that the weak shard you’re checking is associated with the participant you expect it to be.
Sourcefn decode(
config: &Config,
commitment: &Self::Commitment,
checking_data: Self::CheckingData,
shards: &[Self::CheckedShard],
strategy: &impl Strategy,
) -> Result<Vec<u8>, Self::Error>
fn decode( config: &Config, commitment: &Self::Commitment, checking_data: Self::CheckingData, shards: &[Self::CheckedShard], strategy: &impl Strategy, ) -> Result<Vec<u8>, Self::Error>
Decode the data from shards received from other participants.
The data must be decodeable with as few as config.minimum_shards,
including your own shard.
Calls to this function with the same commitment, but with different shards, or shards in a different should also result in the same output data, or in failure. In other words, when using the decoding function in a broader system, you get a guarantee that every participant decoding will see the same final data, even if they receive different shards, or receive them in a different order.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.