use crate::uints::U256;
use fil_actors_runtime_v10::EAM_ACTOR_ID;
use fvm_ipld_encoding::{serde, strict_bytes};
use fvm_shared3::address::Address;
use fvm_shared3::ActorID;
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Eq, Clone, Copy)]
pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]);
impl From<U256> for EthAddress {
fn from(v: U256) -> Self {
let mut bytes = [0u8; 32];
v.to_big_endian(&mut bytes);
Self(bytes[12..].try_into().unwrap())
}
}
impl std::fmt::Debug for EthAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&hex::encode(self.0))
}
}
impl From<EthAddress> for Address {
fn from(addr: EthAddress) -> Self {
From::from(&addr)
}
}
impl From<&EthAddress> for Address {
fn from(addr: &EthAddress) -> Self {
if let Some(id) = addr.as_id() {
Address::new_id(id)
} else {
Address::new_delegated(EAM_ACTOR_ID, addr.as_ref()).unwrap()
}
}
}
impl EthAddress {
pub const fn null() -> Self {
Self([0u8; 20])
}
pub fn from_id(id: u64) -> EthAddress {
let mut bytes = [0u8; 20];
bytes[0] = 0xff;
bytes[12..].copy_from_slice(&id.to_be_bytes());
EthAddress(bytes)
}
pub fn as_id(&self) -> Option<ActorID> {
if !self.is_id() {
return None;
}
Some(u64::from_be_bytes(self.0[12..].try_into().unwrap()))
}
#[inline]
pub fn as_evm_word(&self) -> U256 {
U256::from_big_endian(&self.0)
}
#[inline]
pub fn is_null(&self) -> bool {
self.0 == [0; 20]
}
#[inline]
pub fn is_precompile(&self) -> bool {
let [prefix, middle @ .., _index] = self.0;
(prefix == 0xfe || prefix == 0x00) && middle == [0u8; 18]
}
#[inline]
pub fn is_id(&self) -> bool {
self.0[0] == 0xff && self.0[1..12].iter().all(|&i| i == 0)
}
}
impl AsRef<[u8]> for EthAddress {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::EthAddress;
use crate::uints::U256;
const TYPE_PADDING: &[u8] = &[0; 12];
const ID_ADDRESS_MARKER: &[u8] = &[0xff];
const GOOD_ADDRESS_PADDING: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
macro_rules! id_address_test {
($($name:ident: $input:expr => $expectation:expr,)*) => {
$(
#[test]
fn $name() {
let evm_bytes = $input.concat();
let evm_addr = EthAddress::from(U256::from(evm_bytes.as_slice()));
assert_eq!(
evm_addr.as_id(),
$expectation
);
if let Some(fil_id) = $expectation {
assert_eq!(EthAddress::from_id(fil_id), evm_addr);
}
}
)*
};
}
id_address_test! {
good_address_1: [
TYPE_PADDING,
ID_ADDRESS_MARKER,
GOOD_ADDRESS_PADDING,
vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() ] => Some(1),
good_address_2: [
TYPE_PADDING,
ID_ADDRESS_MARKER,
GOOD_ADDRESS_PADDING,
vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff].as_slice() ] => Some(u16::MAX as u64),
bad_marker: [
TYPE_PADDING,
&[0xfa],
GOOD_ADDRESS_PADDING,
vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() ] => None,
bad_padding: [
TYPE_PADDING,
ID_ADDRESS_MARKER,
&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() ] => None,
bad_marker_and_padding: [
TYPE_PADDING,
&[0xfa],
&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() ] => None,
}
}