#[cfg(feature = "secp256k1")]
pub use crate::schemes::secp256k1::{Address, FromActorIdError};
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
use crate::error::{Result, SignerError};
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
use alloc::{
format,
string::{String, ToString},
};
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
use derive_more::{Debug, Display};
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
use sp_core::crypto::Ss58AddressFormat;
#[cfg(all(feature = "serde", any(feature = "sr25519", feature = "ed25519")))]
use sp_core::crypto::Ss58Codec;
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SubstrateCryptoScheme {
#[cfg(feature = "sr25519")]
Sr25519,
#[cfg(feature = "ed25519")]
Ed25519,
}
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
#[derive(Clone, PartialEq, Eq, Hash, Display, Debug)]
#[display("{ss58}")]
pub struct SubstrateAddress {
pub public_key: [u8; 32],
ss58: String,
scheme: SubstrateCryptoScheme,
}
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
impl SubstrateAddress {
pub(crate) const DEFAULT_PREFIX: u16 = 137;
pub fn new(public_key: [u8; 32], scheme: SubstrateCryptoScheme) -> Result<Self> {
Self::new_with_format(
public_key,
scheme,
Ss58AddressFormat::custom(Self::DEFAULT_PREFIX),
)
}
pub fn new_with_format(
public_key: [u8; 32],
scheme: SubstrateCryptoScheme,
format: Ss58AddressFormat,
) -> Result<Self> {
let ss58 = Self::encode(public_key, format, scheme)?;
Ok(Self {
public_key,
ss58,
scheme,
})
}
#[cfg(feature = "sr25519")]
pub fn from_sr25519(public_key: [u8; 32]) -> Result<Self> {
Self::new(public_key, SubstrateCryptoScheme::Sr25519)
}
#[cfg(feature = "ed25519")]
pub fn from_ed25519(public_key: [u8; 32]) -> Result<Self> {
Self::new(public_key, SubstrateCryptoScheme::Ed25519)
}
pub fn as_ss58(&self) -> &str {
&self.ss58
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.public_key
}
pub fn scheme(&self) -> SubstrateCryptoScheme {
self.scheme
}
#[cfg(feature = "serde")]
pub fn from_ss58(ss58: &str) -> Result<Self> {
#[cfg(feature = "sr25519")]
if let Ok(public) = sp_core::sr25519::Public::from_ss58check(ss58) {
return Ok(Self {
public_key: public.0,
ss58: ss58.to_string(),
scheme: SubstrateCryptoScheme::Sr25519,
});
}
#[cfg(feature = "ed25519")]
if let Ok(public) = sp_core::ed25519::Public::from_ss58check(ss58) {
return Ok(Self {
public_key: public.0,
ss58: ss58.to_string(),
scheme: SubstrateCryptoScheme::Ed25519,
});
}
Err(SignerError::InvalidAddress(format!(
"Invalid SS58 encoding: {ss58}"
)))
}
pub fn recode(&self) -> Result<Self> {
Self::new(self.public_key, self.scheme)
}
fn encode(
public_key: [u8; 32],
format: Ss58AddressFormat,
scheme: SubstrateCryptoScheme,
) -> Result<String> {
match scheme {
#[cfg(feature = "sr25519")]
SubstrateCryptoScheme::Sr25519 => {
let public = sp_core::sr25519::Public::from_raw(public_key);
Ok(public.to_ss58check_with_version(format))
}
#[cfg(feature = "ed25519")]
SubstrateCryptoScheme::Ed25519 => {
let public = sp_core::ed25519::Public::from_raw(public_key);
Ok(public.to_ss58check_with_version(format))
}
#[cfg(not(any(feature = "sr25519", feature = "ed25519")))]
_ => Err(SignerError::FeatureNotEnabled("substrate address encoding")),
}
}
}
#[cfg(any(feature = "sr25519", feature = "ed25519"))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_substrate_address_encode_decode() {
let public_key = [0x42; 32];
#[cfg(feature = "sr25519")]
let scheme = SubstrateCryptoScheme::Sr25519;
#[cfg(all(not(feature = "sr25519"), feature = "ed25519"))]
let scheme = SubstrateCryptoScheme::Ed25519;
let addr = SubstrateAddress::new(public_key, scheme).unwrap();
let ss58 = addr.as_ss58();
let decoded = SubstrateAddress::from_ss58(ss58).unwrap();
assert_eq!(decoded.public_key, public_key);
assert_eq!(decoded.scheme(), scheme);
}
}