Block versions and hard fork schedule

This commit is contained in:
Ignotus Peverell 2017-10-10 00:08:17 +00:00
parent 62954f1549
commit 9310151680
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
5 changed files with 92 additions and 43 deletions

View file

@ -132,6 +132,34 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
return Err(Error::Orphan);
}
// check version, enforces scheduled hard fork
if !consensus::valid_header_version(header.height, header.version) {
error!("Invalid block header version received ({}), maybe update Grin?",
header.version);
return Err(Error::InvalidBlockVersion(header.version));
}
if header.timestamp >
time::now_utc() + time::Duration::seconds(12 * (consensus::BLOCK_TIME_SEC as i64))
{
// refuse blocks more than 12 blocks intervals in future (as in bitcoin)
// TODO add warning in p2p code if local time is too different from peers
return Err(Error::InvalidBlockTime);
}
if !ctx.opts.intersects(SKIP_POW) {
let cycle_size = if ctx.opts.intersects(EASY_POW) {
global::sizeshift()
} else {
consensus::DEFAULT_SIZESHIFT
};
debug!("Validating block with cuckoo size {}", cycle_size);
if !(ctx.pow_verifier)(header, cycle_size as u32) {
return Err(Error::InvalidPow);
}
}
// first I/O cost, better as late as possible
let prev = try!(ctx.store.get_block_header(&header.previous).map_err(
&Error::StoreErr,
));
@ -144,13 +172,6 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
// time progression (but not in CI mode)
return Err(Error::InvalidBlockTime);
}
if header.timestamp >
time::now_utc() + time::Duration::seconds(12 * (consensus::BLOCK_TIME_SEC as i64))
{
// refuse blocks more than 12 blocks intervals in future (as in bitcoin)
// TODO add warning in p2p code if local time is too different from peers
return Err(Error::InvalidBlockTime);
}
if !ctx.opts.intersects(SKIP_POW) {
// verify the proof of work and related parameters
@ -166,16 +187,6 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
if header.difficulty < difficulty {
return Err(Error::DifficultyTooLow);
}
let cycle_size = if ctx.opts.intersects(EASY_POW) {
global::sizeshift()
} else {
consensus::DEFAULT_SIZESHIFT
};
debug!("Validating block with cuckoo size {}", cycle_size);
if !(ctx.pow_verifier)(header, cycle_size as u32) {
return Err(Error::InvalidPow);
}
}
Ok(())

View file

@ -72,6 +72,8 @@ pub enum Error {
OutputNotFound,
/// output spent
OutputSpent,
/// Invalid block version, either a mistake or outdated software
InvalidBlockVersion(u16),
/// Internal issue when trying to save or load data from store
StoreErr(grin_store::Error),
/// Error serializing or deserializing a type

View file

@ -81,6 +81,29 @@ pub fn exceeds_weight(input_len: usize, output_len: usize, kernel_len: usize) ->
kernel_len * BLOCK_KERNEL_WEIGHT > MAX_BLOCK_WEIGHT
}
/// 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;
/// 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: u16) -> bool {
// uncomment below as we go from hard fork to hard fork
if height <= HARD_FORK_INTERVAL && version == 1 {
true
/* } else if height <= 2 * HARD_FORK_INTERVAL && version == 2 {
true */
/* } else if height <= 3 * HARD_FORK_INTERVAL && version == 3 {
true */
/* } else if height <= 4 * HARD_FORK_INTERVAL && version == 4 {
true */
/* } else if height > 4 * HARD_FORK_INTERVAL && version > 4 {
true */
} else {
false
}
}
/// The minimum mining difficulty we'll allow
pub const MINIMUM_DIFFICULTY: u64 = 10;
@ -292,4 +315,27 @@ mod test {
);
}
#[test]
fn hard_fork_1() {
assert!(valid_header_version(0, 1));
assert!(valid_header_version(10, 1));
assert!(!valid_header_version(10, 2));
assert!(valid_header_version(250_000, 1));
assert!(!valid_header_version(250_001, 1));
assert!(!valid_header_version(500_000, 1));
assert!(!valid_header_version(250_001, 2));
}
// #[test]
// fn hard_fork_2() {
// assert!(valid_header_version(0, 1));
// assert!(valid_header_version(10, 1));
// assert!(valid_header_version(10, 2));
// assert!(valid_header_version(250_000, 1));
// assert!(!valid_header_version(250_001, 1));
// assert!(!valid_header_version(500_000, 1));
// assert!(valid_header_version(250_001, 2));
// assert!(valid_header_version(500_000, 2));
// assert!(!valid_header_version(500_001, 2));
// }
}

View file

@ -27,14 +27,6 @@ use ser::{self, Readable, Reader, Writeable, Writer};
use global;
use keychain;
bitflags! {
/// Options for block validation
pub flags BlockFeatures: u8 {
/// No flags
const DEFAULT_BLOCK = 0b00000000,
}
}
/// Errors thrown by Block validation
#[derive(Debug, PartialEq)]
pub enum Error {
@ -61,6 +53,8 @@ impl From<secp::Error> for Error {
/// Block header, fairly standard compared to other blockchains.
#[derive(Clone, Debug, PartialEq)]
pub struct BlockHeader {
/// Version of the block
pub version: u16,
/// Height of this block since the genesis block (height 0)
pub height: u64,
/// Hash of the block previous to this in the chain.
@ -73,8 +67,6 @@ pub struct BlockHeader {
pub range_proof_root: Hash,
/// Merklish root of all transaction kernels in the UTXO set
pub kernel_root: Hash,
/// Features specific to this block, allowing possible future extensions
pub features: BlockFeatures,
/// Nonce increment used to mine this block.
pub nonce: u64,
/// Proof of work data.
@ -89,6 +81,7 @@ impl Default for BlockHeader {
fn default() -> BlockHeader {
let proof_size = global::proofsize();
BlockHeader {
version: 1,
height: 0,
previous: ZERO_HASH,
timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }),
@ -97,7 +90,6 @@ impl Default for BlockHeader {
utxo_root: ZERO_HASH,
range_proof_root: ZERO_HASH,
kernel_root: ZERO_HASH,
features: DEFAULT_BLOCK,
nonce: 0,
pow: Proof::zero(proof_size),
}
@ -109,13 +101,13 @@ impl Writeable for BlockHeader {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
ser_multiwrite!(
writer,
[write_u16, self.version],
[write_u64, self.height],
[write_fixed_bytes, &self.previous],
[write_i64, self.timestamp.to_timespec().sec],
[write_fixed_bytes, &self.utxo_root],
[write_fixed_bytes, &self.range_proof_root],
[write_fixed_bytes, &self.kernel_root],
[write_u8, self.features.bits()]
[write_fixed_bytes, &self.kernel_root]
);
try!(writer.write_u64(self.nonce));
@ -132,18 +124,19 @@ impl Writeable for BlockHeader {
/// Deserialization of a block header
impl Readable for BlockHeader {
fn read(reader: &mut Reader) -> Result<BlockHeader, ser::Error> {
let height = try!(reader.read_u64());
let previous = try!(Hash::read(reader));
let (version, height) = ser_multiread!(reader, read_u16, read_u64);
let previous = Hash::read(reader)?;
let timestamp = reader.read_i64()?;
let utxo_root = try!(Hash::read(reader));
let rproof_root = try!(Hash::read(reader));
let kernel_root = try!(Hash::read(reader));
let (features, nonce) = ser_multiread!(reader, read_u8, read_u64);
let difficulty = try!(Difficulty::read(reader));
let total_difficulty = try!(Difficulty::read(reader));
let pow = try!(Proof::read(reader));
let utxo_root = Hash::read(reader)?;
let rproof_root = Hash::read(reader)?;
let kernel_root = Hash::read(reader)?;
let nonce = reader.read_u64()?;
let difficulty = Difficulty::read(reader)?;
let total_difficulty = Difficulty::read(reader)?;
let pow = Proof::read(reader)?;
Ok(BlockHeader {
version: version,
height: height,
previous: previous,
timestamp: time::at_utc(time::Timespec {
@ -153,9 +146,6 @@ impl Readable for BlockHeader {
utxo_root: utxo_root,
range_proof_root: rproof_root,
kernel_root: kernel_root,
features: BlockFeatures::from_bits(features).ok_or(
ser::Error::CorruptedData,
)?,
pow: pow,
nonce: nonce,
difficulty: difficulty,

View file

@ -29,6 +29,7 @@ pub fn genesis() -> core::Block {
let empty_hash = [].hash();
core::Block {
header: core::BlockHeader {
version: 1,
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: time::Tm {
@ -42,7 +43,6 @@ pub fn genesis() -> core::Block {
utxo_root: empty_hash,
range_proof_root: empty_hash,
kernel_root: empty_hash,
features: core::DEFAULT_BLOCK,
nonce: global::get_genesis_nonce(),
pow: core::Proof::zero(proof_size), // TODO get actual PoW solution
},