use chia_sha2::Sha256;
use serde::{Deserialize, Serialize};
use super::signer_bitmap::SignerBitmap;
use crate::error::CheckpointError;
use crate::primitives::{Bytes32, PublicKey, Signature};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Checkpoint {
pub epoch: u64,
pub state_root: Bytes32,
pub block_root: Bytes32,
pub block_count: u32,
pub tx_count: u64,
pub total_fees: u64,
pub prev_checkpoint: Bytes32,
pub withdrawals_root: Bytes32,
pub withdrawal_count: u32,
}
impl Checkpoint {
#[must_use]
pub fn new() -> Self {
Self {
epoch: 0,
state_root: Bytes32::default(),
block_root: Bytes32::default(),
block_count: 0,
tx_count: 0,
total_fees: 0,
prev_checkpoint: Bytes32::default(),
withdrawals_root: Bytes32::default(),
withdrawal_count: 0,
}
}
#[must_use]
pub fn compute_score(&self, stake_percentage: u64) -> u64 {
stake_percentage * u64::from(self.block_count)
}
pub const HASH_PREIMAGE_LEN: usize = 160;
#[must_use]
pub fn hash_preimage_bytes(&self) -> [u8; Self::HASH_PREIMAGE_LEN] {
fn put(buf: &mut [u8; Checkpoint::HASH_PREIMAGE_LEN], i: &mut usize, bytes: &[u8]) {
buf[*i..*i + bytes.len()].copy_from_slice(bytes);
*i += bytes.len();
}
let mut buf = [0u8; Self::HASH_PREIMAGE_LEN];
let mut i = 0usize;
put(&mut buf, &mut i, &self.epoch.to_le_bytes());
put(&mut buf, &mut i, self.state_root.as_ref());
put(&mut buf, &mut i, self.block_root.as_ref());
put(&mut buf, &mut i, &self.block_count.to_le_bytes());
put(&mut buf, &mut i, &self.tx_count.to_le_bytes());
put(&mut buf, &mut i, &self.total_fees.to_le_bytes());
put(&mut buf, &mut i, self.prev_checkpoint.as_ref());
put(&mut buf, &mut i, self.withdrawals_root.as_ref());
put(&mut buf, &mut i, &self.withdrawal_count.to_le_bytes());
debug_assert_eq!(i, Self::HASH_PREIMAGE_LEN);
buf
}
#[must_use]
pub fn hash(&self) -> Bytes32 {
let mut hasher = Sha256::new();
hasher.update(self.hash_preimage_bytes());
Bytes32::new(hasher.finalize())
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
bincode::serialize(self).expect("Checkpoint serialization should never fail")
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CheckpointError> {
bincode::deserialize(bytes).map_err(|e| CheckpointError::InvalidData(e.to_string()))
}
}
impl Default for Checkpoint {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckpointSubmission {
pub checkpoint: Checkpoint,
pub signer_bitmap: SignerBitmap,
pub aggregate_signature: Signature,
pub aggregate_pubkey: PublicKey,
pub score: u64,
pub submitter: u32,
pub submission_height: Option<u32>,
pub submission_coin: Option<Bytes32>,
}
impl CheckpointSubmission {
#[must_use]
pub fn new(
checkpoint: Checkpoint,
signer_bitmap: SignerBitmap,
aggregate_signature: Signature,
aggregate_pubkey: PublicKey,
score: u64,
submitter: u32,
) -> Self {
Self {
checkpoint,
signer_bitmap,
aggregate_signature,
aggregate_pubkey,
score,
submitter,
submission_height: None,
submission_coin: None,
}
}
#[must_use]
pub fn hash(&self) -> Bytes32 {
self.checkpoint.hash()
}
#[must_use]
pub fn epoch(&self) -> u64 {
self.checkpoint.epoch
}
#[must_use]
pub fn signing_percentage(&self) -> u64 {
self.signer_bitmap.signing_percentage()
}
#[must_use]
pub fn meets_threshold(&self, threshold_pct: u64) -> bool {
self.signer_bitmap.has_threshold(threshold_pct)
}
pub fn record_submission(&mut self, height: u32, coin_id: Bytes32) {
self.submission_height = Some(height);
self.submission_coin = Some(coin_id);
}
#[must_use]
pub fn is_submitted(&self) -> bool {
self.submission_height.is_some()
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
bincode::serialize(self).expect("CheckpointSubmission serialization should never fail")
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CheckpointError> {
bincode::deserialize(bytes).map_err(|e| CheckpointError::InvalidData(e.to_string()))
}
}