diff --git a/crates/storage/src/address.rs b/crates/storage/src/address.rs index 1de861597..70763656a 100644 --- a/crates/storage/src/address.rs +++ b/crates/storage/src/address.rs @@ -16,9 +16,8 @@ use borsh::{BorshDeserialize, BorshSerialize}; use calimero_sdk::serde::{Deserialize, Serialize}; use fixedstr::Flexstr; use thiserror::Error as ThisError; -use uuid::{Bytes, Uuid}; -use crate::env::random_bytes; +use crate::env::{random_bytes, context_id}; /// Globally-unique identifier for an [`Element`](crate::entities::Element). /// @@ -35,101 +34,82 @@ use crate::env::random_bytes; /// system operation. Abstracting the true type away provides a level of /// insulation that is useful for any future changes. /// -#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(BorshSerialize, BorshDeserialize, Copy, Clone, Debug, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize)] #[serde(crate = "calimero_sdk::serde")] -pub struct Id(Uuid); +pub struct Id { + /// The byte array representation of the ID. + bytes: [u8; 32], +} impl Id { /// Creates a new globally-unique identifier. /// + /// Returns the byte array representation of the ID. + /// /// # Examples /// /// ```rust /// use calimero_storage::address::Id; - /// let id = Id::new(); + /// let id = Id::new([0; 32]); + /// assert_eq!(id.as_bytes(), &[0; 32]); /// ``` - /// - #[must_use] - pub fn new() -> Self { - let mut bytes = [0; 16]; - random_bytes(&mut bytes); - Self(uuid::Builder::from_random_bytes(bytes).into_uuid()) - } - - /// Returns a slice of 16 octets containing the value. #[must_use] - pub const fn as_bytes(&self) -> &Bytes { - self.0.as_bytes() + pub fn new(bytes: [u8; 32]) -> Self { + // random_bytes(&mut bytes); + Self { bytes } } /// Root ID which is set to all zeroes by default. #[must_use] - pub const fn root() -> Self { - Self(Uuid::nil()) - } -} - -impl BorshDeserialize for Id { - fn deserialize(buf: &mut &[u8]) -> Result { - if buf.len() < 16 { - return Err(IoError::new( - IoErrorKind::UnexpectedEof, - "Not enough bytes to deserialize Id", - )); - } - let (bytes, rest) = buf.split_at(16); - *buf = rest; - Ok(Self(Uuid::from_slice(bytes).map_err(|err| { - IoError::new(IoErrorKind::InvalidData, err) - })?)) + pub fn root() -> Self { + Id::new(context_id()) } - fn deserialize_reader(reader: &mut R) -> Result { - let mut bytes = [0_u8; 16]; - reader.read_exact(&mut bytes)?; - Ok(Self(Uuid::from_bytes(bytes))) - } -} - -impl BorshSerialize for Id { - fn serialize(&self, writer: &mut W) -> Result<(), IoError> { - writer.write_all(self.0.as_bytes()) + /// Creates a new random globally-unique identifier. + /// + /// # Examples + /// + /// ```rust + /// use calimero_storage::address::Id; + /// let id = Id::random(); + /// ``` + #[must_use] + pub fn random() -> Id { + let mut bytes = [0_u8; 32]; + random_bytes(&mut bytes); + Id::new(bytes) } -} -impl Default for Id { - fn default() -> Self { - Self::new() + /// Returns the byte array representation of the ID. + /// + /// # Examples + /// + /// ```rust + /// use calimero_storage::address::Id; + /// let id = Id::new([0; 32]); + /// assert_eq!(id.as_bytes(), &[0; 32]); + /// ``` + #[must_use] + pub fn as_bytes(&self) -> &[u8; 32] { + &self.bytes } } impl Display for Id { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From<[u8; 16]> for Id { - fn from(bytes: [u8; 16]) -> Self { - Self(Uuid::from_bytes(bytes)) + write!(f, "{:?}", self.bytes) } } -impl From<&[u8; 16]> for Id { - fn from(bytes: &[u8; 16]) -> Self { - Self(Uuid::from_bytes(*bytes)) - } -} - -impl From for [u8; 16] { - fn from(id: Id) -> Self { - *id.0.as_bytes() +impl From<[u8; 32]> for Id { + fn from(bytes: [u8; 32]) -> Self { + Self::new(bytes) } } -impl From for Uuid { +impl From for [u8; 32] { fn from(id: Id) -> Self { - id.0 + id.into() } } diff --git a/crates/storage/src/entities.rs b/crates/storage/src/entities.rs index c085e0de1..7a3fb7717 100644 --- a/crates/storage/src/entities.rs +++ b/crates/storage/src/entities.rs @@ -654,7 +654,7 @@ impl Element { pub fn new(path: &Path) -> Self { let timestamp = time_now(); Self { - id: Id::new(), + id: Id::random(), is_dirty: true, metadata: Metadata { created_at: timestamp, diff --git a/crates/storage/src/interface.rs b/crates/storage/src/interface.rs index f0ab18b1b..2cbdff588 100644 --- a/crates/storage/src/interface.rs +++ b/crates/storage/src/interface.rs @@ -944,8 +944,8 @@ impl MainInterface { /// If an error occurs when interacting with the storage system, an error /// will be returned. /// - pub fn root(context_id: &[u8; 16]) -> Result, StorageError> { - Self::find_by_id(context_id.into()) + pub fn root() -> Result, StorageError> { + Self::find_by_id(Id::root()) } /// Saves an [`Element`](crate::entities::Element) to the storage system. diff --git a/crates/storage/src/store.rs b/crates/storage/src/store.rs index a7c356efd..7f5393057 100644 --- a/crates/storage/src/store.rs +++ b/crates/storage/src/store.rs @@ -17,16 +17,16 @@ pub enum Key { impl Key { /// Converts the key to a byte array. #[must_use] - pub fn to_bytes(&self) -> [u8; 17] { - let mut bytes = [0; 17]; + pub fn to_bytes(&self) -> [u8; 33] { + let mut bytes = [0; 33]; match *self { Self::Index(id) => { bytes[0] = 0; - bytes[1..17].copy_from_slice(id.as_bytes()); + bytes[1..33].copy_from_slice(id.as_bytes()); } Self::Entry(id) => { bytes[0] = 1; - bytes[1..17].copy_from_slice(id.as_bytes()); + bytes[1..33].copy_from_slice(id.as_bytes()); } } bytes diff --git a/crates/storage/src/tests/address.rs b/crates/storage/src/tests/address.rs index 96b6e3399..698246497 100644 --- a/crates/storage/src/tests/address.rs +++ b/crates/storage/src/tests/address.rs @@ -2,57 +2,6 @@ use borsh::to_vec; use claims::assert_err; use super::*; -use crate::tests::common::TEST_UUID; - -#[cfg(test)] -mod id__public_methods { - use super::*; - - #[test] - fn as_bytes() { - assert_eq!(Id(Uuid::from_bytes(TEST_UUID[0])).as_bytes(), &TEST_UUID[0]); - } -} - -#[cfg(test)] -mod id__traits { - use super::*; - - #[test] - fn borsh_deserialization__valid() { - assert_eq!( - Id::try_from_slice(&TEST_UUID[0]).unwrap(), - Id(Uuid::from_bytes(TEST_UUID[0])) - ); - } - - #[test] - fn borsh_deserialization__too_short() { - assert_err!(Id::try_from_slice(&[1, 2, 3])); - } - - #[test] - fn borsh_serialization__valid() { - let serialized = to_vec(&Id(Uuid::from_bytes(TEST_UUID[0]))).unwrap(); - assert_eq!(serialized.len(), 16); - assert_eq!(serialized, TEST_UUID[0]); - } - - #[test] - fn borsh_serialization__roundtrip() { - let id1 = Id::new(); - let id2 = Id::try_from_slice(&to_vec(&id1).unwrap()).unwrap(); - assert_eq!(id1, id2); - } - - #[test] - fn from__for_uuid() { - assert_eq!( - Uuid::from(Id(Uuid::from_bytes(TEST_UUID[0]))).as_bytes(), - &TEST_UUID[0] - ); - } -} #[cfg(test)] mod path__constructor { diff --git a/crates/storage/src/tests/common.rs b/crates/storage/src/tests/common.rs index f2c2a99d9..41bd46cb2 100644 --- a/crates/storage/src/tests/common.rs +++ b/crates/storage/src/tests/common.rs @@ -1,34 +1,12 @@ use std::collections::BTreeMap; -use std::sync::LazyLock; use borsh::{to_vec, BorshDeserialize, BorshSerialize}; use sha2::{Digest, Sha256}; use velcro::btree_map; -use crate::address::Id; use crate::entities::{AtomicUnit, ChildInfo, Collection, Data, Element}; use crate::interface::{Interface, StorageError}; -/// A set of non-empty test UUIDs. -pub const TEST_UUID: [[u8; 16]; 5] = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - [2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - [3, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - [4, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - [5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], -]; - -/// A set of non-empty test IDs. -pub static TEST_ID: LazyLock<[Id; 5]> = LazyLock::new(|| { - [ - Id::from(TEST_UUID[0]), - Id::from(TEST_UUID[1]), - Id::from(TEST_UUID[2]), - Id::from(TEST_UUID[3]), - Id::from(TEST_UUID[4]), - ] -}); - /// For tests against empty data structs. #[derive(BorshDeserialize, BorshSerialize, Clone, Debug, Eq, PartialEq, PartialOrd)] pub struct EmptyData { diff --git a/crates/storage/src/tests/entities.rs b/crates/storage/src/tests/entities.rs index 3810bcb7e..b6987305c 100644 --- a/crates/storage/src/tests/entities.rs +++ b/crates/storage/src/tests/entities.rs @@ -177,7 +177,7 @@ mod child_info__constructor { #[test] fn new() { - let id = Id::new(); + let id = Id::random(); let hash = Sha256::digest(b"1").into(); let info = ChildInfo::new(id, hash); assert_eq!(info.id, id); @@ -191,13 +191,13 @@ mod child_info__public_methods { #[test] fn id() { - let info = ChildInfo::new(Id::new(), Sha256::digest(b"1").into()); + let info = ChildInfo::new(Id::random(), Sha256::digest(b"1").into()); assert_eq!(info.id(), info.id); } #[test] fn merkle_hash() { - let info = ChildInfo::new(Id::new(), Sha256::digest(b"1").into()); + let info = ChildInfo::new(Id::random(), Sha256::digest(b"1").into()); assert_eq!(info.merkle_hash(), info.merkle_hash); } } @@ -208,7 +208,7 @@ mod child_info__traits { #[test] fn display() { - let info = ChildInfo::new(Id::new(), Sha256::digest(b"1").into()); + let info = ChildInfo::new(Id::random(), Sha256::digest(b"1").into()); assert_eq!( format!("{info}"), format!( diff --git a/crates/storage/src/tests/index.rs b/crates/storage/src/tests/index.rs index 4b2f40336..4c4d25995 100644 --- a/crates/storage/src/tests/index.rs +++ b/crates/storage/src/tests/index.rs @@ -1,13 +1,12 @@ use super::*; use crate::store::MainStorage; -use crate::tests::common::TEST_ID; mod index__public_methods { use super::*; #[test] fn add_child_to() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); @@ -19,7 +18,7 @@ mod index__public_methods { assert!(root_index.children.is_empty()); let collection_name = "Books"; - let child_id = Id::new(); + let child_id = Id::random(); let child_own_hash = [2_u8; 32]; let child_full_hash: [u8; 32] = hex::decode("75877bb41d393b5fb8455ce60ecd8dda001d06316496b14dfa7f895656eeca4a") @@ -54,7 +53,7 @@ mod index__public_methods { #[test] fn add_root() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); @@ -68,7 +67,7 @@ mod index__public_methods { #[test] fn get_ancestors_of() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; let child_collection_name = "Books"; let grandchild_collection_name = "Pages"; @@ -76,7 +75,7 @@ mod index__public_methods { assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); - let child_id = Id::new(); + let child_id = Id::random(); let child_hash = [2_u8; 32]; let child_info = ChildInfo::new(child_id, child_hash); assert!( @@ -84,7 +83,7 @@ mod index__public_methods { .is_ok() ); - let grandchild_id = Id::new(); + let grandchild_id = Id::random(); let grandchild_hash = [3_u8; 32]; let grandchild_info = ChildInfo::new(grandchild_id, grandchild_hash); assert!(>::add_child_to( @@ -95,7 +94,7 @@ mod index__public_methods { ) .is_ok()); - let greatgrandchild_id = Id::new(); + let greatgrandchild_id = Id::random(); let greatgrandchild_hash = [4_u8; 32]; let greatgrandchild_info = ChildInfo::new(greatgrandchild_id, greatgrandchild_hash); assert!(>::add_child_to( @@ -142,13 +141,13 @@ mod index__public_methods { #[test] fn get_children_of__single_collection() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); let collection_name = "Books"; - let child1_id = Id::new(); + let child1_id = Id::random(); let child1_own_hash = [2_u8; 32]; let child1_full_hash: [u8; 32] = hex::decode("75877bb41d393b5fb8455ce60ecd8dda001d06316496b14dfa7f895656eeca4a") @@ -156,7 +155,7 @@ mod index__public_methods { .try_into() .unwrap(); - let child2_id = Id::new(); + let child2_id = Id::random(); let child2_own_hash = [3_u8; 32]; let child2_full_hash: [u8; 32] = hex::decode("648aa5c579fb30f38af744d97d6ec840c7a91277a499a0d780f3e7314eca090b") @@ -187,20 +186,20 @@ mod index__public_methods { #[test] fn get_children_of__two_collections() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); let collection1_name = "Pages"; - let child1_id = Id::new(); + let child1_id = Id::random(); let child1_own_hash = [2_u8; 32]; let child1_full_hash: [u8; 32] = hex::decode("75877bb41d393b5fb8455ce60ecd8dda001d06316496b14dfa7f895656eeca4a") .unwrap() .try_into() .unwrap(); - let child2_id = Id::new(); + let child2_id = Id::random(); let child2_own_hash = [3_u8; 32]; let child2_full_hash: [u8; 32] = hex::decode("648aa5c579fb30f38af744d97d6ec840c7a91277a499a0d780f3e7314eca090b") @@ -209,7 +208,7 @@ mod index__public_methods { .unwrap(); let collection2_name = "Reviews"; - let child3_id = Id::new(); + let child3_id = Id::random(); let child3_own_hash = [4_u8; 32]; let child3_full_hash: [u8; 32] = hex::decode("9f4fb68f3e1dac82202f9aa581ce0bbf1f765df0e9ac3c8c57e20f685abab8ed") @@ -250,7 +249,7 @@ mod index__public_methods { #[test] fn get_collection_names_for() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); @@ -259,9 +258,9 @@ mod index__public_methods { let collection2_name = "Chapters"; let mut collection_names = vec![collection1_name.to_owned(), collection2_name.to_owned()]; collection_names.sort(); - let child1_id = Id::new(); + let child1_id = Id::random(); let child1_own_hash = [2_u8; 32]; - let child2_id = Id::new(); + let child2_id = Id::random(); let child2_own_hash = [3_u8; 32]; assert!(>::add_child_to( @@ -287,7 +286,7 @@ mod index__public_methods { #[test] fn get_hashes_for() { - let root_id = TEST_ID[0]; + let root_id = Id::new([0_u8; 32]); let root_own_hash = [1_u8; 32]; let root_full_hash = [0_u8; 32]; @@ -303,7 +302,7 @@ mod index__public_methods { #[test] fn get_parent_id() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); @@ -315,7 +314,7 @@ mod index__public_methods { assert!(root_index.children.is_empty()); let collection_name = "Books"; - let child_id = Id::new(); + let child_id = Id::random(); let child_own_hash = [2_u8; 32]; assert!(>::add_child_to( @@ -335,7 +334,7 @@ mod index__public_methods { #[test] fn get_type_id() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 99).is_ok()); @@ -350,14 +349,14 @@ mod index__public_methods { #[test] fn has_children() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; let collection_name = "Books"; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); assert!(!>::has_children(root_id, collection_name).unwrap()); - let child_id = Id::new(); + let child_id = Id::random(); let child_own_hash = [2_u8; 32]; assert!(>::add_child_to( @@ -372,7 +371,7 @@ mod index__public_methods { #[test] fn remove_child_from() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; assert!(>::add_root(ChildInfo::new(root_id, root_hash), 1).is_ok()); @@ -384,7 +383,7 @@ mod index__public_methods { assert!(root_index.children.is_empty()); let collection_name = "Books"; - let child_id = Id::new(); + let child_id = Id::random(); let child_own_hash = [2_u8; 32]; assert!(>::add_child_to( @@ -409,7 +408,7 @@ mod index__private_methods { #[test] fn get_and_save_index() { - let id = Id::new(); + let id = Id::random(); let hash1 = [1_u8; 32]; let hash2 = [2_u8; 32]; assert!(>::get_index(id).unwrap().is_none()); @@ -429,7 +428,7 @@ mod index__private_methods { #[test] fn save_and_remove_index() { - let id = Id::new(); + let id = Id::random(); let hash1 = [1_u8; 32]; let hash2 = [2_u8; 32]; assert!(>::get_index(id).unwrap().is_none()); @@ -456,23 +455,23 @@ mod hashing { #[test] fn calculate_full_merkle_hash_for__with_children() { - let root_id = TEST_ID[0]; - assert!(>::add_root(ChildInfo::new(TEST_ID[0], [0_u8; 32]), 1).is_ok()); + let root_id = Id::random(); + assert!(>::add_root(ChildInfo::new(root_id, [0_u8; 32]), 1).is_ok()); let collection_name = "Children"; - let child1_id = TEST_ID[1]; + let child1_id = Id::random(); let child1_hash = [1_u8; 32]; let child1_info = ChildInfo::new(child1_id, child1_hash); assert!( >::add_child_to(root_id, collection_name, child1_info, 2).is_ok() ); - let child2_id = TEST_ID[2]; + let child2_id = Id::random(); let child2_hash = [2_u8; 32]; let child2_info = ChildInfo::new(child2_id, child2_hash); assert!( >::add_child_to(root_id, collection_name, child2_info, 2).is_ok() ); - let child3_id = TEST_ID[3]; + let child3_id = Id::random(); let child3_hash = [3_u8; 32]; let child3_info = ChildInfo::new(child3_id, child3_hash); assert!( @@ -507,7 +506,7 @@ mod hashing { #[test] fn recalculate_ancestor_hashes_for() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash = [1_u8; 32]; let child_collection_name = "Books"; let grandchild_collection_name = "Pages"; @@ -518,7 +517,7 @@ mod hashing { let root_index = >::get_index(root_id).unwrap().unwrap(); assert_eq!(root_index.full_hash, [0_u8; 32]); - let child_id = Id::new(); + let child_id = Id::random(); let child_hash = [2_u8; 32]; let child_info = ChildInfo::new(child_id, child_hash); assert!( @@ -537,7 +536,7 @@ mod hashing { "75877bb41d393b5fb8455ce60ecd8dda001d06316496b14dfa7f895656eeca4a" ); - let grandchild_id = Id::new(); + let grandchild_id = Id::random(); let grandchild_hash = [3_u8; 32]; let grandchild_info = ChildInfo::new(grandchild_id, grandchild_hash); assert!(>::add_child_to( @@ -567,7 +566,7 @@ mod hashing { "648aa5c579fb30f38af744d97d6ec840c7a91277a499a0d780f3e7314eca090b" ); - let greatgrandchild_id = Id::new(); + let greatgrandchild_id = Id::random(); let greatgrandchild_hash = [4_u8; 32]; let greatgrandchild_info = ChildInfo::new(greatgrandchild_id, greatgrandchild_hash); assert!(>::add_child_to( @@ -682,7 +681,7 @@ mod hashing { #[test] fn update_hash_for__full() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash0 = [0_u8; 32]; let root_hash1 = [1_u8; 32]; let root_hash2 = [2_u8; 32]; @@ -706,7 +705,7 @@ mod hashing { #[test] fn update_hash_for__own() { - let root_id = Id::new(); + let root_id = Id::random(); let root_hash1 = [1_u8; 32]; let root_hash2 = [2_u8; 32]; diff --git a/crates/storage/src/tests/interface.rs b/crates/storage/src/tests/interface.rs index 1f16e2a86..45e24c7a6 100644 --- a/crates/storage/src/tests/interface.rs +++ b/crates/storage/src/tests/interface.rs @@ -50,7 +50,7 @@ mod interface__public_methods { #[test] fn find_by_id__non_existent() { - assert_none!(Interface::find_by_id::(Id::new()).unwrap()); + assert_none!(Interface::find_by_id::(Id::random()).unwrap()); } #[test]