From d71a5991918305c9ded9b743a98387c5f34f76d8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 12:53:07 +1200 Subject: [PATCH 01/10] Empty equihash crate --- Cargo.toml | 1 + components/equihash/Cargo.toml | 11 +++++++++++ components/equihash/src/lib.rs | 7 +++++++ 3 files changed, 19 insertions(+) create mode 100644 components/equihash/Cargo.toml create mode 100644 components/equihash/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index dd595b1bd..a916d8c59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "bellman", + "components/equihash", "ff", "group", "pairing", diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml new file mode 100644 index 000000000..87caa1917 --- /dev/null +++ b/components/equihash/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "equihash" +description = "The Equihash Proof-of-Work function" +version = "0.0.0" +authors = ["Jack Grigg "] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/components/equihash/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} From 5b2c71e112ade5488ba49bb16f50897d513702f8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 13:01:04 +1200 Subject: [PATCH 02/10] Move Equihash verification APIs into equihash crate --- components/equihash/Cargo.toml | 3 +++ components/equihash/src/lib.rs | 12 +++++------- .../equihash.rs => components/equihash/src/verify.rs | 0 zcash_primitives/Cargo.toml | 1 + zcash_primitives/src/block.rs | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) rename zcash_primitives/src/block/equihash.rs => components/equihash/src/verify.rs (100%) diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml index 87caa1917..edbdc4f7c 100644 --- a/components/equihash/Cargo.toml +++ b/components/equihash/Cargo.toml @@ -9,3 +9,6 @@ license = "MIT OR Apache-2.0" edition = "2018" [dependencies] +blake2b_simd = "0.5" +byteorder = "1" +log = "0.4" diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index 31e1bb209..087f95fb7 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -1,7 +1,5 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +//! The Equihash Proof-of-Work function. + +mod verify; + +pub use verify::{is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive}; diff --git a/zcash_primitives/src/block/equihash.rs b/components/equihash/src/verify.rs similarity index 100% rename from zcash_primitives/src/block/equihash.rs rename to components/equihash/src/verify.rs diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index b40744db0..bfd25d9e7 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -17,6 +17,7 @@ blake2b_simd = "0.5" blake2s_simd = "0.5" byteorder = "1" crypto_api_chachapoly = "0.2.1" +equihash = { version = "0.0", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } fpe = "0.2" hex = "0.3" diff --git a/zcash_primitives/src/block.rs b/zcash_primitives/src/block.rs index 8432cd4c9..fd1edbc03 100644 --- a/zcash_primitives/src/block.rs +++ b/zcash_primitives/src/block.rs @@ -9,7 +9,7 @@ use std::ops::Deref; use crate::serialize::Vector; -pub mod equihash; +pub use equihash; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BlockHash(pub [u8; 32]); From 997657f2562e9d677b12b5be65fab8103c2f7181 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Tue, 7 Jul 2020 02:00:53 -0700 Subject: [PATCH 03/10] Move `error!` logs into proper error type (#14) hey kid, want some error handling? --- components/equihash/src/lib.rs | 4 +- components/equihash/src/verify.rs | 139 +++++++++++++++++++----------- 2 files changed, 90 insertions(+), 53 deletions(-) diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index 087f95fb7..54b192381 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -2,4 +2,6 @@ mod verify; -pub use verify::{is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive}; +pub use verify::{ + is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive, Error, +}; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 38518baa0..3be351aa1 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -4,7 +4,7 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; -use log::error; +use std::fmt; use std::io::Cursor; use std::mem::size_of; @@ -98,6 +98,36 @@ impl Node { } } +#[derive(Debug)] +pub struct Error(Kind); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Invalid solution: {}", self.0) + } +} + +impl std::error::Error for Error {} + +#[derive(Debug)] +enum Kind { + Collision, + OutOfOrder, + DuplicateIdxs, + NonZeroRootHash, +} + +impl fmt::Display for Kind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Kind::Collision => f.write_str("invalid collision length between StepRows"), + Kind::OutOfOrder => f.write_str("Index tree incorrectly ordered"), + Kind::DuplicateIdxs => f.write_str("duplicate indices"), + Kind::NonZeroRootHash => f.write_str("root hash of tree is non-zero"), + } + } +} + fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { let mut personalization: Vec = Vec::from("ZcashPoW"); personalization.write_u32::(n).unwrap(); @@ -199,18 +229,15 @@ fn distinct_indices(a: &Node, b: &Node) -> bool { true } -fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool { +fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> Result<(), Kind> { if !has_collision(a, b, p.collision_byte_length()) { - error!("Invalid solution: invalid collision length between StepRows"); - false + Err(Kind::Collision) } else if b.indices_before(a) { - error!("Invalid solution: Index tree incorrectly ordered"); - false + Err(Kind::OutOfOrder) } else if !distinct_indices(a, b) { - error!("Invalid solution: duplicate indices"); - false + Err(Kind::DuplicateIdxs) } else { - true + Ok(()) } } @@ -220,7 +247,7 @@ pub fn is_valid_solution_iterative( input: &[u8], nonce: &[u8], indices: &[u32], -) -> bool { +) -> Result<(), Error> { let p = Params { n, k }; let mut state = initialise_state(p.n, p.k, p.hash_output()); @@ -238,9 +265,7 @@ pub fn is_valid_solution_iterative( for pair in rows.chunks(2) { let a = &pair[0]; let b = &pair[1]; - if !validate_subtrees(&p, a, b) { - return false; - } + validate_subtrees(&p, a, b).map_err(Error)?; cur_rows.push(Node::from_children_ref(a, b, p.collision_byte_length())); } rows = cur_rows; @@ -248,28 +273,24 @@ pub fn is_valid_solution_iterative( } assert!(rows.len() == 1); - rows[0].is_zero(hash_len) + + if rows[0].is_zero(hash_len) { + Ok(()) + } else { + Err(Error(Kind::NonZeroRootHash)) + } } -fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option { +fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Result { if indices.len() > 1 { let end = indices.len(); let mid = end / 2; - match ( - tree_validator(p, state, &indices[0..mid]), - tree_validator(p, state, &indices[mid..end]), - ) { - (Some(a), Some(b)) => { - if validate_subtrees(p, &a, &b) { - Some(Node::from_children(a, b, p.collision_byte_length())) - } else { - None - } - } - _ => None, - } + let a = tree_validator(p, state, &indices[0..mid])?; + let b = tree_validator(p, state, &indices[mid..end])?; + validate_subtrees(p, &a, &b).map_err(Error)?; + Ok(Node::from_children(a, b, p.collision_byte_length())) } else { - Some(Node::new(&p, &state, indices[0])) + Ok(Node::new(&p, &state, indices[0])) } } @@ -279,23 +300,30 @@ pub fn is_valid_solution_recursive( input: &[u8], nonce: &[u8], indices: &[u32], -) -> bool { +) -> Result<(), Error> { let p = Params { n, k }; let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); state.update(nonce); - match tree_validator(&p, &state, indices) { - Some(root) => { - // Hashes were trimmed, so only need to check remaining length - root.is_zero(p.collision_byte_length()) - } - None => false, + let root = tree_validator(&p, &state, indices)?; + + // Hashes were trimmed, so only need to check remaining length + if root.is_zero(p.collision_byte_length()) { + Ok(()) + } else { + Err(Error(Kind::NonZeroRootHash)) } } -pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { +pub fn is_valid_solution( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + soln: &[u8], +) -> Result<(), Error> { let p = Params { n, k }; let indices = indices_from_minimal(soln, p.collision_bit_length()); @@ -307,12 +335,19 @@ pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8] mod tests { use super::is_valid_solution_iterative; use super::is_valid_solution_recursive; + use super::Error; - fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], indices: &[u32]) -> bool { - let a = is_valid_solution_iterative(n, k, input, nonce, indices); - let b = is_valid_solution_recursive(n, k, input, nonce, indices); - assert!(a == b); - a + fn is_valid_solution( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + indices: &[u32], + ) -> Result<(), Error> { + is_valid_solution_iterative(n, k, input, nonce, indices)?; + is_valid_solution_recursive(n, k, input, nonce, indices)?; + + Ok(()) } #[test] @@ -324,14 +359,14 @@ mod tests { 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, 36473, 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); indices = vec![ 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, 123026, 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, 94769, 118158, 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); indices = vec![ 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, @@ -387,19 +422,19 @@ mod tests { 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, 1971986, ]; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); + is_valid_solution(200, 9, input, &nonce, &indices).unwrap(); nonce[0] = 1; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(!is_valid_solution(200, 9, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); + is_valid_solution(200, 9, input, &nonce, &indices).unwrap_err(); indices = vec![ 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); indices = vec![ 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, @@ -455,8 +490,8 @@ mod tests { 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, 1012424, 1428921, 890113, 1822645, 1082368, 1392894, ]; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); + is_valid_solution(200, 9, input, &nonce, &indices).unwrap(); let input2 = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; indices = vec![ @@ -464,6 +499,6 @@ mod tests { 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, ]; - assert!(is_valid_solution(96, 5, input2, &nonce, &indices)); + is_valid_solution(96, 5, input2, &nonce, &indices).unwrap(); } } From 8759684fad248f5f8d42b8f97685ac42a42c79af Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 7 Jul 2020 22:09:24 +1200 Subject: [PATCH 04/10] equihash: Add parameter validity checks --- components/equihash/src/verify.rs | 44 +++++++++++++++++++------------ 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 3be351aa1..f32f7e256 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -8,7 +8,8 @@ use std::fmt; use std::io::Cursor; use std::mem::size_of; -struct Params { +#[derive(Clone, Copy)] +pub struct Params { n: u32, k: u32, } @@ -20,6 +21,13 @@ struct Node { } impl Params { + pub fn new(n: u32, k: u32) -> Result { + if (k < n) && (n % 8 == 0) { + Ok(Params { n, k }) + } else { + Err(Error(Kind::InvalidParams)) + } + } fn indices_per_hash_output(&self) -> u32 { 512 / self.n } @@ -111,6 +119,7 @@ impl std::error::Error for Error {} #[derive(Debug)] enum Kind { + InvalidParams, Collision, OutOfOrder, DuplicateIdxs, @@ -120,6 +129,7 @@ enum Kind { impl fmt::Display for Kind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Kind::InvalidParams => f.write_str("invalid parameters"), Kind::Collision => f.write_str("invalid collision length between StepRows"), Kind::OutOfOrder => f.write_str("Index tree incorrectly ordered"), Kind::DuplicateIdxs => f.write_str("duplicate indices"), @@ -193,7 +203,12 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { vout } -fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { +fn indices_from_minimal(p: Params, minimal: &[u8]) -> Result, Error> { + let c_bit_len = p.collision_bit_length(); + if minimal.len() != (1 << p.k) * (c_bit_len + 1) / 8 { + return Err(Error(Kind::InvalidParams)); + } + assert!(((c_bit_len + 1) + 7) / 8 <= size_of::()); let len_indices = 8 * size_of::() * minimal.len() / (c_bit_len + 1); let byte_pad = size_of::() - ((c_bit_len + 1) + 7) / 8; @@ -207,7 +222,7 @@ fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { ret.push(i); } - ret + Ok(ret) } fn has_collision(a: &Node, b: &Node, len: usize) -> bool { @@ -242,14 +257,11 @@ fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> Result<(), Kind> { } pub fn is_valid_solution_iterative( - n: u32, - k: u32, + p: Params, input: &[u8], nonce: &[u8], indices: &[u32], ) -> Result<(), Error> { - let p = Params { n, k }; - let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); state.update(nonce); @@ -295,14 +307,11 @@ fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Result Result<(), Error> { - let p = Params { n, k }; - let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); state.update(nonce); @@ -324,18 +333,18 @@ pub fn is_valid_solution( nonce: &[u8], soln: &[u8], ) -> Result<(), Error> { - let p = Params { n, k }; - let indices = indices_from_minimal(soln, p.collision_bit_length()); + let p = Params::new(n, k)?; + let indices = indices_from_minimal(p, soln)?; // Recursive validation is faster - is_valid_solution_recursive(n, k, input, nonce, &indices) + is_valid_solution_recursive(p, input, nonce, &indices) } #[cfg(test)] mod tests { use super::is_valid_solution_iterative; use super::is_valid_solution_recursive; - use super::Error; + use super::{Error, Params}; fn is_valid_solution( n: u32, @@ -344,8 +353,9 @@ mod tests { nonce: &[u8], indices: &[u32], ) -> Result<(), Error> { - is_valid_solution_iterative(n, k, input, nonce, indices)?; - is_valid_solution_recursive(n, k, input, nonce, indices)?; + let p = Params::new(n, k).unwrap(); + is_valid_solution_iterative(p, input, nonce, indices)?; + is_valid_solution_recursive(p, input, nonce, indices)?; Ok(()) } From bcd687cffbd8231856e6d98b624ee544e02c9e18 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 8 Jul 2020 15:13:41 +1200 Subject: [PATCH 05/10] equihash: Remove iterative and recursive APIs The canonical verification API from Heartwood activation is the recursive API exposed through is_valid_solution. We retain is_valid_solution_iterative internally for testing. --- components/equihash/src/lib.rs | 4 +--- components/equihash/src/verify.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index 54b192381..e45a7d999 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -2,6 +2,4 @@ mod verify; -pub use verify::{ - is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive, Error, -}; +pub use verify::{is_valid_solution, Error}; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index f32f7e256..e2ee03338 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -9,7 +9,7 @@ use std::io::Cursor; use std::mem::size_of; #[derive(Clone, Copy)] -pub struct Params { +struct Params { n: u32, k: u32, } @@ -21,7 +21,7 @@ struct Node { } impl Params { - pub fn new(n: u32, k: u32) -> Result { + fn new(n: u32, k: u32) -> Result { if (k < n) && (n % 8 == 0) { Ok(Params { n, k }) } else { @@ -40,6 +40,7 @@ impl Params { fn collision_byte_length(&self) -> usize { (self.collision_bit_length() + 7) / 8 } + #[cfg(test)] fn hash_length(&self) -> usize { ((self.k as usize) + 1) * self.collision_byte_length() } @@ -76,6 +77,7 @@ impl Node { Node { hash, indices } } + #[cfg(test)] fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { let hash: Vec<_> = a .hash @@ -256,7 +258,8 @@ fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> Result<(), Kind> { } } -pub fn is_valid_solution_iterative( +#[cfg(test)] +fn is_valid_solution_iterative( p: Params, input: &[u8], nonce: &[u8], @@ -306,7 +309,7 @@ fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Result Date: Wed, 8 Jul 2020 17:34:52 +1200 Subject: [PATCH 06/10] equihash: Crate documentation --- components/equihash/src/lib.rs | 19 ++++++++++++++++++- components/equihash/src/verify.rs | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index e45a7d999..e29730809 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -1,4 +1,21 @@ -//! The Equihash Proof-of-Work function. +//! Equihash is a Proof-of-Work algorithm, based on a generalization of the Birthday +//! problem which finds colliding hash values. It was designed to be memory-hard; more +//! specifically, the bottle-neck for parallel implementations of Equihash solvers would +//! be memory bandwidth. +//! +//! This crate implements Equihash as specified for the Zcash consensus rules. It can +//! verify solutions for any valid `(n, k)` parameters, as long as the row indices are no +//! larger than 32 bits (that is, `ceiling(((n / (k + 1)) + 1) / 8) <= 4`). +//! +//! References +//! ========== +//! - [Section 7.6.1: Equihash.] Zcash Protocol Specification, version 2020.1.10 or later. +//! - Alex Biryukov and Dmitry Khovratovich. +//! [*Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem.*][BK16] +//! NDSS ’16. +//! +//! [Section 7.6.1: Equihash.]: https://zips.z.cash/protocol/protocol.pdf#equihash +//! [BK16]: https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf mod verify; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index e2ee03338..16159a4bf 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -108,6 +108,7 @@ impl Node { } } +/// An Equihash solution failed to verify. #[derive(Debug)] pub struct Error(Kind); @@ -329,6 +330,8 @@ fn is_valid_solution_recursive( } } +/// Checks whether `soln` is a valid solution for `(input, nonce)` with the +/// parameters `(n, k)`. pub fn is_valid_solution( n: u32, k: u32, From 77406580848ae2dd37f0c17a1bd6f5cc87079b42 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 9 Jul 2020 20:50:58 +1200 Subject: [PATCH 07/10] equihash: Add additional constraints on Params These are requirements of the general Equihash implementation, that are satisfied by the Zcash parameters. --- components/equihash/src/verify.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 16159a4bf..111015c7c 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -22,7 +22,12 @@ struct Node { impl Params { fn new(n: u32, k: u32) -> Result { - if (k < n) && (n % 8 == 0) { + // We place the following requirements on the parameters: + // - n is a multiple of 8, so the hash output has an exact byte length. + // - k >= 3 so the encoded solutions have an exact byte length. + // - k < n, so the collision bit length is at least 1. + // - n is a multiple of k + 1, so we have an integer collision bit length. + if (n % 8 == 0) && (k >= 3) && (k < n) && (n % (k + 1) == 0) { Ok(Params { n, k }) } else { Err(Error(Kind::InvalidParams)) From 72fbd2071d20a7c50ea414aa3a352eef395805b5 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 9 Jul 2020 20:51:52 +1200 Subject: [PATCH 08/10] equihash: Clarify order of operations in indices_from_minimal --- components/equihash/src/verify.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 111015c7c..b77debca3 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -213,7 +213,8 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { fn indices_from_minimal(p: Params, minimal: &[u8]) -> Result, Error> { let c_bit_len = p.collision_bit_length(); - if minimal.len() != (1 << p.k) * (c_bit_len + 1) / 8 { + // Division is exact because k >= 3. + if minimal.len() != ((1 << p.k) * (c_bit_len + 1)) / 8 { return Err(Error(Kind::InvalidParams)); } From 1b059d4ae0494241d6ef7a7aeeecd0ede2aa4f2d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 9 Jul 2020 20:53:50 +1200 Subject: [PATCH 09/10] equihash: Remove unnecessary log dependency --- components/equihash/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml index edbdc4f7c..830809558 100644 --- a/components/equihash/Cargo.toml +++ b/components/equihash/Cargo.toml @@ -11,4 +11,3 @@ edition = "2018" [dependencies] blake2b_simd = "0.5" byteorder = "1" -log = "0.4" From b5bd52e7a12677dd9cb8ec5a8292e39a49b9a639 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 10 Jul 2020 10:24:54 +1200 Subject: [PATCH 10/10] equihash 0.1.0 --- components/equihash/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml index 830809558..86bacb9e3 100644 --- a/components/equihash/Cargo.toml +++ b/components/equihash/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "equihash" description = "The Equihash Proof-of-Work function" -version = "0.0.0" +version = "0.1.0" authors = ["Jack Grigg "] homepage = "https://github.com/zcash/librustzcash" repository = "https://github.com/zcash/librustzcash" diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index bfd25d9e7..4a99d0090 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -17,7 +17,7 @@ blake2b_simd = "0.5" blake2s_simd = "0.5" byteorder = "1" crypto_api_chachapoly = "0.2.1" -equihash = { version = "0.0", path = "../components/equihash" } +equihash = { version = "0.1", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } fpe = "0.2" hex = "0.3"