From 5e9690796b5759a750e64406d8d5115e4834899c Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Mon, 14 Dec 2020 00:00:51 -0500 Subject: [PATCH] Adds modexp, bn128, blake2f, and ed25519 verify precompiles (#239) * Adds modexp, bn128, blake2f, and ed25519 verify precompiles * Fixes tabs * newline * use direct from bytes methods * remove trailing whitespace --- Cargo.lock | 100 ++++++++++- frame/evm/Cargo.toml | 12 +- frame/evm/src/eip_152.rs | 75 ++++++++ frame/evm/src/lib.rs | 3 + frame/evm/src/precompiles.rs | 335 +++++++++++++++++++++++++++++++++++ 5 files changed, 517 insertions(+), 8 deletions(-) create mode 100644 frame/evm/src/eip_152.rs diff --git a/Cargo.lock b/Cargo.lock index 240cba19b..6a4b4de03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2576,6 +2576,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -3386,8 +3389,8 @@ dependencies = [ "approx", "generic-array 0.13.2", "matrixmultiply", - "num-complex", - "num-rational", + "num-complex 0.2.4", + "num-rational 0.2.4", "num-traits", "rand 0.7.3", "rand_distr", @@ -3447,6 +3450,20 @@ dependencies = [ "version_check", ] +[[package]] +name = "num" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +dependencies = [ + "num-bigint 0.3.1", + "num-complex 0.3.1", + "num-integer", + "num-iter", + "num-rational 0.3.2", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -3458,6 +3475,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -3468,6 +3496,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -3478,6 +3515,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.2.4" @@ -3485,7 +3533,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg", - "num-bigint", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint 0.3.1", "num-integer", "num-traits", ] @@ -3631,6 +3691,8 @@ dependencies = [ name = "pallet-evm" version = "2.0.0" dependencies = [ + "ed25519-dalek", + "ethereum-types", "evm", "evm-gasometer", "evm-runtime", @@ -3638,6 +3700,7 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", + "num", "pallet-balances", "pallet-timestamp", "parity-scale-codec", @@ -3650,6 +3713,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "substrate-bn", ] [[package]] @@ -4419,6 +4483,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand" version = "0.7.3" @@ -5006,8 +5079,8 @@ dependencies = [ "futures-timer 3.0.2", "log", "merlin", - "num-bigint", - "num-rational", + "num-bigint 0.2.6", + "num-rational 0.2.4", "num-traits", "parity-scale-codec", "parking_lot 0.10.2", @@ -5915,7 +5988,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb931b1367faadea6b1ab1c306a860ec17aaa5fa39f367d0c744e69d971a1fb2" dependencies = [ "approx", - "num-complex", + "num-complex 0.2.4", "num-traits", "paste", ] @@ -6744,6 +6817,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "substrate-bn" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acb439bec2318e98b83f1e12ddaaf2082d6fc29becc3117714ccb575fa343bc1" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.5.6", + "rustc-hex", +] + [[package]] name = "substrate-build-script-utils" version = "2.0.0" @@ -7698,7 +7784,7 @@ checksum = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" dependencies = [ "libc", "memory_units", - "num-rational", + "num-rational 0.2.4", "num-traits", "parity-wasm 0.41.0", "wasmi-validation", diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index dc5fc790f..297ed3002 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -33,8 +33,18 @@ sha3 = { version = "0.8", default-features = false } impl-trait-for-tuples = "0.1" ripemd160 = { version = "0.9", default-features = false } +num = { version = "0.3", features = ["alloc"], default-features = false, optional = true } +ethereum-types = { version = "0.9.2", default-features = false, optional = true } +bn = { package = "substrate-bn", version = "0.5", default-features = false, optional = true } +ed25519-dalek = { version = "1.0.0", features = ["alloc", "u64_backend"], default-features = false, optional = true } + + [features] -default = ["std"] +default = ["std", "modexp", "ed25519", "bn128", "blake2f"] +blake2f = [] +modexp = ["num"] +ed25519 = ["ed25519-dalek"] +bn128 = ["bn", "ethereum-types"] std = [ "serde", "codec/std", diff --git a/frame/evm/src/eip_152.rs b/frame/evm/src/eip_152.rs new file mode 100644 index 000000000..9501cf6cd --- /dev/null +++ b/frame/evm/src/eip_152.rs @@ -0,0 +1,75 @@ +/// The precomputed values for BLAKE2b [from the spec](https://tools.ietf.org/html/rfc7693#section-2.7) +/// There are 10 16-byte arrays - one for each round +/// the entries are calculated from the sigma constants. +const SIGMA: [[usize; 16]; 10] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], +]; + +/// IV is the initialization vector for BLAKE2b. See https://tools.ietf.org/html/rfc7693#section-2.6 +/// for details. +const IV: [u64; 8] = [ + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, +]; + +#[inline(always)] +/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 +fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { + v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); + v[d] = (v[d] ^ v[a]).rotate_right(32); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(24); + v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); + v[d] = (v[d] ^ v[a]).rotate_right(16); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(63); +} + +/// The Blake2 compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 +/// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final +/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first +/// parameter is modified by the function. +pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { + let mut v = [0u64; 16]; + v[..h.len()].copy_from_slice(h); // First half from state. + v[h.len()..].copy_from_slice(&IV); // Second half from IV. + + v[12] ^= t[0]; + v[13] ^= t[1]; + + if f { + v[14] = !v[14] // Invert all bits if the last-block-flag is set. + } + for i in 0..rounds { + // Message word selection permutation for this round. + let s = &SIGMA[i % 10]; + g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + + g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for i in 0..8 { + h[i] ^= v[i] ^ v[i + 8]; + } +} diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index 9aaa4c200..c8f2e2f1b 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -57,6 +57,9 @@ mod tests; pub mod runner; pub mod precompiles; +#[cfg(feature = "blake2f")] +pub mod eip_152; + pub use crate::precompiles::{Precompile, Precompiles}; pub use crate::runner::Runner; pub use fp_evm::{Account, Log, Vicinity, ExecutionInfo, CallInfo, CreateInfo}; diff --git a/frame/evm/src/precompiles.rs b/frame/evm/src/precompiles.rs index 531f08f97..e62a53ebf 100644 --- a/frame/evm/src/precompiles.rs +++ b/frame/evm/src/precompiles.rs @@ -17,11 +17,26 @@ //! Precompile implementations. +use sp_std::mem::size_of; +use sp_std::cmp::max; 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; +use sp_std::convert::TryFrom; + +#[cfg(feature = "ed25519")] +use ed25519_dalek::{PublicKey, Verifier, Signature}; + +#[cfg(feature = "modexp")] +use num::{BigUint, Zero, One}; + +#[cfg(feature = "blake2")] +use blake2_rfc::blake2b::Blake2b; + +#[cfg(feature = "bn128")] +use ethereum_types::{U256}; /// Custom precompiles to be used by EVM engine. pub trait Precompiles { @@ -165,3 +180,323 @@ impl Precompile for Sha256 { Ok((ExitSucceed::Returned, ret.to_vec(), cost)) } } + +#[cfg(feature = "modexp")] +pub struct Modexp; + +#[cfg(feature = "modexp")] +impl Precompile for Modexp { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?; + let mut buf = [0; 32]; + buf.copy_from_slice(&input[0..32]); + let mut len_bytes = [0u8; 8]; + len_bytes.copy_from_slice(&buf[24..]); + let base_len = u64::from_be_bytes(len_bytes) as usize; + + buf = [0; 32]; + buf.copy_from_slice(&input[32..64]); + len_bytes = [0u8; 8]; + len_bytes.copy_from_slice(&buf[24..]); + let exp_len = u64::from_be_bytes(len_bytes) as usize; + + buf = [0; 32]; + buf.copy_from_slice(&input[64..96]); + len_bytes = [0u8; 8]; + len_bytes.copy_from_slice(&buf[24..]); + let mod_len = u64::from_be_bytes(len_bytes) as usize; + + // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first. + let r = if base_len == 0 && mod_len == 0 { + BigUint::zero() + } else { + // read the numbers themselves. + let mut buf = Vec::with_capacity(max(mod_len, max(base_len, exp_len))); + buf.copy_from_slice(&input[0..base_len]); + let base = BigUint::from_bytes_be(&buf[..base_len]); + + buf = Vec::with_capacity(max(mod_len, max(base_len, exp_len))); + buf.copy_from_slice(&input[base_len..base_len + exp_len]); + let exponent = BigUint::from_bytes_be(&buf[..exp_len]); + + buf = Vec::with_capacity(max(mod_len, max(base_len, exp_len))); + buf.copy_from_slice(&input[(base_len + exp_len)..(base_len + exp_len + mod_len)]); + let modulus = BigUint::from_bytes_be(&buf[..mod_len]); + + if modulus.is_zero() || modulus.is_one() { + BigUint::zero() + } else { + base.modpow(&exponent, &modulus) + } + }; + + // write output to given memory, left padded and same length as the modulus. + let bytes = r.to_bytes_be(); + + // always true except in the case of zero-length modulus, which leads to + // output of length and value 1. + if bytes.len() <= mod_len { + let res_start = mod_len - bytes.len(); + let mut ret = Vec::with_capacity(bytes.len() - mod_len); + ret.copy_from_slice(&bytes[res_start..bytes.len()]); + Ok((ExitSucceed::Returned, ret.to_vec(), cost)) + } else { + Err(ExitError::Other("failed".into())) + } + } +} + +#[cfg(feature = "ed25519")] +pub struct Ed25519Verify; + +#[cfg(feature = "ed25519")] +impl Precompile for Ed25519Verify { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?; + + let len = min(input.len(), 128); + + let mut i = [0u8; 128]; + i[..len].copy_from_slice(&input[..len]); + + let mut buf = [0u8; 4]; + + let msg = &i[0..32]; + let pk = PublicKey::from_bytes(&i[32..64]) + .map_err(|_| ExitError::Other("Public key recover failed".into()))?; + let sig = Signature::try_from(&i[64..128]) + .map_err(|_| ExitError::Other("Signature recover failed".into()))?; + + // https://docs.rs/rust-crypto/0.2.36/crypto/ed25519/fn.verify.html + if pk.verify(msg, &sig).is_ok() { + buf[3] = 0u8; + } else { + buf[3] = 1u8; + }; + + Ok((ExitSucceed::Returned, buf.to_vec(), cost)) + } +} + +#[cfg(feature = "bn128")] +fn read_fr(input: &[u8], start_inx: usize) -> Result { + bn::Fr::from_slice(&input[start_inx..(start_inx + 32)]).map_err(|_| ExitError::Other("Invalid field element".into())) +} + +#[cfg(feature = "bn128")] +fn read_point(input: &[u8], start_inx: usize) -> Result { + use bn::{Fq, AffineG1, G1, Group}; + + let px = Fq::from_slice(&input[start_inx..(start_inx + 32)]).map_err(|_| ExitError::Other("Invalid point x coordinate".into()))?; + let py = Fq::from_slice(&input[(start_inx + 32)..(start_inx + 64)]).map_err(|_| ExitError::Other("Invalid point y coordinate".into()))?; + Ok( + if px == Fq::zero() && py == Fq::zero() { + G1::zero() + } else { + AffineG1::new(px, py).map_err(|_| ExitError::Other("Invalid curve point".into()))?.into() + } + ) +} + +/// The Bn128Add builtin +#[cfg(feature = "bn128")] +pub struct Bn128Add; + +/// The Bn128Mul builtin +#[cfg(feature = "bn128")] +pub struct Bn128Mul; + +/// The Bn128Pairing builtin +#[cfg(feature = "bn128")] +pub struct Bn128Pairing; + +/// The bn128 addition arithmetic precompile. +#[cfg(feature = "bn128")] +impl Precompile for Bn128Add { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?; + + use bn::AffineG1; + + let p1 = read_point(input, 0)?; + let p2 = read_point(input, 64)?; + + let mut buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { + // point not at infinity + sum.x().to_big_endian(&mut buf[0..32]).map_err(|_| ExitError::Other("Cannot fail since 0..32 is 32-byte length".into()))?; + sum.y().to_big_endian(&mut buf[32..64]).map_err(|_| ExitError::Other("Cannot fail since 32..64 is 32-byte length".into()))?; + } + + Ok((ExitSucceed::Returned, buf.to_vec(), cost)) + } +} + +/// The bn128 multiplication arithmetic precompile. +#[cfg(feature = "bn128")] +impl Precompile for Bn128Mul { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + use bn::AffineG1; + + let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?; + + let p = read_point(input, 0)?; + let fr = read_fr(input, 64)?; + + let mut buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p * fr) { + // point not at infinity + sum.x().to_big_endian(&mut buf[0..32]).map_err(|_| ExitError::Other("Cannot fail since 0..32 is 32-byte length".into()))?; + sum.y().to_big_endian(&mut buf[32..64]).map_err(|_| ExitError::Other("Cannot fail since 32..64 is 32-byte length".into()))?; + } + + Ok((ExitSucceed::Returned, buf.to_vec(), cost)) + } +} + +/// The bn128 pairing precompile. +#[cfg(feature = "bn128")] +impl Precompile for Bn128Pairing { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + use bn::{AffineG1, AffineG2, Fq, Fq2, pairing_batch, G1, G2, Gt, Group}; + + let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?; + + let ret_val = if input.is_empty() { + U256::one() + } else { + // (a, b_a, b_b - each 64-byte affine coordinates) + let elements = input.len() / 192; + let mut vals = Vec::new(); + for idx in 0..elements { + let a_x = Fq::from_slice(&input[idx*192..idx*192+32]) + .map_err(|_| ExitError::Other("Invalid a argument x coordinate".into()))?; + + let a_y = Fq::from_slice(&input[idx*192+32..idx*192+64]) + .map_err(|_| ExitError::Other("Invalid a argument y coordinate".into()))?; + + let b_a_y = Fq::from_slice(&input[idx*192+64..idx*192+96]) + .map_err(|_| ExitError::Other("Invalid b argument imaginary coeff x coordinate".into()))?; + + let b_a_x = Fq::from_slice(&input[idx*192+96..idx*192+128]) + .map_err(|_| ExitError::Other("Invalid b argument imaginary coeff y coordinate".into()))?; + + let b_b_y = Fq::from_slice(&input[idx*192+128..idx*192+160]) + .map_err(|_| ExitError::Other("Invalid b argument real coeff x coordinate".into()))?; + + let b_b_x = Fq::from_slice(&input[idx*192+160..idx*192+192]) + .map_err(|_| ExitError::Other("Invalid b argument real coeff y coordinate".into()))?; + + let b_a = Fq2::new(b_a_x, b_a_y); + let b_b = Fq2::new(b_b_x, b_b_y); + let b = if b_a.is_zero() && b_b.is_zero() { + G2::zero() + } else { + G2::from(AffineG2::new(b_a, b_b).map_err(|_| ExitError::Other("Invalid b argument - not on curve".into()))?) + }; + let a = if a_x.is_zero() && a_y.is_zero() { + G1::zero() + } else { + G1::from(AffineG1::new(a_x, a_y).map_err(|_| ExitError::Other("Invalid a argument - not on curve".into()))?) + }; + vals.push((a, b)); + }; + + let mul = pairing_batch(&vals); + + if mul == Gt::one() { + U256::one() + } else { + U256::zero() + } + }; + + let mut buf = [0u8; 32]; + ret_val.to_big_endian(&mut buf); + + Ok((ExitSucceed::Returned, buf.to_vec(), cost)) + } +} + +#[cfg(feature = "blake2f")] +pub struct Blake2F; + +#[cfg(feature = "blake2f")] +impl Precompile for Blake2F { + /// Format of `input`: + /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?; + const BLAKE2_F_ARG_LEN: usize = 213; + + if input.len() != BLAKE2_F_ARG_LEN { + return Err(ExitError::Other("input length for Blake2 F precompile should be exactly 213 bytes".into())); + } + + let mut rounds_buf: [u8; 4] = [0; 4]; + rounds_buf.copy_from_slice(&input[0..4]); + let rounds: u32 = u32::from_le_bytes(rounds_buf); + + let mut h_buf: [u8; 64] = [0; 64]; + h_buf.copy_from_slice(&input[4..48]); + let mut h = [0u64; 8]; + let mut ctr = 0; + for state_word in &mut h { + let mut temp: [u8; 8] = Default::default(); + temp.copy_from_slice(&h_buf[(ctr + 8)..(ctr + 1) * 8]); + *state_word = u64::from_le_bytes(temp).into(); + ctr += 1; + } + + let mut m_buf: [u8; 128] = [0; 128]; + m_buf.copy_from_slice(&input[68..196]); + let mut m = [0u64; 16]; + ctr = 0; + for msg_word in &mut m { + let mut temp: [u8; 8] = Default::default(); + temp.copy_from_slice(&m_buf[(ctr + 8)..(ctr + 1) * 8]); + *msg_word = u64::from_le_bytes(temp).into(); + ctr += 1; + } + + + let mut t_0_buf: [u8; 8] = [0; 8]; + t_0_buf.copy_from_slice(&input[196..204]); + let t_0 = u64::from_le_bytes(t_0_buf); + + let mut t_1_buf: [u8; 8] = [0; 8]; + t_1_buf.copy_from_slice(&input[204..212]); + let t_1 = u64::from_le_bytes(t_1_buf); + + let f = if input[212] == 1 { true } else if input[212] == 0 { false } else { + return Err(ExitError::Other("incorrect final block indicator flag".into())) + }; + + crate::eip_152::compress(&mut h, m, [t_0.into(), t_1.into()], f, rounds as usize); + + let mut output_buf = [0u8; 8 * size_of::()]; + for (i, state_word) in h.iter().enumerate() { + output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes()); + } + + Ok((ExitSucceed::Returned, output_buf.to_vec(), cost)) + } +}