#![deny(missing_docs)]
mod call;
mod genesis;
mod hooks;
#[cfg(feature = "native")]
mod query;
pub use call::*;
pub use genesis::*;
#[cfg(feature = "native")]
pub use query::*;
use sov_modules_api::{CallResponse, Error, ModuleInfo, StateMap, StateValue, WorkingSet};
use sov_state::codec::BcsCodec;
#[cfg_attr(feature = "native", derive(sov_modules_api::ModuleCallJsonSchema))]
#[derive(Clone, ModuleInfo)]
pub struct SequencerRegistry<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> {
#[address]
pub(crate) address: C::Address,
#[module]
pub(crate) bank: sov_bank::Bank<C>,
#[state]
pub(crate) allowed_sequencers: StateMap<Da::Address, C::Address, BcsCodec>,
#[state]
pub(crate) preferred_sequencer: StateValue<Da::Address, BcsCodec>,
#[state]
pub(crate) coins_to_lock: StateValue<sov_bank::Coins<C>>,
}
pub enum SequencerOutcome<Da: sov_modules_api::DaSpec> {
Completed,
Slashed {
sequencer: Da::Address,
},
}
impl<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> sov_modules_api::Module
for SequencerRegistry<C, Da>
{
type Context = C;
type Config = SequencerConfig<C, Da>;
type CallMessage = CallMessage;
fn genesis(&self, config: &Self::Config, working_set: &mut WorkingSet<C>) -> Result<(), Error> {
Ok(self.init_module(config, working_set)?)
}
fn call(
&self,
message: Self::CallMessage,
context: &Self::Context,
working_set: &mut WorkingSet<C>,
) -> Result<CallResponse, Error> {
Ok(match message {
CallMessage::Register { da_address } => {
let da_address = Da::Address::try_from(&da_address)?;
self.register(&da_address, context, working_set)?
}
CallMessage::Exit { da_address } => {
let da_address = Da::Address::try_from(&da_address)?;
self.exit(&da_address, context, working_set)?
}
})
}
}
impl<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> SequencerRegistry<C, Da> {
pub fn get_coins_to_lock(&self, working_set: &mut WorkingSet<C>) -> Option<sov_bank::Coins<C>> {
self.coins_to_lock.get(working_set)
}
pub(crate) fn register_sequencer(
&self,
da_address: &Da::Address,
rollup_address: &C::Address,
working_set: &mut WorkingSet<C>,
) -> anyhow::Result<()> {
if self
.allowed_sequencers
.get(da_address, working_set)
.is_some()
{
anyhow::bail!("sequencer {} already registered", rollup_address)
}
let locker = &self.address;
let coins = self.coins_to_lock.get_or_err(working_set)?;
self.bank
.transfer_from(rollup_address, locker, coins, working_set)?;
self.allowed_sequencers
.set(da_address, rollup_address, working_set);
Ok(())
}
pub fn get_preferred_sequencer(&self, working_set: &mut WorkingSet<C>) -> Option<Da::Address> {
self.preferred_sequencer.get(working_set)
}
pub fn get_preferred_sequencer_rollup_address(
&self,
working_set: &mut WorkingSet<C>,
) -> Option<C::Address> {
self.preferred_sequencer.get(working_set).map(|da_addr| {
self.allowed_sequencers
.get(&da_addr, working_set)
.expect("Preferred Sequencer must have known address on rollup")
})
}
pub fn is_sender_allowed(&self, sender: &Da::Address, working_set: &mut WorkingSet<C>) -> bool {
self.allowed_sequencers.get(sender, working_set).is_some()
}
}