#[doc(hidden)]
pub const STR_002_MODULE_PRESENT: () = ();
use chia_bls::{aggregate, PublicKey, Signature};
use chia_consensus::merkle_set::compute_merkle_set_root;
use chia_protocol::Bytes32;
use chia_sdk_types::{MerkleProof, MerkleTree};
use chia_sha2::Sha256;
use dig_block::{Checkpoint, CheckpointSubmission, SignerBitmap};
use crate::constants::EMPTY_ROOT;
use crate::error::EpochError;
use crate::types::verification::{EpochCheckpointData, EpochCheckpointSignMaterial};
pub fn compute_epoch_block_root(block_hashes: &[Bytes32]) -> Bytes32 {
if block_hashes.is_empty() {
return EMPTY_ROOT;
}
MerkleTree::new(block_hashes).root()
}
pub fn epoch_block_inclusion_proof(block_hashes: &[Bytes32], index: usize) -> Option<MerkleProof> {
if index >= block_hashes.len() {
return None;
}
let tree = MerkleTree::new(block_hashes);
tree.proof(block_hashes[index])
}
pub fn verify_block_inclusion_proof(leaf: Bytes32, proof: &MerkleProof, root: Bytes32) -> bool {
let mut current = {
let mut h = Sha256::new();
h.update([0x01u8]);
h.update(leaf.as_ref());
Bytes32::new(h.finalize())
};
for (level, sibling) in proof.proof.iter().enumerate() {
let bit_is_right = (proof.path >> level) & 1 == 1;
let mut h = Sha256::new();
h.update([0x02u8]);
if bit_is_right {
h.update(sibling.as_ref());
h.update(current.as_ref());
} else {
h.update(current.as_ref());
h.update(sibling.as_ref());
}
current = Bytes32::new(h.finalize());
}
current == root
}
pub fn compute_epoch_withdrawals_root(withdrawal_hashes: &[Bytes32]) -> Bytes32 {
if withdrawal_hashes.is_empty() {
return EMPTY_ROOT;
}
let mut leaves: Vec<[u8; 32]> = withdrawal_hashes.iter().map(|h| h.to_bytes()).collect();
Bytes32::new(compute_merkle_set_root(&mut leaves))
}
#[allow(clippy::too_many_arguments)]
pub fn epoch_checkpoint_sign_material_from_l2_blocks(
network_id: Bytes32,
epoch: u64,
block_hashes: &[Bytes32],
state_root: Bytes32,
withdrawal_hashes: &[Bytes32],
prev_checkpoint: Bytes32,
total_fees: u64,
tx_count: u64,
stake_percentage: u64,
) -> EpochCheckpointSignMaterial {
let block_root = compute_epoch_block_root(block_hashes);
let withdrawals_root = compute_epoch_withdrawals_root(withdrawal_hashes);
let mut checkpoint = Checkpoint::new();
checkpoint.epoch = epoch;
checkpoint.state_root = state_root;
checkpoint.block_root = block_root;
checkpoint.block_count = block_hashes.len() as u32;
checkpoint.tx_count = tx_count;
checkpoint.total_fees = total_fees;
checkpoint.prev_checkpoint = prev_checkpoint;
checkpoint.withdrawals_root = withdrawals_root;
checkpoint.withdrawal_count = withdrawal_hashes.len() as u32;
let checkpoint_hash = checkpoint.hash();
let score = checkpoint.compute_score(stake_percentage);
let data = EpochCheckpointData {
network_id,
epoch,
block_root,
state_root,
withdrawals_root,
checkpoint_hash,
};
let signing_digest = data.signing_digest();
EpochCheckpointSignMaterial {
checkpoint: data,
score,
signing_digest,
}
}
pub fn stored_checkpoint_from_epoch_sign_material_with_aggregate_v1(
material: &EpochCheckpointSignMaterial,
validator_set: &[(u32, PublicKey)],
per_validator: &[(u32, PublicKey, Signature)],
submitter: u32,
) -> Result<CheckpointSubmission, EpochError> {
if per_validator.is_empty() {
return Err(EpochError::DfspBoundary(
"aggregate signature requires at least one signer".to_string(),
));
}
let sigs: Vec<Signature> = per_validator.iter().map(|(_, _, s)| s.clone()).collect();
let agg_sig = aggregate(&sigs);
let mut agg_pk = PublicKey::default();
for (_, pk, _) in per_validator {
agg_pk += pk;
}
let mut bitmap = SignerBitmap::new(validator_set.len() as u32);
for (vi, (idx, _)) in validator_set.iter().enumerate() {
if per_validator.iter().any(|(i, _, _)| i == idx) {
bitmap
.set_signed(vi as u32)
.map_err(|e| EpochError::DfspBoundary(format!("set_signed: {e}")))?;
}
}
let mut cp = Checkpoint::new();
cp.epoch = material.checkpoint.epoch;
cp.state_root = material.checkpoint.state_root;
cp.block_root = material.checkpoint.block_root;
cp.withdrawals_root = material.checkpoint.withdrawals_root;
Ok(CheckpointSubmission::new(
cp,
bitmap,
agg_sig,
agg_pk,
material.score,
submitter,
))
}