diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 7670fbfd3..6a9d40b75 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -821,14 +821,14 @@ impl<'a> Extension<'a> { if pos > 0 { // If we have not yet reached 1,000 / 1,440 blocks then // we can fail immediately as coinbase cannot be mature. - if height < global::coinbase_maturity(height) { + if height < global::coinbase_maturity() { return Err(ErrorKind::ImmatureCoinbase.into()); } // Find the "cutoff" pos in the output MMR based on the // header from 1,000 blocks ago. let cutoff_height = height - .checked_sub(global::coinbase_maturity(height)) + .checked_sub(global::coinbase_maturity()) .unwrap_or(0); let cutoff_header = self.batch.get_header_by_height(cutoff_height)?; let cutoff_pos = cutoff_header.output_mmr_size; diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 52215bd9d..ef7fef954 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -99,7 +99,7 @@ fn test_coinbase_maturity() { let amount = consensus::REWARD; - let lock_height = 1 + global::coinbase_maturity(1); + let lock_height = 1 + global::coinbase_maturity(); assert_eq!(lock_height, 4); // here we build a tx that attempts to spend the earlier coinbase output diff --git a/core/src/consensus.rs b/core/src/consensus.rs index 454283b9c..a6596d43a 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -33,30 +33,34 @@ pub const MICRO_GRIN: u64 = MILLI_GRIN / 1_000; /// Nanogrin, smallest unit, takes a billion to make a grin pub const NANO_GRIN: u64 = 1; -/// The block subsidy amount, one grin per second on average -pub const REWARD: u64 = 60 * GRIN_BASE; - -/// Actual block reward for a given total fee amount -pub fn reward(fee: u64) -> u64 { - REWARD + fee -} - /// Block interval, in seconds, the network will tune its next_target for. Note /// that we may reduce this value in the future as we get more data on mining /// with Cuckoo Cycle, networks improve and block propagation is optimized /// (adjusting the reward accordingly). pub const BLOCK_TIME_SEC: u64 = 60; +/// The block subsidy amount, one grin per second on average +pub const REWARD: u64 = BLOCK_TIME_SEC * GRIN_BASE; + +/// Actual block reward for a given total fee amount +pub fn reward(fee: u64) -> u64 { + REWARD + fee +} + +/// Nominal height for standard time intervals +pub const HOUR_HEIGHT: u64 = 3600 / BLOCK_TIME_SEC; +pub const DAY_HEIGHT: u64 = 24 * HOUR_HEIGHT; +pub const WEEK_HEIGHT: u64 = 7 * DAY_HEIGHT; +pub const YEAR_HEIGHT: u64 = 52 * WEEK_HEIGHT; + /// Number of blocks before a coinbase matures and can be spent -/// set to nominal number of block in one day (1440 with 1-minute blocks) -pub const COINBASE_MATURITY: u64 = 24 * 60 * 60 / BLOCK_TIME_SEC; +pub const COINBASE_MATURITY: u64 = DAY_HEIGHT; /// Ratio the secondary proof of work should take over the primary, as a /// function of block height (time). Starts at 90% losing a percent -/// approximately every week (10000 blocks). Represented as an integer -/// between 0 and 100. +/// approximately every week. Represented as an integer between 0 and 100. pub fn secondary_pow_ratio(height: u64) -> u64 { - 90u64.saturating_sub(height / 10000) + 90u64.saturating_sub(height / WEEK_HEIGHT) } /// Cuckoo-cycle proof size (cycle length) @@ -83,7 +87,7 @@ pub const MAX_SECONDARY_SCALING: u64 = 8 << 11; /// behind the value is the longest bitcoin fork was about 30 blocks, so 5h. We /// add an order of magnitude to be safe and round to 7x24h of blocks to make it /// easier to reason about. -pub const CUT_THROUGH_HORIZON: u32 = 7 * 24 * 3600 / (BLOCK_TIME_SEC as u32); +pub const CUT_THROUGH_HORIZON: u32 = WEEK_HEIGHT as u32; /// Weight of an input when counted against the max block weight capacity pub const BLOCK_INPUT_WEIGHT: usize = 1; @@ -106,12 +110,11 @@ pub const BLOCK_KERNEL_WEIGHT: usize = 2; /// outputs and a single kernel). /// /// A more "standard" block, filled with transactions of 2 inputs, 2 outputs -/// and one kernel, should be around 2_663_333 bytes. +/// and one kernel, should be around 2.66 MB pub const MAX_BLOCK_WEIGHT: usize = 40_000; -/// Fork every 250,000 blocks for first 2 years, simple number and just a -/// little less than 6 months. -pub const HARD_FORK_INTERVAL: u64 = 250_000; +/// Fork every 6 months. +pub const HARD_FORK_INTERVAL: u64 = YEAR_HEIGHT / 2; /// Check whether the block version is valid at a given height, implements /// 6 months interval scheduled hard forks for the first 2 years. @@ -139,7 +142,7 @@ pub const MEDIAN_TIME_WINDOW: u64 = 11; pub const MEDIAN_TIME_INDEX: u64 = MEDIAN_TIME_WINDOW / 2; /// Number of blocks used to calculate difficulty adjustments -pub const DIFFICULTY_ADJUST_WINDOW: u64 = 60; +pub const DIFFICULTY_ADJUST_WINDOW: u64 = HOUR_HEIGHT; /// Average time span of the difficulty adjustment window pub const BLOCK_TIME_WINDOW: u64 = DIFFICULTY_ADJUST_WINDOW * BLOCK_TIME_SEC; @@ -153,12 +156,20 @@ pub const LOWER_TIME_BOUND: u64 = BLOCK_TIME_WINDOW / 2; /// Dampening factor to use for difficulty adjustment pub const DAMP_FACTOR: u64 = 3; +/// Compute difficulty scaling factor as number of siphash bits defining the graph +/// Must be made dependent on height to phase out smaller size over the years +/// This can wait until end of 2019 at latest +pub fn scale(edge_bits: u8) -> u64 { + (2 << (edge_bits - global::base_edge_bits()) as u64) * (edge_bits as u64) +} + /// The initial difficulty at launch. This should be over-estimated /// and difficulty should come down at launch rather than up /// Currently grossly over-estimated at 10% of current -/// ethereum GPUs (assuming 1GPU can solve a block at diff 1 -/// in one block interval) -pub const INITIAL_DIFFICULTY: u64 = 1_000_000; +/// ethereum GPUs (assuming 1GPU can solve a block at diff 1 in one block interval) +/// Pick MUCH more modest value for TESTNET4; CHANGE FOR MAINNET +pub const INITIAL_DIFFICULTY: u64 = 1_000 * (2<<(29-24)) * 29; // scale(SECOND_POW_EDGE_BITS); +/// pub const INITIAL_DIFFICULTY: u64 = 1_000_000 * Difficulty::scale(SECOND_POW_EDGE_BITS); /// Consensus errors #[derive(Clone, Debug, Eq, PartialEq, Fail)] diff --git a/core/src/global.rs b/core/src/global.rs index 34483c653..7beb64ffc 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -20,7 +20,7 @@ use consensus::HeaderInfo; use consensus::{ BASE_EDGE_BITS, BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON, DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, MEDIAN_TIME_WINDOW, PROOFSIZE, - SECOND_POW_EDGE_BITS, + SECOND_POW_EDGE_BITS, DAY_HEIGHT }; use pow::{self, CuckatooContext, EdgeType, PoWContext}; /// An enum collecting sets of parameters used throughout the @@ -50,12 +50,6 @@ pub const AUTOMATED_TESTING_COINBASE_MATURITY: u64 = 3; /// User testing coinbase maturity pub const USER_TESTING_COINBASE_MATURITY: u64 = 3; -/// Old coinbase maturity -/// TODO: obsolete for mainnet together with maturity code below -pub const OLD_COINBASE_MATURITY: u64 = 1_000; -/// soft-fork around Sep 17 2018 on testnet3 -pub const COINBASE_MATURITY_FORK_HEIGHT: u64 = 100_000; - /// Testing cut through horizon in blocks pub const TESTING_CUT_THROUGH_HORIZON: u32 = 20; @@ -70,12 +64,13 @@ pub const TESTNET2_INITIAL_DIFFICULTY: u64 = 1000; pub const TESTNET3_INITIAL_DIFFICULTY: u64 = 30000; /// Testnet 4 initial block difficulty -pub const TESTNET4_INITIAL_DIFFICULTY: u64 = 1; +/// 1_000 times natural scale factor for cuckatoo29 +pub const TESTNET4_INITIAL_DIFFICULTY: u64 = 1_000 * (2<<(29-24)) * 29; -/// Trigger compaction check on average every 1440 blocks (i.e. one day) for FAST_SYNC_NODE, +/// Trigger compaction check on average every day for FAST_SYNC_NODE, /// roll the dice on every block to decide, /// all blocks lower than (BodyHead.height - CUT_THROUGH_HORIZON) will be removed. -pub const COMPACTION_CHECK: u64 = 1440; +pub const COMPACTION_CHECK: u64 = DAY_HEIGHT; /// Types of chain a server can run with, dictates the genesis block and /// and mining parameters used. @@ -180,17 +175,13 @@ pub fn proofsize() -> usize { } } -/// Coinbase maturity for coinbases to be spent at given height -pub fn coinbase_maturity(height: u64) -> u64 { +/// Coinbase maturity for coinbases to be spent +pub fn coinbase_maturity() -> u64 { let param_ref = CHAIN_TYPE.read().unwrap(); match *param_ref { ChainTypes::AutomatedTesting => AUTOMATED_TESTING_COINBASE_MATURITY, ChainTypes::UserTesting => USER_TESTING_COINBASE_MATURITY, - _ => if height < COINBASE_MATURITY_FORK_HEIGHT { - OLD_COINBASE_MATURITY - } else { - COINBASE_MATURITY - }, + _ => COINBASE_MATURITY, } } diff --git a/core/src/pow/common.rs b/core/src/pow/common.rs index 692670d32..7c5ef03e6 100644 --- a/core/src/pow/common.rs +++ b/core/src/pow/common.rs @@ -84,8 +84,10 @@ pub fn set_header_nonce(header: Vec, nonce: Option) -> Result<[u64; 4], let mut header = header.clone(); header.truncate(len - mem::size_of::()); header.write_u32::(n)?; + create_siphash_keys(header) + } else { + create_siphash_keys(header) } - create_siphash_keys(header) } pub fn create_siphash_keys(header: Vec) -> Result<[u64; 4], Error> { @@ -165,15 +167,9 @@ where /// Reset the main keys used for siphash from the header and nonce pub fn reset_header_nonce( &mut self, - mut header: Vec, + header: Vec, nonce: Option, ) -> Result<(), Error> { - // THIS IF LOOKS REDUNDANT SINCE set_header_nonce DOES SAME THING - if let Some(n) = nonce { - let len = header.len(); - header.truncate(len - mem::size_of::()); - header.write_u32::(n)?; - } self.siphash_keys = set_header_nonce(header, nonce)?; Ok(()) } diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index 5d39a3462..989ae277f 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -14,14 +14,14 @@ /// Types for a Cuck(at)oo proof of work and its encapsulation as a fully usable /// proof of work within a block header. -use std::cmp::max; +use std::cmp::{min,max}; use std::ops::{Add, Div, Mul, Sub}; use std::{fmt, iter}; use rand::{thread_rng, Rng}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use consensus::SECOND_POW_EDGE_BITS; +use consensus::{self, SECOND_POW_EDGE_BITS}; use core::hash::Hashed; use global; use ser::{self, Readable, Reader, Writeable, Writer}; @@ -89,11 +89,8 @@ impl Difficulty { /// provided hash and applies the Cuck(at)oo size adjustment factor (see /// https://lists.launchpad.net/mimblewimble/msg00494.html). fn from_proof_adjusted(proof: &Proof) -> Difficulty { - // Adjust the difficulty based on a 2^(N-M)*(N-1) factor, with M being - // the minimum edge_bits and N the provided edge_bits - let edge_bits = proof.edge_bits; - - Difficulty::from_num(proof.raw_difficulty() * Difficulty::scale(edge_bits)) + // scale with natural scaling factor + Difficulty::from_num(proof.scaled_difficulty(consensus::scale(proof.edge_bits))) } /// Same as `from_proof_adjusted` but instead of an adjustment based on @@ -101,7 +98,7 @@ impl Difficulty { /// to scale one PoW against the other. fn from_proof_scaled(proof: &Proof, scaling: u32) -> Difficulty { // Scaling between 2 proof of work algos - Difficulty::from_num(proof.raw_difficulty() * scaling as u64) + Difficulty::from_num(proof.scaled_difficulty(scaling as u64)) } /// Converts the difficulty into a u64 @@ -383,9 +380,10 @@ impl Proof { self.nonces.len() } - /// Difficulty achieved by this proof - fn raw_difficulty(&self) -> u64 { - ::max_value() / self.hash().to_u64() + /// Difficulty achieved by this proof with given scaling factor + fn scaled_difficulty(&self, scale: u64) -> u64 { + let diff = ((scale as u128) << 64) / (self.hash().to_u64() as u128); + min(diff, ::max_value() as u128) as u64 } } diff --git a/core/tests/consensus.rs b/core/tests/consensus.rs index 5895fb18d..ce34ae5ee 100644 --- a/core/tests/consensus.rs +++ b/core/tests/consensus.rs @@ -581,12 +581,12 @@ fn hard_forks() { assert!(valid_header_version(0, 1)); assert!(valid_header_version(10, 1)); assert!(!valid_header_version(10, 2)); - assert!(valid_header_version(249_999, 1)); + assert!(valid_header_version(YEAR_HEIGHT/2-1, 1)); // v2 not active yet - assert!(!valid_header_version(250_000, 2)); - assert!(!valid_header_version(250_000, 1)); - assert!(!valid_header_version(500_000, 1)); - assert!(!valid_header_version(250_001, 2)); + assert!(!valid_header_version(YEAR_HEIGHT/2, 2)); + assert!(!valid_header_version(YEAR_HEIGHT/2, 1)); + assert!(!valid_header_version(YEAR_HEIGHT, 1)); + assert!(!valid_header_version(YEAR_HEIGHT/2+1, 2)); } // #[test] diff --git a/wallet/src/libwallet/internal/restore.rs b/wallet/src/libwallet/internal/restore.rs index 54b97b1d6..28c6be1dd 100644 --- a/wallet/src/libwallet/internal/restore.rs +++ b/wallet/src/libwallet/internal/restore.rs @@ -76,7 +76,7 @@ where ); let lock_height = if *is_coinbase { - *height + global::coinbase_maturity(*height) // ignores on/off spendability around soft fork height + *height + global::coinbase_maturity() } else { *height }; diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index fcf729ced..80d34462e 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -398,7 +398,7 @@ where K: Keychain, { let height = block_fees.height; - let lock_height = height + global::coinbase_maturity(height); // ignores on/off spendability around soft fork height + let lock_height = height + global::coinbase_maturity(); let key_id = block_fees.key_id(); let parent_key_id = wallet.parent_key_id(); diff --git a/wallet/tests/accounts.rs b/wallet/tests/accounts.rs index efe08a960..57c50ca68 100644 --- a/wallet/tests/accounts.rs +++ b/wallet/tests/accounts.rs @@ -75,7 +75,7 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> { // few values to keep things shorter let reward = core::consensus::REWARD; - let cm = global::coinbase_maturity(0); // assume all testing precedes soft fork height + let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height // test default accounts exist wallet::controller::owner_single_use(wallet1.clone(), |api| { diff --git a/wallet/tests/transaction.rs b/wallet/tests/transaction.rs index 019a9a6df..0b27d00a3 100644 --- a/wallet/tests/transaction.rs +++ b/wallet/tests/transaction.rs @@ -79,7 +79,7 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> { // few values to keep things shorter let reward = core::consensus::REWARD; - let cm = global::coinbase_maturity(0); // assume all testing precedes soft fork height + let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height // mine a few blocks let _ = common::award_blocks_to_wallet(&chain, wallet1.clone(), 10); @@ -324,7 +324,7 @@ fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> { // few values to keep things shorter let reward = core::consensus::REWARD; - let cm = global::coinbase_maturity(0); // assume all testing precedes soft fork height + let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height // mine a few blocks let _ = common::award_blocks_to_wallet(&chain, wallet1.clone(), 5);