use alloc::vec::Vec;
use core::cmp::min;
use evm::{
GasMutState,
interpreter::{ExitError, ExitException, ExitResult, ExitSucceed},
};
use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
use primitive_types::{H256, U256};
use sha3::{Digest, Keccak256};
use crate::{PurePrecompile, linear_cost};
pub struct ECRecover;
impl<G: GasMutState> PurePrecompile<G> for ECRecover {
fn execute(&self, i: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
const COST_BASE: u64 = 3000;
const COST_WORD: u64 = 0;
try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost(
i.len() as u64,
COST_BASE,
COST_WORD
)))));
let mut input = [0u8; 128];
input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]);
if input[32..63] != [0u8; 31] || ![27, 28].contains(&input[63]) {
return (ExitSucceed::Returned.into(), Vec::new());
}
let mut msg = [0u8; 32];
let mut sig = [0u8; 64];
msg[0..32].copy_from_slice(&input[0..32]);
sig[0..32].copy_from_slice(&input[64..96]); sig[32..64].copy_from_slice(&input[96..128]);
let work = || -> Result<_, ExitError> {
let mut raw_recid = input[63] - 27;
let mut sig = Signature::from_bytes((&sig[..]).into())
.map_err(|_| ExitException::Other("invalid ecdsa sig".into()))?;
if let Some(sig_normalized) = sig.normalize_s() {
sig = sig_normalized;
raw_recid ^= 1;
}
let recid = RecoveryId::from_byte(raw_recid)
.ok_or(ExitException::Other("invalid recoverty id".into()))?;
let pubkey = VerifyingKey::recover_from_prehash(&msg[..], &sig, recid)
.map_err(|_| ExitException::Other("recover key failed".into()))?;
let mut address = H256::from_slice(
Keccak256::digest(&pubkey.to_encoded_point(false).as_bytes()[1..]).as_slice(),
);
address.0[0..12].copy_from_slice(&[0u8; 12]);
Ok(address)
};
match work() {
Ok(address) => (ExitSucceed::Returned.into(), address.0.to_vec()),
Err(_) => (ExitSucceed::Returned.into(), Vec::new()),
}
}
}
pub struct Sha256;
impl<G: GasMutState> PurePrecompile<G> for Sha256 {
fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
const COST_BASE: u64 = 60;
const COST_WORD: u64 = 12;
try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost(
input.len() as u64,
COST_BASE,
COST_WORD
)))));
let mut ret = [0u8; 32];
let hash = sha2::Sha256::digest(input);
ret[0..32].copy_from_slice(&hash);
(ExitSucceed::Returned.into(), ret.to_vec())
}
}
pub struct Ripemd160;
impl<G: GasMutState> PurePrecompile<G> for Ripemd160 {
fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
const COST_BASE: u64 = 600;
const COST_WORD: u64 = 120;
try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost(
input.len() as u64,
COST_BASE,
COST_WORD
)))));
let mut ret = [0u8; 32];
let hash = ripemd::Ripemd160::digest(input);
ret[12..32].copy_from_slice(&hash);
(ExitSucceed::Returned.into(), ret.to_vec())
}
}
pub struct Identity;
impl<G: GasMutState> PurePrecompile<G> for Identity {
fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
const COST_BASE: u64 = 15;
const COST_WORD: u64 = 3;
try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost(
input.len() as u64,
COST_BASE,
COST_WORD
)))));
(ExitSucceed::Returned.into(), input.to_vec())
}
}