#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use alloc::rc::Rc;
#[cfg(feature = "std")]
use std::rc::Rc;
use bigint::Gas;
#[cfg(all(not(feature = "std"), any(feature = "rust-secp256k1", feature = "c-secp256k1")))]
use core::cmp::min;
#[cfg(all(feature = "std", any(feature = "rust-secp256k1", feature = "c-secp256k1")))]
use std::cmp::min;
use digest::{Digest, FixedOutput};
use ripemd160::Ripemd160;
use sha2::Sha256;
#[cfg(any(feature = "rust-secp256k1", feature = "c-secp256k1"))]
use sha3::Keccak256;
#[cfg(feature = "rust-secp256k1")]
use secp256k1::{recover, Error, Message, RecoveryId, Signature};
#[cfg(feature = "c-secp256k1")]
use secp256k1::{Error, Message, RecoverableSignature, RecoveryId, SECP256K1};
use crate::errors::{OnChainError, RuntimeError};
pub trait Precompiled: Sync {
fn step(&self, _: &[u8]) -> Rc<Vec<u8>> {
unimplemented!()
}
fn gas(&self, _: &[u8]) -> Gas {
unimplemented!()
}
fn gas_and_step(&self, data: &[u8], gas_limit: Gas) -> Result<(Gas, Rc<Vec<u8>>), RuntimeError> {
let gas = self.gas(data);
if gas > gas_limit {
Err(RuntimeError::OnChain(OnChainError::EmptyGas))
} else {
Ok((gas, self.step(data)))
}
}
}
pub struct IDPrecompiled;
impl Precompiled for IDPrecompiled {
fn gas(&self, data: &[u8]) -> Gas {
Gas::from(15u64) + Gas::from(3u64) * gas_div_ceil(Gas::from(data.len()), Gas::from(32u64))
}
fn step(&self, data: &[u8]) -> Rc<Vec<u8>> {
Rc::new(data.into())
}
}
pub static ID_PRECOMPILED: IDPrecompiled = IDPrecompiled;
pub struct RIP160Precompiled;
impl Precompiled for RIP160Precompiled {
fn gas(&self, data: &[u8]) -> Gas {
Gas::from(600u64) + Gas::from(120u64) * gas_div_ceil(Gas::from(data.len()), Gas::from(32u64))
}
fn step(&self, data: &[u8]) -> Rc<Vec<u8>> {
let mut ripemd = Ripemd160::default();
ripemd.input(data);
let fixed = ripemd.fixed_result();
let mut result: [u8; 32] = [0u8; 32];
for i in 0..20 {
result[i + 12] = fixed[i];
}
Rc::new(result.as_ref().into())
}
}
pub static RIP160_PRECOMPILED: RIP160Precompiled = RIP160Precompiled;
pub struct SHA256Precompiled;
impl Precompiled for SHA256Precompiled {
fn gas(&self, data: &[u8]) -> Gas {
Gas::from(60u64) + Gas::from(12u64) * gas_div_ceil(Gas::from(data.len()), Gas::from(32u64))
}
fn step(&self, data: &[u8]) -> Rc<Vec<u8>> {
let mut sha2 = Sha256::default();
sha2.input(data);
let fixed = sha2.fixed_result();
let mut result: [u8; 32] = [0u8; 32];
for i in 0..32 {
result[i] = fixed[i];
}
Rc::new(result.as_ref().into())
}
}
pub static SHA256_PRECOMPILED: SHA256Precompiled = SHA256Precompiled;
pub struct ECRECPrecompiled;
#[cfg(any(feature = "c-secp256k1", feature = "rust-secp256k1"))]
impl Precompiled for ECRECPrecompiled {
fn gas(&self, _: &[u8]) -> Gas {
Gas::from(3000u64)
}
fn step(&self, datao: &[u8]) -> Rc<Vec<u8>> {
let mut data = [0u8; 128];
let copy_bytes = min(datao.len(), 128);
data[..copy_bytes].clone_from_slice(&datao[..copy_bytes]);
match kececrec(&data) {
Ok(mut ret) => {
for i in &mut ret[..12] {
*i = 0
}
Rc::new(ret.as_ref().into())
}
Err(_) => Rc::new(Vec::new()),
}
}
}
#[cfg(all(not(feature = "c-secp256k1"), not(feature = "rust-secp256k1")))]
impl Precompiled for ECRECPrecompiled {
fn gas_and_step(&self, _: &[u8], _: Gas) -> Result<(Gas, Rc<Vec<u8>>), RuntimeError> {
use crate::errors::NotSupportedError;
Err(RuntimeError::NotSupported(NotSupportedError::PrecompiledNotSupported))
}
}
pub static ECREC_PRECOMPILED: ECRECPrecompiled = ECRECPrecompiled;
fn gas_div_ceil(a: Gas, b: Gas) -> Gas {
if a % b == Gas::zero() {
a / b
} else {
a / b + Gas::from(1u64)
}
}
#[cfg(feature = "c-secp256k1")]
fn kececrec(data: &[u8]) -> Result<[u8; 32], Error> {
let message = Message::from_slice(&data[0..32])?;
let recid_raw = match data[63] {
27 | 28 if data[32..63] == [0; 31] => data[63] - 27,
_ => return Err(Error::InvalidRecoveryId),
};
let recid = RecoveryId::from_i32(i32::from(recid_raw))?;
let sig = RecoverableSignature::from_compact(&SECP256K1, &data[64..128], recid)?;
let recovered = SECP256K1.recover(&message, &sig)?;
let key = recovered.serialize_vec(&SECP256K1, false);
let ret_generic = Keccak256::digest(&key[1..65]);
let mut ret = [0u8; 32];
for i in 0..32 {
ret[i] = ret_generic[i];
}
Ok(ret)
}
#[cfg(feature = "rust-secp256k1")]
fn kececrec(data: &[u8]) -> Result<[u8; 32], Error> {
let mut message_raw = [0u8; 32];
message_raw[..32].clone_from_slice(&data[..32]);
let message = Message::parse(&message_raw);
let recid_raw = match data[63] {
27 | 28 if data[32..63] == [0; 31] => data[63] - 27,
_ => return Err(Error::InvalidRecoveryId),
};
let recid = RecoveryId::parse(recid_raw)?;
let mut sig_raw = [0u8; 64];
sig_raw[..64].clone_from_slice(&data[64..128]);
let sig = Signature::parse(&sig_raw);
let recovered = recover(&message, &sig, &recid)?;
let key = recovered.serialize();
let ret_generic = Keccak256::digest(&key[1..65]);
let mut ret = [0u8; 32];
ret[..32].clone_from_slice(&ret_generic[..32]);
Ok(ret)
}