Skip to content

Commit

Permalink
Merge pull request #1408 from oasisprotocol/ptrus/feature/sha512-256
Browse files Browse the repository at this point in the history
evm: add sha512-256 precompile
  • Loading branch information
ptrus authored Jul 17, 2023
2 parents db7d146 + 733096f commit dafc660
Show file tree
Hide file tree
Showing 8 changed files with 408 additions and 32 deletions.
254 changes: 247 additions & 7 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion runtime-sdk/modules/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ hmac = "0.11.0"
rand_core = { version = "0.6.4", default-features = false }

# Ethereum.
ethabi = { version = "18.0.0", default-features = false, features = ["std"]}
ethabi = { version = "18.0.0", default-features = false, features = ["std"] }
ethereum = "0.14"
evm = "0.37.0"
fixed-hash = "0.8.0"
Expand All @@ -36,5 +36,10 @@ rlp = "0.5.2"
uint = "0.9.1"

[dev-dependencies]
criterion = "0.5.1"
oasis-runtime-sdk = { path = "../..", features = ["test"] }
rand = "0.7.3"

[[bench]]
name = "criterion_benchmark"
harness = false
75 changes: 75 additions & 0 deletions runtime-sdk/modules/evm/benches/criterion_benchmark.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion runtime-sdk/modules/evm/src/precompile/confidential.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Implements the confidential precompiles.
use std::{collections::HashMap, convert::TryInto};

use ethabi::{ParamType, Token};
Expand Down Expand Up @@ -400,7 +401,7 @@ mod test {

use oasis_runtime_sdk::crypto::signature::{self, SignatureType};

use crate::precompile::{test::*, PrecompileResult};
use crate::precompile::{testing::*, PrecompileResult};

#[test]
fn test_x25519_derive() {
Expand Down
51 changes: 29 additions & 22 deletions runtime-sdk/modules/evm/src/precompile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ use primitive_types::H160;
use crate::{backend::EVMBackendExt, Config};

mod confidential;
mod sha512;
mod standard;

#[cfg(test)]
mod test;
pub mod testing;

// Some types matching evm::executor::stack.
type PrecompileResult = Result<PrecompileOutput, PrecompileFailure>;
Expand Down Expand Up @@ -80,33 +80,40 @@ impl<Cfg: Config, B: EVMBackendExt> PrecompileSet for Precompiles<'_, Cfg, B> {
if !self.is_precompile(address) {
return None;
}
Some(match (address[0], address[19]) {
(0, 1) => standard::call_ecrecover(handle),
(0, 2) => standard::call_sha256(handle),
(0, 3) => standard::call_ripemd160(handle),
(0, 4) => standard::call_datacopy(handle),
(0, 5) => standard::call_bigmodexp(handle),
(1, 1) => confidential::call_random_bytes(handle, self.backend),
(1, 2) => confidential::call_x25519_derive(handle),
(1, 3) => confidential::call_deoxysii_seal(handle),
(1, 4) => confidential::call_deoxysii_open(handle),
(1, 5) => confidential::call_keypair_generate(handle),
(1, 6) => confidential::call_sign(handle),
(1, 7) => confidential::call_verify(handle),
(1, 8) => confidential::call_curve25519_compute_public(handle),
Some(match (address[0], address[18], address[19]) {
// Standard precompiles.
(0, 0, 1) => standard::call_ecrecover(handle),
(0, 0, 2) => standard::call_sha256(handle),
(0, 0, 3) => standard::call_ripemd160(handle),
(0, 0, 4) => standard::call_datacopy(handle),
(0, 0, 5) => standard::call_bigmodexp(handle),
// Confidential precompiles.
(1, 0, 1) => confidential::call_random_bytes(handle, self.backend),
(1, 0, 2) => confidential::call_x25519_derive(handle),
(1, 0, 3) => confidential::call_deoxysii_seal(handle),
(1, 0, 4) => confidential::call_deoxysii_open(handle),
(1, 0, 5) => confidential::call_keypair_generate(handle),
(1, 0, 6) => confidential::call_sign(handle),
(1, 0, 7) => confidential::call_verify(handle),
(1, 0, 8) => confidential::call_curve25519_compute_public(handle),
// Other precompiles.
(1, 1, 1) => sha512::call_sha512_256(handle),
_ => return Cfg::additional_precompiles().and_then(|pc| pc.execute(handle)),
})
}

fn is_precompile(&self, address: H160) -> bool {
// All Ethereum precompiles are zero except for the last byte, which is no more than five.
// Otherwise, when confidentiality is enabled, Oasis precompiles start with one and have a last byte of no more than four.
let addr_bytes = address.as_bytes();
let (first, last) = (address[0], addr_bytes[19]);
(address[1..19].iter().all(|b| *b == 0)
let (a0, a18, a19) = (address[0], addr_bytes[18], addr_bytes[19]);
(address[1..18].iter().all(|b| *b == 0)
&& matches!(
(first, last, Cfg::CONFIDENTIAL),
(0, 1..=5, _) | (1, 1..=8, true)
(a0, a18, a19, Cfg::CONFIDENTIAL),
// Standard.
(0, 0, 1..=5, _) |
// Confidential.
(1, 0, 1..=8, true) |
// Other.
(1, 1, 1, _)
))
|| Cfg::additional_precompiles()
.map(|pc| pc.is_precompile(address))
Expand Down
46 changes: 46 additions & 0 deletions runtime-sdk/modules/evm/src/precompile/sha512.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Implements sha512_256 precompile.
use evm::{
executor::stack::{PrecompileHandle, PrecompileOutput},
ExitSucceed,
};
use ripemd160::Digest as _;
use sha2::Sha512Trunc256;

use super::{record_linear_cost, PrecompileResult};

pub(super) fn call_sha512_256(handle: &mut impl PrecompileHandle) -> PrecompileResult {
// Costs were computed by benchmarking and comparing to SHA256 and using the SHA256 costs (defined by EVM spec).
// See benches/criterion_benchmark.rs for the benchmarks.
record_linear_cost(handle, handle.input().len() as u64, 115, 13)?;

let mut hasher = Sha512Trunc256::new();
hasher.update(handle.input());
let digest = hasher.finalize();

Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: digest.to_vec(),
})
}

#[cfg(test)]
mod test {
use super::super::testing::*;

#[test]
fn test_sha512_256() {
let input = "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02";
let ret = call_contract(
H160([
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0x01,
]),
&hex::decode(input).unwrap(),
3000,
)
.unwrap();
assert_eq!(
hex::encode(ret.unwrap().output),
"41f7883fc8df1d31b1b1f7c0379f7b5a990d457347d997fdd76a2f4bb5812342"
);
}
}
3 changes: 2 additions & 1 deletion runtime-sdk/modules/evm/src/precompile/standard.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Implements the standard precompiles as defined in the EVM specification.
use std::{
cmp::{max, min, Ordering},
convert::TryFrom,
Expand Down Expand Up @@ -295,7 +296,7 @@ fn calculate_modexp_gas_cost(

#[cfg(test)]
mod test {
use super::{super::test::*, *};
use super::{super::testing::*, *};
// The following test data is from "go-ethereum/core/vm/contracts_test.go"

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ impl<'a> PrecompileHandle for MockPrecompileHandle<'a> {
}
}

#[doc(hidden)]
pub fn call_contract(address: H160, input: &[u8], gas_limit: u64) -> Option<PrecompileResult> {
let context: Context = Context {
address: Default::default(),
Expand Down

0 comments on commit dafc660

Please sign in to comment.