Skip to content
This repository has been archived by the owner on Feb 9, 2024. It is now read-only.

Commit

Permalink
Use BagOfBytes in Hex32Bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sajjon committed Feb 6, 2024
1 parent d0b02e0 commit 1e07ae6
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 77 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "038
bip32 = "0.5.1" # only need Secp256k1, to do validation of PublicKey
ed25519-dalek = "1.0.1"
rand = "0.8.5"
delegate = "0.12.0"
itertools = { version = "0.12.0" }
enum-as-inner = "0.6.0"
identified_vec = { version = "0.1.11", features = ["serde", "id_prim"] }
Expand Down
1 change: 1 addition & 0 deletions profile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ itertools = { version = "0.12.0" }
bip39 = { version = "2.0.0", features = ["serde"] }
time-util = { version = "0.3.4", features = ["chrono"] }
derive_more = { workspace = true }
delegate = { workspace = true }
assert-json-diff = "2.0.2"

[build-dependencies]
Expand Down
7 changes: 6 additions & 1 deletion profile/src/hierarchical_deterministic/bip39/mnemonic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,28 @@ impl Mnemonic {
language: language.into(),
}
}

pub fn from_entropy(entropy: &[u8]) -> Self {
let internal = bip39::Mnemonic::from_entropy(entropy).unwrap();
Self::from_internal(internal)
}

pub fn from_hex32(bytes: Hex32Bytes) -> Self {
Self::from_entropy(&bytes.bytes())
Self::from_entropy(&bytes.to_vec())
}

pub fn generate_new() -> Self {
Self::from_hex32(Hex32Bytes::generate())
}

fn internal(&self) -> bip39::Mnemonic {
bip39::Mnemonic::from_str(&self.phrase()).unwrap()
}

pub fn phrase(&self) -> String {
self.words.iter().map(|w| w.word.to_string()).join(" ")
}

pub fn from_phrase(phrase: &str) -> Result<Self> {
bip39::Mnemonic::from_str(phrase)
.map_err(|_| CommonError::InvalidMnemonicPhrase)
Expand Down
46 changes: 31 additions & 15 deletions profile/src/wallet_kit_common/types/bag_of_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,6 @@ pub fn bag_of_bytes_append_cafe(to: &BagOfBytes) -> BagOfBytes {
to.appending(vec![0xca, 0xfe])
}

impl BagOfBytes {
/// Instantiates a new `BagOfBytes` from bytes generated by
/// a CSPRNG.
pub fn generate() -> Self {
generate_32_bytes().into()
}
}

impl BagOfBytes {
pub fn to_hex(&self) -> String {
hex_encode(self.bytes())
Expand Down Expand Up @@ -188,45 +180,59 @@ impl HasPlaceholder for BagOfBytes {
}
}

impl From<Hex32Bytes> for BagOfBytes {
fn from(value: Hex32Bytes) -> Self {
value.to_vec().into()
}
}

impl BagOfBytes {
/// `aced...``
/// A placeholder used to facilitate unit tests.
pub fn placeholder_aced() -> Self {
Self::from_str(&"aced".repeat(16)).expect("aced...")
Hex32Bytes::placeholder_aced().into()
}

/// `babe...``
/// A placeholder used to facilitate unit tests.
pub fn placeholder_babe() -> Self {
Self::from_str(&"babe".repeat(16)).expect("babe...")
Hex32Bytes::placeholder_babe().into()
}

/// `cafe...``
/// A placeholder used to facilitate unit tests.
pub fn placeholder_cafe() -> Self {
Self::from_str(&"cafe".repeat(16)).expect("cafe...")
Hex32Bytes::placeholder_cafe().into()
}

/// `dead...``
/// A placeholder used to facilitate unit tests.
pub fn placeholder_dead() -> Self {
Self::from_str(&"dead".repeat(16)).expect("dead...")
Hex32Bytes::placeholder_dead().into()
}

/// `ecad...``
/// A placeholder used to facilitate unit tests.
pub fn placeholder_ecad() -> Self {
Self::from_str(&"ecad".repeat(16)).expect("ecad...")
Hex32Bytes::placeholder_ecad().into()
}

/// `fade...``
/// A placeholder used to facilitate unit tests.
pub fn placeholder_fade() -> Self {
Self::from_str(&"fade".repeat(16)).expect("fade...")
Hex32Bytes::placeholder_fade().into()
}
}

impl BagOfBytes {
pub fn len(&self) -> usize {
self.bytes.len()
}

pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}

/// Returns a clone of the inner bytes as a `Vec`.
pub fn to_vec(&self) -> Vec<u8> {
Vec::from(self.bytes())
Expand Down Expand Up @@ -293,6 +299,16 @@ mod tests {
assert_ne!(BagOfBytes::placeholder(), BagOfBytes::placeholder_other());
}

#[test]
fn len() {
assert_eq!(BagOfBytes::placeholder().len(), 32);
}

#[test]
fn is_empty() {
assert_eq!(BagOfBytes::placeholder().is_empty(), false);
}

#[test]
fn from_string_roundtrip() {
let str =
Expand Down Expand Up @@ -366,7 +382,7 @@ mod tests {
let mut set: HashSet<Vec<u8>> = HashSet::new();
let n = 100;
for _ in 0..n {
let bytes = BagOfBytes::generate();
let bytes = BagOfBytes::from(generate_32_bytes());
set.insert(bytes.to_vec());
}
assert_eq!(set.len(), n);
Expand Down
127 changes: 67 additions & 60 deletions profile/src/wallet_kit_common/types/hex_32bytes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::prelude::*;
use delegate::delegate;
use radix_engine_common::crypto::{Hash, IsHash};

/// Serializable 32 bytes which **always** serializes as a **hex** string, this is useful
Expand All @@ -20,47 +21,86 @@ use radix_engine_common::crypto::{Hash, IsHash};
#[display("{}", self.to_hex())]
#[debug("{}", self.to_hex())]
pub struct Hex32Bytes {
bytes: Vec<u8>, // FIXME: We REALLY want `[u8; 32]` - but that does not work in UniFFI land with `uniffi::Record` - so we should write an UniffiCustomTypeConverter for this (yet another one...)
bag_of_bytes: BagOfBytes,
}

#[uniffi::export]
pub fn new_hex32_bytes_from(bytes: Vec<u8>) -> Result<Hex32Bytes> {
Hex32Bytes::try_from(bytes)
impl TryFrom<BagOfBytes> for Hex32Bytes {
type Error = CommonError;

fn try_from(value: BagOfBytes) -> Result<Self> {
if value.len() != 32 {
return Err(CommonError::InvalidByteCountExpected32(value.len()));
}
Ok(Self {
bag_of_bytes: value,
})
}
}

impl Hex32Bytes {
/// Instantiates a new `Hex32Bytes` from bytes generated by
/// Instantiates a new `BagOfBytes` from bytes generated by
/// a CSPRNG.
pub fn generate() -> Self {
generate_32_bytes()
.try_into()
.expect("Should be able to generate 32 bytes.")
Hex32Bytes {
bag_of_bytes: BagOfBytes::from(generate_32_bytes()),
}
}

/// Tries to decode the string `s` into a `Hex32Bytes`. Will fail
/// if the string is not valid hex or if the decoded bytes does
/// not have length 32.
pub fn from_hex(s: &str) -> Result<Self> {
Self::from_str(s)
}

/// Instantiates a new `Hex32Bytes` from the 32 bytes, by cloning them.
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
Self {
bag_of_bytes: bytes.into(),
}
}
}

impl Hex32Bytes {
pub fn to_hex(&self) -> String {
hex_encode(self.bytes())
/// Returns a references to the inner array slice.
pub fn bytes(&self) -> [u8; 32] {
self.bag_of_bytes
.to_vec()
.as_slice()
.try_into()
.expect("32 bytes")
}
}

impl From<Hash> for Hex32Bytes {
/// Instantiates a new `Hex32Bytes` from the `Hash` (32 bytes).
fn from(value: Hash) -> Self {
Self::from_bytes(&value.into_bytes())
impl TryFrom<Vec<u8>> for Hex32Bytes {
type Error = CommonError;

fn try_from(value: Vec<u8>) -> Result<Self> {
BagOfBytes::from(value).try_into()
}
}

impl FromStr for Hex32Bytes {
type Err = CommonError;

/// Tries to decode the string `s` into a `Hex32Bytes`. Will fail
/// if the string is not valid hex or if the decoded bytes does
/// not have length 32.
fn from_str(s: &str) -> Result<Self, Self::Err> {
hex_decode(s)
.map_err(|_| CommonError::StringNotHex(s.to_owned()))
.and_then(|v| v.try_into())
s.parse::<BagOfBytes>().and_then(|v| v.try_into())
}
}

impl From<Hash> for Hex32Bytes {
/// Instantiates a new `Hex32Bytes` from the `Hash` (32 bytes).
fn from(value: Hash) -> Self {
Self::from_bytes(&value.into_bytes())
}
}

impl Hex32Bytes {
delegate! {
to self.bag_of_bytes{
pub fn to_hex(&self) -> String;
pub fn to_vec(&self) -> Vec<u8>;
}
}
}

Expand Down Expand Up @@ -115,45 +155,9 @@ impl Hex32Bytes {
}
}

impl Hex32Bytes {
/// Returns a clone of the inner bytes as a `Vec`.
pub fn to_vec(&self) -> Vec<u8> {
Vec::from(self.bytes())
}

/// Returns a references to the inner array slice.
pub fn bytes(&self) -> [u8; 32] {
self.bytes.clone().as_slice().try_into().expect("32 bytes")
}
}

impl TryFrom<Vec<u8>> for Hex32Bytes {
type Error = CommonError;

/// Tries to turn the `Vec` into a `Hex32Bytes`. Will fail
/// if `bytes` does not have length 32.
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() != 32 {
return Err(CommonError::InvalidByteCountExpected32(value.len()));
}
Ok(Self { bytes: value })
}
}

impl Hex32Bytes {
/// Instantiates a new `Hex32Bytes` from the 32 bytes, by cloning them.
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
Self {
bytes: bytes.to_vec(),
}
}

/// Tries to decode the string `s` into a `Hex32Bytes`. Will fail
/// if the string is not valid hex or if the decoded bytes does
/// not have length 32.
pub fn from_hex(s: &str) -> Result<Self> {
Self::from_str(s)
}
#[uniffi::export]
pub fn new_hex32_bytes_from(bytes: Vec<u8>) -> Result<Hex32Bytes> {
Hex32Bytes::try_from(bytes)
}

#[cfg(test)]
Expand Down Expand Up @@ -271,7 +275,10 @@ mod uniffi_tests {
#[test]
fn new_ok() {
let bytes = generate_32_bytes();
assert_eq!(new_hex32_bytes_from(bytes.clone()).unwrap().bytes, bytes);
assert_eq!(
new_hex32_bytes_from(bytes.clone()).unwrap().to_vec(),
bytes
);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion profile/tests/uniffi/bindings/test_hex32_bytes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension Hex32Bytes {
func test() throws {
let bytes = try Data.random(byteCount: 32)
let hex32 = try Hex32Bytes(data: bytes)
assert(hex32.bytes == bytes)
assert(hex32.bagOfBytes == bytes)
}

try! test()

0 comments on commit 1e07ae6

Please sign in to comment.