header version 4 accompanied by new AR PoW (#3334)

This commit is contained in:
John Tromp 2020-06-04 15:05:56 +02:00 committed by GitHub
parent 450d235358
commit f4295917a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 339 additions and 39 deletions

View file

@ -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 /// Floonet second hard fork height, set to happen around 2019-12-19
pub const FLOONET_SECOND_HARD_FORK: u64 = 298_080; 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. /// AutomatedTesting and UserTesting HF1 height.
pub const TESTING_FIRST_HARD_FORK: u64 = 3; pub const TESTING_FIRST_HARD_FORK: u64 = 3;
@ -154,8 +157,10 @@ pub fn header_version(height: u64) -> HeaderVersion {
HeaderVersion(1) HeaderVersion(1)
} else if height < FLOONET_SECOND_HARD_FORK { } else if height < FLOONET_SECOND_HARD_FORK {
HeaderVersion(2) HeaderVersion(2)
} else if height < 3 * HARD_FORK_INTERVAL { } else if height < FLOONET_THIRD_HARD_FORK {
HeaderVersion(3) HeaderVersion(3)
} else if height < 4 * HARD_FORK_INTERVAL {
HeaderVersion(4)
} else { } else {
HeaderVersion(hf_interval) 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 /// Check whether the block version is valid at a given height, implements
/// 6 months interval scheduled hard forks for the first 2 years. /// 6 months interval scheduled hard forks for the first 2 years.
pub fn valid_header_version(height: u64, version: HeaderVersion) -> bool { 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 /// 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; pub const AR_SCALE_DAMP_FACTOR: u64 = 13;
/// Compute weight of a graph as number of siphash bits defining the graph /// 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 /// The height dependence allows a 30-week linear transition from C31+ to C32+ starting after 1 year
/// Later phase outs are on hold for now
pub fn graph_weight(height: u64, edge_bits: u8) -> u64 { pub fn graph_weight(height: u64, edge_bits: u8) -> u64 {
let mut xpr_edge_bits = edge_bits as u64; let mut xpr_edge_bits = edge_bits as u64;

View file

@ -24,8 +24,8 @@ use crate::consensus::{
}; };
use crate::core::block::HeaderVersion; use crate::core::block::HeaderVersion;
use crate::pow::{ use crate::pow::{
self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckaroom_ctx, new_cuckatoo_ctx, EdgeType, self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckaroom_ctx, new_cuckarooz_ctx,
PoWContext, new_cuckatoo_ctx, EdgeType, PoWContext,
}; };
use std::cell::Cell; use std::cell::Cell;
use util::OneTime; use util::OneTime;
@ -225,8 +225,11 @@ where
{ {
let chain_type = get_chain_type(); let chain_type = get_chain_type();
match 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 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)) => { ChainTypes::Mainnet if valid_header_version(height, HeaderVersion(3)) => {
new_cuckaroom_ctx(edge_bits, proof_size) new_cuckaroom_ctx(edge_bits, proof_size)
} }
@ -237,6 +240,9 @@ where
// Same for Floonet // Same for Floonet
ChainTypes::Floonet if edge_bits > 29 => new_cuckatoo_ctx(edge_bits, proof_size, max_sols), 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)) => { ChainTypes::Floonet if valid_header_version(height, HeaderVersion(3)) => {
new_cuckaroom_ctx(edge_bits, proof_size) new_cuckaroom_ctx(edge_bits, proof_size)
} }

View file

@ -41,6 +41,7 @@ mod common;
pub mod cuckaroo; pub mod cuckaroo;
pub mod cuckarood; pub mod cuckarood;
pub mod cuckaroom; pub mod cuckaroom;
pub mod cuckarooz;
pub mod cuckatoo; pub mod cuckatoo;
mod error; mod error;
#[allow(dead_code)] #[allow(dead_code)]
@ -51,6 +52,7 @@ mod types;
pub use crate::pow::cuckaroo::{new_cuckaroo_ctx, CuckarooContext}; pub use crate::pow::cuckaroo::{new_cuckaroo_ctx, CuckarooContext};
pub use crate::pow::cuckarood::{new_cuckarood_ctx, CuckaroodContext}; pub use crate::pow::cuckarood::{new_cuckarood_ctx, CuckaroodContext};
pub use crate::pow::cuckaroom::{new_cuckaroom_ctx, CuckaroomContext}; 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::cuckatoo::{new_cuckatoo_ctx, CuckatooContext};
pub use crate::pow::error::Error; pub use crate::pow::error::Error;
use chrono::prelude::{DateTime, NaiveDateTime, Utc}; use chrono::prelude::{DateTime, NaiveDateTime, Utc};

193
core/src/pow/cuckarooz.rs Normal file
View file

@ -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 }
}
}

View file

@ -13,7 +13,7 @@
use grin_core::consensus::{ use grin_core::consensus::{
secondary_pow_ratio, valid_header_version, FLOONET_FIRST_HARD_FORK, FLOONET_SECOND_HARD_FORK, 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::core::HeaderVersion;
use grin_core::global; use grin_core::global;
@ -84,7 +84,6 @@ fn hard_forks() {
FLOONET_FIRST_HARD_FORK, FLOONET_FIRST_HARD_FORK,
HeaderVersion(1) HeaderVersion(1)
)); ));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion(1)));
assert!(valid_header_version( assert!(valid_header_version(
FLOONET_SECOND_HARD_FORK - 1, FLOONET_SECOND_HARD_FORK - 1,
HeaderVersion(2) HeaderVersion(2)
@ -105,19 +104,70 @@ fn hard_forks() {
FLOONET_SECOND_HARD_FORK, FLOONET_SECOND_HARD_FORK,
HeaderVersion(1) HeaderVersion(1)
)); ));
assert!(valid_header_version(
assert!(!valid_header_version(YEAR_HEIGHT - 1, HeaderVersion(2))); FLOONET_THIRD_HARD_FORK - 1,
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,
HeaderVersion(3) 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)
));
} }

View file

@ -438,25 +438,70 @@ fn hard_forks() {
assert!(valid_header_version(0, HeaderVersion(1))); assert!(valid_header_version(0, HeaderVersion(1)));
assert!(valid_header_version(10, HeaderVersion(1))); assert!(valid_header_version(10, HeaderVersion(1)));
assert!(!valid_header_version(10, HeaderVersion(2))); assert!(!valid_header_version(10, HeaderVersion(2)));
assert!(valid_header_version(YEAR_HEIGHT / 2 - 1, HeaderVersion(1))); assert!(valid_header_version(
assert!(valid_header_version(YEAR_HEIGHT / 2, HeaderVersion(2))); HARD_FORK_INTERVAL - 1,
assert!(valid_header_version(YEAR_HEIGHT / 2 + 1, HeaderVersion(2))); HeaderVersion(1)
assert!(!valid_header_version(YEAR_HEIGHT / 2, HeaderVersion(1))); ));
assert!(!valid_header_version(YEAR_HEIGHT, 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(
assert!(valid_header_version(YEAR_HEIGHT, HeaderVersion(3))); HARD_FORK_INTERVAL * 2 - 1,
assert!(valid_header_version(YEAR_HEIGHT + 1, HeaderVersion(3))); HeaderVersion(2)
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( 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) 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)
));
} }