use crate::consensus::{Context, Finalization, Notarization, Scheme};
use bytes::{Buf, BufMut};
use commonware_codec::{varint::UInt, Encode, EncodeSize, Error, Read, ReadExt, Write};
use commonware_consensus::{types::Height, CertifiableBlock, Heightable};
use commonware_cryptography::{sha256::Digest, Digestible, Hasher, Sha256};
use commonware_parallel::Strategy;
use commonware_utils::sys_rng;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Block {
pub context: Context,
pub parent: Digest,
pub height: Height,
pub timestamp: u64,
digest: Digest,
}
impl Block {
fn compute_digest(
context: &Context,
parent: &Digest,
height: Height,
timestamp: u64,
) -> Digest {
let mut hasher = Sha256::new();
hasher.update(&context.encode());
hasher.update(parent);
hasher.update(&height.get().to_be_bytes());
hasher.update(×tamp.to_be_bytes());
hasher.finalize()
}
pub fn new(context: Context, parent: Digest, height: Height, timestamp: u64) -> Self {
let digest = Self::compute_digest(&context, &parent, height, timestamp);
Self {
context,
parent,
height,
timestamp,
digest,
}
}
}
impl Write for Block {
fn write(&self, writer: &mut impl BufMut) {
self.context.write(writer);
self.parent.write(writer);
self.height.write(writer);
UInt(self.timestamp).write(writer);
}
}
impl Read for Block {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
let context = Context::read(reader)?;
let parent = Digest::read(reader)?;
let height = Height::read(reader)?;
let timestamp = UInt::read(reader)?.0;
let digest = Self::compute_digest(&context, &parent, height, timestamp);
Ok(Self {
context,
parent,
height,
timestamp,
digest,
})
}
}
impl EncodeSize for Block {
fn encode_size(&self) -> usize {
self.context.encode_size()
+ self.parent.encode_size()
+ self.height.encode_size()
+ UInt(self.timestamp).encode_size()
}
}
impl Digestible for Block {
type Digest = Digest;
fn digest(&self) -> Digest {
self.digest
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Notarized {
pub proof: Notarization,
pub block: Block,
}
impl Notarized {
pub fn new(proof: Notarization, block: Block) -> Self {
Self { proof, block }
}
pub fn verify(&self, scheme: &Scheme, strategy: &impl Strategy) -> bool {
self.proof.verify(&mut sys_rng(), scheme, strategy)
}
}
impl Write for Notarized {
fn write(&self, buf: &mut impl BufMut) {
self.proof.write(buf);
self.block.write(buf);
}
}
impl Read for Notarized {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
let proof = Notarization::read(buf)?;
let block = Block::read(buf)?;
if proof.proposal.payload != block.digest() {
return Err(Error::Invalid(
"types::Notarized",
"Proof payload does not match block digest",
));
}
Ok(Self { proof, block })
}
}
impl EncodeSize for Notarized {
fn encode_size(&self) -> usize {
self.proof.encode_size() + self.block.encode_size()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Finalized {
pub proof: Finalization,
pub block: Block,
}
impl Finalized {
pub fn new(proof: Finalization, block: Block) -> Self {
Self { proof, block }
}
pub fn verify(&self, scheme: &Scheme, strategy: &impl Strategy) -> bool {
self.proof.verify(&mut sys_rng(), scheme, strategy)
}
}
impl Write for Finalized {
fn write(&self, buf: &mut impl BufMut) {
self.proof.write(buf);
self.block.write(buf);
}
}
impl Read for Finalized {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
let proof = Finalization::read(buf)?;
let block = Block::read(buf)?;
if proof.proposal.payload != block.digest() {
return Err(Error::Invalid(
"types::Finalized",
"Proof payload does not match block digest",
));
}
Ok(Self { proof, block })
}
}
impl EncodeSize for Finalized {
fn encode_size(&self) -> usize {
self.proof.encode_size() + self.block.encode_size()
}
}
impl commonware_consensus::Block for Block {
fn parent(&self) -> Digest {
self.parent
}
}
impl Heightable for Block {
fn height(&self) -> Height {
self.height
}
}
impl CertifiableBlock for Block {
type Context = Context;
fn context(&self) -> Self::Context {
self.context.clone()
}
}