lssa/nssa/core/src/account.rs
2025-11-27 06:03:43 +02:00

180 lines
4.8 KiB
Rust

#[cfg(feature = "host")]
use std::{fmt::Display, str::FromStr};
#[cfg(feature = "host")]
use base58::{FromBase58, ToBase58};
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use crate::program::ProgramId;
pub type Nonce = u128;
pub type Data = Vec<u8>;
/// Account to be used both in public and private contexts
#[derive(
Serialize, Deserialize, Clone, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
#[cfg_attr(any(feature = "host", test), derive(Debug))]
pub struct Account {
pub program_owner: ProgramId,
pub balance: u128,
pub data: Data,
pub nonce: Nonce,
}
#[derive(Serialize, Deserialize, Clone)]
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
pub struct AccountWithMetadata {
pub account: Account,
pub is_authorized: bool,
pub account_id: AccountId,
}
#[cfg(feature = "host")]
impl AccountWithMetadata {
pub fn new(account: Account, is_authorized: bool, account_id: impl Into<AccountId>) -> Self {
Self {
account,
is_authorized,
account_id: account_id.into(),
}
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize)]
#[cfg_attr(
any(feature = "host", test),
derive(Debug, Copy, PartialOrd, Ord, Default)
)]
pub struct AccountId {
value: [u8; 32],
}
impl AccountId {
pub fn new(value: [u8; 32]) -> Self {
Self { value }
}
pub fn value(&self) -> &[u8; 32] {
&self.value
}
}
impl AsRef<[u8]> for AccountId {
fn as_ref(&self) -> &[u8] {
&self.value
}
}
#[cfg(feature = "host")]
#[derive(Debug, thiserror::Error)]
pub enum AccountIdError {
#[error("invalid base58")]
InvalidBase58(#[from] anyhow::Error),
#[error("invalid length: expected 32 bytes, got {0}")]
InvalidLength(usize),
}
#[cfg(feature = "host")]
impl FromStr for AccountId {
type Err = AccountIdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let bytes = s
.from_base58()
.map_err(|err| anyhow::anyhow!("Invalid base58 err {err:?}"))?;
if bytes.len() != 32 {
return Err(AccountIdError::InvalidLength(bytes.len()));
}
let mut value = [0u8; 32];
value.copy_from_slice(&bytes);
Ok(AccountId { value })
}
}
#[cfg(feature = "host")]
impl Display for AccountId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value.to_base58())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::program::DEFAULT_PROGRAM_ID;
#[test]
fn test_zero_balance_account_data_creation() {
let new_acc = Account::default();
assert_eq!(new_acc.balance, 0);
}
#[test]
fn test_zero_nonce_account_data_creation() {
let new_acc = Account::default();
assert_eq!(new_acc.nonce, 0);
}
#[test]
fn test_empty_data_account_data_creation() {
let new_acc = Account::default();
assert!(new_acc.data.is_empty());
}
#[test]
fn test_default_program_owner_account_data_creation() {
let new_acc = Account::default();
assert_eq!(new_acc.program_owner, DEFAULT_PROGRAM_ID);
}
#[cfg(feature = "host")]
#[test]
fn test_account_with_metadata_constructor() {
let account = Account {
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
balance: 1337,
data: b"testing_account_with_metadata_constructor".to_vec(),
nonce: 0xdeadbeef,
};
let fingerprint = AccountId::new([8; 32]);
let new_acc_with_metadata = AccountWithMetadata::new(account.clone(), true, fingerprint);
assert_eq!(new_acc_with_metadata.account, account);
assert!(new_acc_with_metadata.is_authorized);
assert_eq!(new_acc_with_metadata.account_id, fingerprint);
}
#[test]
fn parse_valid_account_id() {
let base58_str = "11111111111111111111111111111111";
let account_id: AccountId = base58_str.parse().unwrap();
assert_eq!(account_id.value, [0u8; 32]);
}
#[test]
fn parse_invalid_base58() {
let base58_str = "00".repeat(32); // invalid base58 chars
let result = base58_str.parse::<AccountId>().unwrap_err();
assert!(matches!(result, AccountIdError::InvalidBase58(_)));
}
#[test]
fn parse_wrong_length_short() {
let base58_str = "11".repeat(31); // 62 chars = 31 bytes
let result = base58_str.parse::<AccountId>().unwrap_err();
assert!(matches!(result, AccountIdError::InvalidLength(_)));
}
#[test]
fn parse_wrong_length_long() {
let base58_str = "11".repeat(33); // 66 chars = 33 bytes
let result = base58_str.parse::<AccountId>().unwrap_err();
assert!(matches!(result, AccountIdError::InvalidLength(_)));
}
}