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); 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( let prev = try!(ctx.store.get_block_header(&header.previous).map_err(
&Error::StoreErr, &Error::StoreErr,
)); ));
@ -144,13 +172,6 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
// time progression (but not in CI mode) // time progression (but not in CI mode)
return Err(Error::InvalidBlockTime); 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) { if !ctx.opts.intersects(SKIP_POW) {
// verify the proof of work and related parameters // 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 { if header.difficulty < difficulty {
return Err(Error::DifficultyTooLow); 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(()) Ok(())

View file

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

View file

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