#[cfg(feature = "uniffi")]
use crate::error::UniffiError;
use base64::prelude::*;
use blockstore::block::CidError;
use celestia_proto::serializers::cow_str::CowStr;
use cid::CidGeneric;
use multihash::Multihash;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use tendermint::hash::SHA256_HASH_SIZE;
#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
use wasm_bindgen::prelude::*;
mod namespace_proof;
mod namespaced_hash;
mod namespaced_merkle_tree;
pub use self::namespace_proof::{EMPTY_LEAVES, NamespaceProof};
pub use self::namespaced_hash::{
HASH_SIZE, NAMESPACED_HASH_SIZE, NamespacedHashExt, RawNamespacedHash,
};
pub use self::namespaced_merkle_tree::{MerkleHash, NamespacedSha2Hasher, Nmt, NmtExt};
use crate::{Error, Result};
pub use nmt_rs::NamespaceMerkleHasher;
pub const NS_VER_SIZE: usize = 1;
pub const NS_ID_SIZE: usize = 28;
pub const NS_SIZE: usize = NS_VER_SIZE + NS_ID_SIZE;
pub const NS_ID_V0_SIZE: usize = 10;
pub const NMT_MULTIHASH_CODE: u64 = 0x7700;
pub const NMT_CODEC: u64 = 0x7701;
pub const NMT_ID_SIZE: usize = 2 * NS_SIZE + SHA256_HASH_SIZE;
pub type NamespacedHash = nmt_rs::NamespacedHash<NS_SIZE>;
pub type Proof = nmt_rs::simple_merkle::proof::Proof<NamespacedSha2Hasher>;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(
all(feature = "wasm-bindgen", target_arch = "wasm32"),
wasm_bindgen(inspectable)
)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
pub struct Namespace(nmt_rs::NamespaceId<NS_SIZE>);
impl Namespace {
pub const TRANSACTION: Namespace = Namespace::const_v0([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const PAY_FOR_BLOB: Namespace = Namespace::const_v0([0, 0, 0, 0, 0, 0, 0, 0, 0, 4]);
pub const PRIMARY_RESERVED_PADDING: Namespace = Namespace::MAX_PRIMARY_RESERVED;
pub const MAX_PRIMARY_RESERVED: Namespace =
Namespace::const_v0([0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff]);
pub const MIN_SECONDARY_RESERVED: Namespace = Namespace::const_v255(0);
pub const TAIL_PADDING: Namespace = Namespace::const_v255(0xfe);
pub const PARITY_SHARE: Namespace = Namespace::const_v255(0xff);
pub fn from_raw(bytes: &[u8]) -> Result<Self> {
if bytes.len() != NS_SIZE {
return Err(Error::InvalidNamespaceSize);
}
Namespace::new(bytes[0], &bytes[1..])
}
pub fn new(version: u8, id: &[u8]) -> Result<Self> {
match version {
0 => Self::new_v0(id),
255 => Self::new_v255(id),
n => Err(Error::UnsupportedNamespaceVersion(n)),
}
}
pub fn new_v0(id: &[u8]) -> Result<Self> {
let id_pos = match id.len() {
NS_ID_SIZE => NS_ID_SIZE - NS_ID_V0_SIZE,
n if n <= NS_ID_V0_SIZE => 0,
_ => return Err(Error::InvalidNamespaceSize),
};
let prefix = &id[..id_pos];
let id = &id[id_pos..];
if prefix.iter().any(|&x| x != 0) {
return Err(Error::InvalidNamespaceV0);
}
let mut bytes = [0u8; NS_SIZE];
bytes[NS_SIZE - id.len()..].copy_from_slice(id);
Ok(Namespace(nmt_rs::NamespaceId(bytes)))
}
pub(crate) const fn new_unchecked(bytes: [u8; NS_SIZE]) -> Self {
Namespace(nmt_rs::NamespaceId(bytes))
}
pub const fn const_v0(id: [u8; NS_ID_V0_SIZE]) -> Self {
let mut bytes = [0u8; NS_SIZE];
let start = NS_SIZE - NS_ID_V0_SIZE;
bytes[start] = id[0];
bytes[start + 1] = id[1];
bytes[start + 2] = id[2];
bytes[start + 3] = id[3];
bytes[start + 4] = id[4];
bytes[start + 5] = id[5];
bytes[start + 6] = id[6];
bytes[start + 7] = id[7];
bytes[start + 8] = id[8];
bytes[start + 9] = id[9];
Namespace(nmt_rs::NamespaceId(bytes))
}
pub const fn const_v255(id: u8) -> Self {
let mut bytes = [255u8; NS_SIZE];
bytes[NS_ID_SIZE] = id;
Namespace(nmt_rs::NamespaceId(bytes))
}
pub fn new_v255(id: &[u8]) -> Result<Self> {
if id.len() != NS_ID_SIZE {
return Err(Error::InvalidNamespaceSize);
}
let (id, prefix) = id.split_last().unwrap();
if prefix.iter().all(|&x| x == 0xff) {
Ok(Namespace::const_v255(*id))
} else {
Err(Error::InvalidNamespaceV255)
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.0.0
}
pub fn version(&self) -> u8 {
self.as_bytes()[0]
}
pub fn id(&self) -> &[u8] {
&self.as_bytes()[1..]
}
pub fn id_v0(&self) -> Option<&[u8]> {
if self.version() == 0 {
let start = NS_SIZE - NS_ID_V0_SIZE;
Some(&self.as_bytes()[start..])
} else {
None
}
}
pub fn is_reserved(&self) -> bool {
*self <= Namespace::MAX_PRIMARY_RESERVED || *self >= Namespace::MIN_SECONDARY_RESERVED
}
}
#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
#[wasm_bindgen]
impl Namespace {
#[wasm_bindgen(js_name = NS_SIZE, getter)]
pub fn js_ns_size() -> usize {
NS_SIZE
}
#[wasm_bindgen(js_name = TRANSACTION, getter)]
pub fn js_transaction() -> Namespace {
Namespace::TRANSACTION
}
#[wasm_bindgen(js_name = PAY_FOR_BLOB, getter)]
pub fn js_pay_for_blob() -> Namespace {
Namespace::PAY_FOR_BLOB
}
#[wasm_bindgen(js_name = PRIMARY_RESERVED_PADDING, getter)]
pub fn js_primary_reserved_padding() -> Namespace {
Namespace::PRIMARY_RESERVED_PADDING
}
#[wasm_bindgen(js_name = MAX_PRIMARY_RESERVED, getter)]
pub fn js_max_primary_reserved() -> Namespace {
Namespace::MAX_PRIMARY_RESERVED
}
#[wasm_bindgen(js_name = MIN_SECONDARY_RESERVED, getter)]
pub fn js_min_secondary_reserved() -> Namespace {
Namespace::MIN_SECONDARY_RESERVED
}
#[wasm_bindgen(js_name = TAIL_PADDING, getter)]
pub fn js_tail_padding() -> Namespace {
Namespace::TAIL_PADDING
}
#[wasm_bindgen(js_name = PARITY_SHARE, getter)]
pub fn js_parity_share() -> Namespace {
Namespace::PARITY_SHARE
}
#[wasm_bindgen(js_name = newV0)]
pub fn js_new_v0(id: Vec<u8>) -> Result<Self> {
Self::new_v0(&id.to_vec())
}
#[wasm_bindgen(js_name = fromRaw)]
pub fn js_from_raw(raw: Vec<u8>) -> Result<Self> {
Self::from_raw(&raw.to_vec())
}
#[wasm_bindgen(js_name = asBytes)]
pub fn js_as_bytes(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
#[wasm_bindgen(js_name = version, getter)]
pub fn js_version(&self) -> u8 {
self.version()
}
#[wasm_bindgen(js_name = id, getter)]
pub fn js_id(&self) -> Vec<u8> {
self.id().to_vec()
}
}
#[cfg(feature = "uniffi")]
#[uniffi::export]
impl Namespace {
#[uniffi::constructor(name = "new")]
fn uniffi_new(version: u8, id: Vec<u8>) -> Result<Self, UniffiError> {
Ok(Namespace::new(version, &id)?)
}
#[uniffi::constructor(name = "new_v0")]
fn uniffi_new_v0(id: Vec<u8>) -> Result<Self, UniffiError> {
Ok(Namespace::new_v0(&id)?)
}
#[uniffi::method]
pub fn bytes(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
#[uniffi::method(name = "version")]
pub fn uniffi_version(&self) -> u8 {
self.version()
}
#[uniffi::method(name = "id")]
pub fn uniffi_id(&self) -> Vec<u8> {
self.id().to_vec()
}
#[uniffi::method(name = "id_v0")]
pub fn uniffi_id_v0(&self) -> Option<Vec<u8>> {
self.id_v0().map(ToOwned::to_owned)
}
#[uniffi::method(name = "is_reserved")]
pub fn uniffi_is_reserved(&self) -> bool {
self.is_reserved()
}
}
impl From<Namespace> for nmt_rs::NamespaceId<NS_SIZE> {
fn from(value: Namespace) -> Self {
value.0
}
}
impl From<nmt_rs::NamespaceId<NS_SIZE>> for Namespace {
fn from(value: nmt_rs::NamespaceId<NS_SIZE>) -> Self {
Namespace(value)
}
}
impl std::ops::Deref for Namespace {
type Target = nmt_rs::NamespaceId<NS_SIZE>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Serialize for Namespace {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = BASE64_STANDARD.encode(self.0);
serializer.serialize_str(&s)
}
}
impl<'de> Deserialize<'de> for Namespace {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut buf = [0u8; NS_SIZE * 2];
let s = CowStr::deserialize(deserializer)?;
let len = BASE64_STANDARD
.decode_slice(s, &mut buf)
.map_err(|e| serde::de::Error::custom(e.to_string()))?;
Namespace::from_raw(&buf[..len]).map_err(|e| serde::de::Error::custom(e.to_string()))
}
}
pub struct NodePair(NamespacedHash, NamespacedHash);
impl NodePair {
fn validate_namespace_order(&self) -> Result<()> {
let NodePair(left, right) = self;
left.validate_namespace_order()?;
right.validate_namespace_order()?;
if left.max_namespace() > right.min_namespace() {
return Err(Error::InvalidNmtNodeOrder);
}
Ok(())
}
}
impl TryFrom<NodePair> for CidGeneric<NMT_ID_SIZE> {
type Error = CidError;
fn try_from(nodes: NodePair) -> Result<Self, Self::Error> {
nodes
.validate_namespace_order()
.map_err(|e| CidError::InvalidDataFormat(e.to_string()))?;
let hasher = NamespacedSha2Hasher::with_ignore_max_ns(true);
let digest = hasher.hash_nodes(&nodes.0, &nodes.1).to_array();
let mh = Multihash::wrap(NMT_MULTIHASH_CODE, &digest).unwrap();
Ok(CidGeneric::new_v1(NMT_CODEC, mh))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test as test;
#[test]
fn namespace_id_8_bytes() {
let nid = Namespace::new_v0(&[1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
let expected_nid = Namespace(nmt_rs::NamespaceId([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, ]));
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_8_bytes_with_prefix() {
let nid = Namespace::new_v0(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, ])
.unwrap();
let expected_nid = Namespace(nmt_rs::NamespaceId([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, ]));
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_10_bytes() {
let nid = Namespace::new_v0(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).unwrap();
let expected_nid = Namespace(nmt_rs::NamespaceId([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ]));
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_max() {
let nid = Namespace::new(0xff, &[0xff; 28]).unwrap();
let expected_nid = Namespace::PARITY_SHARE;
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_const_v0() {
let nid = Namespace::const_v0([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
let expected_nid = Namespace(nmt_rs::NamespaceId([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ]));
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_const_v255() {
let nid = Namespace::const_v255(0xab);
let expected_nid = Namespace(nmt_rs::NamespaceId([
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xab, ]));
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_10_bytes_with_prefix() {
let nid = Namespace::new_v0(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
])
.unwrap();
let expected_nid = Namespace(nmt_rs::NamespaceId([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ]));
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_with_invalid_prefix() {
let e = Namespace::new_v0(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
])
.unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceV0));
}
#[test]
fn namespace_id_11_bytes() {
let e = Namespace::new_v0(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]).unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceSize));
}
#[test]
fn namespace_id_v255_too_long() {
let e = Namespace::new_v255(&[0xff; 29]).unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceSize));
}
#[test]
fn namespace_id_v255_too_short() {
let e = Namespace::new_v255(&[0xff; 27]).unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceSize));
}
#[test]
fn namespace_id_max_invalid_prefix() {
let namespace = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
];
let e = Namespace::new_v255(namespace).unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceV255));
}
#[test]
fn namespace_id_from_raw_bytes() {
let nid = Namespace::from_raw(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ])
.unwrap();
let expected_nid = Namespace(nmt_rs::NamespaceId([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ]));
assert_eq!(nid, expected_nid);
}
#[test]
fn namespace_id_with_28_raw_bytes() {
let e = Namespace::from_raw(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ])
.unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceSize));
}
#[test]
fn namespace_id_with_30_raw_bytes() {
let e = Namespace::from_raw(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ])
.unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceSize));
}
#[test]
fn max_namespace_id_from_raw_bytes() {
let nid = Namespace::from_raw(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff,
])
.unwrap();
let expected_nid = Namespace::PARITY_SHARE;
assert_eq!(nid, expected_nid);
}
#[test]
fn invalid_max_namespace_id_from_raw_bytes() {
let e = Namespace::from_raw(&[
0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
])
.unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceV255));
let e = Namespace::from_raw(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0xff,
])
.unwrap_err();
assert!(matches!(e, Error::InvalidNamespaceV255));
}
#[test]
fn invalid_version() {
let e = Namespace::from_raw(&[
254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
])
.unwrap_err();
assert!(matches!(e, Error::UnsupportedNamespaceVersion(254)));
}
#[test]
fn test_generate_inner_multihash() {
let ns0 = Namespace::new_v0(&[1]).unwrap();
let ns1 = Namespace::new_v0(&[2]).unwrap();
let ns2 = Namespace::new_v0(&[3]).unwrap();
let nodes = NodePair(
NamespacedHash::with_min_and_max_ns(*ns0, *ns1),
NamespacedHash::with_min_and_max_ns(*ns1, *ns2),
);
let cid = CidGeneric::try_from(nodes).unwrap();
assert_eq!(cid.codec(), NMT_CODEC);
let hash = cid.hash();
assert_eq!(hash.code(), NMT_MULTIHASH_CODE);
assert_eq!(hash.size(), NAMESPACED_HASH_SIZE as u8);
let hash = NamespacedHash::from_raw(hash.digest()).unwrap();
assert_eq!(hash.min_namespace(), *ns0);
assert_eq!(hash.max_namespace(), *ns2);
}
#[test]
fn invalid_ns_order_result() {
let ns0 = Namespace::new_v0(&[1]).unwrap();
let ns1 = Namespace::new_v0(&[2]).unwrap();
let ns2 = Namespace::new_v0(&[3]).unwrap();
let nodes = NodePair(
NamespacedHash::with_min_and_max_ns(*ns1, *ns2),
NamespacedHash::with_min_and_max_ns(*ns0, *ns0),
);
let result = CidGeneric::try_from(nodes).unwrap_err();
assert_eq!(
result,
CidError::InvalidDataFormat("Invalid nmt node order".to_string())
);
}
#[test]
fn test_read_multihash() {
let multihash = [
0x81, 0xEE, 0x01, 0x5A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, ];
let mh = Multihash::<NAMESPACED_HASH_SIZE>::from_bytes(&multihash).unwrap();
assert_eq!(mh.code(), NMT_CODEC);
assert_eq!(mh.size(), NAMESPACED_HASH_SIZE as u8);
let hash = NamespacedHash::from_raw(mh.digest()).unwrap();
assert_eq!(hash.min_namespace(), *Namespace::new_v0(&[1]).unwrap());
assert_eq!(hash.max_namespace(), *Namespace::new_v0(&[9]).unwrap());
assert_eq!(hash.hash(), [0xFF; 32]);
}
#[test]
fn test_serialize_namespace_binary() {
let ns = Namespace::new_v0(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).unwrap();
let serialized = postcard::to_allocvec(&ns).unwrap();
let deserialized: Namespace = postcard::from_bytes(&serialized).unwrap();
assert_eq!(ns, deserialized);
}
}