contract_transcode/
account_id.rs1use scale::{
18 Decode,
19 Encode,
20};
21use serde::{
22 Deserialize,
23 Serialize,
24};
25
26#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
47pub struct AccountId32(pub [u8; 32]);
48
49impl scale_info::TypeInfo for AccountId32 {
51 type Identity = Self;
52
53 fn type_info() -> scale_info::Type {
54 scale_info::Type::builder()
55 .path(::scale_info::Path::new("AccountId32", "sp_core::crypto"))
56 .composite(
57 scale_info::build::Fields::unnamed()
58 .field(|f| f.ty::<[u8; 32]>().type_name("[u8; 32]").docs(&[])),
59 )
60 }
61}
62
63impl AsRef<[u8]> for AccountId32 {
64 fn as_ref(&self) -> &[u8] {
65 &self.0[..]
66 }
67}
68
69impl AsRef<[u8; 32]> for AccountId32 {
70 fn as_ref(&self) -> &[u8; 32] {
71 &self.0
72 }
73}
74
75impl From<[u8; 32]> for AccountId32 {
76 fn from(x: [u8; 32]) -> Self {
77 AccountId32(x)
78 }
79}
80
81impl<'a> TryFrom<&'a [u8]> for AccountId32 {
82 type Error = ();
83 fn try_from(x: &'a [u8]) -> Result<AccountId32, ()> {
84 if x.len() == 32 {
85 let mut data = [0; 32];
86 data.copy_from_slice(x);
87 Ok(AccountId32(data))
88 } else {
89 Err(())
90 }
91 }
92}
93
94impl AccountId32 {
95 pub fn to_ss58check(&self) -> String {
98 const SUBSTRATE_SS58_PREFIX: u8 = 42;
103 let mut v = vec![SUBSTRATE_SS58_PREFIX];
105 v.extend(self.0);
107 let r = ss58hash(&v);
109 v.extend(&r[0..2]);
110 use base58::ToBase58;
112 v.to_base58()
113 }
114
115 fn from_ss58check(s: &str) -> Result<Self, FromSs58Error> {
119 const CHECKSUM_LEN: usize = 2;
120 let body_len = 32;
121
122 use base58::FromBase58;
123 let data = s.from_base58().map_err(|_| FromSs58Error::BadBase58)?;
124 if data.len() < 2 {
125 return Err(FromSs58Error::BadLength)
126 }
127 let prefix_len = match data[0] {
128 0..=63 => 1,
129 64..=127 => 2,
130 _ => return Err(FromSs58Error::InvalidPrefix),
131 };
132 if data.len() != prefix_len + body_len + CHECKSUM_LEN {
133 return Err(FromSs58Error::BadLength)
134 }
135 let hash = ss58hash(&data[0..body_len + prefix_len]);
136 let checksum = &hash[0..CHECKSUM_LEN];
137 if data[body_len + prefix_len..body_len + prefix_len + CHECKSUM_LEN] != *checksum
138 {
139 return Err(FromSs58Error::InvalidChecksum)
141 }
142
143 let result = data[prefix_len..body_len + prefix_len]
144 .try_into()
145 .map_err(|_| FromSs58Error::BadLength)?;
146 Ok(AccountId32(result))
147 }
148}
149
150#[derive(thiserror::Error, Clone, Copy, Eq, PartialEq, Debug)]
152#[allow(missing_docs)]
153pub enum FromSs58Error {
154 #[error("Base 58 requirement is violated")]
155 BadBase58,
156 #[error("Length is bad")]
157 BadLength,
158 #[error("Invalid checksum")]
159 InvalidChecksum,
160 #[error("Invalid SS58 prefix byte.")]
161 InvalidPrefix,
162}
163
164fn ss58hash(data: &[u8]) -> Vec<u8> {
167 use blake2::{
168 Blake2b512,
169 Digest,
170 };
171 const PREFIX: &[u8] = b"SS58PRE";
172 let mut ctx = Blake2b512::new();
173 ctx.update(PREFIX);
174 ctx.update(data);
175 ctx.finalize().to_vec()
176}
177
178impl Serialize for AccountId32 {
179 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
180 where
181 S: serde::Serializer,
182 {
183 serializer.serialize_str(&self.to_ss58check())
184 }
185}
186
187impl<'de> Deserialize<'de> for AccountId32 {
188 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
189 where
190 D: serde::Deserializer<'de>,
191 {
192 AccountId32::from_ss58check(&String::deserialize(deserializer)?)
193 .map_err(|e| serde::de::Error::custom(format!("{e:?}")))
194 }
195}
196
197impl std::fmt::Display for AccountId32 {
198 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
199 write!(f, "{}", self.to_ss58check())
200 }
201}
202
203impl std::str::FromStr for AccountId32 {
204 type Err = FromSs58Error;
205 fn from_str(s: &str) -> Result<Self, Self::Err> {
206 AccountId32::from_ss58check(s)
207 }
208}
209
210#[cfg(test)]
211mod test {
212 use super::*;
213
214 use sp_core::crypto::Ss58Codec;
215 use sp_keyring::AccountKeyring;
216
217 #[test]
218 fn ss58_is_compatible_with_substrate_impl() {
219 let keyrings = vec![
220 AccountKeyring::Alice,
221 AccountKeyring::Bob,
222 AccountKeyring::Charlie,
223 ];
224
225 for keyring in keyrings {
226 let substrate_account = keyring.to_account_id();
227 let local_account = AccountId32(substrate_account.clone().into());
230
231 let substrate_ss58 = substrate_account.to_ss58check();
233 assert_eq!(substrate_ss58, local_account.to_ss58check());
234
235 assert_eq!(
237 sp_core::crypto::AccountId32::from_ss58check(&substrate_ss58).unwrap(),
238 substrate_account
239 );
240 assert_eq!(
241 AccountId32::from_ss58check(&substrate_ss58).unwrap(),
242 local_account
243 );
244 }
245 }
246}