2016-11-11 03:02:47 +03:00
|
|
|
// Copyright 2016 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.
|
|
|
|
|
|
|
|
//! All the rules required for a cryptocurrency to have reach consensus across
|
|
|
|
//! the whole network are complex and hard to completely isolate. Some can be
|
|
|
|
//! simple parameters (like block reward), others complex algorithms (like
|
|
|
|
//! Merkle sum trees or reorg rules). However, as long as they're simple
|
2016-11-14 22:21:18 +03:00
|
|
|
//! enough, consensus-relevant constants and short functions should be kept
|
2016-11-11 03:02:47 +03:00
|
|
|
//! here.
|
|
|
|
|
2016-11-16 01:37:49 +03:00
|
|
|
use std::cmp;
|
|
|
|
|
|
|
|
use core::target::Target;
|
|
|
|
|
2016-11-11 03:02:47 +03:00
|
|
|
/// The block subsidy amount
|
|
|
|
pub const REWARD: u64 = 1_000_000_000;
|
|
|
|
|
2016-11-16 01:37:49 +03:00
|
|
|
/// 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: u8 = 60;
|
2016-11-11 03:02:47 +03:00
|
|
|
|
|
|
|
/// Cuckoo-cycle proof size (cycle length)
|
|
|
|
pub const PROOFSIZE: usize = 42;
|
|
|
|
|
2016-11-16 01:37:49 +03:00
|
|
|
/// Origin Cuckoo Cycle size shift used by the genesis block.
|
|
|
|
pub const DEFAULT_SIZESHIFT: u8 = 25;
|
|
|
|
|
|
|
|
/// Maximum Cuckoo Cycle size shift we'll ever use. We adopt a schedule that
|
|
|
|
/// progressively increases the size as the target becomes lower.
|
|
|
|
/// Start => 25
|
|
|
|
/// MAX_TARGET >> 12 => 26
|
|
|
|
/// MAX_TARGET >> 20 => 27
|
|
|
|
/// MAX_TARGET >> 28 => 28
|
|
|
|
/// MAX_TARGET >> 36 => 29
|
|
|
|
pub const MAX_SIZESHIFT: u8 = 29;
|
2016-11-11 03:02:47 +03:00
|
|
|
|
|
|
|
/// Default Cuckoo Cycle easiness, high enough to have good likeliness to find
|
|
|
|
/// a solution.
|
|
|
|
pub const EASINESS: u32 = 50;
|
|
|
|
|
2016-11-16 01:37:49 +03:00
|
|
|
/// Difficulty adjustment somewhat inspired by Ethereum's. Tuned to add or
|
|
|
|
/// remove 1/1024th of the target for each 10 seconds of deviation from the 30
|
|
|
|
/// seconds block time. Increases Cuckoo size shift by one when next_target
|
|
|
|
/// reaches soft max.
|
|
|
|
pub fn next_target(ts: i64, prev_ts: i64, prev_target: Target, prev_cuckoo_sz: u8) -> (Target, u8) {
|
|
|
|
// increase the cuckoo size when the target gets lower than the soft min as
|
|
|
|
// long as we're not at the max size already; target gets 2x to compensate for
|
|
|
|
// increased next_target
|
2016-11-30 07:03:43 +03:00
|
|
|
let soft_min = SOFT_MIN_TARGET >> (((prev_cuckoo_sz - cmp::min(DEFAULT_SIZESHIFT, prev_cuckoo_sz)) * 8) as usize);
|
2016-11-16 01:37:49 +03:00
|
|
|
let (ptarget, clen) = if prev_target < soft_min && prev_cuckoo_sz < MAX_SIZESHIFT {
|
|
|
|
(prev_target << 1, prev_cuckoo_sz + 1)
|
|
|
|
} else {
|
|
|
|
(prev_target, prev_cuckoo_sz)
|
|
|
|
};
|
|
|
|
|
|
|
|
// target is increased/decreased by multiples of 1/1024th of itself
|
|
|
|
let incdec = ptarget >> 10;
|
|
|
|
// signed deviation from desired value divided by ten and bounded in [-3, 3]
|
|
|
|
let delta = cmp::max(cmp::min((ts - prev_ts - (BLOCK_TIME_SEC as i64)) / 10, 3),
|
|
|
|
-3);
|
|
|
|
// increase or decrease the target based on the sign of delta by a shift of
|
|
|
|
// |delta|-1; keep as-is for delta of zero
|
|
|
|
let new_target = match delta {
|
|
|
|
1...3 => ptarget + (incdec << ((delta - 1) as usize)),
|
|
|
|
-3...-1 => ptarget - (incdec << ((-delta - 1) as usize)),
|
|
|
|
_ => ptarget,
|
|
|
|
};
|
|
|
|
|
|
|
|
// cannot exceed the maximum target
|
|
|
|
if new_target > MAX_TARGET {
|
|
|
|
(MAX_TARGET, clen)
|
|
|
|
} else {
|
2016-11-30 07:03:43 +03:00
|
|
|
(new_target.truncate(), clen)
|
2016-11-16 01:37:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Max target hash, lowest next_target
|
2016-11-30 07:03:43 +03:00
|
|
|
pub const MAX_TARGET: Target = Target([0xf, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0]);
|
2016-11-16 01:37:49 +03:00
|
|
|
|
|
|
|
/// Target limit under which we start increasing the size shift on Cuckoo cycle.
|
2016-11-30 07:03:43 +03:00
|
|
|
pub const SOFT_MIN_TARGET: Target = Target([0, 0, 0xf, 0xff, 0xff, 0xff, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0]);
|
2016-11-16 01:37:49 +03:00
|
|
|
|
2016-11-11 04:19:03 +03:00
|
|
|
/// Default number of blocks in the past when cross-block cut-through will start
|
2016-11-16 01:37:49 +03:00
|
|
|
/// happening. Needs to be long enough to not overlap with a long reorg.
|
|
|
|
/// Rational
|
2016-11-11 04:19:03 +03:00
|
|
|
/// 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 48h of blocks to make it
|
|
|
|
/// easier to reason about.
|
|
|
|
pub const CUT_THROUGH_HORIZON: u32 = 48 * 3600 / (BLOCK_TIME_SEC as u32);
|
|
|
|
|
2016-12-08 05:54:28 +03:00
|
|
|
/// The maximum size we're willing to accept for any message. Enforced by the peer-to-peer networking layer only for DoS protection.
|
|
|
|
pub const MAX_MSG_LEN: u64 = 20_000_000;
|
2016-11-16 01:37:49 +03:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
/// Checks different next_target adjustments and difficulty boundaries
|
|
|
|
fn next_target_adjustment() {
|
|
|
|
// can't do lower than min
|
|
|
|
assert_eq!(next_target(60, 0, MAX_TARGET, 26), (MAX_TARGET, 26));
|
|
|
|
assert_eq!(next_target(90, 30, MAX_TARGET, 26), (MAX_TARGET, 26));
|
|
|
|
assert_eq!(next_target(60, 0, MAX_TARGET, 26), (MAX_TARGET, 26));
|
|
|
|
|
|
|
|
// lower next_target if gap too short, even negative
|
|
|
|
assert_eq!(next_target(50, 0, MAX_TARGET, 26).0,
|
2016-11-30 22:15:49 +03:00
|
|
|
(MAX_TARGET - (MAX_TARGET >> 10)).truncate());
|
2016-11-16 01:37:49 +03:00
|
|
|
assert_eq!(next_target(40, 0, MAX_TARGET, 26).0,
|
2016-11-30 22:15:49 +03:00
|
|
|
(MAX_TARGET - ((MAX_TARGET >> 10) << 1)).truncate());
|
2016-11-16 01:37:49 +03:00
|
|
|
assert_eq!(next_target(0, 20, MAX_TARGET, 26).0,
|
2016-11-30 22:15:49 +03:00
|
|
|
(MAX_TARGET - ((MAX_TARGET >> 10) << 2)).truncate());
|
2016-11-16 01:37:49 +03:00
|
|
|
|
|
|
|
// raise next_target if gap too wide
|
|
|
|
let lower_target = MAX_TARGET >> 8;
|
|
|
|
assert_eq!(next_target(70, 0, lower_target, 26).0,
|
2016-11-30 22:15:49 +03:00
|
|
|
(lower_target + (lower_target >> 10)).truncate());
|
2016-11-16 01:37:49 +03:00
|
|
|
assert_eq!(next_target(80, 0, lower_target, 26).0,
|
2016-11-30 22:15:49 +03:00
|
|
|
(lower_target + ((lower_target >> 10) << 1)).truncate());
|
2016-11-16 01:37:49 +03:00
|
|
|
assert_eq!(next_target(200, 0, lower_target, 26).0,
|
2016-11-30 22:15:49 +03:00
|
|
|
(lower_target + ((lower_target >> 10) << 2)).truncate());
|
2016-11-16 01:37:49 +03:00
|
|
|
|
|
|
|
// close enough, no adjustment
|
|
|
|
assert_eq!(next_target(65, 0, lower_target, 26).0, lower_target);
|
|
|
|
assert_eq!(next_target(55, 0, lower_target, 26).0, lower_target);
|
|
|
|
|
|
|
|
// increase cuckoo size if next_target goes above soft max, target is doubled,
|
|
|
|
// up to 29
|
|
|
|
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 1, 25),
|
|
|
|
((SOFT_MIN_TARGET >> 1) << 1, 26));
|
|
|
|
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 9, 26),
|
|
|
|
((SOFT_MIN_TARGET >> 9) << 1, 27));
|
|
|
|
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 17, 27),
|
|
|
|
((SOFT_MIN_TARGET >> 17) << 1, 28));
|
|
|
|
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 25, 28),
|
|
|
|
((SOFT_MIN_TARGET >> 25) << 1, 29));
|
|
|
|
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 33, 29),
|
|
|
|
(SOFT_MIN_TARGET >> 33, 29));
|
|
|
|
|
|
|
|
// should only increase on the according previous size
|
|
|
|
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 1, 26),
|
|
|
|
(SOFT_MIN_TARGET >> 1, 26));
|
|
|
|
}
|
|
|
|
}
|