diff --git a/types/src/blob/commitment.rs b/types/src/blob/commitment.rs index c7280f213..1fa9bcc15 100644 --- a/types/src/blob/commitment.rs +++ b/types/src/blob/commitment.rs @@ -30,7 +30,7 @@ use crate::{InfoByte, Share}; /// root as shown below: /// /// ```text -/// row root +/// NMT: row root /// / \ /// o subtree root /// / \ / \ diff --git a/types/src/block.rs b/types/src/block.rs index 2379c0bcb..dc1723878 100644 --- a/types/src/block.rs +++ b/types/src/block.rs @@ -8,6 +8,7 @@ use crate::{bail_validation, Error, Result, ValidateBasic, ValidationError}; pub(crate) const GENESIS_HEIGHT: u64 = 1; +/// The height of the block in Celestia network. pub type Height = tendermint::block::Height; impl ValidateBasic for Header { @@ -95,7 +96,9 @@ impl ValidateBasic for CommitSig { } } +/// An extention trait for the [`Commit`] to perform additional actions. pub trait CommitExt { + /// Get the signed [`Vote`] from the [`Commit`] at the given index. fn vote_sign_bytes(&self, chain_id: &chain::Id, signature_idx: usize) -> Result>; } diff --git a/types/src/byzantine.rs b/types/src/byzantine.rs index 853d7df28..aa53a23bb 100644 --- a/types/src/byzantine.rs +++ b/types/src/byzantine.rs @@ -22,6 +22,20 @@ pub const MULTIHASH_SHA256_NAMESPACE_FLAGGED_SIZE: usize = 2 * NS_SIZE + SHA256_ type Cid = CidGeneric; type Multihash = MultihashGeneric; +/// A proof that the block producer incorrectly encoded [`ExtendedDataSquare`]. +/// +/// Some malicious actor may incorrectly extend the original data with the +/// recovery data, thus making it impossible to reconstruct the original +/// information. +/// +/// Light node cannot detect such behaviour with [`Data Availability Sampling`]. +/// When the full node collects all the [`Share`]s of a row or column of the [`ExtendedDataSquare`] +/// and detects that it was incorrectly encoded, it should create and announce +/// [`BadEncodingFraudProof`] so that light nodes can reject this block. +/// +/// [`Data Availability Sampling`]: https://docs.celestia.org/learn/how-celestia-works/data-availability-layer +/// [`ExtendedDataSquare`]: crate::ExtendedDataSquare +/// [`Share`]: crate::share::Share #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde( try_from = "RawBadEncodingFraudProof", diff --git a/types/src/consts.rs b/types/src/consts.rs index db592aab1..6eed6d8b7 100644 --- a/types/src/consts.rs +++ b/types/src/consts.rs @@ -1,24 +1,36 @@ +//! Constants used within celestia ecosystem. + +/// The size of the SHA256 hash. pub const HASH_SIZE: usize = tendermint::hash::SHA256_HASH_SIZE; // celestia-core/types/genesis +/// Constants related to genesis definition. pub mod genesis { + /// Max length of the chain ID. pub const MAX_CHAIN_ID_LEN: usize = 50; } // celestia-core/version/version +/// Constants related to the protocol versions. pub mod version { - // BLOCK_PROTOCOL versions all block data structures and processing. - // This includes validity of blocks and state updates. + /// Version of all the block data structures and processing. + /// + /// This includes validity of blocks and state updates. pub const BLOCK_PROTOCOL: u64 = 11; } +/// Constants defined in [`celestia-app`] consensus nodes. +/// +/// [`celestia-app`]: https://github.com/celestiaorg/celestia-app pub mod appconsts { pub use global_consts::*; pub use v1::*; // celestia-app/pkg/appconsts/v1/app_consts mod v1 { + /// Maximum width of a single subtree root when generating blob's commitment. pub const SUBTREE_ROOT_THRESHOLD: u64 = 64; + /// Maximum width of the original data square. pub const SQUARE_SIZE_UPPER_BOUND: usize = 128; } @@ -26,63 +38,68 @@ pub mod appconsts { mod global_consts { use crate::nmt::NS_SIZE; - /// the size of the namespace. + /// The size of the namespace. pub const NAMESPACE_SIZE: usize = NS_SIZE; - /// the size of a share in bytes. + /// The size of a share in bytes. pub const SHARE_SIZE: usize = 512; - /// the number of bytes reserved for information. The info - /// byte contains the share version and a sequence start idicator. + /// The number of bytes reserved for the share metadata. + /// + /// The info byte contains the share version and a sequence start idicator. pub const SHARE_INFO_BYTES: usize = 1; - /// the number of bytes reserved for the sequence length - /// that is present in the first share of a sequence. + /// The number of bytes reserved for the sequence length in a share. + /// + /// That is present in the first share of a sequence. pub const SEQUENCE_LEN_BYTES: usize = 4; - /// the first share version format. + /// The first share version format. pub const SHARE_VERSION_ZERO: u8 = 0; - /// the number of bytes reserved for the location of - /// the first unit (transaction, ISR) in a compact share. + /// The number of bytes reserved for the location of the first unit (transaction, ISR) in a compact share. pub const COMPACT_SHARE_RESERVED_BYTES: usize = 4; - /// the number of bytes usable for data in the first compact share of a sequence. + /// The number of bytes usable for data in the first compact share of a sequence. pub const FIRST_COMPACT_SHARE_CONTENT_SIZE: usize = SHARE_SIZE - NAMESPACE_SIZE - SHARE_INFO_BYTES - SEQUENCE_LEN_BYTES - COMPACT_SHARE_RESERVED_BYTES; - /// the number of bytes usable for data in a continuation compact share of a sequence. + /// The number of bytes usable for data in a continuation compact share of a sequence. pub const CONTINUATION_COMPACT_SHARE_CONTENT_SIZE: usize = SHARE_SIZE - NAMESPACE_SIZE - SHARE_INFO_BYTES - COMPACT_SHARE_RESERVED_BYTES; - /// the number of bytes usable for data in the first sparse share of a sequence. + /// The number of bytes usable for data in the first sparse share of a sequence. pub const FIRST_SPARSE_SHARE_CONTENT_SIZE: usize = SHARE_SIZE - NAMESPACE_SIZE - SHARE_INFO_BYTES - SEQUENCE_LEN_BYTES; - /// the number of bytes usable for data in a continuation sparse share of a sequence. + /// The number of bytes usable for data in a continuation sparse share of a sequence. pub const CONTINUATION_SPARSE_SHARE_CONTENT_SIZE: usize = SHARE_SIZE - NAMESPACE_SIZE - SHARE_INFO_BYTES; - /// the smallest original square width. + /// The smallest original square width. pub const MIN_SQUARE_SIZE: usize = 1; - /// the minimum number of shares allowed in the original data square. + /// The minimum number of shares allowed in the original data square. pub const MIN_SHARE_COUNT: usize = MIN_SQUARE_SIZE * MIN_SQUARE_SIZE; - /// the maximum value a share version can be. + /// The maximum value a share version can be. pub const MAX_SHARE_VERSION: u8 = 127; } } // celestia-app/pkg/da/data_availability_header +/// Constants related to the [`DataAvailabilityHeader`]. +/// +/// [`DataAvailabilityHeader`]: crate::DataAvailabilityHeader pub mod data_availability_header { pub const MAX_EXTENDED_SQUARE_WIDTH: usize = super::appconsts::SQUARE_SIZE_UPPER_BOUND * 2; pub const MIN_EXTENDED_SQUARE_WIDTH: usize = super::appconsts::MIN_SQUARE_SIZE * 2; } +/// Constants related to the underlying cosmos sdk. pub mod cosmos { use const_format::concatcp; diff --git a/types/src/data_availability_header.rs b/types/src/data_availability_header.rs index 4c9dc72f9..543314c84 100644 --- a/types/src/data_availability_header.rs +++ b/types/src/data_availability_header.rs @@ -11,25 +11,84 @@ use crate::hash::Hash; use crate::nmt::{NamespacedHash, NamespacedHashExt}; use crate::{bail_validation, Error, Result, ValidateBasic, ValidationError}; +/// Header with commitments of the data availability. +/// +/// It consists of the root hashes of the merkle trees created from each +/// row and column of the [`ExtendedDataSquare`]. Those are used to prove +/// the inclusion of the data in a block. +/// +/// The hash of this header is a hash of all rows and columns and thus a +/// data commitment of the block. +/// +/// # Example +/// +/// ```no_run +/// # use celestia_types::{ExtendedHeader, Height, Share}; +/// # use celestia_types::nmt::{Namespace, NamespaceProof}; +/// # fn extended_header() -> ExtendedHeader { +/// # unimplemented!(); +/// # } +/// # fn shares_with_proof(_: Height, _: &Namespace) -> (Vec, NamespaceProof) { +/// # unimplemented!(); +/// # } +/// // fetch the block header and data for your namespace +/// let namespace = Namespace::new_v0(&[1, 2, 3, 4]).unwrap(); +/// let eh = extended_header(); +/// let (shares, proof) = shares_with_proof(eh.height(), &namespace); +/// +/// // get the data commitment for a given row +/// let dah = eh.dah; +/// let root = dah.row_root(0).unwrap(); +/// +/// // verify a proof of the inclusion of the shares +/// assert!(proof.verify_complete_namespace(&root, &shares, *namespace).is_ok()); +/// ``` +/// +/// [`ExtendedDataSquare`]: crate::rsmt2d::ExtendedDataSquare #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde( try_from = "RawDataAvailabilityHeader", into = "RawDataAvailabilityHeader" )] pub struct DataAvailabilityHeader { + /// Merkle roots of the [`ExtendedDataSquare`] rows. + /// + /// [`ExtendedDataSquare`]: crate::rsmt2d::ExtendedDataSquare pub row_roots: Vec, + /// Merkle roots of the [`ExtendedDataSquare`] columns. + /// + /// [`ExtendedDataSquare`]: crate::rsmt2d::ExtendedDataSquare pub column_roots: Vec, } impl DataAvailabilityHeader { + /// Get the root of a row with the given index. pub fn row_root(&self, row: usize) -> Option { self.row_roots.get(row).cloned() } + /// Get the root of a column with the given index. pub fn column_root(&self, column: usize) -> Option { self.column_roots.get(column).cloned() } + /// Compute the combined hash of all rows and columns. + /// + /// This is the data commitment for the block. + /// + /// # Example + /// + /// ``` + /// # use celestia_types::ExtendedHeader; + /// # fn get_extended_header() -> ExtendedHeader { + /// # let s = include_str!("../test_data/chain1/extended_header_block_1.json"); + /// # serde_json::from_str(s).unwrap() + /// # } + /// let eh = get_extended_header(); + /// let dah = eh.dah; + /// + /// assert_eq!(dah.hash(), eh.header.data_hash); + /// ``` pub fn hash(&self) -> Hash { let all_roots: Vec<_> = self .row_roots diff --git a/types/src/error.rs b/types/src/error.rs index cd8f3fa39..cf5941ee4 100644 --- a/types/src/error.rs +++ b/types/src/error.rs @@ -1,7 +1,9 @@ use crate::consts::appconsts; +/// Alias for a `Result` with the error type [`celestia_types::Error`]. pub type Result = std::result::Result; +/// Representation of all the errors that can occur when interacting with [`celestia_types`]. #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Unsupported namesapce version: {0}")] @@ -104,18 +106,28 @@ pub enum Error { UnsupportedFraudProofType(String), } +/// Representation of the errors that can occur when validating data. +/// +/// See [`ValidateBasic`] +/// +/// [`ValidateBasic`]: crate::ValidateBasic #[derive(Debug, thiserror::Error)] pub enum ValidationError { - #[error("Not enought voiting power (got {0}, needed {1})")] + #[error("Not enought voting power (got {0}, needed {1})")] NotEnoughVotingPower(u64, u64), #[error("{0}")] Other(String), } +/// Representation of the errors that can occur when verifying data. +/// +/// See [`ExtendedHeader::verify`]. +/// +/// [`ExtendedHeader::verify`]: crate::ExtendedHeader::verify #[derive(Debug, thiserror::Error)] pub enum VerificationError { - #[error("Not enought voiting power (got {0}, needed {1})")] + #[error("Not enought voting power (got {0}, needed {1})")] NotEnoughVotingPower(u64, u64), #[error("{0}")] diff --git a/types/src/extended_header.rs b/types/src/extended_header.rs index d3e5a2d5b..4c738b389 100644 --- a/types/src/extended_header.rs +++ b/types/src/extended_header.rs @@ -16,18 +16,51 @@ use crate::{ bail_validation, bail_verification, DataAvailabilityHeader, Error, Result, ValidateBasic, }; +/// Information about a tendermint validator. pub type Validator = validator::Info; +/// A collection of the tendermint validators. pub type ValidatorSet = validator::Set; #[cfg(any(not(target_arch = "wasm32"), feature = "wasm-bindgen"))] const VERIFY_CLOCK_DRIFT: Duration = Duration::from_secs(10); +/// Block header together with the relevant Data Availability metadata. +/// +/// [`ExtendedHeader`]s are used to announce and describe the blocks +/// in the Celestia network. +/// +/// Before being used, each header should be validated and verified with the header you trust. +/// +/// # Example +/// +/// ``` +/// # use celestia_types::ExtendedHeader; +/// # fn trusted_genesis_header() -> ExtendedHeader { +/// # let s = include_str!("../test_data/chain1/extended_header_block_1.json"); +/// # serde_json::from_str(s).unwrap() +/// # } +/// # fn some_untrusted_header() -> ExtendedHeader { +/// # let s = include_str!("../test_data/chain1/extended_header_block_27.json"); +/// # serde_json::from_str(s).unwrap() +/// # } +/// let genesis_header = trusted_genesis_header(); +/// +/// // fetch new header +/// let fetched_header = some_untrusted_header(); +/// +/// fetched_header.validate().expect("Invalid block header"); +/// genesis_header.verify(&fetched_header).expect("Malicious header received"); +/// ``` #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(try_from = "RawExtendedHeader", into = "RawExtendedHeader")] pub struct ExtendedHeader { + /// Tendermint block header. pub header: Header, + /// Commit metadata and signatures from validators committing the block. pub commit: Commit, + /// Information about the set of validators commiting the block. pub validator_set: ValidatorSet, + /// Header of the block data availability. pub dah: DataAvailabilityHeader, } @@ -38,29 +71,34 @@ impl Display for ExtendedHeader { } impl ExtendedHeader { - /// Decode and then validate header. + /// Decode protobuf encoded header and then validate it. pub fn decode_and_validate(bytes: &[u8]) -> Result { let header = ExtendedHeader::decode(bytes)?; header.validate()?; Ok(header) } + /// Get the block chain id. pub fn chain_id(&self) -> &Id { &self.header.chain_id } + /// Get the block height. pub fn height(&self) -> Height { self.header.height } + /// Get the block time. pub fn time(&self) -> Time { self.header.time } + /// Get the block hash. pub fn hash(&self) -> Hash { self.commit.block_id.hash } + /// Get the hash of the previous header. pub fn last_header_hash(&self) -> Hash { self.header .last_block_id @@ -69,6 +107,28 @@ impl ExtendedHeader { } /// Validate header. + /// + /// Performs a consistency check of the data included in the header. + /// + /// # Errors + /// + /// If validation fails, this function will return an error with a reason of failure. + /// + /// ``` + /// # use celestia_types::ExtendedHeader; + /// # fn get_header(_: usize) -> ExtendedHeader { + /// # let s = include_str!("../test_data/chain1/extended_header_block_27.json"); + /// # serde_json::from_str(s).unwrap() + /// # } + /// // fetch new header + /// let mut fetched_header = get_header(15); + /// + /// assert!(fetched_header.validate().is_ok()); + /// + /// fetched_header.dah.row_roots = vec![]; + /// + /// assert!(fetched_header.validate().is_err()); + /// ``` pub fn validate(&self) -> Result<()> { self.header.validate_basic()?; self.commit.validate_basic()?; @@ -121,6 +181,16 @@ impl ExtendedHeader { } /// Verify an untrusted header. + /// + /// Ensures that the untrusted header can be trusted by comparing it with `self`. + /// + /// # Errors + /// + /// If validation fails, this function will return an error with a reason of failure. + /// + /// Please note that if verifying unadjacent headers, the verification will always + /// fail if the validator set commiting those blocks was changed. If that is the case, + /// consider verifying the untrusted header with a more recent or even previous header. pub fn verify(&self, untrusted: &ExtendedHeader) -> Result<()> { if untrusted.height() <= self.height() { bail_verification!( @@ -193,13 +263,35 @@ impl ExtendedHeader { Ok(()) } - /// Verify a chain of untrusted headers. + /// Verify a chain of adjacent untrusted headers. /// /// # Note /// /// This method does not do validation for optimization purposes. /// Validation should be done from before and ideally with /// [`ExtendedHeader::decode_and_validate`]. + /// + /// # Errors + /// + /// If validation fails, this function will return an error with a reason of failure. + /// This function will also return an error if untrusted headers are not adjacent + /// to each other. + /// + /// # Example + /// + /// ``` + /// # use std::ops::Range; + /// # use celestia_types::ExtendedHeader; + /// # let s = include_str!("../test_data/chain3/extended_header_block_1_to_256.json"); + /// # let headers: Vec = serde_json::from_str(s).unwrap(); + /// # let trusted_genesis = || headers[0].clone(); + /// # // substract one as heights start from 1 + /// # let get_headers_range = |r: Range| (&headers[r.start - 1..r.end - 1]).to_vec(); + /// let genesis_header = trusted_genesis(); + /// let next_headers = get_headers_range(5..50); + /// + /// assert!(genesis_header.verify_range(&next_headers).is_ok()); + /// ``` pub fn verify_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> { let mut trusted = self; @@ -222,13 +314,42 @@ impl ExtendedHeader { Ok(()) } - /// Verify a chain of untrusted headers make sure that are adjacent to `self`. + /// Verify a chain of adjacent untrusted headers and make sure + /// they are adjacent to `self`. /// /// # Note /// /// This method does not do validation for optimization purposes. /// Validation should be done from before and ideally with /// [`ExtendedHeader::decode_and_validate`]. + /// + /// # Errors + /// + /// If validation fails, this function will return an error with a reason of failure. + /// This function will also return an error if untrusted headers are not adjacent + /// to each other and to `self`. + /// + /// # Example + /// + /// ``` + /// # use std::ops::Range; + /// # use celestia_types::ExtendedHeader; + /// # let s = include_str!("../test_data/chain3/extended_header_block_1_to_256.json"); + /// # let headers: Vec = serde_json::from_str(s).unwrap(); + /// # let trusted_genesis = || headers[0].clone(); + /// # // substract one as heights start from 1 + /// # let get_headers_range = |r: Range| (&headers[r.start - 1..r.end - 1]).to_vec(); + /// let genesis_header = trusted_genesis(); + /// let next_headers = get_headers_range(5..50); + /// + /// // fails, not adjacent to genesis + /// assert!(genesis_header.verify_adjacent_range(&next_headers).is_err()); + /// + /// let next_headers = get_headers_range(2..50); + /// + /// // succeeds + /// genesis_header.verify_adjacent_range(&next_headers).unwrap(); + /// ``` pub fn verify_adjacent_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> { if untrusted.is_empty() { return Ok(()); diff --git a/types/src/fraud_proof.rs b/types/src/fraud_proof.rs index a7503dace..2051822c6 100644 --- a/types/src/fraud_proof.rs +++ b/types/src/fraud_proof.rs @@ -1,3 +1,7 @@ +//! Fraud proof related types and traits. +//! +//! A fraud proof is a proof of the detected malicious actions done to the network. + use std::convert::Infallible; use serde::{Deserialize, Serialize, Serializer}; @@ -5,24 +9,32 @@ use tendermint::block::Height; use tendermint::Hash; use tendermint_proto::Protobuf; -use crate::{byzantine::BadEncodingFraudProof, Error, ExtendedHeader, Result}; +pub use crate::byzantine::BadEncodingFraudProof; +use crate::{Error, ExtendedHeader, Result}; +/// A proof of the malicious actions done to the network. pub trait FraudProof { + /// Name of the proof type. const TYPE: &'static str; - // HeaderHash returns the block hash. + /// HeaderHash returns the block hash. fn header_hash(&self) -> Hash; - // Height returns the block height corresponding to the Proof. + /// Height returns the block height corresponding to the Proof. fn height(&self) -> Height; - // Checks the validity of the fraud proof. - // - // # Errors - // Returns an error if some conditions don't pass and thus fraud proof is not valid. + /// Checks the validity of the fraud proof. + /// + /// # Errors + /// + /// Returns an error if some conditions don't pass and thus fraud proof is not valid. fn validate(&self, header: &ExtendedHeader) -> Result<()>; } +/// Raw representation of the generic fraud proof. +/// +/// Consists of the name of the proof type and the protobuf serialized payload +/// holding the proof itself. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct RawFraudProof { proof_type: String, @@ -30,6 +42,7 @@ pub struct RawFraudProof { data: Vec, } +/// Aggregation of all the supported fraud proofs. #[derive(Clone, Debug, PartialEq, Deserialize)] #[serde(try_from = "RawFraudProof", into = "RawFraudProof")] #[non_exhaustive] diff --git a/types/src/hash.rs b/types/src/hash.rs index a4de19959..4c021ab44 100644 --- a/types/src/hash.rs +++ b/types/src/hash.rs @@ -1,6 +1,15 @@ +//! Celestia hash related types and traits. + +/// The hash type used commonly in the Celestia. pub type Hash = tendermint::hash::Hash; +/// A trait extending the [`Hash`] functionalities. +/// +/// [`Hash`]: crate::hash::Hash pub trait HashExt { + /// Get the default `SHA256` hash. + /// + /// This is equivalent to `sha256sum /dev/null`. fn default_sha256() -> Hash; } diff --git a/types/src/nmt.rs b/types/src/nmt.rs index d8c9cdb01..4b6bd581e 100644 --- a/types/src/nmt.rs +++ b/types/src/nmt.rs @@ -1,3 +1,22 @@ +//! Namespaces and Namespaced Merkle Tree. +//! +//! [`Namespace`] is a key to your data in celestia. Whenever you publish blobs +//! to the Celestia network, you have to assign them to some namespace. You can +//! later use that namespace to acquire the data back from the network. +//! +//! All the data in each block is also ordered by it's [`Namespace`]. Things like +//! transactions or trailing bytes have it's own namespaces too, tho those are reserved +//! for block producers and cannot be utilized when submitting data. +//! +//! The fact that data in Celestia blocks is ordered allows for various optimizations +//! regarding proving it's inclusion or absence. There is a namespace-aware implementation +//! of merkle trees, called 'Namespace Merkle Tree', shortened as [`Nmt`]. +//! +//! The generic implementation of this merkle tree lives in [`nmt-rs`]. This module is +//! a wrapper around that with Celestia specific defaults and functionalities. +//! +//! [`nmt-rs`]: https://github.com/sovereign-labs/nmt-rs + use base64::prelude::*; use nmt_rs::simple_merkle::db::MemDb; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -10,20 +29,56 @@ pub use self::namespace_proof::NamespaceProof; pub use self::namespaced_hash::{NamespacedHash, NamespacedHashExt, RawNamespacedHash}; use crate::{Error, Result}; +/// Namespace version size in bytes. pub const NS_VER_SIZE: usize = 1; +/// Namespace id size in bytes. pub const NS_ID_SIZE: usize = 28; +/// Namespace size in bytes. pub const NS_SIZE: usize = NS_VER_SIZE + NS_ID_SIZE; +/// Size of the user-defined namespace suffix in bytes. For namespaces in version `0`. pub const NS_ID_V0_SIZE: usize = 10; +/// Hasher for generating [`Namespace`] aware hashes. pub type NamespacedSha2Hasher = nmt_rs::NamespacedSha2Hasher; +/// [`Namespace`] aware merkle tree. pub type Nmt = nmt_rs::NamespaceMerkleTree, NamespacedSha2Hasher, NS_SIZE>; +/// Namespace of the data published to the celestia network. +/// +/// The [`Namespace`] is a single byte defining the version +/// followed by 28 bytes specifying concrete ID of the namespace. +/// +/// Currently there are two versions of namespaces: +/// - version `0` - the one allowing for the custom namespace ids. It requires an id to start +/// with 18 0x00 bytes followed by a user specified suffix (except reserved ones, see below). +/// - version `0xff` - for secondary reserved namespaces +/// +/// Some namespaces are reserved for the block creation purposes and cannot be used +/// when submitting the blobs to celestia. Those fall into one of the two categories: +/// - primary reserved namespaces - those use version `0` and have id lower or equal to `0xff` +/// so they are always placed in blocks before user-submitted data. +/// - secondary reserved namespaces - those use version `0xff` so they are always placed after +/// user-submitted data. #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct Namespace(nmt_rs::NamespaceId); impl Namespace { pub const MAX: Namespace = Namespace(nmt_rs::NamespaceId::MAX_ID); + /// Create a new [`Namespace`] from the raw bytes. + /// + /// # Errors + /// + /// This function will return an error if the slice length is different than [`NS_SIZE`] + /// or if the namespace is invalid. + /// + /// # Example + /// + /// ``` + /// use celestia_types::nmt::{Namespace, NS_SIZE}; + /// let raw = [0; NS_SIZE]; + /// let namespace = Namespace::from_raw(&raw).unwrap(); + /// ``` pub fn from_raw(bytes: &[u8]) -> Result { if bytes.len() != NS_SIZE { return Err(Error::InvalidNamespaceSize); @@ -32,6 +87,20 @@ impl Namespace { Namespace::new(bytes[0], &bytes[1..]) } + /// Create a new [`Namespace`] from the version and id. + /// + /// # Errors + /// + /// This function will return an error if the version doesn't match + /// or if the namespace is invalid. + /// + /// # Example + /// + /// ``` + /// use celestia_types::nmt::{Namespace, NS_SIZE}; + /// let raw = [0; NS_SIZE]; + /// let namespace = Namespace::from_raw(&raw).unwrap(); + /// ``` pub fn new(version: u8, id: &[u8]) -> Result { match version { 0 => Self::new_v0(id),