1use crate::types::*;
2use parity_scale_codec::alloc::string::ToString;
3use scale_info::prelude::string::String;
4
5#[inline(always)]
9fn blake2<const N: usize>(data: &[u8]) -> [u8; N] {
10 blake2b_simd::Params::new()
11 .hash_length(N)
12 .hash(data)
13 .as_bytes()
14 .try_into()
15 .expect("slice is always the necessary length")
16}
17
18pub(crate) fn blake2_256(data: &[u8]) -> [u8; 32] {
20 blake2(data)
21}
22
23fn _encode_account(open_part: &[u8]) -> AccountId32
33where
34 AccountId32: From<[u8; 32]>,
35{
36 let mut checksum = blake2_256(open_part);
37 checksum[0..open_part.len()].clone_from_slice(open_part);
38 return AccountId32::from(checksum);
39}
40
41pub fn encode_app_agent_account(app_agent_id: &AppAgentId) -> AccountId32
58where
59 AccountId32: From<[u8; 32]>,
60{
61 let mut open_part: [u8; 5] = [0u8; 5];
62 open_part[0..4].copy_from_slice(&app_agent_id.to_le_bytes());
63 open_part[4] = APP_AGENT_ADDRESS_IDENTIFIER;
64
65 return _encode_account(&open_part);
66}
67
68pub fn encode_transactional_account(
87 app_agent_id: &AppAgentId,
88 ta_id: &TransactionalId,
89) -> AccountId32
90where
91 AppAgentId: Into<u32>,
92 TransactionalId: Into<u32>,
93 AccountId32: From<[u8; 32]>,
94{
95 let mut open_part: [u8; 9] = [0u8; 9];
96 open_part[0..4].copy_from_slice(&app_agent_id.to_le_bytes());
97 open_part[4] = TRANSACTIONAL_ADDRESS_IDENTIFIER;
98 open_part[5..9].copy_from_slice(&ta_id.to_le_bytes());
99
100 return _encode_account(&open_part);
101}
102
103pub fn encode_named_account(app_agent_id: &AppAgentId, name: &AddressName) -> AccountId32
123where
124 AppAgentId: Into<u32>,
125 AccountId32: From<[u8; 32]>,
126{
127 let mut open_part: [u8; 15] = [0u8; 15];
128 open_part[0..4].copy_from_slice(&app_agent_id.to_le_bytes());
129 open_part[4] = NAMED_ADDRESS_IDENTIFIER;
130 open_part[5..15].copy_from_slice(name.as_bytes());
131
132 return _encode_account(&open_part);
133}
134
135fn decode_account_ids(account: &AccountId32) -> BlockchainAccountIds
138where
139 AccountId32: From<[u8; 32]>,
140{
141 let account_bytes: &[u8; 32] = account.as_ref();
142
143 let type_identifier: AddressIdentifierType = account_bytes[4];
144 match type_identifier {
145 APP_AGENT_ADDRESS_IDENTIFIER => {
146 let open_part: &[u8; 5] = &account_bytes[0..5].try_into().unwrap();
148 let checksum: &[u8; 27] = &account_bytes[5..32].try_into().unwrap();
149 let calculated_checksum: [u8; 32] = blake2_256(open_part);
150 let calculated_checksum_trimmed: &[u8; 27] =
151 (&calculated_checksum[5..32]).try_into().unwrap();
152
153 if checksum == calculated_checksum_trimmed {
154 let app_agent_id_bytes = account_bytes[0..4].try_into().unwrap();
156 let app_agent_id: AppAgentId = u32::from_le_bytes(app_agent_id_bytes);
157
158 return BlockchainAccountIds::AppAgent(app_agent_id);
159 };
160 }
161 TRANSACTIONAL_ADDRESS_IDENTIFIER => {
162 let open_part: &[u8; 9] = &account_bytes[0..9].try_into().unwrap();
164 let checksum: &[u8; 23] = &account_bytes[9..32].try_into().unwrap();
165 let calculated_checksum: [u8; 32] = blake2_256(open_part);
166 let calculated_checksum_trimmed: &[u8; 23] =
167 (&calculated_checksum[9..32]).try_into().unwrap();
168
169 if checksum == calculated_checksum_trimmed {
170 let app_agent_id_bytes = account_bytes[0..4].try_into().unwrap();
172 let app_agent_id: AppAgentId = u32::from_le_bytes(app_agent_id_bytes);
173 let ta_id_bytes = account_bytes[5..9].try_into().unwrap();
176 let ta_id: TransactionalId = u32::from_le_bytes(ta_id_bytes);
177
178 return BlockchainAccountIds::Transactional((app_agent_id, ta_id));
179 };
180 }
181 NAMED_ADDRESS_IDENTIFIER => {
182 let open_part: &[u8; 15] = &account_bytes[0..15].try_into().unwrap();
184 let checksum: &[u8; 17] = &account_bytes[15..32].try_into().unwrap();
185 let calculated_checksum: [u8; 32] = blake2_256(open_part);
186 let calculated_checksum_trimmed: &[u8; 17] =
187 (&calculated_checksum[15..32]).try_into().unwrap();
188
189 if checksum == calculated_checksum_trimmed {
190 let app_agent_id_bytes = account_bytes[..4].try_into().unwrap();
192 let app_agent_id: AppAgentId = u32::from_le_bytes(app_agent_id_bytes);
193 let address_name = account_bytes[5..15].try_into().unwrap();
194
195 return BlockchainAccountIds::Named((app_agent_id, address_name));
196 };
197 }
198 _ => {}
199 }
200
201 return BlockchainAccountIds::Regular;
202}
203
204pub fn decode_account(account: AccountId32) -> BlockchainAddressInfo {
217 match decode_account_ids(&account) {
218 BlockchainAccountIds::Regular => BlockchainAddressInfo::from_regular_account(account),
219 BlockchainAccountIds::AppAgent(app_agent_id) => {
220 BlockchainAddressInfo::from_app_agent_account(account, app_agent_id)
221 }
222 BlockchainAccountIds::Transactional((app_agent_id, ta_id)) => {
223 BlockchainAddressInfo::from_ta_account(account, app_agent_id, ta_id)
224 }
225 BlockchainAccountIds::Named((app_agent_id, address_name)) => {
226 BlockchainAddressInfo::from_named_account(account, app_agent_id, address_name)
227 }
228 }
229}
230
231pub fn decode_app_agent_account(account: &AccountId32) -> Result<AppAgentId, String> {
242 match decode_account_ids(account) {
243 BlockchainAccountIds::AppAgent(app_agent_id) => Ok(app_agent_id),
244 _ => Err("Provided account is not an AppAgent keyless account".to_string()),
245 }
246}
247
248pub fn decode_transactional_account(
260 account: &AccountId32,
261) -> Result<(AppAgentId, TransactionalId), String> {
262 match decode_account_ids(account) {
263 BlockchainAccountIds::Transactional((app_agent_id, ta_id)) => Ok((app_agent_id, ta_id)),
264 _ => Err("Provided account is not a Transactional keyless account".to_string()),
265 }
266}
267
268pub fn decode_named_account(account: &AccountId32) -> Result<(AppAgentId, AddressName), String> {
280 match decode_account_ids(account) {
281 BlockchainAccountIds::Named((app_agent_id, address_name)) => {
282 Ok((app_agent_id, address_name))
283 }
284 _ => Err("Provided account is not a Named keyless account".to_string()),
285 }
286}
287
288pub fn is_keyless_account(account: &AccountId32) -> bool {
298 match decode_account_ids(account) {
299 BlockchainAccountIds::Regular => false,
300 BlockchainAccountIds::AppAgent(_)
301 | BlockchainAccountIds::Transactional(_)
302 | BlockchainAccountIds::Named(_) => true,
303 }
304}