use miden_core::Word;
use miden_crypto::dsa::ecdsa_k256_keccak::Signature;
use crate::MIN_PROOF_SECURITY_LEVEL;
use crate::block::{BlockBody, BlockHeader, BlockProof};
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
#[derive(Debug, thiserror::Error)]
pub enum ProvenBlockError {
#[error(
"ECDSA signature verification failed based on the proven block's header commitment, validator public key and signature"
)]
InvalidSignature,
#[error(
"header tx commitment ({header_tx_commitment}) does not match body tx commitment ({body_tx_commitment})"
)]
TxCommitmentMismatch {
header_tx_commitment: Word,
body_tx_commitment: Word,
},
#[error(
"proven block header note root ({header_root}) does not match the corresponding body's note root ({body_root})"
)]
NoteRootMismatch { header_root: Word, body_root: Word },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProvenBlock {
header: BlockHeader,
body: BlockBody,
signature: Signature,
proof: BlockProof,
}
impl ProvenBlock {
pub fn new(
header: BlockHeader,
body: BlockBody,
signature: Signature,
proof: BlockProof,
) -> Result<Self, ProvenBlockError> {
let proven_block = Self { header, signature, body, proof };
proven_block.validate()?;
Ok(proven_block)
}
pub fn new_unchecked(
header: BlockHeader,
body: BlockBody,
signature: Signature,
proof: BlockProof,
) -> Self {
Self { header, signature, body, proof }
}
pub fn validate(&self) -> Result<(), ProvenBlockError> {
self.validate_signature()?;
self.validate_tx_commitment()?;
self.validate_note_root()?;
Ok(())
}
pub fn proof_security_level(&self) -> u32 {
MIN_PROOF_SECURITY_LEVEL
}
pub fn header(&self) -> &BlockHeader {
&self.header
}
pub fn body(&self) -> &BlockBody {
&self.body
}
pub fn signature(&self) -> &Signature {
&self.signature
}
pub fn proof(&self) -> &BlockProof {
&self.proof
}
pub fn into_parts(self) -> (BlockHeader, BlockBody, Signature, BlockProof) {
(self.header, self.body, self.signature, self.proof)
}
fn validate_signature(&self) -> Result<(), ProvenBlockError> {
if !self.signature.verify(self.header.commitment(), self.header.validator_key()) {
Err(ProvenBlockError::InvalidSignature)
} else {
Ok(())
}
}
fn validate_tx_commitment(&self) -> Result<(), ProvenBlockError> {
let header_tx_commitment = self.header.tx_commitment();
let body_tx_commitment = self.body.transactions().commitment();
if header_tx_commitment != body_tx_commitment {
Err(ProvenBlockError::TxCommitmentMismatch { header_tx_commitment, body_tx_commitment })
} else {
Ok(())
}
}
fn validate_note_root(&self) -> Result<(), ProvenBlockError> {
let header_root = self.header.note_root();
let body_root = self.body.compute_block_note_tree().root();
if header_root != body_root {
Err(ProvenBlockError::NoteRootMismatch { header_root, body_root })
} else {
Ok(())
}
}
}
impl Serializable for ProvenBlock {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.header.write_into(target);
self.body.write_into(target);
self.signature.write_into(target);
self.proof.write_into(target);
}
}
impl Deserializable for ProvenBlock {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let block = Self {
header: BlockHeader::read_from(source)?,
body: BlockBody::read_from(source)?,
signature: Signature::read_from(source)?,
proof: BlockProof::read_from(source)?,
};
Ok(block)
}
}