From d8d7abc848e7e4806eccb3ee14b6093947360aee Mon Sep 17 00:00:00 2001 From: David Date: Tue, 10 Sep 2019 00:26:45 +0200 Subject: [PATCH] Add blake2_f precompile (#11017) * feat: implement eip1108 * doc nit: price per point pair Co-Authored-By: David * fix: test-build * fix: update chain specs * fix: basic_authority.json chain spec * fix: change `eip_transition == 0x7fffffffffffff` * Merge na-eip1108 * Pre-compile plumbing * Use type alias instead of struct * Extract the data from incoming EVM call and forward it to blake2_f * Fetch parity-common from git for now * Fix broken cost impl Add remaining test vectors * cleanup * Trailing comma in json spec * Update ethcore/builtin/src/lib.rs Co-Authored-By: Andronik Ordian * Update ethcore/builtin/src/lib.rs Co-Authored-By: Andronik Ordian * Rename pricer to be Blake2-specific Return a cost of u32::MAX if conversion fails Fix docs and todos * Fix error handling in cost() * fix chainspec * EIP-152 crate * Update ethcore/builtin/src/lib.rs Co-Authored-By: Seun LanLege * Address grumbles * Update ethcore/builtin/src/lib.rs Co-Authored-By: Niklas Adolfsson * Switch tests to use hex! * remove parity-crypto from git * Sort out the SIGMA * Prefer arrays to vecs * Update util/EIP-152/src/lib.rs Co-Authored-By: Niklas Adolfsson * Update json/src/spec/builtin.rs Co-Authored-By: Andronik Ordian * Update json/src/spec/builtin.rs Co-Authored-By: Andronik Ordian * Review feedback * Do not change chainspecs yet * Do not assume endianess: precompile output is LE --- Cargo.lock | 17 +- ethcore/builtin/Cargo.toml | 4 +- ethcore/builtin/src/lib.rs | 396 +++++++++++++++++++++++++++---------- json/src/spec/builtin.rs | 18 ++ util/EIP-152/Cargo.toml | 14 ++ util/EIP-152/src/lib.rs | 192 ++++++++++++++++++ 6 files changed, 532 insertions(+), 109 deletions(-) create mode 100644 util/EIP-152/Cargo.toml create mode 100644 util/EIP-152/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 9253bef72..ee93d28f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -789,6 +789,13 @@ name = "edit-distance" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "eip-152" +version = "0.1.0" +dependencies = [ + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "eip-712" version = "0.1.0" @@ -1110,15 +1117,17 @@ name = "ethcore-builtin" version = "0.1.0" dependencies = [ "bn 0.4.4 (git+https://github.com/paritytech/bn)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "eip-152 0.1.0", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "ethkey 0.3.0", + "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-crypto 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1592,7 +1601,7 @@ dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1911,7 +1920,7 @@ dependencies = [ [[package]] name = "hex-literal" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hex-literal-impl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5276,7 +5285,7 @@ dependencies = [ "checksum hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8e04cb7a5051270ef3fa79f8c7604d581ecfa73d520e74f554e45541c4b5881a" "checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" -"checksum hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3da68162fdd2147e66682e78e729bd77f93b4c99656db058c5782d8c6b6225a" +"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" "checksum hex-literal-impl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06095d08c7c05760f11a071b3e1d4c5b723761c01bd8d7201c30a9536668a612" "checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" "checksum home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "80dff82fb58cfbbc617fb9a9184b010be0529201553cda50ad04372bc2333aff" diff --git a/ethcore/builtin/Cargo.toml b/ethcore/builtin/Cargo.toml index 792efaed1..dcf0ab0c9 100644 --- a/ethcore/builtin/Cargo.toml +++ b/ethcore/builtin/Cargo.toml @@ -14,7 +14,9 @@ keccak-hash = "0.2.0" log = "0.4" num = { version = "0.1", default-features = false, features = ["bigint"] } parity-bytes = "0.1" +eip-152 = { path = "../../util/EIP-152" } parity-crypto = "0.4.0" +byteorder = "1.3.2" [dev-dependencies] -rustc-hex = "1.0" +hex-literal = "0.2.1" diff --git a/ethcore/builtin/src/lib.rs b/ethcore/builtin/src/lib.rs index 8fdb4cb35..71504aee4 100644 --- a/ethcore/builtin/src/lib.rs +++ b/ethcore/builtin/src/lib.rs @@ -18,10 +18,12 @@ use std::{ cmp::{max, min}, - io::{self, Read}, + io::{self, Read, Cursor}, + mem::size_of, }; use bn; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use ethereum_types::{H256, U256}; use ethjson; use ethkey::{Signature, recover as ec_recover}; @@ -30,6 +32,7 @@ use log::{warn, trace}; use num::{BigUint, Zero, One}; use parity_bytes::BytesRef; use parity_crypto::digest; +use eip_152::compress; /// Native implementation of a built-in contract. trait Implementation: Send + Sync { @@ -44,6 +47,22 @@ trait Pricer: Send + Sync { fn cost(&self, input: &[u8], at: u64) -> U256; } +/// Pricing for the Blake2 compression function (aka "F"). +/// Computes the price as a fixed cost per round where the number of rounds is part of the input +/// byte slice. +pub type Blake2FPricer = u64; + +impl Pricer for Blake2FPricer { + fn cost(&self, input: &[u8], _at: u64) -> U256 { + use std::convert::TryInto; + let (rounds_bytes, _) = input.split_at(std::mem::size_of::()); + // Returning zero if the conversion fails is fine because `execute()` will check the length + // and bail with the appropriate error. + let rounds = u32::from_be_bytes(rounds_bytes.try_into().unwrap_or([0u8; 4])); + U256::from(*self as u128 * rounds as u128) + } +} + /// A linear pricing model. This computes a price using a base cost and a cost per-word. struct Linear { base: usize, @@ -199,6 +218,9 @@ impl Builtin { impl From for Builtin { fn from(b: ethjson::spec::Builtin) -> Self { let pricer: Box = match b.pricing { + ethjson::spec::Pricing::Blake2F(cost_per_round) => { + Box::new(cost_per_round) + }, ethjson::spec::Pricing::Linear(linear) => { Box::new(Linear { base: linear.base, @@ -208,7 +230,7 @@ impl From for Builtin { ethjson::spec::Pricing::Modexp(exp) => { Box::new(ModexpPricer { divisor: if exp.divisor == 0 { - warn!("Zero modexp divisor specified. Falling back to default."); + warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default."); 10 } else { exp.divisor @@ -256,6 +278,7 @@ fn ethereum_builtin(name: &str) -> Box { "alt_bn128_add" => Box::new(Bn128Add) as Box, "alt_bn128_mul" => Box::new(Bn128Mul) as Box, "alt_bn128_pairing" => Box::new(Bn128Pairing) as Box, + "blake2_f" => Box::new(Blake2F) as Box, _ => panic!("invalid builtin name: {}", name), } } @@ -267,6 +290,10 @@ fn ethereum_builtin(name: &str) -> Box { // - sha256 // - ripemd160 // - modexp (EIP198) +// - alt_bn128_add +// - alt_bn128_mul +// - alt_bn128_pairing +// - blake2_f (The Blake2 compression function F, EIP-152) #[derive(Debug)] struct Identity; @@ -292,6 +319,9 @@ struct Bn128Mul; #[derive(Debug)] struct Bn128Pairing; +#[derive(Debug)] +struct Blake2F; + impl Implementation for Identity { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { output.write(0, input); @@ -337,6 +367,60 @@ impl Implementation for Sha256 { } } +impl Implementation 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(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + const BLAKE2_F_ARG_LEN: usize = 213; + const PROOF: &str = "Checked the length of the input above; qed"; + + if input.len() != BLAKE2_F_ARG_LEN { + trace!(target: "builtin", "input length for Blake2 F precompile should be exactly 213 bytes, was {}", input.len()); + return Err("input length for Blake2 F precompile should be exactly 213 bytes") + } + + let mut cursor = Cursor::new(input); + let rounds = cursor.read_u32::().expect(PROOF); + + // state vector, h + let mut h = [0u64; 8]; + for state_word in h.iter_mut() { + *state_word = cursor.read_u64::().expect(PROOF); + } + + // message block vector, m + let mut m = [0u64; 16]; + for msg_word in m.iter_mut() { + *msg_word = cursor.read_u64::().expect(PROOF); + } + + // 2w-bit offset counter, t + let t = [ + cursor.read_u64::().expect(PROOF), + cursor.read_u64::().expect(PROOF), + ]; + + // final block indicator flag, "f" + let f = match input.last() { + Some(1) => true, + Some(0) => false, + _ => { + trace!(target: "builtin", "incorrect final block indicator flag, was: {:?}", input.last()); + return Err("incorrect final block indicator flag") + } + }; + + compress(&mut h, m, t, 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()); + } + output.write(0, &output_buf[..]); + Ok(()) + } +} + impl Implementation for Ripemd160 { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { let hash = digest::ripemd160(input); @@ -526,7 +610,7 @@ impl Implementation for Bn128Pairing { } if let Err(err) = self.execute_with_error(input, output) { - trace!("Pairining error: {:?}", err); + trace!(target: "builtin", "Pairing error: {:?}", err); return Err(err) } Ok(()) @@ -599,9 +683,117 @@ mod tests { use ethjson::uint::Uint; use num::{BigUint, Zero, One}; use parity_bytes::BytesRef; - use rustc_hex::FromHex; + use hex_literal::hex; use super::{Builtin, Linear, ethereum_builtin, Pricer, ModexpPricer, modexp as me}; + #[test] + fn blake2f_cost() { + let f = Builtin { + pricer: Box::new(123), + native: ethereum_builtin("blake2_f"), + activate_at: 0, + }; + // 5 rounds + let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let mut output = [0u8; 64]; + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).unwrap(); + + assert_eq!(f.cost(&input[..], 0), U256::from(123*5)); + } + + #[test] + fn blake2_f_is_err_on_invalid_length() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 1 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-1 + let input = hex!("00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let mut out = [0u8; 64]; + + let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes"); + } + + #[test] + fn blake2_f_is_err_on_invalid_length_2() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 2 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-2 + let input = hex!("000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let mut out = [0u8; 64]; + + let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes"); + } + + #[test] + fn blake2_f_is_err_on_bad_finalization_flag() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 3 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-3 + let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002"); + let mut out = [0u8; 64]; + + let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "incorrect final block indicator flag"); + } + + #[test] + fn blake2_f_zero_rounds_is_ok_test_vector_4() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 4 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-4 + let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); + let mut output = [0u8; 64]; + blake2.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).unwrap(); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn blake2_f_test_vector_5() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 5 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-5 + let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); + let mut out = [0u8; 64]; + blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); + assert_eq!(&out[..], &expected[..]); + } + + #[test] + fn blake2_f_test_vector_6() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 6 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-6 + let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000"); + let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); + let mut out = [0u8; 64]; + blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); + assert_eq!(&out[..], &expected[..]); + } + + #[test] + fn blake2_f_test_vector_7() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 7 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-7 + let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); + let mut out = [0u8; 64]; + blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); + assert_eq!(&out[..], &expected[..]); + } + + #[ignore] + #[test] + fn blake2_f_test_vector_8() { + let blake2 = ethereum_builtin("blake2_f"); + // Test vector 8 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-8 + // Note this test is slow, 4294967295/0xffffffff rounds take a while. + let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615"); + let mut out = [0u8; 64]; + blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); + assert_eq!(&out[..], &expected[..]); + } + #[test] fn modexp_func() { // n^0 % m == 1 @@ -663,19 +855,19 @@ mod tests { let mut o = [255u8; 32]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]); + assert_eq!(&o[..], hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); let mut o8 = [255u8; 8]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); - assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]); + assert_eq!(&o8[..], hex!("e3b0c44298fc1c14")); let mut o34 = [255u8; 34]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); - assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]); + assert_eq!(&o34[..], &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff")[..]); let mut ov = vec![]; f.execute(&i[..], &mut BytesRef::Flexible(&mut ov)).expect("Builtin should not fail"); - assert_eq!(&ov[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]); + assert_eq!(&ov[..], &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")[..]); } #[test] @@ -686,59 +878,59 @@ mod tests { let mut o = [255u8; 32]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]); + assert_eq!(&o[..], &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")[..]); let mut o8 = [255u8; 8]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); - assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]); + assert_eq!(&o8[..], &hex!("0000000000000000")[..]); let mut o34 = [255u8; 34]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); - assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]); + assert_eq!(&o34[..], &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff")[..]); } #[test] fn ecrecover() { let f = ethereum_builtin("ecrecover"); - let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); + let i = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); let mut o = [255u8; 32]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]); + assert_eq!(&o[..], &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")[..]); let mut o8 = [255u8; 8]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); - assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]); + assert_eq!(&o8[..], &hex!("0000000000000000")[..]); let mut o34 = [255u8; 34]; f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); - assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]); + assert_eq!(&o34[..], &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff")[..]); - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); let mut o = [255u8; 32]; f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); + assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000"); let mut o = [255u8; 32]; f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); + assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap(); + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b"); let mut o = [255u8; 32]; f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); + assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap(); + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b"); let mut o = [255u8; 32]; f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); + assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap(); + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); let mut o = [255u8; 32]; f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); + assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); // TODO: Should this (corrupted version of the above) fail rather than returning some address? /* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); @@ -758,21 +950,21 @@ mod tests { // test for potential gas cost multiplication overflow { - let input = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3").unwrap(); + let input = hex!("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3"); let expected_cost = U256::max_value(); assert_eq!(f.cost(&input[..], 0), expected_cost.into()); } // test for potential exp len overflow { - let input = FromHex::from_hex("\ - 00000000000000000000000000000000000000000000000000000000000000ff\ - 2a1e530000000000000000000000000000000000000000000000000000000000\ + let input = hex!(" + 00000000000000000000000000000000000000000000000000000000000000ff + 2a1e530000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); + ); let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); let expected_cost = U256::max_value(); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should fail"); @@ -782,17 +974,17 @@ mod tests { // fermat's little theorem example. { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 03\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ + let input = hex!(" + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000020 + 03 + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" - ).unwrap(); + ); let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); + let expected = hex!("0000000000000000000000000000000000000000000000000000000000000001"); let expected_cost = 13056; f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); @@ -802,16 +994,16 @@ mod tests { // second example from EIP: zero base. { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ + let input = hex!(" + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000020 + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" - ).unwrap(); + ); let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); let expected_cost = 13056; f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); @@ -821,17 +1013,17 @@ mod tests { // another example from EIP: zero-padding { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000002\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 03\ - ffff\ + let input = hex!(" + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000002 + 0000000000000000000000000000000000000000000000000000000000000020 + 03 + ffff 80" - ).unwrap(); + ); let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab").unwrap(); + let expected = hex!("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); let expected_cost = 768; f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); @@ -841,13 +1033,13 @@ mod tests { // zero-length modulus. { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000002\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 03\ + let input = hex!(" + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000002 + 0000000000000000000000000000000000000000000000000000000000000000 + 03 ffff" - ).unwrap(); + ); let mut output = vec![]; let expected_cost = 0; @@ -869,21 +1061,21 @@ mod tests { // zero-points additions { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ + let input = hex!(" + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); + ); let mut output = vec![0u8; 64]; - let expected = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ + let expected = hex!(" + 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); + ); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); + assert_eq!(output, &expected[..]); } // no input, should not fail @@ -892,23 +1084,23 @@ mod tests { let input = BytesRef::Fixed(&mut empty); let mut output = vec![0u8; 64]; - let expected = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ + let expected = hex!(" + 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); + ); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); + assert_eq!(output, &expected[..]); } // should fail - point not on curve { - let input = FromHex::from_hex("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ + let input = hex!(" + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111" - ).unwrap(); + ); let mut output = vec![0u8; 64]; @@ -928,29 +1120,29 @@ mod tests { // zero-point multiplication { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ + let input = hex!(" + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 0200000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); + ); let mut output = vec![0u8; 64]; - let expected = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ + let expected = hex!(" + 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); + ); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); + assert_eq!(output, &expected[..]); } // should fail - point not on curve { - let input = FromHex::from_hex("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ + let input = hex!(" + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 0f00000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); + ); let mut output = vec![0u8; 64]; @@ -991,16 +1183,12 @@ mod tests { } } - fn bytes(s: &'static str) -> Vec { - FromHex::from_hex(s).expect("static str should contain valid hex bytes") - } - #[test] fn bn128_pairing_empty() { // should not fail, because empty input is a valid input of 0 elements empty_test( builtin_pairing(), - bytes("0000000000000000000000000000000000000000000000000000000000000001"), + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), ); } @@ -1009,12 +1197,12 @@ mod tests { // should fail - point not on curve error_test( builtin_pairing(), - &bytes("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ + &hex!(" + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111" ), Some("not on curve"), @@ -1026,9 +1214,9 @@ mod tests { // should fail - input length is invalid error_test( builtin_pairing(), - &bytes("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ + &hex!(" + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111" ), Some("Invalid input length"), diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs index df0414d0f..20ebbc3c6 100644 --- a/json/src/spec/builtin.rs +++ b/json/src/spec/builtin.rs @@ -18,6 +18,9 @@ use uint::Uint; +/// Price per round of Blake2 compression. +pub type Blake2F = u64; + /// Linear pricing. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] @@ -65,6 +68,8 @@ pub struct AltBn128Pairing { #[serde(deny_unknown_fields)] #[serde(rename_all = "snake_case")] pub enum Pricing { + /// Pricing for Blake2 compression function: each call costs the same amount per round. + Blake2F(Blake2F), /// Linear pricing. Linear(Linear), /// Pricing for modular exponentiation. @@ -107,6 +112,19 @@ mod tests { assert!(deserialized.activate_at.is_none()); } + #[test] + fn deserialization_blake2_f_builtin() { + let s = r#"{ + "name": "blake2_f", + "activate_at": "0xffffff", + "pricing": { "blake2_f": 123 } + }"#; + let deserialized: Builtin = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.name, "blake2_f"); + assert_eq!(deserialized.pricing, Pricing::Blake2F(123)); + assert!(deserialized.activate_at.is_some()); + } + #[test] fn activate_at() { let s = r#"{ diff --git a/util/EIP-152/Cargo.toml b/util/EIP-152/Cargo.toml new file mode 100644 index 000000000..fe65d0110 --- /dev/null +++ b/util/EIP-152/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "eip-152" +version = "0.1.0" +authors = ["Parity Technologies "] +repository = "https://github.com/paritytech/parity-ethereum" +documentation = "https://docs.rs/eip-152" +readme = "README.md" +description = "eip-512 blake2 F compression function" +keywords = ["eip-152", "eip152", "eip"] +license = "GPL-3.0" +edition = "2018" + +[dependencies] +rustc-hex = "2.0.1" diff --git a/util/EIP-152/src/lib.rs b/util/EIP-152/src/lib.rs new file mode 100644 index 000000000..fd68b9072 --- /dev/null +++ b/util/EIP-152/src/lib.rs @@ -0,0 +1,192 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +/// 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]; + } +} + + +#[cfg(test)] +mod tests { + use crate::compress; + use rustc_hex::FromHex; + + #[test] + fn test_blake2_f() { + // test from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#example-usage-in-solidity + let mut h_in = [ + 0x6a09e667f2bdc948_u64, 0xbb67ae8584caa73b_u64, + 0x3c6ef372fe94f82b_u64, 0xa54ff53a5f1d36f1_u64, + 0x510e527fade682d1_u64, 0x9b05688c2b3e6c1f_u64, + 0x1f83d9abfb41bd6b_u64, 0x5be0cd19137e2179_u64, + ]; + + let m = [ + 0x0000000000636261_u64, 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, + ]; + let c = [3, 0]; + let f = true; + let rounds = 12; + let h_out: [u64; 8] = [ + 0x0D4D1C983FA580BA_u64, 0xE9F6129FB697276A_u64, 0xB7C45A68142F214C_u64, + 0xD1A2FFDB6FBB124B_u64, 0x2D79AB2A39C5877D_u64, 0x95CC3345DED552C2_u64, + 0x5A92F1DBA88AD318_u64, 0x239900D4ED8623B9_u64, + ]; + + compress(&mut h_in, m, c, f, rounds); + + assert_eq!(h_in, h_out); + } + + fn to_u64_slice(vec: &[u8], slice: &mut [u64]) { + vec.chunks(8).enumerate().for_each(|(index, val)| { + slice[index] = u64::from_le_bytes([val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]]) + }) + } + + #[test] + fn test_vectors_from_eip() { + let vec = vec![ + ( + // Test vector 4 + "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b", + ), + ( // test vector 5 + "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", + ), + ( + // Test vector 6 + "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000", + "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735", + ), + ( + // Test vector 7 + "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421", + ), + // Test vector 8 – u32::MAX rounds – too slow to run +// ( +// "ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", +// "fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615", +// ), + ]; + for (hex, output) in vec { + let hex = hex; + let bytes: Vec = hex.from_hex().unwrap(); + + assert_eq!(bytes.len(), 213); + + let mut h = [0u64; 8]; + let mut m = [0u64; 16]; + let mut t = [0u64; 2]; + + let rounds = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); + let f = match bytes[212] { + 1 => true, + 0 => false, + _ => unreachable!() + }; + + to_u64_slice(&bytes[4..68], &mut h); + to_u64_slice(&bytes[68..196], &mut m); + to_u64_slice(&bytes[196..212], &mut t); + + compress(&mut h, m, t, f, rounds as usize); + + let output: Vec = output.from_hex().unwrap(); + + let mut out = [0u64; 8]; + to_u64_slice(&output[..], &mut out); + + assert_eq!(out, h); + } + } +}