use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use sp_runtime::AccountId32;
use sp_std::ops::Deref;
pub type AppAgentId = u32;
pub type TransactionalId = u32;
pub type AddressIdentifierType = u8;
pub const APP_AGENT_ADDRESS_IDENTIFIER: AddressIdentifierType = 1;
pub const TRANSACTIONAL_ADDRESS_IDENTIFIER: AddressIdentifierType = 2;
pub const NAMED_ADDRESS_IDENTIFIER: AddressIdentifierType = 3;
#[derive(
Clone,
Encode,
Decode,
Debug,
Eq,
PartialEq,
scale_info::TypeInfo,
Ord,
PartialOrd,
MaxEncodedLen,
Default,
Copy,
)]
pub struct AddressName([u8; 10]);
impl AddressName {
const ALLOWED_CHARS: &'static [u8] =
b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-#";
pub fn new(input: &[u8]) -> Result<Self, &'static str> {
if input.len() != 10 {
return Err("Input must be exactly 10 bytes long");
}
if input
.iter()
.all(|&b| AddressName::ALLOWED_CHARS.contains(&b))
{
let mut array = [0u8; 10];
array.copy_from_slice(input);
Ok(AddressName(array))
} else {
Err("Input contains invalid characters")
}
}
pub fn as_bytes(&self) -> &[u8; 10] {
&self.0
}
}
impl Deref for AddressName {
type Target = [u8; 10];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<&[u8]> for AddressName {
type Error = &'static str;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
AddressName::new(value)
}
}
#[derive(Eq, PartialEq, Debug, scale_info::TypeInfo, Clone, Copy)]
pub enum AddressType {
Regular,
AppAgent,
Transactional,
Named,
}
#[derive(Eq, PartialEq, Debug)]
pub struct BlockchainAddressInfo {
pub account_id: AccountId32,
pub address_type: AddressType,
pub app_agent_id: Option<AppAgentId>,
pub ta_id: Option<TransactionalId>,
pub address_name: Option<AddressName>,
}
impl BlockchainAddressInfo {
pub fn from_regular_account(account_id: AccountId32) -> Self {
Self {
account_id,
address_type: AddressType::Regular,
app_agent_id: None,
ta_id: None,
address_name: None,
}
}
pub fn from_app_agent_account(account_id: AccountId32, app_agent_id: AppAgentId) -> Self {
Self {
account_id,
address_type: AddressType::AppAgent,
app_agent_id: Some(app_agent_id),
ta_id: None,
address_name: None,
}
}
pub fn from_ta_account(
account_id: AccountId32,
app_agent_id: AppAgentId,
ta_id: TransactionalId,
) -> Self {
Self {
account_id,
address_type: AddressType::Transactional,
app_agent_id: Some(app_agent_id),
ta_id: Some(ta_id),
address_name: None,
}
}
pub fn from_named_account(
account_id: AccountId32,
app_agent_id: AppAgentId,
name: AddressName,
) -> Self {
Self {
account_id,
address_type: AddressType::Named,
app_agent_id: Some(app_agent_id),
ta_id: None,
address_name: Some(name),
}
}
pub fn is_keyless(&self) -> bool {
self.address_type != AddressType::Regular
}
}
pub(super) enum BlockchainAccountIds {
Regular,
AppAgent(AppAgentId),
Transactional((AppAgentId, TransactionalId)),
Named((AppAgentId, AddressName)),
}
#[cfg(test)]
mod tests_address_name {
use super::*;
#[test]
fn test_valid_address_name() {
let valid_address = b"abcdEF1234";
assert!(AddressName::new(valid_address).is_ok());
}
#[test]
fn test_invalid_length() {
let short_address = b"abcdEF123";
let long_address = b"abcdEF12345";
assert_eq!(
AddressName::new(short_address).unwrap_err(),
"Input must be exactly 10 bytes long"
);
assert_eq!(
AddressName::new(long_address).unwrap_err(),
"Input must be exactly 10 bytes long"
);
}
#[test]
fn test_invalid_characters() {
let invalid_address1 = b"abcdEF@123";
let invalid_address2 = b"abcdEF*123";
let invalid_address3 = b"abcdEF/123";
assert_eq!(
AddressName::new(invalid_address1).unwrap_err(),
"Input contains invalid characters"
);
assert_eq!(
AddressName::new(invalid_address2).unwrap_err(),
"Input contains invalid characters"
);
assert_eq!(
AddressName::new(invalid_address3).unwrap_err(),
"Input contains invalid characters"
);
}
#[test]
fn test_edge_cases() {
let valid_address1 = b"0000000000";
let valid_address2 = b"##########";
let valid_address3 = b"abcdEFghij";
let valid_address4 = b"1234567890";
assert!(AddressName::new(valid_address1).is_ok());
assert!(AddressName::new(valid_address2).is_ok());
assert!(AddressName::new(valid_address3).is_ok());
assert!(AddressName::new(valid_address4).is_ok());
}
}