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

Add blake2_f precompile #11017

Merged
merged 40 commits into from
Sep 9, 2019
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7bf1f51
feat: implement eip1108
niklasad1 Aug 29, 2019
0e2c0c1
doc nit: price per point pair
niklasad1 Aug 30, 2019
4a9bc52
fix: test-build
niklasad1 Aug 30, 2019
4a10715
fix: update chain specs
niklasad1 Aug 30, 2019
7c54aa4
fix: basic_authority.json chain spec
niklasad1 Aug 30, 2019
0e8ae07
fix: change `eip_transition == 0x7fffffffffffff`
niklasad1 Aug 30, 2019
42044fe
Merge na-eip1108
dvdplm Sep 2, 2019
6194306
Pre-compile plumbing
dvdplm Sep 2, 2019
8abba67
Use type alias instead of struct
dvdplm Sep 2, 2019
1de24c7
Extract the data from incoming EVM call and forward it to blake2_f
dvdplm Sep 2, 2019
2975573
Fetch parity-common from git for now
dvdplm Sep 2, 2019
30aceb5
Merge branch 'master' into dp/feature/eip-152-add-blake2-precompile
dvdplm Sep 2, 2019
e23bf2c
Fix broken cost impl
dvdplm Sep 2, 2019
41f044c
cleanup
dvdplm Sep 2, 2019
3fd92e9
Trailing comma in json spec
dvdplm Sep 3, 2019
edd360a
Merge branch 'master' into dp/feature/eip-152-add-blake2-precompile
dvdplm Sep 3, 2019
4c1ddbb
Update ethcore/builtin/src/lib.rs
dvdplm Sep 3, 2019
dea870d
Update ethcore/builtin/src/lib.rs
dvdplm Sep 3, 2019
af5ac2e
Rename pricer to be Blake2-specific
dvdplm Sep 3, 2019
29c0337
Fix error handling in cost()
dvdplm Sep 3, 2019
bd988bd
fix chainspec
dvdplm Sep 3, 2019
263e359
EIP-152 crate
seunlanlege Sep 3, 2019
7e4b02b
Merge branch 'dp/feature/eip-152-add-blake2-precompile' of github.com…
seunlanlege Sep 3, 2019
181de92
Update ethcore/builtin/src/lib.rs
dvdplm Sep 3, 2019
9592c48
Address grumbles
dvdplm Sep 3, 2019
4e296fd
Merge branch 'dp/feature/eip-152-add-blake2-precompile' of github.com…
dvdplm Sep 3, 2019
83b5690
Update ethcore/builtin/src/lib.rs
dvdplm Sep 3, 2019
4e7bf2b
Switch tests to use hex!
dvdplm Sep 3, 2019
283c1de
Merge branch 'dp/feature/eip-152-add-blake2-precompile' of github.com…
dvdplm Sep 3, 2019
deacea1
remove parity-crypto from git
dvdplm Sep 3, 2019
6d4381b
Sort out the SIGMA
dvdplm Sep 4, 2019
c26dc42
Prefer arrays to vecs
dvdplm Sep 4, 2019
81d6556
Update util/EIP-152/src/lib.rs
dvdplm Sep 4, 2019
68c376e
Update json/src/spec/builtin.rs
dvdplm Sep 4, 2019
ad61a15
Update json/src/spec/builtin.rs
dvdplm Sep 4, 2019
362bd90
Review feedback
dvdplm Sep 4, 2019
5818117
Merge branch 'dp/feature/eip-152-add-blake2-precompile' of github.com…
dvdplm Sep 4, 2019
bb991b5
Merge branch 'master' into dp/feature/eip-152-add-blake2-precompile
dvdplm Sep 9, 2019
aa258e7
Do not change chainspecs yet
dvdplm Sep 9, 2019
b1dbd02
Do not assume endianess: precompile output is LE
dvdplm Sep 9, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion ethcore/builtin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
parity-crypto = "0.4.0"
parity-crypto = { version = "0.4.0", git = "https://github.com/paritytech/parity-common/", branch = "dp/feature/add-blake2_f-toparity-crypto"}
ordian marked this conversation as resolved.
Show resolved Hide resolved
byteorder = "1.3.2"

[dev-dependencies]
rustc-hex = "1.0"
hex-literal = "0.2.1"
184 changes: 182 additions & 2 deletions ethcore/builtin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@

use std::{
cmp::{max, min},
io::{self, Read},
io::{self, Read, Cursor},
};

use bn;
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use ethereum_types::{H256, U256};
use ethjson;
use ethkey::{Signature, recover as ec_recover};
use keccak_hash::keccak;
use log::{warn, trace};
use num::{BigUint, Zero, One};
use parity_bytes::BytesRef;
use parity_crypto::digest;
use parity_crypto::{blake2_f, digest};

/// Native implementation of a built-in contract.
trait Implementation: Send + Sync {
Expand All @@ -44,6 +45,19 @@ trait Pricer: Send + Sync {
fn cost(&self, input: &[u8], at: u64) -> U256;
}

/// A linear pricing model. This computes a price using a base cost and a cost per-word.
ordian marked this conversation as resolved.
Show resolved Hide resolved
pub type Fixed = u64;

impl Pricer for Fixed {
fn cost(&self, input: &[u8], _at: u64) -> U256 {
use std::convert::TryInto;
let (rounds_bytes, _) = input.split_at(std::mem::size_of::<u32>());
// todo[dvdplm] what's the error handling strategy here? panic? return U256::MAX? Is the input len checked before this call?
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
let rounds = u32::from_be_bytes(rounds_bytes.try_into().unwrap());
U256::from(*self * rounds as u64)
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// A linear pricing model. This computes a price using a base cost and a cost per-word.
struct Linear {
base: usize,
Expand Down Expand Up @@ -199,6 +213,9 @@ impl Builtin {
impl From<ethjson::spec::Builtin> for Builtin {
fn from(b: ethjson::spec::Builtin) -> Self {
let pricer: Box<dyn Pricer> = match b.pricing {
ethjson::spec::Pricing::Fixed(fixed) => {
Box::new(fixed)
},
ethjson::spec::Pricing::Linear(linear) => {
Box::new(Linear {
base: linear.base,
Expand Down Expand Up @@ -256,6 +273,7 @@ fn ethereum_builtin(name: &str) -> Box<dyn Implementation> {
"alt_bn128_add" => Box::new(Bn128Add) as Box<dyn Implementation>,
"alt_bn128_mul" => Box::new(Bn128Mul) as Box<dyn Implementation>,
"alt_bn128_pairing" => Box::new(Bn128Pairing) as Box<dyn Implementation>,
"blake2_f" => Box::new(Blake2F) as Box<dyn Implementation>,
_ => panic!("invalid builtin name: {}", name),
}
}
Expand All @@ -267,6 +285,10 @@ fn ethereum_builtin(name: &str) -> Box<dyn Implementation> {
// - 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;
Expand All @@ -292,6 +314,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);
Expand Down Expand Up @@ -337,6 +362,52 @@ impl Implementation for Sha256 {
}
}

impl Implementation for Blake2F {
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> {
const BLAKE2_F_ARG_LEN: usize = 213;

if input.len() != BLAKE2_F_ARG_LEN {
// todo[dvdplm] what's the right 'target' here?
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
trace!(target: "evm", "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 rdr = Cursor::new(input);
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
let rounds = rdr.read_u32::<BigEndian>().expect("todo – prove this is ok or handle");
dvdplm marked this conversation as resolved.
Show resolved Hide resolved

// state vector, h
let mut h = [0u64; 8];
for i in 0..8 {
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
h[i] = rdr.read_u64::<LittleEndian>().expect("todo - prove this is ok or handle");
}

// message block vector, m
let mut m = [0u64; 16];
for i in 0..16 {
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
m[i] = rdr.read_u64::<LittleEndian>().expect("todo - prove this is ok or handle");
}

// 2w-bit offset counter, t
let t0 = rdr.read_u64::<LittleEndian>().expect("todo - prove this is ok or handle");
let t1 = rdr.read_u64::<LittleEndian>().expect("todo - prove this is ok or handle");

// final block indicator flag, "f"
let f = match input.last() {
Some(1) => true,
Some(0) => false,
_ => {
trace!(target: "evm", "incorrect final block indicator flag, was: {:?}", input.last());
return Err("incorrect final block indicator flag")
}
};

blake2_f::blake2_f(&mut h, m, [t0, t1], f, rounds as usize);

output.write(0, unsafe { std::slice::from_raw_parts(h.as_ptr() as *const u8, h.len() * std::mem::size_of::<u64>())});
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}
}

impl Implementation for Ripemd160 {
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> {
let hash = digest::ripemd160(input);
Expand Down Expand Up @@ -600,8 +671,117 @@ mod tests {
use num::{BigUint, Zero, One};
use parity_bytes::BytesRef;
use rustc_hex::FromHex;
use hex_literal::hex; // todo[dvdplm] switch tests to hex_literal
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
9 changes: 9 additions & 0 deletions ethcore/res/ethereum/foundation.json
Original file line number Diff line number Diff line change
Expand Up @@ -3958,6 +3958,15 @@
}
}
},
"0x0000000000000000000000000000000000000009": {
"builtin": {
"name": "blake2_f",
"activate_at": "0xffffff",
"pricing": {
"fixed": 1
}
}
},
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
"0x3282791d6fd713f1e94f4bfd565eaa78b3a0599d": {
"balance": "0x487a9a304539440000"
},
Expand Down
Loading