#![allow(missing_docs)]
use std::cmp::Ordering;
use std::ops::Bound::{Excluded, Included};
use nom::{
error::{context, ContextError, ParseError},
sequence::tuple,
IResult, Parser,
};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::block_header::{BlockHeaderDenunciationData, SecuredHeader};
use crate::endorsement::{EndorsementDenunciationData, SecureShareEndorsement};
use crate::slot::{Slot, SlotDeserializer, SlotSerializer};
use crate::secure_share::Id;
use massa_hash::{Hash, HashDeserializer, HashSerializer};
use massa_serialization::{
Deserializer, SerializeError, Serializer, U32VarIntDeserializer, U32VarIntSerializer,
};
use massa_signature::{
MassaSignatureError, PublicKey, PublicKeyDeserializer, Signature, SignatureDeserializer,
};
use variant_count::VariantCount;
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EndorsementDenunciation {
public_key: PublicKey,
slot: Slot,
index: u32,
hash_1: Hash,
hash_2: Hash,
signature_1: Signature,
signature_2: Signature,
}
impl EndorsementDenunciation {
fn compute_hash_for_sig_verif(
public_key: &PublicKey,
slot: &Slot,
index: &u32,
content_hash: &Hash,
) -> Hash {
let mut hash_data = Vec::new();
hash_data.extend(public_key.to_bytes());
let denunciation_data = EndorsementDenunciationData::new(*slot, *index);
hash_data.extend(&denunciation_data.to_bytes());
hash_data.extend(content_hash.to_bytes());
Hash::compute_from(&hash_data)
}
pub fn get_public_key(&self) -> &PublicKey {
&self.public_key
}
pub fn get_slot(&self) -> &Slot {
&self.slot
}
pub fn get_index(&self) -> &u32 {
&self.index
}
pub fn get_hash_1(&self) -> &Hash {
&self.hash_1
}
pub fn get_hash_2(&self) -> &Hash {
&self.hash_2
}
pub fn get_signature_1(&self) -> &Signature {
&self.signature_1
}
pub fn get_signature_2(&self) -> &Signature {
&self.signature_2
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlockHeaderDenunciation {
public_key: PublicKey,
slot: Slot,
hash_1: Hash,
hash_2: Hash,
signature_1: Signature,
signature_2: Signature,
}
impl BlockHeaderDenunciation {
fn compute_hash_for_sig_verif(
public_key: &PublicKey,
slot: &Slot,
content_hash: &Hash,
) -> Hash {
let mut hash_data = Vec::new();
hash_data.extend(public_key.to_bytes());
let de_data = BlockHeaderDenunciationData::new(*slot);
hash_data.extend(de_data.to_bytes());
hash_data.extend(content_hash.to_bytes());
Hash::compute_from(&hash_data)
}
pub fn get_public_key(&self) -> &PublicKey {
&self.public_key
}
pub fn get_slot(&self) -> &Slot {
&self.slot
}
pub fn get_hash_1(&self) -> &Hash {
&self.hash_1
}
pub fn get_hash_2(&self) -> &Hash {
&self.hash_2
}
pub fn get_signature_1(&self) -> &Signature {
&self.signature_1
}
pub fn get_signature_2(&self) -> &Signature {
&self.signature_2
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum Denunciation {
Endorsement(EndorsementDenunciation),
BlockHeader(BlockHeaderDenunciation),
}
#[allow(dead_code)]
impl Denunciation {
pub fn is_for_endorsement(&self) -> bool {
matches!(self, Denunciation::Endorsement(_))
}
pub fn is_for_block_header(&self) -> bool {
matches!(self, Denunciation::BlockHeader(_))
}
pub fn is_also_for_endorsement(
&self,
s_endorsement: &SecureShareEndorsement,
) -> Result<bool, DenunciationError> {
match self {
Denunciation::BlockHeader(_) => Ok(false),
Denunciation::Endorsement(endo_de) => {
let content_hash = s_endorsement.id.get_hash();
let hash = EndorsementDenunciation::compute_hash_for_sig_verif(
&endo_de.public_key,
&endo_de.slot,
&endo_de.index,
content_hash,
);
Ok(endo_de.slot == s_endorsement.content.slot
&& endo_de.index == s_endorsement.content.index
&& endo_de.public_key == s_endorsement.content_creator_pub_key
&& endo_de.hash_1 != *content_hash
&& endo_de.hash_2 != *content_hash
&& endo_de
.public_key
.verify_signature(&hash, &s_endorsement.signature)
.is_ok())
}
}
}
pub fn is_also_for_block_header(
&self,
s_block_header: &SecuredHeader,
) -> Result<bool, DenunciationError> {
match self {
Denunciation::Endorsement(_) => Ok(false),
Denunciation::BlockHeader(endo_bh) => {
let content_hash = s_block_header.id.get_hash();
let hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
&endo_bh.public_key,
&endo_bh.slot,
content_hash,
);
Ok(endo_bh.slot == s_block_header.content.slot
&& endo_bh.public_key == s_block_header.content_creator_pub_key
&& endo_bh.hash_1 != *content_hash
&& endo_bh.hash_2 != *content_hash
&& endo_bh
.public_key
.verify_signature(&hash, &s_block_header.signature)
.is_ok())
}
}
}
pub fn is_valid(&self) -> bool {
let (signature_1, signature_2, hash_1, hash_2, public_key) = match self {
Denunciation::Endorsement(de) => {
let hash_1 = EndorsementDenunciation::compute_hash_for_sig_verif(
&de.public_key,
&de.slot,
&de.index,
&de.hash_1,
);
let hash_2 = EndorsementDenunciation::compute_hash_for_sig_verif(
&de.public_key,
&de.slot,
&de.index,
&de.hash_2,
);
(
de.signature_1,
de.signature_2,
hash_1,
hash_2,
de.public_key,
)
}
Denunciation::BlockHeader(de) => {
let hash_1 = BlockHeaderDenunciation::compute_hash_for_sig_verif(
&de.public_key,
&de.slot,
&de.hash_1,
);
let hash_2 = BlockHeaderDenunciation::compute_hash_for_sig_verif(
&de.public_key,
&de.slot,
&de.hash_2,
);
(
de.signature_1,
de.signature_2,
hash_1,
hash_2,
de.public_key,
)
}
};
hash_1 != hash_2
&& public_key.verify_signature(&hash_1, &signature_1).is_ok()
&& public_key.verify_signature(&hash_2, &signature_2).is_ok()
}
pub fn get_slot(&self) -> &Slot {
match self {
Denunciation::Endorsement(de) => &de.slot,
Denunciation::BlockHeader(de) => &de.slot,
}
}
pub fn get_index(&self) -> Option<&u32> {
match self {
Denunciation::BlockHeader(_) => None,
Denunciation::Endorsement(de) => Some(&de.index),
}
}
pub fn get_public_key(&self) -> &PublicKey {
match self {
Denunciation::Endorsement(de) => &de.public_key,
Denunciation::BlockHeader(de) => &de.public_key,
}
}
pub fn is_expired(
denunciation_slot_period: &u64,
slot_period: &u64,
denunciation_expire_periods: &u64,
) -> bool {
slot_period.checked_sub(*denunciation_slot_period) > Some(*denunciation_expire_periods)
}
}
impl TryFrom<(&SecureShareEndorsement, &SecureShareEndorsement)> for Denunciation {
type Error = DenunciationError;
fn try_from(
(s_e1, s_e2): (&SecureShareEndorsement, &SecureShareEndorsement),
) -> Result<Self, Self::Error> {
if s_e1.content.slot != s_e2.content.slot
|| s_e1.content.index != s_e2.content.index
|| s_e1.content_creator_pub_key != s_e2.content_creator_pub_key
|| s_e1.id == s_e2.id
{
return Err(DenunciationError::InvalidInput(format!(
"Not the same slot, index or public key or same hash for {:?} & {:?}",
s_e1, s_e2
)));
}
let s_e1_hash_content = s_e1.id.get_hash();
let s_e1_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
&s_e1.content_creator_pub_key,
&s_e1.content.slot,
&s_e1.content.index,
s_e1_hash_content,
);
let s_e2_hash_content = s_e2.id.get_hash();
let s_e2_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
&s_e1.content_creator_pub_key,
&s_e1.content.slot,
&s_e1.content.index,
s_e2_hash_content,
);
s_e1.content_creator_pub_key
.verify_signature(&s_e1_hash, &s_e1.signature)?;
s_e1.content_creator_pub_key
.verify_signature(&s_e2_hash, &s_e2.signature)?;
Ok(Denunciation::Endorsement(EndorsementDenunciation {
public_key: s_e1.content_creator_pub_key,
slot: s_e1.content.slot,
index: s_e1.content.index,
signature_1: s_e1.signature,
signature_2: s_e2.signature,
hash_1: *s_e1_hash_content,
hash_2: *s_e2_hash_content,
}))
}
}
impl TryFrom<(&SecuredHeader, &SecuredHeader)> for Denunciation {
type Error = DenunciationError;
fn try_from((s_bh1, s_bh2): (&SecuredHeader, &SecuredHeader)) -> Result<Self, Self::Error> {
if s_bh1.content.slot != s_bh2.content.slot
|| s_bh1.content_creator_pub_key != s_bh2.content_creator_pub_key
|| s_bh1.id == s_bh2.id
{
return Err(DenunciationError::InvalidInput(format!(
"Not the same slot or public key or same hash for {:?} & {:?}",
s_bh1, s_bh2
)));
}
let s_bh1_hash_content = s_bh1.id.get_hash();
let s_bh1_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
&s_bh1.content_creator_pub_key,
&s_bh1.content.slot,
s_bh1_hash_content,
);
let s_bh2_hash_content = s_bh2.id.get_hash();
let s_bh2_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
&s_bh1.content_creator_pub_key,
&s_bh1.content.slot,
s_bh2_hash_content,
);
s_bh1
.content_creator_pub_key
.verify_signature(&s_bh1_hash, &s_bh1.signature)?;
s_bh1
.content_creator_pub_key
.verify_signature(&s_bh2_hash, &s_bh2.signature)?;
Ok(Denunciation::BlockHeader(BlockHeaderDenunciation {
public_key: s_bh1.content_creator_pub_key,
slot: s_bh1.content.slot,
signature_1: s_bh1.signature,
signature_2: s_bh2.signature,
hash_1: *s_bh1_hash_content,
hash_2: *s_bh2_hash_content,
}))
}
}
#[allow(missing_docs)]
#[derive(IntoPrimitive, Debug, TryFromPrimitive, VariantCount)]
#[repr(u32)]
pub enum DenunciationTypeId {
BlockHeader = 0,
Endorsement = 1,
}
impl From<&Denunciation> for DenunciationTypeId {
fn from(value: &Denunciation) -> Self {
match value {
Denunciation::Endorsement(_) => DenunciationTypeId::Endorsement,
Denunciation::BlockHeader(_) => DenunciationTypeId::BlockHeader,
}
}
}
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum DenunciationError {
#[error("Invalid endorsements or block headers, cannot create denunciation: {0}")]
InvalidInput(String),
#[error("signature error: {0}")]
Signature(#[from] MassaSignatureError),
#[error("serialization error: {0}")]
Serialization(#[from] SerializeError),
}
struct EndorsementDenunciationSerializer {
slot_serializer: SlotSerializer,
u32_serializer: U32VarIntSerializer,
hash_serializer: HashSerializer,
}
impl EndorsementDenunciationSerializer {
const fn new() -> Self {
Self {
slot_serializer: SlotSerializer::new(),
u32_serializer: U32VarIntSerializer::new(),
hash_serializer: HashSerializer::new(),
}
}
}
impl Default for EndorsementDenunciationSerializer {
fn default() -> Self {
Self::new()
}
}
impl Serializer<EndorsementDenunciation> for EndorsementDenunciationSerializer {
fn serialize(
&self,
value: &EndorsementDenunciation,
buffer: &mut Vec<u8>,
) -> Result<(), SerializeError> {
buffer.extend(value.public_key.to_bytes());
self.slot_serializer.serialize(&value.slot, buffer)?;
self.u32_serializer.serialize(&value.index, buffer)?;
self.hash_serializer.serialize(&value.hash_1, buffer)?;
self.hash_serializer.serialize(&value.hash_2, buffer)?;
buffer.extend(value.signature_1.to_bytes());
buffer.extend(value.signature_2.to_bytes());
Ok(())
}
}
struct EndorsementDenunciationDeserializer {
slot_deserializer: SlotDeserializer,
index_deserializer: U32VarIntDeserializer,
hash_deserializer: HashDeserializer,
pubkey_deserializer: PublicKeyDeserializer,
signature_deserializer: SignatureDeserializer,
}
impl EndorsementDenunciationDeserializer {
const fn new(thread_count: u8, endorsement_count: u32) -> Self {
EndorsementDenunciationDeserializer {
slot_deserializer: SlotDeserializer::new(
(Included(0), Included(u64::MAX)),
(Included(0), Excluded(thread_count)),
),
index_deserializer: U32VarIntDeserializer::new(
Included(0),
Excluded(endorsement_count),
),
hash_deserializer: HashDeserializer::new(),
pubkey_deserializer: PublicKeyDeserializer::new(),
signature_deserializer: SignatureDeserializer::new(),
}
}
}
impl Deserializer<EndorsementDenunciation> for EndorsementDenunciationDeserializer {
fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
&self,
buffer: &'a [u8],
) -> IResult<&'a [u8], EndorsementDenunciation, E> {
context(
"Failed Endorsement Denunciation deserialization",
tuple((
context("Failed public key deserialization", |input| {
self.pubkey_deserializer.deserialize(input)
}),
context("Failed slot deserialization", |input| {
self.slot_deserializer.deserialize(input)
}),
context("Failed slot deserialization", |input| {
self.index_deserializer.deserialize(input)
}),
context("Failed hash 1 deserialization", |input| {
self.hash_deserializer.deserialize(input)
}),
context("Failed hash 2 deserialization", |input| {
self.hash_deserializer.deserialize(input)
}),
context("Failed signature 1 deserialization", |input| {
self.signature_deserializer.deserialize(input)
}),
context("Failed signature 2 deserialization", |input| {
self.signature_deserializer.deserialize(input)
}),
)),
)
.map(
|(public_key, slot, index, hash_1, hash_2, signature_1, signature_2)| {
EndorsementDenunciation {
public_key,
slot,
index,
hash_1,
hash_2,
signature_1,
signature_2,
}
},
)
.parse(buffer)
}
}
struct BlockHeaderDenunciationSerializer {
slot_serializer: SlotSerializer,
hash_serializer: HashSerializer,
}
impl BlockHeaderDenunciationSerializer {
const fn new() -> Self {
Self {
slot_serializer: SlotSerializer::new(),
hash_serializer: HashSerializer::new(),
}
}
}
impl Default for BlockHeaderDenunciationSerializer {
fn default() -> Self {
Self::new()
}
}
impl Serializer<BlockHeaderDenunciation> for BlockHeaderDenunciationSerializer {
fn serialize(
&self,
value: &BlockHeaderDenunciation,
buffer: &mut Vec<u8>,
) -> Result<(), SerializeError> {
buffer.extend(value.public_key.to_bytes());
self.slot_serializer.serialize(&value.slot, buffer)?;
self.hash_serializer.serialize(&value.hash_1, buffer)?;
self.hash_serializer.serialize(&value.hash_2, buffer)?;
buffer.extend(value.signature_1.to_bytes());
buffer.extend(value.signature_2.to_bytes());
Ok(())
}
}
struct BlockHeaderDenunciationDeserializer {
slot_deserializer: SlotDeserializer,
hash_deserializer: HashDeserializer,
pubkey_deserializer: PublicKeyDeserializer,
signature_deserializer: SignatureDeserializer,
}
impl BlockHeaderDenunciationDeserializer {
pub const fn new(thread_count: u8) -> Self {
Self {
slot_deserializer: SlotDeserializer::new(
(Included(0), Included(u64::MAX)),
(Included(0), Excluded(thread_count)),
),
hash_deserializer: HashDeserializer::new(),
pubkey_deserializer: PublicKeyDeserializer::new(),
signature_deserializer: SignatureDeserializer::new(),
}
}
}
impl Deserializer<BlockHeaderDenunciation> for BlockHeaderDenunciationDeserializer {
fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
&self,
buffer: &'a [u8],
) -> IResult<&'a [u8], BlockHeaderDenunciation, E> {
context(
"Failed BlockHeader Denunciation deserialization",
tuple((
context("Failed public key deserialization", |input| {
self.pubkey_deserializer.deserialize(input)
}),
context("Failed slot deserialization", |input| {
self.slot_deserializer.deserialize(input)
}),
context("Failed hash 1 deserialization", |input| {
self.hash_deserializer.deserialize(input)
}),
context("Failed hash 2 deserialization", |input| {
self.hash_deserializer.deserialize(input)
}),
context("Failed signature 1 deserialization", |input| {
self.signature_deserializer.deserialize(input)
}),
context("Failed signature 2 deserialization", |input| {
self.signature_deserializer.deserialize(input)
}),
)),
)
.map(
|(public_key, slot, hash_1, hash_2, signature_1, signature_2)| {
BlockHeaderDenunciation {
public_key,
slot,
hash_1,
hash_2,
signature_1,
signature_2,
}
},
)
.parse(buffer)
}
}
pub struct DenunciationSerializer {
endo_de_serializer: EndorsementDenunciationSerializer,
blkh_de_serializer: BlockHeaderDenunciationSerializer,
type_id_serializer: U32VarIntSerializer,
}
impl DenunciationSerializer {
pub const fn new() -> Self {
Self {
endo_de_serializer: EndorsementDenunciationSerializer::new(),
blkh_de_serializer: BlockHeaderDenunciationSerializer::new(),
type_id_serializer: U32VarIntSerializer::new(),
}
}
}
impl Default for DenunciationSerializer {
fn default() -> Self {
Self::new()
}
}
impl Serializer<Denunciation> for DenunciationSerializer {
fn serialize(&self, value: &Denunciation, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
let de_type_id = DenunciationTypeId::from(value);
self.type_id_serializer
.serialize(&u32::from(de_type_id), buffer)?;
match value {
Denunciation::Endorsement(de) => {
self.endo_de_serializer.serialize(de, buffer)?;
}
Denunciation::BlockHeader(de) => {
self.blkh_de_serializer.serialize(de, buffer)?;
}
}
Ok(())
}
}
pub struct DenunciationDeserializer {
endo_de_deserializer: EndorsementDenunciationDeserializer,
blkh_de_deserializer: BlockHeaderDenunciationDeserializer,
type_id_deserializer: U32VarIntDeserializer,
}
impl DenunciationDeserializer {
pub const fn new(thread_count: u8, endorsement_count: u32) -> Self {
Self {
endo_de_deserializer: EndorsementDenunciationDeserializer::new(
thread_count,
endorsement_count,
),
blkh_de_deserializer: BlockHeaderDenunciationDeserializer::new(thread_count),
type_id_deserializer: U32VarIntDeserializer::new(
Included(0),
Excluded(DenunciationTypeId::VARIANT_COUNT as u32),
),
}
}
}
impl Deserializer<Denunciation> for DenunciationDeserializer {
fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
&self,
buffer: &'a [u8],
) -> IResult<&'a [u8], Denunciation, E> {
let (rem, de_type_id_) = context("Failed Denunciation type id deserialization", |input| {
self.type_id_deserializer.deserialize(input)
})
.parse(buffer)?;
let de_type_id = DenunciationTypeId::try_from(de_type_id_).map_err(|_| {
nom::Err::Error(ParseError::from_error_kind(
buffer,
nom::error::ErrorKind::Fail,
))
})?;
match de_type_id {
DenunciationTypeId::Endorsement => {
let (rem2, endo_de) = self.endo_de_deserializer.deserialize(rem)?;
IResult::Ok((rem2, Denunciation::Endorsement(endo_de)))
}
DenunciationTypeId::BlockHeader => {
let (rem2, blkh_de) = self.blkh_de_deserializer.deserialize(rem)?;
IResult::Ok((rem2, Denunciation::BlockHeader(blkh_de)))
}
}
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum DenunciationIndex {
BlockHeader {
slot: Slot,
},
Endorsement {
slot: Slot,
index: u32,
},
}
impl DenunciationIndex {
pub fn get_slot(&self) -> &Slot {
match self {
DenunciationIndex::BlockHeader { slot } => slot,
DenunciationIndex::Endorsement { slot, .. } => slot,
}
}
pub fn get_index(&self) -> Option<&u32> {
match self {
DenunciationIndex::BlockHeader { .. } => None,
DenunciationIndex::Endorsement { slot: _, index } => Some(index),
}
}
pub fn get_hash(&self) -> Hash {
let mut buffer = u32::from(DenunciationIndexTypeId::from(self))
.to_le_bytes()
.to_vec();
match self {
DenunciationIndex::BlockHeader { slot } => buffer.extend(slot.to_bytes_key()),
DenunciationIndex::Endorsement { slot, index } => {
buffer.extend(slot.to_bytes_key());
buffer.extend(index.to_le_bytes());
}
}
Hash::compute_from(&buffer)
}
}
impl From<&Denunciation> for DenunciationIndex {
fn from(value: &Denunciation) -> Self {
match value {
Denunciation::Endorsement(endo_de) => DenunciationIndex::Endorsement {
slot: endo_de.slot,
index: endo_de.index,
},
Denunciation::BlockHeader(blkh_de) => {
DenunciationIndex::BlockHeader { slot: blkh_de.slot }
}
}
}
}
impl From<&DenunciationPrecursor> for DenunciationIndex {
fn from(value: &DenunciationPrecursor) -> Self {
match value {
DenunciationPrecursor::Endorsement(de_p) => DenunciationIndex::Endorsement {
slot: de_p.slot,
index: de_p.index,
},
DenunciationPrecursor::BlockHeader(de_p) => {
DenunciationIndex::BlockHeader { slot: de_p.slot }
}
}
}
}
impl Ord for DenunciationIndex {
fn cmp(&self, other: &Self) -> Ordering {
let self_key = (
self.get_slot(),
self.get_index().unwrap_or(&0),
u32::from(DenunciationIndexTypeId::from(self)),
);
let other_key = (
other.get_slot(),
other.get_index().unwrap_or(&0),
u32::from(DenunciationIndexTypeId::from(other)),
);
self_key.cmp(&other_key)
}
}
impl PartialOrd for DenunciationIndex {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(IntoPrimitive, Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u32)]
enum DenunciationIndexTypeId {
BlockHeader = 0,
Endorsement = 1,
}
impl From<&DenunciationIndex> for DenunciationIndexTypeId {
fn from(value: &DenunciationIndex) -> Self {
match value {
DenunciationIndex::BlockHeader { .. } => DenunciationIndexTypeId::BlockHeader,
DenunciationIndex::Endorsement { .. } => DenunciationIndexTypeId::Endorsement,
}
}
}
#[derive(Clone)]
pub struct DenunciationIndexSerializer {
u32_serializer: U32VarIntSerializer,
slot_serializer: SlotSerializer,
index_serializer: U32VarIntSerializer,
}
impl DenunciationIndexSerializer {
pub const fn new() -> Self {
Self {
u32_serializer: U32VarIntSerializer::new(),
slot_serializer: SlotSerializer::new(),
index_serializer: U32VarIntSerializer::new(),
}
}
}
impl Default for DenunciationIndexSerializer {
fn default() -> Self {
Self::new()
}
}
impl Serializer<DenunciationIndex> for DenunciationIndexSerializer {
fn serialize(
&self,
value: &DenunciationIndex,
buffer: &mut Vec<u8>,
) -> Result<(), SerializeError> {
match value {
DenunciationIndex::BlockHeader { slot } => {
self.u32_serializer
.serialize(&u32::from(DenunciationIndexTypeId::BlockHeader), buffer)?;
self.slot_serializer.serialize(slot, buffer)?;
}
DenunciationIndex::Endorsement { slot, index } => {
self.u32_serializer
.serialize(&u32::from(DenunciationIndexTypeId::Endorsement), buffer)?;
self.slot_serializer.serialize(slot, buffer)?;
self.index_serializer.serialize(index, buffer)?;
}
}
Ok(())
}
}
#[derive(Clone)]
pub struct DenunciationIndexDeserializer {
id_deserializer: U32VarIntDeserializer,
slot_deserializer: SlotDeserializer,
index_deserializer: U32VarIntDeserializer,
}
impl DenunciationIndexDeserializer {
pub const fn new(thread_count: u8, endorsement_count: u32) -> Self {
Self {
id_deserializer: U32VarIntDeserializer::new(Included(0), Included(u32::MAX)),
slot_deserializer: SlotDeserializer::new(
(Included(0), Included(u64::MAX)),
(Included(0), Excluded(thread_count)),
),
index_deserializer: U32VarIntDeserializer::new(
Included(0),
Included(endorsement_count),
),
}
}
}
impl Deserializer<DenunciationIndex> for DenunciationIndexDeserializer {
fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
&self,
buffer: &'a [u8],
) -> IResult<&'a [u8], DenunciationIndex, E> {
let (input, id) = self.id_deserializer.deserialize(buffer)?;
let id = DenunciationIndexTypeId::try_from(id).map_err(|_| {
nom::Err::Error(ParseError::from_error_kind(
buffer,
nom::error::ErrorKind::Eof,
))
})?;
match id {
DenunciationIndexTypeId::BlockHeader => {
context("Failed slot deserialization", |input| {
self.slot_deserializer.deserialize(input)
})
.map(|slot| DenunciationIndex::BlockHeader { slot })
.parse(input)
}
DenunciationIndexTypeId::Endorsement => context(
"Failed Endorsement denunciation index",
tuple((
context("Failed slot deserialization", |input| {
self.slot_deserializer.deserialize(input)
}),
context("Failed index deserialization", |input| {
self.index_deserializer.deserialize(input)
}),
)),
)
.map(|(slot, index)| DenunciationIndex::Endorsement { slot, index })
.parse(input),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct EndorsementDenunciationPrecursor {
pub public_key: PublicKey,
pub slot: Slot,
pub index: u32,
hash: Hash,
signature: Signature,
}
#[derive(Debug, Clone, PartialEq)]
pub struct BlockHeaderDenunciationPrecursor {
pub public_key: PublicKey,
pub slot: Slot,
hash: Hash,
signature: Signature,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DenunciationPrecursor {
Endorsement(EndorsementDenunciationPrecursor),
BlockHeader(BlockHeaderDenunciationPrecursor),
}
impl DenunciationPrecursor {
pub fn get_slot(&self) -> &Slot {
match self {
DenunciationPrecursor::Endorsement(endo_de_p) => &endo_de_p.slot,
DenunciationPrecursor::BlockHeader(blkh_de_p) => &blkh_de_p.slot,
}
}
pub fn get_public_key(&self) -> &PublicKey {
match self {
DenunciationPrecursor::Endorsement(endo_de_p) => &endo_de_p.public_key,
DenunciationPrecursor::BlockHeader(blkh_de_p) => &blkh_de_p.public_key,
}
}
}
impl From<&SecureShareEndorsement> for DenunciationPrecursor {
fn from(value: &SecureShareEndorsement) -> Self {
DenunciationPrecursor::Endorsement(EndorsementDenunciationPrecursor {
public_key: value.content_creator_pub_key,
slot: value.content.slot,
index: value.content.index,
hash: *value.id.get_hash(),
signature: value.signature,
})
}
}
impl From<&SecuredHeader> for DenunciationPrecursor {
fn from(value: &SecuredHeader) -> Self {
DenunciationPrecursor::BlockHeader(BlockHeaderDenunciationPrecursor {
public_key: value.content_creator_pub_key,
slot: value.content.slot,
hash: *value.id.get_hash(),
signature: value.signature,
})
}
}
impl TryFrom<(&DenunciationPrecursor, &DenunciationPrecursor)> for Denunciation {
type Error = DenunciationError;
fn try_from(
(de_p_1, de_p_2): (&DenunciationPrecursor, &DenunciationPrecursor),
) -> Result<Self, Self::Error> {
match (de_p_1, de_p_2) {
(
DenunciationPrecursor::BlockHeader(de_p_blkh_1),
DenunciationPrecursor::BlockHeader(de_p_blkh_2),
) => {
if de_p_blkh_1.slot != de_p_blkh_2.slot
|| de_p_blkh_1.public_key != de_p_blkh_2.public_key
|| de_p_blkh_1.hash == de_p_blkh_2.hash
{
return Err(DenunciationError::InvalidInput(
format!("Not the same slot or public key or same hash for de precursor: {:?} & {:?}", de_p_blkh_1, de_p_blkh_2)
));
}
let de_p_blkh_1_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
&de_p_blkh_1.public_key,
&de_p_blkh_1.slot,
&de_p_blkh_1.hash,
);
let de_p_blkh_2_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
&de_p_blkh_2.public_key,
&de_p_blkh_2.slot,
&de_p_blkh_2.hash,
);
de_p_blkh_1
.public_key
.verify_signature(&de_p_blkh_1_hash, &de_p_blkh_1.signature)?;
de_p_blkh_1
.public_key
.verify_signature(&de_p_blkh_2_hash, &de_p_blkh_2.signature)?;
Ok(Denunciation::BlockHeader(BlockHeaderDenunciation {
public_key: de_p_blkh_1.public_key,
slot: de_p_blkh_1.slot,
signature_1: de_p_blkh_1.signature,
signature_2: de_p_blkh_2.signature,
hash_1: de_p_blkh_1.hash,
hash_2: de_p_blkh_2.hash,
}))
}
(
DenunciationPrecursor::Endorsement(de_p_endo_1),
DenunciationPrecursor::Endorsement(de_p_endo_2),
) => {
if de_p_endo_1.slot != de_p_endo_2.slot
|| de_p_endo_1.index != de_p_endo_2.index
|| de_p_endo_1.public_key != de_p_endo_2.public_key
|| de_p_endo_1.hash == de_p_endo_2.hash
{
return Err(DenunciationError::InvalidInput(
format!("Not the same slot, index or public key or same hash for de precursor: {:?} & {:?}", de_p_endo_1, de_p_endo_2)
));
}
let de_p_endo_1_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
&de_p_endo_1.public_key,
&de_p_endo_1.slot,
&de_p_endo_1.index,
&de_p_endo_1.hash,
);
let de_p_endo_2_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
&de_p_endo_2.public_key,
&de_p_endo_2.slot,
&de_p_endo_2.index,
&de_p_endo_2.hash,
);
de_p_endo_1
.public_key
.verify_signature(&de_p_endo_1_hash, &de_p_endo_1.signature)?;
de_p_endo_1
.public_key
.verify_signature(&de_p_endo_2_hash, &de_p_endo_2.signature)?;
Ok(Denunciation::Endorsement(EndorsementDenunciation {
public_key: de_p_endo_1.public_key,
slot: de_p_endo_1.slot,
index: de_p_endo_1.index,
signature_1: de_p_endo_1.signature,
signature_2: de_p_endo_2.signature,
hash_1: de_p_endo_1.hash,
hash_2: de_p_endo_2.hash,
}))
}
_ => {
Err(DenunciationError::InvalidInput(
"Mixed endorsement and block header denunciation precursors".to_string(),
))
}
}
}
}
#[cfg(any(test, feature = "test-exports"))]
impl Denunciation {
pub fn check_invariants(&self) -> Result<(), Box<dyn std::error::Error>> {
if !self.is_valid() {
return Err(format!("Denunciation is invalid: {:?}", self).into());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use massa_serialization::DeserializeError;
use massa_signature::KeyPair;
use crate::block_id::BlockId;
use crate::config::{CHAINID, ENDORSEMENT_COUNT, THREAD_COUNT};
use crate::endorsement::{Endorsement, EndorsementSerializer, SecureShareEndorsement};
use crate::secure_share::{Id, SecureShareContent};
use crate::test_exports::{
gen_block_headers_for_denunciation, gen_endorsements_for_denunciation,
};
#[test]
fn test_endorsement_denunciation() {
let (_slot, _keypair, s_endorsement_1, s_endorsement_2, _s_endorsement_3) =
gen_endorsements_for_denunciation(None, None);
let denunciation: Denunciation = (&s_endorsement_1, &s_endorsement_2).try_into().unwrap();
assert!(denunciation.is_for_endorsement());
assert!(denunciation.is_valid());
}
#[test]
fn test_endorsement_denunciation_invalid_1() {
let (slot, keypair, s_endorsement_1, _s_endorsement_2, _s_endorsement_3) =
gen_endorsements_for_denunciation(None, None);
let endorsement_4 = Endorsement {
slot,
index: 9,
endorsed_block: BlockId::generate_from_hash(Hash::compute_from("foo".as_bytes())),
};
let s_endorsement_4 = Endorsement::new_verifiable(
endorsement_4,
EndorsementSerializer::new(),
&keypair,
*CHAINID,
)
.unwrap();
let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_4));
assert!(matches!(
denunciation,
Err(DenunciationError::InvalidInput(..))
));
let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_1));
assert!(matches!(
denunciation,
Err(DenunciationError::InvalidInput(..))
));
}
#[test]
fn test_endorsement_denunciation_is_for() {
let (slot, keypair, s_endorsement_1, s_endorsement_2, s_endorsement_3) =
gen_endorsements_for_denunciation(None, None);
let denunciation: Denunciation = (&s_endorsement_1, &s_endorsement_2).try_into().unwrap();
assert!(denunciation.is_for_endorsement());
assert!(denunciation.is_valid());
let endorsement_4 = Endorsement {
slot,
index: 9,
endorsed_block: BlockId::generate_from_hash(Hash::compute_from("foo".as_bytes())),
};
let s_endorsement_4 = Endorsement::new_verifiable(
endorsement_4,
EndorsementSerializer::new(),
&keypair,
*CHAINID,
)
.unwrap();
assert!(!denunciation
.is_also_for_endorsement(&s_endorsement_4)
.unwrap());
assert!(denunciation
.is_also_for_endorsement(&s_endorsement_3)
.unwrap());
assert!(denunciation.is_valid());
}
#[test]
fn test_block_header_denunciation() {
let (_slot, _keypair, s_block_header_1, s_block_header_2, s_block_header_3) =
gen_block_headers_for_denunciation(None, None);
let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
assert!(denunciation.is_for_block_header());
assert!(denunciation.is_valid());
assert!(denunciation
.is_also_for_block_header(&s_block_header_3)
.unwrap());
}
#[test]
fn test_forge_invalid_denunciation() {
let keypair = KeyPair::generate(0).unwrap();
let slot_1 = Slot::new(4, 2);
let slot_2 = Slot::new(3, 7);
let endorsement_1 = Endorsement {
slot: slot_1,
index: 0,
endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk1".as_bytes())),
};
let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
endorsement_1,
EndorsementSerializer::new(),
&keypair,
*CHAINID,
)
.unwrap();
let endorsement_2 = Endorsement {
slot: slot_2,
index: 0,
endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk2".as_bytes())),
};
let s_endorsement_2: SecureShareEndorsement = Endorsement::new_verifiable(
endorsement_2,
EndorsementSerializer::new(),
&keypair,
*CHAINID,
)
.unwrap();
let de_forged_1 = Denunciation::Endorsement(EndorsementDenunciation {
public_key: keypair.get_public_key(),
slot: slot_1,
index: 0,
hash_1: *s_endorsement_1.id.get_hash(), hash_2: *s_endorsement_1.id.get_hash(),
signature_1: s_endorsement_1.signature,
signature_2: s_endorsement_1.signature,
});
assert!(!de_forged_1.is_valid());
let de_forged_2 = Denunciation::Endorsement(EndorsementDenunciation {
public_key: keypair.get_public_key(),
slot: slot_2,
index: 0,
hash_1: *s_endorsement_1.id.get_hash(),
hash_2: *s_endorsement_2.id.get_hash(),
signature_1: s_endorsement_1.signature,
signature_2: s_endorsement_2.signature,
});
assert!(!de_forged_2.is_valid());
}
#[test]
fn test_endorsement_denunciation_ser_der() {
let (_, _, s_endorsement_1, s_endorsement_2, _) =
gen_endorsements_for_denunciation(None, None);
let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
let mut buffer = Vec::new();
let de_ser = EndorsementDenunciationSerializer::new();
match denunciation {
Denunciation::Endorsement(de) => {
de_ser.serialize(&de, &mut buffer).unwrap();
let de_der =
EndorsementDenunciationDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
assert!(rem.is_empty());
assert_eq!(de, de_der_res);
}
Denunciation::BlockHeader(_) => {
unimplemented!()
}
}
}
#[test]
fn test_block_header_denunciation_ser_der() {
let (_, _, s_block_header_1, s_block_header_2, _) =
gen_block_headers_for_denunciation(None, None);
let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
let mut buffer = Vec::new();
let de_ser = BlockHeaderDenunciationSerializer::new();
match denunciation {
Denunciation::Endorsement(_) => {
unimplemented!()
}
Denunciation::BlockHeader(de) => {
de_ser.serialize(&de, &mut buffer).unwrap();
let de_der = BlockHeaderDenunciationDeserializer::new(THREAD_COUNT);
let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
assert!(rem.is_empty());
assert_eq!(de, de_der_res);
}
}
}
#[test]
fn test_denunciation_ser_der() {
let (_, _, s_block_header_1, s_block_header_2, _) =
gen_block_headers_for_denunciation(None, None);
let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
let mut buffer = Vec::new();
let de_ser = DenunciationSerializer::new();
de_ser.serialize(&denunciation, &mut buffer).unwrap();
let de_der = DenunciationDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
assert!(rem.is_empty());
assert_eq!(denunciation, de_der_res);
let (_, _, s_endorsement_1, s_endorsement_2, _) =
gen_endorsements_for_denunciation(None, None);
let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
buffer.clear();
de_ser.serialize(&denunciation, &mut buffer).unwrap();
let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
assert!(rem.is_empty());
assert_eq!(denunciation, de_der_res);
}
#[test]
fn test_denunciation_precursor() {
let (_, _, s_block_header_1, s_block_header_2, _) =
gen_block_headers_for_denunciation(None, None);
let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
let de_p_1 = DenunciationPrecursor::from(&s_block_header_1);
let de_p_2 = DenunciationPrecursor::from(&s_block_header_2);
let denunciation_2: Denunciation = (&de_p_1, &de_p_2).try_into().unwrap();
assert_eq!(denunciation, denunciation_2);
let (_, _, s_endorsement_1, s_endorsement_2, _) =
gen_endorsements_for_denunciation(None, None);
let denunciation_3 = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
let de_p_3 = DenunciationPrecursor::from(&s_endorsement_1);
let de_p_4 = DenunciationPrecursor::from(&s_endorsement_2);
let denunciation_4: Denunciation = (&de_p_3, &de_p_4).try_into().unwrap();
assert_eq!(denunciation_3, denunciation_4);
}
#[test]
fn test_denunciation_index_ser_der() {
let (_, _, s_block_header_1, s_block_header_2, _) =
gen_block_headers_for_denunciation(None, None);
let denunciation_1: Denunciation =
(&s_block_header_1, &s_block_header_2).try_into().unwrap();
let denunciation_index_1 = DenunciationIndex::from(&denunciation_1);
let (_, _, s_endorsement_1, s_endorsement_2, _) =
gen_endorsements_for_denunciation(None, None);
let denunciation_2 = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
let denunciation_index_2 = DenunciationIndex::from(&denunciation_2);
let mut buffer = Vec::new();
let de_idx_ser = DenunciationIndexSerializer::new();
de_idx_ser
.serialize(&denunciation_index_1, &mut buffer)
.unwrap();
let de_idx_der = DenunciationIndexDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
let (rem, de_idx_der_res) = de_idx_der.deserialize::<DeserializeError>(&buffer).unwrap();
assert!(rem.is_empty());
assert_eq!(denunciation_index_1, de_idx_der_res);
let mut buffer = Vec::new();
let de_idx_ser = DenunciationIndexSerializer::new();
de_idx_ser
.serialize(&denunciation_index_2, &mut buffer)
.unwrap();
let de_idx_der = DenunciationIndexDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
let (rem, de_idx_der_res) = de_idx_der.deserialize::<DeserializeError>(&buffer).unwrap();
assert!(rem.is_empty());
assert_eq!(denunciation_index_2, de_idx_der_res);
}
}