Skip to content

Commit

Permalink
add uuid
Browse files Browse the repository at this point in the history
  • Loading branch information
loloicci committed Apr 19, 2024
1 parent 855f485 commit 937c19e
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ sha2 = "0.10.3"
serde = { workspace = true, features = ["std"] }
serde-json-wasm = { version = "1.0.1", default-features = false, features = ["std"] }
thiserror = "1.0.26"
uuid = { version = "1.0.0", features = ["v5", "serde"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
bech32 = "0.11.0"
Expand Down
2 changes: 2 additions & 0 deletions packages/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod stdack;
mod storage;
mod traits;
mod types;
mod uuid;

/// This module is to simplify no_std imports
pub(crate) mod prelude;
Expand Down Expand Up @@ -79,6 +80,7 @@ pub use crate::stdack::StdAck;
pub use crate::storage::MemoryStorage;
pub use crate::traits::{Api, Querier, QuerierResult, QuerierWrapper, Storage};
pub use crate::types::{BlockInfo, ContractInfo, Env, MessageInfo, TransactionInfo};
pub use crate::uuid::{new_uuid, Uuid};

// Exposed in wasm build only

Expand Down
118 changes: 118 additions & 0 deletions packages/std/src/uuid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::ops::Deref;
use std::str::FromStr;
use uuid as raw_uuid;

use crate::{from_json, to_json_vec};
use crate::{Api, Env, StdResult, Storage};

/// Uuid Provides a Uuid that can be used deterministically.
/// Use internally Uuidv5 and NAMESPACE_OID.
/// The name is combined with contract address, block height, and increased sequential.
#[derive(
Serialize, Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema,
)]
pub struct Uuid(#[schemars(with = "String")] raw_uuid::Uuid);
impl Uuid {
pub fn as_slice(&self) -> &[u8] {
&self.as_bytes()[0..16]
}

// Port the new_v5 implementation of uuid to use deps.api
// https://github.com/uuid-rs/uuid/blob/2d6c147bdfca9612263dd7e82e26155f7ef8bf32/src/v5.rs#L33
fn new_v5(api: &dyn Api, namespace: &Uuid, name: &[u8]) -> StdResult<Self> {
let message = [namespace.as_bytes(), name].concat();
let buffer = api.sha1_calculate(&message)?;

let mut bytes = raw_uuid::Bytes::default();
bytes.copy_from_slice(&buffer[..16]);
let mut builder = raw_uuid::Builder::from_bytes(bytes);
builder
.set_variant(raw_uuid::Variant::RFC4122)
.set_version(raw_uuid::Version::Sha1);

Ok(Uuid(builder.into_uuid()))
}
}

const CONTRACT_UUID_SEQ_NUM_KEY: &[u8] = b"_contract_uuid_seq_num";

pub fn new_uuid(env: &Env, storage: &mut dyn Storage, api: &dyn Api) -> StdResult<Uuid> {
let raw_seq_num = storage.get(CONTRACT_UUID_SEQ_NUM_KEY);
let seq_num: u16 = match raw_seq_num {
Some(data) => from_json(data).unwrap(),
None => 0,
};
let next_seq_num: u16 = seq_num.wrapping_add(1);
let uuid_name = &[
env.contract.address.as_bytes(),
&env.block.height.to_be_bytes(),
&seq_num.to_be_bytes(),
]
.concat();
storage.set(
CONTRACT_UUID_SEQ_NUM_KEY,
&(to_json_vec(&next_seq_num).unwrap()),
);

Uuid::new_v5(api, &Uuid(raw_uuid::Uuid::NAMESPACE_OID), uuid_name)
}

impl Deref for Uuid {
type Target = raw_uuid::Uuid;
fn deref(&self) -> &raw_uuid::Uuid {
&self.0
}
}

impl FromStr for Uuid {
type Err = uuid::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parsed = raw_uuid::Uuid::parse_str(s);
match parsed {
Ok(data) => Ok(Uuid(data)),
Err(err) => Err(err),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::testing::{mock_env, MockApi, MockStorage};
use uuid as raw_uuid;

#[test]
fn generate_uuid_v5() {
let env = mock_env();
let api = MockApi::default();
let mut storage = MockStorage::new();

let uuid1 = new_uuid(&env, &mut storage, &api).unwrap();

assert_eq!(uuid1.get_variant(), uuid::Variant::RFC4122);
assert_eq!(uuid1.get_version(), Some(uuid::Version::Sha1));

let uuid2 = new_uuid(&env, &mut storage, &api).unwrap();
assert_ne!(uuid1, uuid2);
}

#[test]
fn same_output_as_raw_uuid() {
let env = mock_env();
let api = MockApi::default();
let mut storage = MockStorage::new();
let our_uuid = new_uuid(&env, &mut storage, &api).unwrap();

let uuid_name = &[
env.contract.address.as_bytes(),
&env.block.height.to_be_bytes(),
&0u16.to_be_bytes(),
]
.concat();
let raw = raw_uuid::Uuid::new_v5(&raw_uuid::Uuid::NAMESPACE_OID, uuid_name);

assert_eq!(our_uuid.to_string(), raw.to_string());
}
}

0 comments on commit 937c19e

Please sign in to comment.