use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Hash(pub [u8; 32]);
impl Hash {
pub fn new(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() == 32 {
let mut arr = [0u8; 32];
arr.copy_from_slice(bytes);
Some(Self(arr))
} else {
None
}
}
pub fn zero() -> Self {
Self([0u8; 32])
}
pub fn combine(&self, other: &Hash) -> Self {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(self.0);
hasher.update(other.0);
let result = hasher.finalize();
Self(result.into())
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
impl From<[u8; 32]> for Hash {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}
impl Default for Hash {
fn default() -> Self {
Self::zero()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Address(pub [u8; 32]);
impl Address {
pub fn new(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() == 32 {
let mut arr = [0u8; 32];
arr.copy_from_slice(bytes);
Some(Self(arr))
} else {
None
}
}
pub fn zero() -> Self {
Self([0u8; 32])
}
pub fn to_base58(&self) -> String {
bs58::encode(&self.0).into_string()
}
pub fn from_base58(s: &str) -> Result<Self, bs58::decode::Error> {
let bytes = bs58::decode(s).into_vec()?;
Self::from_bytes(&bytes).ok_or(bs58::decode::Error::BufferTooSmall)
}
pub fn is_valid_hex(s: &str) -> bool {
let s = s.strip_prefix("0x").unwrap_or(s);
s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit())
}
pub fn from_hex(s: &str) -> Result<Self, crate::error::TenzroError> {
Self::from_hex_checksummed(s)
}
pub fn from_hex_checksummed(s: &str) -> Result<Self, crate::error::TenzroError> {
let s = s.strip_prefix("0x").unwrap_or(s);
if s.len() == 40 {
let bytes = hex::decode(s).map_err(|e| crate::error::TenzroError::InvalidAddress(format!("Invalid hex: {}", e)))?;
use sha3::{Digest, Keccak256};
let mut hasher = Keccak256::new();
hasher.update(s.to_lowercase().as_bytes());
let hash = hasher.finalize();
for (i, c) in s.chars().enumerate() {
if c.is_alphabetic() {
let hash_byte = hash[i / 2];
let hash_nibble = if i % 2 == 0 {
hash_byte >> 4
} else {
hash_byte & 0x0f
};
let should_be_uppercase = hash_nibble >= 8;
if c.is_uppercase() != should_be_uppercase {
return Err(crate::error::TenzroError::InvalidAddress("Invalid EIP-55 checksum".to_string()));
}
}
}
let mut addr = [0u8; 32];
addr[12..].copy_from_slice(&bytes);
Ok(Self(addr))
} else if s.len() == 64 {
let bytes = hex::decode(s).map_err(|e| crate::error::TenzroError::InvalidAddress(format!("Invalid hex: {}", e)))?;
let mut addr = [0u8; 32];
addr.copy_from_slice(&bytes);
Ok(Self(addr))
} else {
Err(crate::error::TenzroError::InvalidAddress(format!("Invalid address length: expected 40 or 64 hex chars, got {}", s.len())))
}
}
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_base58())
}
}
impl From<[u8; 32]> for Address {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}
impl Default for Address {
fn default() -> Self {
Self::zero()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Default)]
pub struct Signature {
#[serde(deserialize_with = "crate::validation::bounded_signature_bytes")]
pub bytes: Vec<u8>,
#[serde(deserialize_with = "crate::validation::bounded_public_key_bytes")]
pub public_key: Vec<u8>,
}
impl Signature {
pub fn new(bytes: Vec<u8>, public_key: Vec<u8>) -> Self {
Self { bytes, public_key }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct BlockHeight(pub u64);
impl BlockHeight {
pub fn new(height: u64) -> Self {
Self(height)
}
pub fn genesis() -> Self {
Self(0)
}
pub fn next(self) -> Self {
Self(self.0 + 1)
}
pub fn prev(self) -> Option<Self> {
if self.0 > 0 {
Some(Self(self.0 - 1))
} else {
None
}
}
}
impl fmt::Display for BlockHeight {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u64> for BlockHeight {
fn from(height: u64) -> Self {
Self(height)
}
}
impl Default for BlockHeight {
fn default() -> Self {
Self::genesis()
}
}
impl BlockHeight {
pub fn as_u64(&self) -> u64 {
self.0
}
}
impl std::ops::Add<u64> for BlockHeight {
type Output = Self;
fn add(self, rhs: u64) -> Self::Output {
Self(self.0 + rhs)
}
}
impl std::ops::Sub<u64> for BlockHeight {
type Output = Self;
fn sub(self, rhs: u64) -> Self::Output {
Self(self.0.saturating_sub(rhs))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Nonce(pub u64);
impl Nonce {
pub fn new(value: u64) -> Self {
Self(value)
}
pub fn initial() -> Self {
Self(0)
}
pub fn next(self) -> Self {
Self(self.0 + 1)
}
}
impl fmt::Display for Nonce {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u64> for Nonce {
fn from(value: u64) -> Self {
Self(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(Default)]
pub struct Timestamp(pub i64);
impl Timestamp {
pub fn new(millis: i64) -> Self {
Self(millis)
}
pub fn now() -> Self {
Self(chrono::Utc::now().timestamp_millis())
}
pub fn as_millis(&self) -> i64 {
self.0
}
pub fn as_secs(&self) -> i64 {
self.0 / 1000
}
}
impl From<Timestamp> for std::time::SystemTime {
fn from(ts: Timestamp) -> Self {
std::time::UNIX_EPOCH + std::time::Duration::from_millis(ts.0 as u64)
}
}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<i64> for Timestamp {
fn from(millis: i64) -> Self {
Self(millis)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct ChainId(pub u64);
impl<'de> Deserialize<'de> for ChainId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = u64::deserialize(deserializer)?;
let chain = ChainId(value);
crate::validation::validate_chain_id(chain).map_err(serde::de::Error::custom)?;
Ok(chain)
}
}
impl ChainId {
pub const MAINNET: Self = Self(1);
pub const TESTNET: Self = Self(1337);
pub const DEVNET: Self = Self(31337);
pub fn new(id: u64) -> Self {
Self(id)
}
pub fn try_new(id: u64) -> Result<Self, crate::error::TenzroError> {
let chain = Self(id);
crate::validation::validate_chain_id(chain)?;
Ok(chain)
}
pub fn mainnet() -> Self {
Self::MAINNET
}
pub fn testnet() -> Self {
Self::TESTNET
}
pub fn devnet() -> Self {
Self::DEVNET
}
pub fn as_u64(&self) -> u64 {
self.0
}
pub fn is_valid(&self) -> bool {
crate::validation::validate_chain_id(*self).is_ok()
}
}
impl fmt::Display for ChainId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u64> for ChainId {
fn from(id: u64) -> Self {
Self(id)
}
}
pub mod u128_serde {
use serde::de::{self, Visitor};
use serde::{Deserializer, Serializer};
use std::fmt;
pub fn serialize<S: Serializer>(value: &u128, serializer: S) -> Result<S::Ok, S::Error> {
if *value <= u64::MAX as u128 {
serializer.serialize_u64(*value as u64)
} else {
serializer.serialize_str(&value.to_string())
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u128, D::Error> {
struct U128Visitor;
impl<'de> Visitor<'de> for U128Visitor {
type Value = u128;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a non-negative integer or decimal string fitting in u128")
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<u128, E> {
Ok(v as u128)
}
fn visit_u128<E: de::Error>(self, v: u128) -> Result<u128, E> {
Ok(v)
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<u128, E> {
if v < 0 {
Err(E::custom(format!("negative integer {v} is not a valid u128")))
} else {
Ok(v as u128)
}
}
fn visit_i128<E: de::Error>(self, v: i128) -> Result<u128, E> {
if v < 0 {
Err(E::custom(format!("negative integer {v} is not a valid u128")))
} else {
Ok(v as u128)
}
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<u128, E> {
s.parse::<u128>()
.map_err(|e| E::custom(format!("invalid u128 string {s:?}: {e}")))
}
fn visit_string<E: de::Error>(self, s: String) -> Result<u128, E> {
self.visit_str(&s)
}
}
deserializer.deserialize_any(U128Visitor)
}
}
pub mod u128_serde_opt {
use serde::de::{self, Visitor};
use serde::{Deserializer, Serializer};
use std::fmt;
pub fn serialize<S: Serializer>(
value: &Option<u128>,
serializer: S,
) -> Result<S::Ok, S::Error> {
match value {
None => serializer.serialize_none(),
Some(v) if *v <= u64::MAX as u128 => serializer.serialize_u64(*v as u64),
Some(v) => serializer.serialize_str(&v.to_string()),
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<u128>, D::Error> {
struct OptU128Visitor;
impl<'de> Visitor<'de> for OptU128Visitor {
type Value = Option<u128>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null, a non-negative integer, or a decimal string fitting in u128")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<u128>, E> {
Ok(None)
}
fn visit_none<E: de::Error>(self) -> Result<Option<u128>, E> {
Ok(None)
}
fn visit_some<D2: Deserializer<'de>>(
self,
deserializer: D2,
) -> Result<Option<u128>, D2::Error> {
super::u128_serde::deserialize(deserializer).map(Some)
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Option<u128>, E> {
Ok(Some(v as u128))
}
fn visit_u128<E: de::Error>(self, v: u128) -> Result<Option<u128>, E> {
Ok(Some(v))
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Option<u128>, E> {
if v < 0 {
Err(E::custom(format!("negative integer {v} is not a valid u128")))
} else {
Ok(Some(v as u128))
}
}
fn visit_i128<E: de::Error>(self, v: i128) -> Result<Option<u128>, E> {
if v < 0 {
Err(E::custom(format!("negative integer {v} is not a valid u128")))
} else {
Ok(Some(v as u128))
}
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Option<u128>, E> {
s.parse::<u128>()
.map(Some)
.map_err(|e| E::custom(format!("invalid u128 string {s:?}: {e}")))
}
fn visit_string<E: de::Error>(self, s: String) -> Result<Option<u128>, E> {
self.visit_str(&s)
}
}
deserializer.deserialize_any(OptU128Visitor)
}
}
#[cfg(test)]
mod u128_serde_tests {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct Wrap {
#[serde(with = "super::u128_serde")]
v: u128,
}
#[test]
fn round_trips_small_value_as_number() {
let w = Wrap { v: 42 };
let json = serde_json::to_string(&w).unwrap();
assert_eq!(json, r#"{"v":42}"#);
let back: Wrap = serde_json::from_str(&json).unwrap();
assert_eq!(back, w);
}
#[test]
fn round_trips_large_value_as_string() {
let w = Wrap { v: 5_000_000_000_000_000_000_000u128 };
let json = serde_json::to_string(&w).unwrap();
assert_eq!(json, r#"{"v":"5000000000000000000000"}"#);
let back: Wrap = serde_json::from_str(&json).unwrap();
assert_eq!(back, w);
}
#[test]
fn deserializes_large_raw_number() {
let json = r#"{"v":5000000000000000000000}"#;
let back: Wrap = serde_json::from_str(json).unwrap();
assert_eq!(back.v, 5_000_000_000_000_000_000_000u128);
}
#[test]
fn deserializes_string_at_u64_boundary() {
let json = r#"{"v":"18446744073709551616"}"#; let back: Wrap = serde_json::from_str(json).unwrap();
assert_eq!(back.v, (u64::MAX as u128) + 1);
}
#[test]
fn rejects_negative_string() {
let json = r#"{"v":"-1"}"#;
assert!(serde_json::from_str::<Wrap>(json).is_err());
}
#[test]
fn rejects_negative_number() {
let json = r#"{"v":-1}"#;
assert!(serde_json::from_str::<Wrap>(json).is_err());
}
}