Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HardFork3 #3334

Merged
merged 2 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
19 changes: 14 additions & 5 deletions core/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,18 @@ 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference the exact time is currently 2020-06-19 18:49:36 +0000 UTC


/// AutomatedTesting and UserTesting first hard fork height.
pub const TESTING_FIRST_HARD_FORK: u64 = 3;

/// AutomatedTesting and UserTesting second hard fork height.
pub const TESTING_SECOND_HARD_FORK: u64 = 6;

/// AutomatedTesting and UserTesting third hard fork height.
pub const TESTING_THIRD_HARD_FORK: u64 = 9;
tromp marked this conversation as resolved.
Show resolved Hide resolved

/// Compute possible block version at a given height, implements
/// 6 months interval scheduled hard forks for the first 2 years.
pub fn header_version(height: u64) -> HeaderVersion {
Expand All @@ -151,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)
}
Expand All @@ -162,8 +170,10 @@ pub fn header_version(height: u64) -> HeaderVersion {
HeaderVersion(1)
} else if height < TESTING_SECOND_HARD_FORK {
HeaderVersion(2)
} else if height < 3 * HARD_FORK_INTERVAL {
} else if height < TESTING_THIRD_HARD_FORK {
HeaderVersion(3)
} else if height < 4 * HARD_FORK_INTERVAL {
HeaderVersion(4)
} else {
HeaderVersion(hf_interval)
}
Expand All @@ -174,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)
}
Comment on lines 186 to 188
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized this logic is only correct for mainnet.
It is not valid for either floonet or usertesting which do not use HARD_FORK_INTERVAL directly.

This is not specific to this PR and I don't think it has a material impact here - but we should revisit this.

Tracking here - #3335

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't floonet and usertesting be limited to 2 years worth of block just like mainnet is for version 4? Of course that will be changed downward at the next HF, but for now it looks fine to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah maybe that is fine actually.
I was originally thinking automatedtesting wants to be able to test an invalid block due to excessive block height. But its going to be based on header version anyway.

👍


/// Number of blocks used to calculate difficulty adjustments
Expand All @@ -194,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;

Expand Down
12 changes: 9 additions & 3 deletions core/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -187,8 +187,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
tromp marked this conversation as resolved.
Show resolved Hide resolved
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)
}
Expand All @@ -199,6 +202,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)
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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};
Expand Down
193 changes: 193 additions & 0 deletions core/src/pow/cuckarooz.rs
Original file line number Diff line number Diff line change
@@ -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<T>(
edge_bits: u8,
proof_size: usize,
) -> Result<Box<dyn PoWContext<T>>, 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<T>
where
T: EdgeType,
{
params: CuckooParams<T>,
}

impl<T> PoWContext<T> for CuckaroozContext<T>
where
T: EdgeType,
{
fn set_header_nonce(
&mut self,
header: Vec<u8>,
nonce: Option<u32>,
_solve: bool,
) -> Result<(), Error> {
self.params.reset_header_nonce(header, nonce)
}

fn find_cycles(&mut self) -> Result<Vec<Proof>, 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::<u64>(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::<u64>(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<T>(edge_bits: u8, proof_size: usize) -> CuckaroozContext<T>
where
T: EdgeType,
{
let params = CuckooParams::new(edge_bits, proof_size).unwrap();
CuckaroozContext { params }
}
}
Loading