Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
pallet-evm: add builtin support for the four basic Ethereum precompil…
Browse files Browse the repository at this point in the history
…es (#6743)

* pallet-evm: add builtin support for the four basic Ethereum precompiles

* linear_cost -> ensure_linear_cost to directly return OutOfGas error
  • Loading branch information
sorpaas committed Jul 30, 2020
1 parent 9d11944 commit 5947d43
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ rlp = { version = "0.4", default-features = false }
evm = { version = "0.17", default-features = false }
sha3 = { version = "0.8", default-features = false }
impl-trait-for-tuples = "0.1"
ripemd160 = { version = "0.9", default-features = false }

[features]
default = ["std"]
Expand All @@ -45,4 +46,5 @@ std = [
"primitive-types/std",
"evm/std",
"pallet-timestamp/std",
"ripemd160/std",
]
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
#![cfg_attr(not(feature = "std"), no_std)]

mod backend;
mod precompiles;
mod tests;
pub mod precompiles;

pub use crate::precompiles::{Precompile, Precompiles};
pub use crate::backend::{Account, Log, Vicinity, Backend};
Expand Down
99 changes: 98 additions & 1 deletion src/precompiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use sp_std::vec::Vec;
//! Builtin precompiles.

use sp_std::{cmp::min, vec::Vec};
use sp_core::H160;
use evm::{ExitError, ExitSucceed};
use ripemd160::Digest;
use impl_trait_for_tuples::impl_for_tuples;

/// Custom precompiles to be used by EVM engine.
Expand Down Expand Up @@ -67,3 +70,97 @@ impl Precompiles for Tuple {
None
}
}

/// Linear gas cost
fn ensure_linear_cost(
target_gas: Option<usize>,
len: usize,
base: usize,
word: usize
) -> Result<usize, ExitError> {
let cost = base.checked_add(
word.checked_mul(len.saturating_add(31) / 32).ok_or(ExitError::OutOfGas)?
).ok_or(ExitError::OutOfGas)?;

if let Some(target_gas) = target_gas {
if cost > target_gas {
return Err(ExitError::OutOfGas)
}
}

Ok(cost)
}

/// The identity precompile.
pub struct Identity;

impl Precompile for Identity {
fn execute(
input: &[u8],
target_gas: Option<usize>,
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?;

Ok((ExitSucceed::Returned, input.to_vec(), cost))
}
}

/// The ecrecover precompile.
pub struct ECRecover;

impl Precompile for ECRecover {
fn execute(
i: &[u8],
target_gas: Option<usize>,
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
let cost = ensure_linear_cost(target_gas, i.len(), 3000, 0)?;

let mut input = [0u8; 128];
input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]);

let mut msg = [0u8; 32];
let mut sig = [0u8; 65];

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]);
sig[64] = input[63];

let pubkey = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg)
.map_err(|_| ExitError::Other("Public key recover failed"))?;
let mut address = sp_io::hashing::keccak_256(&pubkey);
address[0..12].copy_from_slice(&[0u8; 12]);

Ok((ExitSucceed::Returned, address.to_vec(), cost))
}
}

/// The ripemd precompile.
pub struct Ripemd160;

impl Precompile for Ripemd160 {
fn execute(
input: &[u8],
target_gas: Option<usize>,
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
let cost = ensure_linear_cost(target_gas, input.len(), 600, 120)?;

let ret = ripemd160::Ripemd160::digest(input).to_vec();
Ok((ExitSucceed::Returned, ret, cost))
}
}

/// The sha256 precompile.
pub struct Sha256;

impl Precompile for Sha256 {
fn execute(
input: &[u8],
target_gas: Option<usize>,
) -> core::result::Result<(ExitSucceed, Vec<u8>, usize), ExitError> {
let cost = ensure_linear_cost(target_gas, input.len(), 60, 12)?;

let ret = sp_io::hashing::sha2_256(input);
Ok((ExitSucceed::Returned, ret.to_vec(), cost))
}
}

0 comments on commit 5947d43

Please sign in to comment.