Skip to content
This repository was archived by the owner on Nov 11, 2022. It is now read-only.
Closed
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1488196
Refactor representation of Vec<Option<Signature>> as Vec<u128> and Ve…
de-sh May 10, 2021
d80be92
Add compression logic
de-sh May 12, 2021
8d888d9
Refactor imported name and trait impl
de-sh May 18, 2021
49c8a5c
Fix compilation.
tomusdrw May 18, 2021
fc6db82
Fix and finish implementing new encoder for `SignedCommitment`
de-sh May 18, 2021
b7cddb5
Merge branch 'master' into bitfield
de-sh May 18, 2021
3e122b5
Add `From<Temp...>` and `codec::Decode` for `SignedCommitment<...>`
de-sh May 27, 2021
681b594
Remove extra bounds. (#2)
tomusdrw May 30, 2021
fc0a36f
Fix use of bitwise operation in decompression of `Signatures`
de-sh May 30, 2021
07cacba
Fix hex tested against encoding of compressed `SignedCommitment`
de-sh May 30, 2021
112adb7
Fix variable names for generic block size use
de-sh May 30, 2021
68c047e
Clippy suggestions
de-sh May 31, 2021
e7eddbe
Update beefy-primitives/src/commitment.rs
de-sh May 31, 2021
fafd5b6
Add simple struct documentation and rename variables
de-sh May 31, 2021
a2fa0fc
Refactor out the use of `From`
de-sh May 31, 2021
d5d4b88
Add test for large Singnatures where (1024*2+2)/3 `Some`s exist
de-sh May 31, 2021
922103d
Simplify decoding
de-sh Jun 15, 2021
49aacfc
Fix for edge cases
de-sh Jun 15, 2021
cbe4f7d
Remove unecessary trait bounds and simply `pack()` & `unpack()`
de-sh Jun 15, 2021
1139296
Move to using encoder side fix to simplify decoder
de-sh Jun 18, 2021
7211ca2
Merge remote-tracking branch 'paritytech/master' into bitfield
de-sh Jun 30, 2021
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
134 changes: 131 additions & 3 deletions beefy-primitives/src/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use codec::{Decode, Encode, Error, Input};
use sp_std::{cmp, prelude::*};

use crate::ValidatorSetId;
Expand All @@ -23,7 +24,7 @@ use crate::ValidatorSetId;
/// The commitment contains a [payload] extracted from the finalized block at height [block_number].
/// GRANDPA validators collect signatures on commitments and a stream of such signed commitments
/// (see [SignedCommitment]) forms the BEEFY protocol.
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
pub struct Commitment<TBlockNumber, TPayload> {
/// The payload being signed.
///
Expand Down Expand Up @@ -77,7 +78,10 @@ where
}

/// A commitment with matching GRANDPA validators' signatures.
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
///
/// Note that SCALE-encoding of the structure is optimized for size efficiency over the wire,
/// please take a look at custom [`Encode`] and [`Decode`] implementations and [`TemporarySignatures`] struct.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SignedCommitment<TBlockNumber, TPayload, TSignature> {
/// The commitment signatures are collected for.
pub commitment: Commitment<TBlockNumber, TPayload>,
Expand All @@ -95,6 +99,130 @@ impl<TBlockNumber, TPayload, TSignature> SignedCommitment<TBlockNumber, TPayload
}
}

/// Type to be used to denote placement of signatures
type BitField = Vec<u8>;
/// Compress 8 bit values into a single u8 Byte
const CONTAINER_BIT_SIZE: usize = 8;

/// Temporary representation used for encoding efficiency.
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
struct TemporarySignatures<TCommitment, TSignature> {
commitment: TCommitment,
signatures_from: BitField,
signatures_no: u32,
signatures: Vec<TSignature>,
}

impl<'a, TBlockNumber, TPayload, TSignature> From<&'a SignedCommitment<TBlockNumber, TPayload, TSignature>>
for TemporarySignatures<&'a Commitment<TBlockNumber, TPayload>, &'a TSignature>
where
TSignature: Encode,
TBlockNumber: Encode,
TPayload: Encode,
{
/// Convert `SignedCommitment`s into `TemporarySignatures` that are packed better for
/// network transport.
fn from(signed_commitment: &'a SignedCommitment<TBlockNumber, TPayload, TSignature>) -> Self {
let SignedCommitment { commitment, signatures } = signed_commitment;
let signatures_no = signatures.len() as u32;
let mut signatures_from: BitField = vec![];
let mut raw_signatures: Vec<&TSignature> = vec![];

for signature in signatures {
match signature {
Some(value) => raw_signatures.push(value),
None => (),
}
}

let bits: Vec<u8> = signatures.iter().map(|x| if x.is_some() { 1 } else { 0 }).collect();
let chunks = bits.chunks(CONTAINER_BIT_SIZE);
for chunk in chunks {
let mut iter = chunk.iter().copied();
let mut v = iter.next().unwrap() as u8;

for bit in iter {
v <<= 1;
v |= bit as u8;
}

signatures_from.push(v);
}

Self {
commitment,
signatures_from,
signatures_no,
signatures: raw_signatures,
}
}
}

impl<TBlockNumber, TPayload, TSignature> Encode for SignedCommitment<TBlockNumber, TPayload, TSignature>
where
TSignature: Encode,
TBlockNumber: Encode,
TPayload: Encode,
{
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
let temp = TemporarySignatures::from(self);
temp.using_encoded(f)
}
}

impl<TBlockNumber, TPayload, TSignature> From<TemporarySignatures<Commitment<TBlockNumber, TPayload>, TSignature>>
for SignedCommitment<TBlockNumber, TPayload, TSignature>
where
TBlockNumber: Decode,
TPayload: Decode,
TSignature: Decode,
{
/// Convert `TemporarySignatures` back into `SignedCommitment`.
fn from(temporary_signatures: TemporarySignatures<Commitment<TBlockNumber, TPayload>, TSignature>) -> Self {
let TemporarySignatures {
commitment,
signatures_from,
mut signatures_no,
signatures,
} = temporary_signatures;
let mut bits: Vec<u8> = vec![];
let last_block = signatures_no % CONTAINER_BIT_SIZE as u32;
for block in signatures_from {
let start_bit = if signatures_no > last_block {
0
} else {
CONTAINER_BIT_SIZE - last_block as usize
};

for bit in start_bit..CONTAINER_BIT_SIZE {
let bit_position = CONTAINER_BIT_SIZE - bit - 1;
bits.push(block >> bit_position & 1);
signatures_no -= 1;
}
}

let mut next_signature = signatures.into_iter();
let signatures: Vec<Option<TSignature>> = bits
.iter()
.map(|&x| if x == 1 { next_signature.next() } else { None })
.collect();

Self { commitment, signatures }
}
}

impl<TBlockNumber, TPayload, TSignature> Decode for SignedCommitment<TBlockNumber, TPayload, TSignature>
where
TBlockNumber: Decode,
TPayload: Decode,
TSignature: Decode,
{
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let temp = TemporarySignatures::decode(input)?;
Ok(temp.into())
}
}

/// A [SignedCommitment] with a version number. This variant will be appended
/// to the block justifications for the block for which the signed commitment
/// has been generated.
Expand Down Expand Up @@ -157,7 +285,7 @@ mod tests {
assert_eq!(
encoded,
hex_literal::hex!(
"3048656c6c6f20576f726c6421050000000000000000000000000000000000000000000000100000011001020304011005060708"
"3048656c6c6f20576f726c64210500000000000000000000000000000000000000000000000403040000000810010203041005060708"
)
);
}
Expand Down