From f4295917a00a9495bb0952877cd3869c15cecb99 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Thu, 4 Jun 2020 15:05:56 +0200 Subject: [PATCH] header version 4 accompanied by new AR PoW (#3334) --- core/src/consensus.rs | 12 +- core/src/global.rs | 12 +- core/src/pow.rs | 2 + core/src/pow/cuckarooz.rs | 193 ++++++++++++++++++++++++++++++++ core/tests/consensus_floonet.rs | 80 ++++++++++--- core/tests/consensus_mainnet.rs | 79 ++++++++++--- 6 files changed, 339 insertions(+), 39 deletions(-) create mode 100644 core/src/pow/cuckarooz.rs diff --git a/core/src/consensus.rs b/core/src/consensus.rs index 6701f35e2..12f52208a 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -133,6 +133,9 @@ pub const FLOONET_FIRST_HARD_FORK: u64 = 185_040; /// Floonet second hard fork height, set to happen around 2019-12-19 pub const FLOONET_SECOND_HARD_FORK: u64 = 298_080; +/// Floonet second hard fork height, set to happen around 2020-06-20 +pub const FLOONET_THIRD_HARD_FORK: u64 = 552_960; + /// AutomatedTesting and UserTesting HF1 height. pub const TESTING_FIRST_HARD_FORK: u64 = 3; @@ -154,8 +157,10 @@ pub fn header_version(height: u64) -> HeaderVersion { HeaderVersion(1) } else if height < FLOONET_SECOND_HARD_FORK { HeaderVersion(2) - } else if height < 3 * HARD_FORK_INTERVAL { + } else if height < FLOONET_THIRD_HARD_FORK { HeaderVersion(3) + } else if height < 4 * HARD_FORK_INTERVAL { + HeaderVersion(4) } else { HeaderVersion(hf_interval) } @@ -179,7 +184,7 @@ pub fn header_version(height: u64) -> HeaderVersion { /// Check whether the block version is valid at a given height, implements /// 6 months interval scheduled hard forks for the first 2 years. pub fn valid_header_version(height: u64, version: HeaderVersion) -> bool { - height < 3 * HARD_FORK_INTERVAL && version == header_version(height) + height < 4 * HARD_FORK_INTERVAL && version == header_version(height) } /// Number of blocks used to calculate difficulty adjustments @@ -199,8 +204,7 @@ pub const DIFFICULTY_DAMP_FACTOR: u64 = 3; pub const AR_SCALE_DAMP_FACTOR: u64 = 13; /// Compute weight of a graph as number of siphash bits defining the graph -/// Must be made dependent on height to phase out C31 in early 2020 -/// Later phase outs are on hold for now +/// The height dependence allows a 30-week linear transition from C31+ to C32+ starting after 1 year pub fn graph_weight(height: u64, edge_bits: u8) -> u64 { let mut xpr_edge_bits = edge_bits as u64; diff --git a/core/src/global.rs b/core/src/global.rs index 6377c1e8c..64909da14 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -24,8 +24,8 @@ use crate::consensus::{ }; use crate::core::block::HeaderVersion; use crate::pow::{ - self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckaroom_ctx, new_cuckatoo_ctx, EdgeType, - PoWContext, + self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckaroom_ctx, new_cuckarooz_ctx, + new_cuckatoo_ctx, EdgeType, PoWContext, }; use std::cell::Cell; use util::OneTime; @@ -225,8 +225,11 @@ where { let chain_type = get_chain_type(); match chain_type { - // Mainnet has Cuckaroo(d)29 for AR and Cuckatoo31+ for AF + // Mainnet has Cuckaroo{,d,m,z}29 for AR and Cuckatoo31+ for AF ChainTypes::Mainnet if edge_bits > 29 => new_cuckatoo_ctx(edge_bits, proof_size, max_sols), + ChainTypes::Mainnet if valid_header_version(height, HeaderVersion(4)) => { + new_cuckarooz_ctx(edge_bits, proof_size) + } ChainTypes::Mainnet if valid_header_version(height, HeaderVersion(3)) => { new_cuckaroom_ctx(edge_bits, proof_size) } @@ -237,6 +240,9 @@ where // Same for Floonet ChainTypes::Floonet if edge_bits > 29 => new_cuckatoo_ctx(edge_bits, proof_size, max_sols), + ChainTypes::Floonet if valid_header_version(height, HeaderVersion(4)) => { + new_cuckarooz_ctx(edge_bits, proof_size) + } ChainTypes::Floonet if valid_header_version(height, HeaderVersion(3)) => { new_cuckaroom_ctx(edge_bits, proof_size) } diff --git a/core/src/pow.rs b/core/src/pow.rs index 847e3439b..b9eb2eb28 100644 --- a/core/src/pow.rs +++ b/core/src/pow.rs @@ -41,6 +41,7 @@ mod common; pub mod cuckaroo; pub mod cuckarood; pub mod cuckaroom; +pub mod cuckarooz; pub mod cuckatoo; mod error; #[allow(dead_code)] @@ -51,6 +52,7 @@ mod types; pub use crate::pow::cuckaroo::{new_cuckaroo_ctx, CuckarooContext}; pub use crate::pow::cuckarood::{new_cuckarood_ctx, CuckaroodContext}; pub use crate::pow::cuckaroom::{new_cuckaroom_ctx, CuckaroomContext}; +pub use crate::pow::cuckarooz::{new_cuckarooz_ctx, CuckaroozContext}; pub use crate::pow::cuckatoo::{new_cuckatoo_ctx, CuckatooContext}; pub use crate::pow::error::Error; use chrono::prelude::{DateTime, NaiveDateTime, Utc}; diff --git a/core/src/pow/cuckarooz.rs b/core/src/pow/cuckarooz.rs new file mode 100644 index 000000000..6781be8a2 --- /dev/null +++ b/core/src/pow/cuckarooz.rs @@ -0,0 +1,193 @@ +// Copyright 2020 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of Cuckarooz Cycle, based on Cuckoo Cycle designed by +//! John Tromp. Ported to Rust from https://github.com/tromp/cuckoo. +//! +//! Cuckarooz is a variation of Cuckaroo that's tweaked at the third HardFork +//! to maintain ASIC-Resistance, as introduced in +//! https://forum.grin.mw/t/introducing-the-final-tweak-cuckarooz +//! It completes the choices of undirected vs directed edges and bipartite vs +//! monopartite graphs, and is named after the last letter of the alphabet +//! accordingly. + +use crate::global; +use crate::pow::common::{CuckooParams, EdgeType}; +use crate::pow::error::{Error, ErrorKind}; +use crate::pow::siphash::siphash_block; +use crate::pow::{PoWContext, Proof}; + +/// Instantiate a new CuckaroozContext as a PowContext. Note that this can't +/// be moved in the PoWContext trait as this particular trait needs to be +/// convertible to an object trait. +pub fn new_cuckarooz_ctx( + edge_bits: u8, + proof_size: usize, +) -> Result>, Error> +where + T: EdgeType + 'static, +{ + let params = CuckooParams::new(edge_bits, proof_size)?; + Ok(Box::new(CuckaroozContext { params })) +} + +/// Cuckarooz cycle context. Only includes the verifier for now. +pub struct CuckaroozContext +where + T: EdgeType, +{ + params: CuckooParams, +} + +impl PoWContext for CuckaroozContext +where + T: EdgeType, +{ + fn set_header_nonce( + &mut self, + header: Vec, + nonce: Option, + _solve: bool, + ) -> Result<(), Error> { + self.params.reset_header_nonce(header, nonce) + } + + fn find_cycles(&mut self) -> Result, Error> { + unimplemented!() + } + + fn verify(&self, proof: &Proof) -> Result<(), Error> { + if proof.proof_size() != global::proofsize() { + return Err(ErrorKind::Verification("wrong cycle length".to_owned()).into()); + } + let nonces = &proof.nonces; + let mut uvs = vec![0u64; 2 * proof.proof_size()]; + let mut xoruv: u64 = 0; + + for n in 0..proof.proof_size() { + if nonces[n] > to_u64!(self.params.edge_mask) { + return Err(ErrorKind::Verification("edge too big".to_owned()).into()); + } + if n > 0 && nonces[n] <= nonces[n - 1] { + return Err(ErrorKind::Verification("edges not ascending".to_owned()).into()); + } + // 21 is standard siphash rotation constant + let edge = to_edge!( + T, + siphash_block(&self.params.siphash_keys, nonces[n], 21, true) + ); + uvs[2 * n] = to_u64!(edge & self.params.edge_mask); + uvs[2 * n + 1] = to_u64!((edge >> 32) & self.params.edge_mask); + xoruv ^= uvs[2 * n] ^ uvs[2 * n + 1]; + } + if xoruv != 0 { + return Err(ErrorKind::Verification("endpoints don't match up".to_owned()).into()); + } + let mut n = 0; + let mut i = 0; + let mut j; + loop { + // follow cycle + j = i; + let mut k = j; + loop { + k = (k + 1) % (2 * self.params.proof_size); + if k == i { + break; + } + if uvs[k] == uvs[i] { + // find other edge endpoint matching one at i + if j != i { + return Err(ErrorKind::Verification("branch in cycle".to_owned()).into()); + } + j = k; + } + } + if j == i { + return Err(ErrorKind::Verification("cycle dead ends".to_owned()).into()); + } + i = j ^ 1; + n += 1; + if i == 0 { + break; + } + } + if n == self.params.proof_size { + Ok(()) + } else { + Err(ErrorKind::Verification("cycle too short".to_owned()).into()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + // empty header, nonce 71 + static V1_19_HASH: [u64; 4] = [ + 0xd129f63fba4d9a85, + 0x457dcb3666c5e09c, + 0x045247a2e2ee75f7, + 0x1a0f2e1bcb9d93ff, + ]; + static V1_19_SOL: [u64; 42] = [ + 0x33b6, 0x487b, 0x88b7, 0x10bf6, 0x15144, 0x17cb7, 0x22621, 0x2358e, 0x23775, 0x24fb3, + 0x26b8a, 0x2876c, 0x2973e, 0x2f4ba, 0x30a62, 0x3a36b, 0x3ba5d, 0x3be67, 0x3ec56, 0x43141, + 0x4b9c5, 0x4fa06, 0x51a5c, 0x523e5, 0x53d08, 0x57d34, 0x5c2de, 0x60bba, 0x62509, 0x64d69, + 0x6803f, 0x68af4, 0x6bd52, 0x6f041, 0x6f900, 0x70051, 0x7097d, 0x735e8, 0x742c2, 0x79ae5, + 0x7f64d, 0x7fd49, + ]; + + // empty header, nonce 15 + static V2_29_HASH: [u64; 4] = [ + 0x34bb4c75c929a2f5, + 0x21df13263aa81235, + 0x37d00939eae4be06, + 0x473251cbf6941553, + ]; + static V2_29_SOL: [u64; 42] = [ + 0x49733a, 0x1d49107, 0x253d2ca, 0x5ad5e59, 0x5b671bd, 0x5dcae1c, 0x5f9a589, 0x65e9afc, + 0x6a59a45, 0x7d9c6d3, 0x7df96e4, 0x8b26174, 0xa17b430, 0xa1c8c0d, 0xa8a0327, 0xabd7402, + 0xacb7c77, 0xb67524f, 0xc1c15a6, 0xc7e2c26, 0xc7f5d8d, 0xcae478a, 0xdea9229, 0xe1ab49e, + 0xf57c7db, 0xfb4e8c5, 0xff314aa, 0x110ccc12, 0x143e546f, 0x17007af8, 0x17140ea2, + 0x173d7c5d, 0x175cd13f, 0x178b8880, 0x1801edc5, 0x18c8f56b, 0x18c8fe6d, 0x19f1a31a, + 0x1bb028d1, 0x1caaa65a, 0x1cf29bc2, 0x1dbde27d, + ]; + + #[test] + fn cuckarooz19_29_vectors() { + global::set_local_chain_type(global::ChainTypes::Mainnet); + let mut ctx19 = new_impl::(19, 42); + ctx19.params.siphash_keys = V1_19_HASH.clone(); + assert!(ctx19 + .verify(&Proof::new(V1_19_SOL.to_vec().clone())) + .is_ok()); + assert!(ctx19.verify(&Proof::zero(42)).is_err()); + let mut ctx29 = new_impl::(29, 42); + ctx29.params.siphash_keys = V2_29_HASH.clone(); + assert!(ctx29 + .verify(&Proof::new(V2_29_SOL.to_vec().clone())) + .is_ok()); + assert!(ctx29.verify(&Proof::zero(42)).is_err()); + } + + fn new_impl(edge_bits: u8, proof_size: usize) -> CuckaroozContext + where + T: EdgeType, + { + let params = CuckooParams::new(edge_bits, proof_size).unwrap(); + CuckaroozContext { params } + } +} diff --git a/core/tests/consensus_floonet.rs b/core/tests/consensus_floonet.rs index b793d9b9d..3e89e283a 100644 --- a/core/tests/consensus_floonet.rs +++ b/core/tests/consensus_floonet.rs @@ -13,7 +13,7 @@ use grin_core::consensus::{ secondary_pow_ratio, valid_header_version, FLOONET_FIRST_HARD_FORK, FLOONET_SECOND_HARD_FORK, - YEAR_HEIGHT, + FLOONET_THIRD_HARD_FORK, HARD_FORK_INTERVAL, }; use grin_core::core::HeaderVersion; use grin_core::global; @@ -84,7 +84,6 @@ fn hard_forks() { FLOONET_FIRST_HARD_FORK, HeaderVersion(1) )); - assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion(1))); assert!(valid_header_version( FLOONET_SECOND_HARD_FORK - 1, HeaderVersion(2) @@ -105,19 +104,70 @@ fn hard_forks() { FLOONET_SECOND_HARD_FORK, HeaderVersion(1) )); - - assert!(!valid_header_version(YEAR_HEIGHT - 1, HeaderVersion(2))); - assert!(valid_header_version(YEAR_HEIGHT - 1, HeaderVersion(3))); - assert!(valid_header_version(YEAR_HEIGHT, HeaderVersion(3))); - assert!(valid_header_version(YEAR_HEIGHT + 1, HeaderVersion(3))); - // v4 not active yet - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(4))); - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(3))); - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(2))); - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(1))); - assert!(!valid_header_version(YEAR_HEIGHT * 2, HeaderVersion(3))); - assert!(!valid_header_version( - YEAR_HEIGHT * 3 / 2 + 1, + assert!(valid_header_version( + FLOONET_THIRD_HARD_FORK - 1, HeaderVersion(3) )); + assert!(valid_header_version( + FLOONET_THIRD_HARD_FORK, + HeaderVersion(4) + )); + assert!(valid_header_version( + FLOONET_THIRD_HARD_FORK + 1, + HeaderVersion(4) + )); + assert!(!valid_header_version( + FLOONET_THIRD_HARD_FORK, + HeaderVersion(3) + )); + assert!(!valid_header_version( + FLOONET_THIRD_HARD_FORK, + HeaderVersion(2) + )); + assert!(!valid_header_version( + FLOONET_THIRD_HARD_FORK, + HeaderVersion(1) + )); + + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 2 - 1, + HeaderVersion(1) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 2 - 1, + HeaderVersion(2) + )); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 2 - 1, + HeaderVersion(3) + )); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 2, + HeaderVersion(3) + )); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 2 + 1, + HeaderVersion(3) + )); + // v5 not active yet + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(5) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(4) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(3) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(2) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(1) + )); } diff --git a/core/tests/consensus_mainnet.rs b/core/tests/consensus_mainnet.rs index c9fcacd75..1d770e7a9 100644 --- a/core/tests/consensus_mainnet.rs +++ b/core/tests/consensus_mainnet.rs @@ -438,25 +438,70 @@ fn hard_forks() { assert!(valid_header_version(0, HeaderVersion(1))); assert!(valid_header_version(10, HeaderVersion(1))); assert!(!valid_header_version(10, HeaderVersion(2))); - assert!(valid_header_version(YEAR_HEIGHT / 2 - 1, HeaderVersion(1))); - assert!(valid_header_version(YEAR_HEIGHT / 2, HeaderVersion(2))); - assert!(valid_header_version(YEAR_HEIGHT / 2 + 1, HeaderVersion(2))); - assert!(!valid_header_version(YEAR_HEIGHT / 2, HeaderVersion(1))); - assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion(1))); + assert!(valid_header_version( + HARD_FORK_INTERVAL - 1, + HeaderVersion(1) + )); + assert!(!valid_header_version(HARD_FORK_INTERVAL, HeaderVersion(1))); + assert!(valid_header_version(HARD_FORK_INTERVAL, HeaderVersion(2))); + assert!(valid_header_version( + HARD_FORK_INTERVAL + 1, + HeaderVersion(2) + )); - assert!(valid_header_version(YEAR_HEIGHT - 1, HeaderVersion(2))); - assert!(valid_header_version(YEAR_HEIGHT, HeaderVersion(3))); - assert!(valid_header_version(YEAR_HEIGHT + 1, HeaderVersion(3))); - assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion(2))); - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(2))); - // v4 not active yet - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(4))); - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(3))); - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(2))); - assert!(!valid_header_version(YEAR_HEIGHT * 3 / 2, HeaderVersion(1))); - assert!(!valid_header_version(YEAR_HEIGHT * 2, HeaderVersion(3))); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 2 - 1, + HeaderVersion(2) + )); assert!(!valid_header_version( - YEAR_HEIGHT * 3 / 2 + 1, + HARD_FORK_INTERVAL * 2, + HeaderVersion(2) + )); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 2, HeaderVersion(3) )); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 2 + 1, + HeaderVersion(3) + )); + + assert!(valid_header_version( + HARD_FORK_INTERVAL * 3 - 1, + HeaderVersion(3) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 3, + HeaderVersion(3) + )); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 3, + HeaderVersion(4) + )); + assert!(valid_header_version( + HARD_FORK_INTERVAL * 3 + 1, + HeaderVersion(4) + )); + + // v5 not active yet + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(5) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(4) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(3) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(2) + )); + assert!(!valid_header_version( + HARD_FORK_INTERVAL * 4, + HeaderVersion(1) + )); }