diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 3b841fd..e1951c4 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -10,7 +10,8 @@ thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } chacha20 = { version = "0.9", default-features = false } k256 = { version = "0.13.3", optional = true } +hex = { version = "0.4.3", optional = true } [features] default = [] -host = ["thiserror", "bytemuck", "k256"] +host = ["thiserror", "bytemuck", "k256", "hex"] diff --git a/nssa/core/src/address.rs b/nssa/core/src/address.rs new file mode 100644 index 0000000..69a6979 --- /dev/null +++ b/nssa/core/src/address.rs @@ -0,0 +1,132 @@ +use risc0_zkvm::sha::{Impl, Sha256}; +use std::{fmt::Display, str::FromStr}; + +use serde::{Deserialize, Serialize}; + +use crate::account::AccountId; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct Address { + value: [u8; 32], +} + +impl Address { + pub fn new(value: [u8; 32]) -> Self { + Self { value } + } + + pub fn value(&self) -> &[u8; 32] { + &self.value + } +} + +impl AsRef<[u8]> for Address { + fn as_ref(&self) -> &[u8] { + &self.value + } +} + +#[derive(Debug, thiserror::Error)] +pub enum AddressError { + #[error("invalid hex")] + InvalidHex(#[from] hex::FromHexError), + #[error("invalid length: expected 32 bytes, got {0}")] + InvalidLength(usize), +} + +impl FromStr for Address { + type Err = AddressError; + fn from_str(s: &str) -> Result { + let bytes = hex::decode(s)?; + if bytes.len() != 32 { + return Err(AddressError::InvalidLength(bytes.len())); + } + let mut value = [0u8; 32]; + value.copy_from_slice(&bytes); + Ok(Address { value }) + } +} + +impl Display for Address { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.value)) + } +} + +impl Serialize for Address { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let hex_string = self.to_string(); + + hex_string.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let hex_string = String::deserialize(deserializer)?; + + Address::from_str(&hex_string).map_err(serde::de::Error::custom) + } +} + +impl From<&Address> for AccountId { + fn from(address: &Address) -> Self { + const PUBLIC_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/NSSA/v0.1/AccountId/Public/\x00\x00\x00\x00"; + + let mut bytes = PUBLIC_ACCOUNT_ID_PREFIX.to_vec(); + bytes.extend_from_slice(&address.value); + AccountId::new(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap()) + } +} + +#[cfg(test)] +mod tests { + use super::{Address, AddressError}; + + #[test] + fn parse_valid_address() { + let hex_str = "00".repeat(32); // 64 hex chars = 32 bytes + let addr: Address = hex_str.parse().unwrap(); + assert_eq!(addr.value, [0u8; 32]); + } + + #[test] + fn parse_invalid_hex() { + let hex_str = "zz".repeat(32); // invalid hex chars + let result = hex_str.parse::
().unwrap_err(); + assert!(matches!(result, AddressError::InvalidHex(_))); + } + + #[test] + fn parse_wrong_length_short() { + let hex_str = "00".repeat(31); // 62 chars = 31 bytes + let result = hex_str.parse::
().unwrap_err(); + assert!(matches!(result, AddressError::InvalidLength(_))); + } + + #[test] + fn parse_wrong_length_long() { + let hex_str = "00".repeat(33); // 66 chars = 33 bytes + let result = hex_str.parse::
().unwrap_err(); + assert!(matches!(result, AddressError::InvalidLength(_))); + } + + // #[test] + // fn test_account_id_from_address() { + // let address: Address = "37".repeat(32).parse().unwrap(); + // let expected_account_id = AccountId::new([ + // 93, 223, 66, 245, 78, 230, 157, 188, 110, 161, 134, 255, 137, 177, 220, 88, 37, 44, + // 243, 91, 236, 4, 36, 147, 185, 112, 21, 49, 234, 4, 107, 185, + // ]); + // + // let account_id = AccountId::from(&address); + // + // assert_eq!(account_id, expected_account_id); + // } +} diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index 2275e31..5a8ca2b 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -6,6 +6,9 @@ pub mod encryption; mod nullifier; pub mod program; +#[cfg(feature = "host")] +pub mod address; + pub use circuit_io::{PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput}; pub use commitment::{Commitment, CommitmentSetDigest, MembershipProof, compute_digest_for_path}; pub use encryption::{EncryptionScheme, SharedSecretKey}; diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index 31591ad..e3ef5cf 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -1,4 +1,4 @@ -pub mod address; +// pub mod address; pub mod encoding; pub mod error; mod merkle_tree; @@ -8,7 +8,7 @@ pub mod public_transaction; mod signature; mod state; -pub use address::Address; +pub use nssa_core::address::Address; pub use nssa_core::account::{Account, AccountId}; pub use privacy_preserving_transaction::{ PrivacyPreservingTransaction, circuit::execute_and_prove, diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index 8af9212..dea77e9 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -1,14 +1,12 @@ use std::collections::{HashMap, HashSet}; use nssa_core::{ - account::{Account, AccountWithMetadata}, - program::validate_execution, + account::{Account, AccountWithMetadata}, address::Address, program::validate_execution }; use sha2::{Digest, digest::FixedOutput}; use crate::{ V01State, - address::Address, error::NssaError, public_transaction::{Message, WitnessSet}, }; diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index ac163d8..2feb6a7 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,3 +1,5 @@ +use nssa_core::address::Address; + use crate::{PrivateKey, error::NssaError}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -27,6 +29,13 @@ impl PublicKey { } } +impl From<&PublicKey> for Address { + fn from(value: &PublicKey) -> Self { + // TODO: Check specs + Self::new(*value.value()) + } +} + #[cfg(test)] mod test { use crate::{PublicKey, error::NssaError, signature::bip340_test_vectors}; diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 65b440f..ddb659f 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -1,12 +1,10 @@ use crate::{ - address::Address, error::NssaError, merkle_tree::MerkleTree, + error::NssaError, merkle_tree::MerkleTree, privacy_preserving_transaction::PrivacyPreservingTransaction, program::Program, public_transaction::PublicTransaction, }; use nssa_core::{ - Commitment, CommitmentSetDigest, MembershipProof, Nullifier, - account::Account, - program::{DEFAULT_PROGRAM_ID, ProgramId}, + account::Account, address::Address, program::{ProgramId, DEFAULT_PROGRAM_ID}, Commitment, CommitmentSetDigest, MembershipProof, Nullifier }; use std::collections::{HashMap, HashSet};