use super::{TransactionLegacySigned, TransactionLegacyUnsigned};
use rlp::Encodable;
use sp_core::{H160, U256};
use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256};
impl TransactionLegacyUnsigned {
pub fn recover_eth_address(rlp_encoded: &[u8], signature: &[u8; 65]) -> Result<H160, ()> {
let hash = keccak_256(rlp_encoded);
let mut addr = H160::default();
let pk = secp256k1_ecdsa_recover(&signature, &hash).map_err(|_| ())?;
addr.assign_from_slice(&keccak_256(&pk[..])[12..]);
Ok(addr)
}
}
impl TransactionLegacySigned {
pub fn from(
transaction_legacy_unsigned: TransactionLegacyUnsigned,
signature: &[u8; 65],
) -> TransactionLegacySigned {
let r = U256::from_big_endian(&signature[..32]);
let s = U256::from_big_endian(&signature[32..64]);
let recovery_id = signature[64] as u32;
let v = transaction_legacy_unsigned
.chain_id
.map(|chain_id| chain_id * 2 + 35 + recovery_id)
.unwrap_or_else(|| U256::from(27) + recovery_id);
TransactionLegacySigned { transaction_legacy_unsigned, r, s, v }
}
pub fn raw_signature(&self) -> Result<[u8; 65], ()> {
let mut s = [0u8; 65];
self.r.write_as_big_endian(s[0..32].as_mut());
self.s.write_as_big_endian(s[32..64].as_mut());
s[64] = self.extract_recovery_id().ok_or(())?;
Ok(s)
}
fn extract_recovery_id(&self) -> Option<u8> {
if let Some(chain_id) = self.transaction_legacy_unsigned.chain_id {
let v: u64 = self.v.try_into().ok()?;
let chain_id: u64 = chain_id.try_into().ok()?;
let r = v.checked_sub(chain_id.checked_mul(2)?)?.checked_sub(35)?;
r.try_into().ok()
} else {
self.v.try_into().ok()
}
}
pub fn recover_eth_address(&self) -> Result<H160, ()> {
let rlp_encoded = self.transaction_legacy_unsigned.rlp_bytes();
TransactionLegacyUnsigned::recover_eth_address(&rlp_encoded, &self.raw_signature()?)
}
}