use crate::utils::codec::{Decode, Encode};
use crate::{
Hashable, PrimitiveError, PrimitiveResult, Validate,
crypto::hashing::{Blake3Hasher, Hasher},
header::Header,
transaction::Transaction,
types::{AccountId, BlockNumber, Gas, Hash, ServiceId, Signature, TimeSlot, Timestamp, Weight},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct Block {
pub header: Header,
pub body: BlockBody,
}
impl Block {
pub fn new(header: Header, body: BlockBody) -> Self {
Self { header, body }
}
pub fn number(&self) -> BlockNumber {
self.header.number
}
pub fn hash(&self) -> Hash {
self.header.hash()
}
pub fn parent_hash(&self) -> Hash {
self.header.parent_hash
}
pub fn timestamp(&self) -> Timestamp {
self.header.timestamp
}
pub fn weight(&self) -> Weight {
self.body.weight()
}
pub fn is_genesis(&self) -> bool {
self.header.number == BlockNumber(0)
}
pub fn transactions(&self) -> &[Transaction] {
&self.body.transactions
}
pub fn size(&self) -> usize {
self.header.encoded_size() + self.body.encoded_size()
}
}
impl Validate for Block {
fn validate(&self) -> PrimitiveResult<()> {
self.header.validate()?;
self.body.validate()?;
if self.header.extrinsics_root != self.body.hash() {
return Err(PrimitiveError::InvalidBlock(
"Header extrinsics root doesn't match computed body hash".to_string(),
));
}
if self.size() > crate::constants::MAX_BLOCK_SIZE as usize {
return Err(PrimitiveError::SizeLimit(
"Block exceeds maximum size limit".to_string(),
));
}
Ok(())
}
}
impl Hashable for Block {
fn hash(&self) -> Hash {
self.header.hash()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct BlockBody {
pub transactions: Vec<Transaction>,
pub availability: AvailabilityData,
pub work_reports: Vec<WorkReport>,
pub guarantees: Vec<Guarantee>,
pub preimages: Vec<Preimage>,
}
impl BlockBody {
pub fn new() -> Self {
Self {
transactions: Vec::new(),
availability: AvailabilityData::new(),
work_reports: Vec::new(),
guarantees: Vec::new(),
preimages: Vec::new(),
}
}
pub fn with_transactions(transactions: Vec<Transaction>) -> Self {
Self {
transactions,
availability: AvailabilityData::new(),
work_reports: Vec::new(),
guarantees: Vec::new(),
preimages: Vec::new(),
}
}
pub fn add_transaction(&mut self, transaction: Transaction) {
self.transactions.push(transaction);
}
pub fn weight(&self) -> Weight {
self.transactions
.iter()
.map(|tx| tx.weight())
.sum::<Weight>()
+ Weight(self.work_reports.len() as u64) * 1000
+ Weight(self.guarantees.len() as u64) * 500
+ self
.preimages
.iter()
.map(|p| Weight(p.data.len() as u64))
.sum::<Weight>()
}
pub fn transaction_count(&self) -> usize {
self.transactions.len()
}
pub fn is_empty(&self) -> bool {
self.transactions.is_empty()
&& self.work_reports.is_empty()
&& self.guarantees.is_empty()
&& self.preimages.is_empty()
}
pub fn encoded_size(&self) -> usize {
self.encode().len()
}
}
impl Default for BlockBody {
fn default() -> Self {
Self::new()
}
}
impl Validate for BlockBody {
fn validate(&self) -> PrimitiveResult<()> {
for (i, tx) in self.transactions.iter().enumerate() {
tx.validate().map_err(|e| {
PrimitiveError::InvalidBlock(format!("Invalid transaction at index {}: {}", i, e))
})?;
}
for (i, report) in self.work_reports.iter().enumerate() {
report.validate().map_err(|e| {
PrimitiveError::InvalidBlock(format!("Invalid work report at index {}: {}", i, e))
})?;
}
for (i, guarantee) in self.guarantees.iter().enumerate() {
guarantee.validate().map_err(|e| {
PrimitiveError::InvalidBlock(format!("Invalid guarantee at index {}: {}", i, e))
})?;
}
Ok(())
}
}
impl Hashable for BlockBody {
fn hash(&self) -> Hash {
let hasher = Blake3Hasher::new();
hasher.hash(&self.encode())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct AvailabilityData {
pub chunks: Vec<AvailabilityChunk>,
}
impl AvailabilityData {
pub fn new() -> Self {
Self { chunks: Vec::new() }
}
}
impl Default for AvailabilityData {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct AvailabilityChunk {
pub index: u32,
pub data: Vec<u8>,
pub proof: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct WorkReport {
pub core_index: u16,
pub package_hash: Hash,
pub result: WorkResult,
pub context: WorkContext,
pub authorizer_signature: Signature,
}
impl WorkReport {
pub fn new(
core_index: u16,
package_hash: Hash,
result: WorkResult,
context: WorkContext,
authorizer_signature: Signature,
) -> Self {
Self {
core_index,
package_hash,
result,
context,
authorizer_signature,
}
}
}
impl Validate for WorkReport {
fn validate(&self) -> PrimitiveResult<()> {
if self.core_index >= crate::constants::MAX_CORES as u16 {
return Err(PrimitiveError::InvalidBlock(
"Core index exceeds maximum".to_string(),
));
}
self.result.validate()?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct WorkResult {
pub service_id: ServiceId,
pub payload_hash: Hash,
pub gas_used: Gas,
pub result_data: Vec<u8>,
}
impl Validate for WorkResult {
fn validate(&self) -> PrimitiveResult<()> {
if self.result_data.len() > crate::constants::MAX_ACCUMULATION_SIZE as usize {
return Err(PrimitiveError::SizeLimit(
"Work result data exceeds maximum size".to_string(),
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct WorkContext {
pub anchor: Hash,
pub state_root: Hash,
pub lookup_anchor: Hash,
pub prerequisite: Option<Hash>,
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct Guarantee {
pub work_report: Hash,
pub timeslot: TimeSlot,
pub signatures: Vec<(AccountId, Signature)>,
}
impl Validate for Guarantee {
fn validate(&self) -> PrimitiveResult<()> {
if self.signatures.is_empty() {
return Err(PrimitiveError::InvalidBlock(
"Guarantee must have at least one signature".to_string(),
));
}
let mut signers = std::collections::HashSet::new();
for (signer, _) in &self.signatures {
if !signers.insert(signer) {
return Err(PrimitiveError::InvalidBlock(
"Duplicate signer in guarantee".to_string(),
));
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
pub struct Preimage {
pub hash: Hash,
pub data: Vec<u8>,
}
impl Preimage {
pub fn new(data: Vec<u8>) -> Self {
let hasher = Blake3Hasher::new();
let hash = hasher.hash(&data);
Self { hash, data }
}
pub fn verify(&self) -> bool {
let hasher = Blake3Hasher::new();
hasher.hash(&self.data) == self.hash
}
}