1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]

mod capabilities;
#[cfg(feature = "native")]
mod query;

#[cfg(feature = "native")]
pub use query::{BlobStorageRpcImpl, BlobStorageRpcServer, Response};
use sov_chain_state::TransitionHeight;
use sov_modules_api::{Module, ModuleInfo};
use sov_state::{StateMap, WorkingSet};

/// For how many slots deferred blobs are stored before being executed
const DEFERRED_SLOTS_COUNT: u64 = 1;

/// Blob storage contains only address and vector of blobs
#[cfg_attr(feature = "native", derive(sov_modules_api::ModuleCallJsonSchema))]
#[derive(Clone, ModuleInfo)]
pub struct BlobStorage<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> {
    /// The address of blob storage module
    /// Note: this is address is generated by the module framework and the corresponding private key is unknown.
    #[address]
    pub(crate) address: C::Address,

    /// Actual storage of blobs
    /// DA block number => vector of blobs
    /// Caller controls the order of blobs in the vector
    #[state]
    pub(crate) blobs: StateMap<u64, Vec<Vec<u8>>>,

    #[module]
    pub(crate) sequencer_registry: sov_sequencer_registry::SequencerRegistry<C>,

    #[module]
    chain_state: sov_chain_state::ChainState<C, Da>,
}

/// Non standard methods for blob storage
impl<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> BlobStorage<C, Da> {
    /// Store blobs for given block number, overwrite if already exists
    pub fn store_blobs(
        &self,
        slot_height: TransitionHeight,
        blobs: &[&Da::BlobTransaction],
        working_set: &mut WorkingSet<C::Storage>,
    ) -> anyhow::Result<()> {
        let mut raw_blobs: Vec<Vec<u8>> = Vec::with_capacity(blobs.len());
        for blob in blobs {
            raw_blobs.push(bincode::serialize(blob)?);
        }
        self.blobs.set(&slot_height, &raw_blobs, working_set);
        Ok(())
    }

    /// Take all blobs for given block number, return empty vector if not exists
    /// Returned blobs are removed from the storage
    pub fn take_blobs_for_slot_height(
        &self,
        slot_height: TransitionHeight,
        working_set: &mut WorkingSet<C::Storage>,
    ) -> Vec<Da::BlobTransaction> {
        self.blobs
            .remove(&slot_height, working_set)
            .unwrap_or_default()
            .iter()
            .map(|b| bincode::deserialize(b).expect("malformed blob was stored previously"))
            .collect()
    }

    // TODO: Migrate to generic: https://github.com/Sovereign-Labs/sovereign-sdk/issues/622
    pub(crate) fn get_preferred_sequencer(
        &self,
        working_set: &mut WorkingSet<C::Storage>,
    ) -> Option<Vec<u8>> {
        self.sequencer_registry.get_preferred_sequencer(working_set)
    }

    pub(crate) fn get_current_slot_height(
        &self,
        working_set: &mut WorkingSet<C::Storage>,
    ) -> TransitionHeight {
        self.chain_state.get_slot_height(working_set)
    }

    pub(crate) fn get_deferred_slots_count(
        &self,
        _working_set: &mut WorkingSet<C::Storage>,
    ) -> u64 {
        DEFERRED_SLOTS_COUNT
    }
}

/// Empty module implementation
impl<C: sov_modules_api::Context, Da: sov_modules_api::DaSpec> Module for BlobStorage<C, Da> {
    type Context = C;
    type Config = ();
    type CallMessage = sov_modules_api::NonInstantiable;
}