mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +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);
|
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(())
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue