#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
pub mod call;
pub mod genesis;
pub mod hooks;
#[cfg(feature = "native")]
pub mod query;
use borsh::{BorshDeserialize, BorshSerialize};
#[cfg(feature = "native")]
pub use query::{ChainStateRpcImpl, ChainStateRpcServer};
use serde::{Deserialize, Serialize};
use sov_modules_api::{DaSpec, Error, ModuleInfo, ValidityConditionChecker};
use sov_state::codec::BcsCodec;
use sov_state::WorkingSet;
pub type TransitionHeight = u64;
#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct StateTransitionId<Da: DaSpec> {
da_block_hash: Da::SlotHash,
post_state_root: [u8; 32],
validity_condition: Da::ValidityCondition,
}
impl<Da: DaSpec> StateTransitionId<Da> {
pub fn new(
da_block_hash: Da::SlotHash,
post_state_root: [u8; 32],
validity_condition: Da::ValidityCondition,
) -> Self {
Self {
da_block_hash,
post_state_root,
validity_condition,
}
}
}
impl<Da: DaSpec> StateTransitionId<Da> {
pub fn compare_hashes(&self, da_block_hash: &Da::SlotHash, post_state_root: &[u8; 32]) -> bool {
self.da_block_hash == *da_block_hash && self.post_state_root == *post_state_root
}
pub fn post_state_root(&self) -> [u8; 32] {
self.post_state_root
}
pub fn da_block_hash(&self) -> &Da::SlotHash {
&self.da_block_hash
}
pub fn validity_condition(&self) -> &Da::ValidityCondition {
&self.validity_condition
}
pub fn validity_condition_check<Checker: ValidityConditionChecker<Da::ValidityCondition>>(
&self,
checker: &mut Checker,
) -> Result<(), <Checker as ValidityConditionChecker<Da::ValidityCondition>>::Error> {
checker.check(&self.validity_condition)
}
}
#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct TransitionInProgress<Da: DaSpec> {
da_block_hash: Da::SlotHash,
validity_condition: Da::ValidityCondition,
}
impl<Da: DaSpec> TransitionInProgress<Da> {
pub fn new(da_block_hash: Da::SlotHash, validity_condition: Da::ValidityCondition) -> Self {
Self {
da_block_hash,
validity_condition,
}
}
}
#[derive(Clone, ModuleInfo)]
pub struct ChainState<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> {
#[address]
address: C::Address,
#[state]
slot_height: sov_state::StateValue<TransitionHeight>,
#[state]
historical_transitions: sov_state::StateMap<TransitionHeight, StateTransitionId<Da>, BcsCodec>,
#[state]
in_progress_transition: sov_state::StateValue<TransitionInProgress<Da>, BcsCodec>,
#[state]
genesis_hash: sov_state::StateValue<[u8; 32]>,
#[state]
genesis_height: sov_state::StateValue<TransitionHeight>,
}
pub struct ChainStateConfig {
pub initial_slot_height: TransitionHeight,
}
impl<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> ChainState<C, Da> {
pub fn get_slot_height(&self, working_set: &mut WorkingSet<C::Storage>) -> TransitionHeight {
self.slot_height
.get(working_set)
.expect("Slot height should be set at initialization")
}
pub fn get_genesis_hash(&self, working_set: &mut WorkingSet<C::Storage>) -> Option<[u8; 32]> {
self.genesis_hash.get(working_set)
}
pub fn get_genesis_height(
&self,
working_set: &mut WorkingSet<C::Storage>,
) -> Option<TransitionHeight> {
self.genesis_height.get(working_set)
}
pub fn get_in_progress_transition(
&self,
working_set: &mut WorkingSet<C::Storage>,
) -> Option<TransitionInProgress<Da>> {
self.in_progress_transition.get(working_set)
}
pub fn get_historical_transitions(
&self,
transition_num: TransitionHeight,
working_set: &mut WorkingSet<C::Storage>,
) -> Option<StateTransitionId<Da>> {
self.historical_transitions
.get(&transition_num, working_set)
}
}
impl<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> sov_modules_api::Module
for ChainState<C, Da>
{
type Context = C;
type Config = ChainStateConfig;
type CallMessage = sov_modules_api::NonInstantiable;
fn genesis(
&self,
config: &Self::Config,
working_set: &mut WorkingSet<C::Storage>,
) -> Result<(), Error> {
Ok(self.init_module(config, working_set)?)
}
}