use crate::events::BlockEvent;
use alloc::{
collections::{btree_map::BTreeMap, btree_set::BTreeSet},
vec::Vec,
};
use core::num::NonZeroU64;
use gear_core::ids::prelude::CodeIdExt as _;
use gprimitives::{ActorId, CodeId, H256, MessageId};
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
pub type ProgramStates = BTreeMap<ActorId, StateHashWithQueueSize>;
#[derive(Debug, Clone, Copy, Default, Encode, Decode, TypeInfo, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct BlockHeader {
pub height: u32,
pub timestamp: u64,
pub parent_hash: H256,
}
impl BlockHeader {
pub fn dummy(height: u32) -> Self {
let mut parent_hash = [0; 32];
parent_hash[..4].copy_from_slice(&height.to_le_bytes());
Self {
height,
timestamp: height as u64 * 12,
parent_hash: parent_hash.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockData {
pub hash: H256,
pub header: BlockHeader,
pub events: Vec<BlockEvent>,
}
impl BlockData {
pub fn to_simple(&self) -> SimpleBlockData {
SimpleBlockData {
hash: self.hash,
header: self.header,
}
}
}
#[derive(
Debug, derive_more::Display, Copy, Clone, PartialEq, Eq, Encode, Decode, TypeInfo, Default,
)]
#[display("Block(hash: {hash}, height: {}, parent: {}, ts: {})", header.height, header.parent_hash, header.timestamp)]
pub struct SimpleBlockData {
pub hash: H256,
pub header: BlockHeader,
}
#[derive(Clone, Debug, Copy, Default, PartialEq, Eq, Encode, Decode, derive_more::IsVariant)]
pub enum PromisePolicy {
Enabled,
#[default]
Disabled,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::IsVariant, Default)]
pub enum PromiseEmissionMode {
AlwaysEmit,
#[default]
ConsensusDriven,
}
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, Default, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize))]
pub struct StateHashWithQueueSize {
pub hash: H256,
pub canonical_queue_size: u8,
pub injected_queue_size: u8,
}
impl StateHashWithQueueSize {
pub fn zero() -> Self {
Self {
hash: H256::zero(),
canonical_queue_size: 0,
injected_queue_size: 0,
}
}
}
#[derive(Debug, Clone, Default, Encode, Decode, TypeInfo, PartialEq, Eq)]
pub struct CodeBlobInfo {
pub timestamp: u64,
pub tx_hash: H256,
}
#[derive(Clone, PartialEq, Eq, derive_more::Debug)]
pub struct CodeAndIdUnchecked {
#[debug("{:#x} bytes", code.len())]
pub code: Vec<u8>,
pub code_id: CodeId,
}
#[derive(Clone, PartialEq, Eq, derive_more::Debug)]
pub struct CodeAndId {
#[debug("{:#x} bytes", code.len())]
code: Vec<u8>,
code_id: CodeId,
}
impl CodeAndId {
pub fn new(code: Vec<u8>) -> Self {
let code_id = CodeId::generate(&code);
Self { code, code_id }
}
pub fn code(&self) -> &[u8] {
&self.code
}
pub fn code_id(&self) -> CodeId {
self.code_id
}
pub fn from_unchecked(code_and_id: CodeAndIdUnchecked) -> Self {
let CodeAndIdUnchecked { code, code_id } = code_and_id;
assert_eq!(
code_id,
CodeId::generate(&code),
"CodeId does not match the provided code"
);
Self { code, code_id }
}
pub fn into_unchecked(self) -> CodeAndIdUnchecked {
CodeAndIdUnchecked {
code: self.code,
code_id: self.code_id,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, TypeInfo)]
pub struct ProtocolTimelines {
pub genesis_ts: u64,
pub era: NonZeroU64,
pub election: u64,
pub slot: NonZeroU64,
}
impl ProtocolTimelines {
#[inline(always)]
pub fn era_from_ts(&self, ts: u64) -> Option<u64> {
ts.checked_sub(self.genesis_ts)
.map(|delta| delta / self.era.get())
}
#[inline(always)]
pub fn era_start_ts(&self, era_index: u64) -> Option<u64> {
era_index
.checked_mul(self.era.get())?
.checked_add(self.genesis_ts)
}
#[inline(always)]
pub fn era_election_start_ts(&self, era_index: u64) -> Option<u64> {
self.era_start_ts(era_index)?.checked_add(
self.era
.get()
.checked_sub(self.election)
.expect("Incorrect Timelines - era duration < election duration"),
)
}
#[inline(always)]
pub fn slot_from_ts(&self, ts: u64) -> Option<u64> {
ts.checked_sub(self.genesis_ts)
.map(|delta| delta / self.slot.get())
}
}
pub type Rfm = (ActorId, ActorId);
pub type Sd = (ActorId, MessageId);
pub type Sum = ActorId;
pub type ScheduledTask = gear_core::tasks::ScheduledTask<Rfm, Sd, Sum>;
pub type Schedule = BTreeMap<u32, BTreeSet<ScheduledTask>>;
#[cfg(test)]
mod tests {
use super::*;
fn mock_timelines() -> ProtocolTimelines {
ProtocolTimelines {
genesis_ts: 10,
era: NonZeroU64::new(234).unwrap(),
election: 200,
slot: NonZeroU64::new(10).unwrap(),
}
}
#[test]
fn test_era_from_ts_calculation() {
let timelines = mock_timelines();
assert_eq!(timelines.era_from_ts(10), Some(0));
assert_eq!(timelines.era_from_ts(45), Some(0));
assert_eq!(timelines.era_from_ts(243), Some(0));
assert_eq!(timelines.era_from_ts(244), Some(1));
assert_eq!(timelines.era_from_ts(333), Some(1));
}
#[test]
fn era_from_ts_returns_none_before_genesis() {
let result = ProtocolTimelines {
genesis_ts: 100,
..mock_timelines()
}
.era_from_ts(50);
assert_eq!(result, None);
}
#[test]
fn test_era_start_calculation() {
let timelines = mock_timelines();
assert_eq!(timelines.era_start_ts(0), Some(10));
assert_eq!(timelines.era_start_ts(0), Some(10));
assert_eq!(timelines.era_start_ts(0), Some(10));
assert_eq!(timelines.era_start_ts(1), Some(244));
assert_eq!(timelines.era_start_ts(1), Some(244));
}
}