use std::{
fmt::{Debug, Display},
str::FromStr,
};
use crate::{
convert::{decode, encode},
error::DecodeError,
version,
};
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PrivateKey(pub [u8; 32]);
impl Debug for PrivateKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PrivateKey(")?;
write!(
f,
"{}",
&self
.0
.iter()
.map(|b| format!("{b:02x}"))
.collect::<String>()
)?;
write!(f, ")")?;
Ok(())
}
}
impl PrivateKey {
pub fn to_string(&self) -> String {
encode(version::PRIVATE_KEY_ED25519, &self.0)
}
pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
match payload.try_into() {
Ok(ed25519) => Ok(Self(ed25519)),
Err(_) => Err(DecodeError::Invalid),
}
}
pub fn from_string(s: &str) -> Result<Self, DecodeError> {
let (ver, payload) = decode(s)?;
match ver {
version::PRIVATE_KEY_ED25519 => Self::from_payload(&payload),
_ => Err(DecodeError::Invalid),
}
}
}
impl Display for PrivateKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl FromStr for PrivateKey {
type Err = DecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
PrivateKey::from_string(s)
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PublicKey(pub [u8; 32]);
impl Debug for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PublicKey(")?;
write!(
f,
"{}",
&self
.0
.iter()
.map(|b| format!("{b:02x}"))
.collect::<String>()
)?;
write!(f, ")")?;
Ok(())
}
}
impl PublicKey {
pub fn to_string(&self) -> String {
encode(version::PUBLIC_KEY_ED25519, &self.0)
}
pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
match payload.try_into() {
Ok(ed25519) => Ok(Self(ed25519)),
Err(_) => Err(DecodeError::Invalid),
}
}
pub fn from_string(s: &str) -> Result<Self, DecodeError> {
let (ver, payload) = decode(s)?;
match ver {
version::PUBLIC_KEY_ED25519 => Self::from_payload(&payload),
_ => Err(DecodeError::Invalid),
}
}
}
impl Display for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl FromStr for PublicKey {
type Err = DecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
PublicKey::from_string(s)
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct MuxedAccount {
pub ed25519: [u8; 32],
pub id: u64,
}
impl Debug for MuxedAccount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MuxedAccount(")?;
write!(
f,
"{}",
&self
.ed25519
.iter()
.map(|b| format!("{b:02x}"))
.collect::<String>()
)?;
write!(f, ", ")?;
write!(f, "{}", self.id)?;
write!(f, ")")?;
Ok(())
}
}
impl MuxedAccount {
pub fn to_string(&self) -> String {
let mut payload: [u8; 40] = [0; 40];
let (ed25519, id) = payload.split_at_mut(32);
ed25519.copy_from_slice(&self.ed25519);
id.copy_from_slice(&self.id.to_be_bytes());
encode(version::MUXED_ACCOUNT_ED25519, &payload)
}
pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
if payload.len() < 40 {
return Err(DecodeError::Invalid);
}
let (ed25519, id) = payload.split_at(32);
Ok(Self {
ed25519: ed25519.try_into().map_err(|_| DecodeError::Invalid)?,
id: u64::from_be_bytes(id.try_into().map_err(|_| DecodeError::Invalid)?),
})
}
pub fn from_string(s: &str) -> Result<Self, DecodeError> {
let (ver, payload) = decode(s)?;
match ver {
version::MUXED_ACCOUNT_ED25519 => Self::from_payload(&payload),
_ => Err(DecodeError::Invalid),
}
}
}
impl Display for MuxedAccount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl FromStr for MuxedAccount {
type Err = DecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
MuxedAccount::from_string(s)
}
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct SignedPayload {
pub ed25519: [u8; 32],
pub payload: Vec<u8>,
}
impl Debug for SignedPayload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MuxedAccount(")?;
write!(
f,
"{}",
&self
.ed25519
.iter()
.map(|b| format!("{b:02x}"))
.collect::<String>()
)?;
write!(f, ", ")?;
write!(
f,
"{}",
&self
.payload
.iter()
.map(|b| format!("{b:02x}"))
.collect::<String>()
)?;
write!(f, ")")?;
Ok(())
}
}
impl SignedPayload {
pub fn to_string(&self) -> String {
let inner_payload_len = self.payload.len();
let payload_len = 32 + 4 + inner_payload_len + (4 - inner_payload_len % 4) % 4;
let inner_payload_len_u32: u32 = inner_payload_len
.try_into()
.expect("payload length larger than u32::MAX");
let mut payload = vec![0; payload_len];
payload[..32].copy_from_slice(&self.ed25519);
payload[32..32 + 4].copy_from_slice(&(inner_payload_len_u32).to_be_bytes());
payload[32 + 4..32 + 4 + inner_payload_len].copy_from_slice(&self.payload);
encode(version::SIGNED_PAYLOAD_ED25519, &payload)
}
pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
const MAX_INNER_PAYLOAD_LENGTH: u32 = 64;
const MIN_LENGTH: usize = 32 + 4 + 4;
const MAX_LENGTH: usize = 32 + 4 + (MAX_INNER_PAYLOAD_LENGTH as usize);
let payload_len = payload.len();
if !(MIN_LENGTH..=MAX_LENGTH).contains(&payload_len) {
return Err(DecodeError::Invalid);
}
let inner_payload_len = u32::from_be_bytes(
(&payload[32..32 + 4])
.try_into()
.map_err(|_| DecodeError::Invalid)?,
);
if inner_payload_len > MAX_INNER_PAYLOAD_LENGTH {
return Err(DecodeError::Invalid);
}
if (inner_payload_len + (4 - inner_payload_len % 4) % 4) as usize != payload_len - 32 - 4 {
return Err(DecodeError::Invalid);
}
let ed25519 = (&payload[0..32])
.try_into()
.map_err(|_| DecodeError::Invalid)?;
let inner_payload = &payload[32 + 4..32 + 4 + inner_payload_len as usize];
Ok(Self {
ed25519,
payload: inner_payload.to_vec(),
})
}
pub fn from_string(s: &str) -> Result<Self, DecodeError> {
let (ver, payload) = decode(s)?;
match ver {
version::SIGNED_PAYLOAD_ED25519 => Self::from_payload(&payload),
_ => Err(DecodeError::Invalid),
}
}
}
impl Display for SignedPayload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl FromStr for SignedPayload {
type Err = DecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
SignedPayload::from_string(s)
}
}