mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 08:51:08 +03:00
Block versions and hard fork schedule
This commit is contained in:
parent
62954f1549
commit
9310151680
5 changed files with 92 additions and 43 deletions
|
@ -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(())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue