use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use sha3::{Digest, Keccak256}; use sp_core::{ecdsa, H160, H256}; use scale_info::TypeInfo; /// Public key for an Ethereum / Moonbeam compatible account #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode, sp_core::RuntimeDebug)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct EthereumSigner([u8; 20]); impl sp_runtime::traits::IdentifyAccount for EthereumSigner { type AccountId = AccountId20; fn into_account(self) -> AccountId20 { AccountId20(self.0) } } impl From<[u8; 20]> for EthereumSigner { fn from(x: [u8; 20]) -> Self { EthereumSigner(x) } } impl From for EthereumSigner { fn from(x: ecdsa::Public) -> Self { let decompressed = libsecp256k1::PublicKey::parse_slice( &x.0, Some(libsecp256k1::PublicKeyFormat::Compressed), ) .expect("Wrong compressed public key provided") .serialize(); let mut m = [0u8; 64]; m.copy_from_slice(&decompressed[1..65]); let account = H160::from(H256::from_slice(Keccak256::digest(&m).as_slice())); EthereumSigner(account.into()) } } impl From for EthereumSigner { fn from(x: libsecp256k1::PublicKey) -> Self { let mut m = [0u8; 64]; m.copy_from_slice(&x.serialize()[1..65]); let account = H160::from(H256::from_slice(Keccak256::digest(&m).as_slice())); EthereumSigner(account.into()) } } //TODO Maybe this should be upstreamed into Frontier (And renamed accordingly) so that it can // be used in palletEVM as well. It may also need more traits such as AsRef, AsMut, etc like // AccountId32 has. /// The account type to be used in Moonbeam. It is a wrapper for 20 fixed bytes. We prefer to use /// a dedicated type to prevent using arbitrary 20 byte arrays were AccountIds are expected. With /// the introduction of the `scale-info` crate this benefit extends even to non-Rust tools like /// Polkadot JS. #[derive( Eq, PartialEq, Copy, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Default, PartialOrd, Ord, )] pub struct AccountId20(pub [u8; 20]); #[cfg(feature = "std")] impl_serde::impl_fixed_hash_serde!(AccountId20, 20); #[cfg(feature = "std")] impl std::fmt::Display for AccountId20 { //TODO This is a pretty quck-n-dirty implementation. Perhaps we should add // checksum casing here? I bet there is a crate for that. // Maybe this one https://github.com/miguelmota/rust-eth-checksum fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.0) } } impl core::fmt::Debug for AccountId20 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:?}", H160(self.0)) } } impl From<[u8; 20]> for AccountId20 { fn from(bytes: [u8; 20]) -> Self { Self(bytes) } } impl Into<[u8; 20]> for AccountId20 { fn into(self: Self) -> [u8; 20] { self.0 } } impl From for AccountId20 { fn from(h160: H160) -> Self { Self(h160.0) } } impl Into for AccountId20 { fn into(self: Self) -> H160 { H160(self.0) } } #[cfg(feature = "std")] impl std::str::FromStr for AccountId20 { type Err = &'static str; fn from_str(input: &str) -> Result { H160::from_str(input) .map(Into::into) .map_err(|_| "invalid hex address.") } }