height (header version) specific output PMMR root rules (#3147)

* height (header version) specific output PMMR root rules

* cleanup

* cleanup based on PR feedback

* address feedback
This commit is contained in:
Antioch Peverell 2019-12-05 11:55:10 +00:00 committed by GitHub
parent 3a333ae00f
commit 0b21ee607a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 38 deletions

View file

@ -47,7 +47,10 @@ pub struct TxHashSetHandler {
impl TxHashSetHandler { impl TxHashSetHandler {
// gets roots // gets roots
fn get_roots(&self) -> Result<TxHashSet, Error> { fn get_roots(&self) -> Result<TxHashSet, Error> {
Ok(TxHashSet::from_head(w(&self.chain)?)) let res = TxHashSet::from_head(w(&self.chain)?).context(ErrorKind::Internal(
"failed to read roots from txhashset".to_owned(),
))?;
Ok(res)
} }
// gets last n outputs inserted in to the tree // gets last n outputs inserted in to the tree

View file

@ -116,13 +116,16 @@ pub struct TxHashSet {
} }
impl TxHashSet { impl TxHashSet {
pub fn from_head(head: Arc<chain::Chain>) -> TxHashSet { /// A TxHashSet in the context of the api is simply the collection of PMMR roots.
let roots = head.get_txhashset_roots(); /// We can obtain these in a lightweight way by reading them from the head of the chain.
TxHashSet { /// We will have validated the roots on this header against the roots of the txhashset.
output_root_hash: roots.output_root().to_hex(), pub fn from_head(chain: Arc<chain::Chain>) -> Result<TxHashSet, chain::Error> {
range_proof_root_hash: roots.rproof_root.to_hex(), let header = chain.head_header()?;
kernel_root_hash: roots.kernel_root.to_hex(), Ok(TxHashSet {
} output_root_hash: header.output_root.to_hex(),
range_proof_root_hash: header.range_proof_root.to_hex(),
kernel_root_hash: header.kernel_root.to_hex(),
})
} }
} }

View file

@ -30,8 +30,7 @@ use crate::store;
use crate::txhashset; use crate::txhashset;
use crate::txhashset::{PMMRHandle, TxHashSet}; use crate::txhashset::{PMMRHandle, TxHashSet};
use crate::types::{ use crate::types::{
BlockStatus, ChainAdapter, NoStatus, Options, OutputMMRPosition, Tip, TxHashSetRoots, BlockStatus, ChainAdapter, NoStatus, Options, OutputMMRPosition, Tip, TxHashsetWriteStatus,
TxHashsetWriteStatus,
}; };
use crate::util::secp::pedersen::{Commitment, RangeProof}; use crate::util::secp::pedersen::{Commitment, RangeProof};
use crate::util::RwLock; use crate::util::RwLock;
@ -590,15 +589,9 @@ impl Chain {
Ok((prev_root, extension.roots()?, extension.sizes())) Ok((prev_root, extension.roots()?, extension.sizes()))
})?; })?;
// Set the prev_root on the header.
b.header.prev_root = prev_root;
// Set the output, rangeproof and kernel MMR roots.
b.header.output_root = roots.output_root();
b.header.range_proof_root = roots.rproof_root;
b.header.kernel_root = roots.kernel_root;
// Set the output and kernel MMR sizes. // Set the output and kernel MMR sizes.
// Note: We need to do this *before* calculating the roots as the output_root
// depends on the output_mmr_size
{ {
// Carefully destructure these correctly... // Carefully destructure these correctly...
let (output_mmr_size, _, kernel_mmr_size) = sizes; let (output_mmr_size, _, kernel_mmr_size) = sizes;
@ -606,6 +599,14 @@ impl Chain {
b.header.kernel_mmr_size = kernel_mmr_size; b.header.kernel_mmr_size = kernel_mmr_size;
} }
// Set the prev_root on the header.
b.header.prev_root = prev_root;
// Set the output, rangeproof and kernel MMR roots.
b.header.output_root = roots.output_root(&b.header);
b.header.range_proof_root = roots.rproof_root;
b.header.kernel_root = roots.kernel_root;
Ok(()) Ok(())
} }
@ -634,11 +635,6 @@ impl Chain {
txhashset.merkle_proof(commit) txhashset.merkle_proof(commit)
} }
/// Returns current txhashset roots.
pub fn get_txhashset_roots(&self) -> TxHashSetRoots {
self.txhashset.read().roots()
}
/// Provides a reading view into the current kernel state. /// Provides a reading view into the current kernel state.
pub fn kernel_data_read(&self) -> Result<File, Error> { pub fn kernel_data_read(&self) -> Result<File, Error> {
let txhashset = self.txhashset.read(); let txhashset = self.txhashset.read();

View file

@ -312,12 +312,16 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext<'_>) -> Result<(
// First I/O cost, delayed as late as possible. // First I/O cost, delayed as late as possible.
let prev = prev_header_store(header, &mut ctx.batch)?; let prev = prev_header_store(header, &mut ctx.batch)?;
// make sure this header has a height exactly one higher than the previous // This header height must increase the height from the previous header by exactly 1.
// header
if header.height != prev.height + 1 { if header.height != prev.height + 1 {
return Err(ErrorKind::InvalidBlockHeight.into()); return Err(ErrorKind::InvalidBlockHeight.into());
} }
// This header must have a valid header version for its height.
if !consensus::valid_header_version(header.height, header.version) {
return Err(ErrorKind::InvalidBlockVersion(header.version).into());
}
if header.timestamp <= prev.timestamp { if header.timestamp <= prev.timestamp {
// prevent time warp attacks and some timestamp manipulations by forcing strict // prevent time warp attacks and some timestamp manipulations by forcing strict
// time progression // time progression

View file

@ -18,7 +18,7 @@ use chrono::prelude::{DateTime, Utc};
use std::sync::Arc; use std::sync::Arc;
use crate::core::core::hash::{Hash, Hashed, ZERO_HASH}; use crate::core::core::hash::{Hash, Hashed, ZERO_HASH};
use crate::core::core::{Block, BlockHeader}; use crate::core::core::{Block, BlockHeader, HeaderVersion};
use crate::core::pow::Difficulty; use crate::core::pow::Difficulty;
use crate::core::ser::{self, PMMRIndexHashable}; use crate::core::ser::{self, PMMRIndexHashable};
use crate::error::{Error, ErrorKind}; use crate::error::{Error, ErrorKind};
@ -193,24 +193,26 @@ pub struct TxHashSetRoots {
} }
impl TxHashSetRoots { impl TxHashSetRoots {
/// Accessor for the underlying output PMMR root /// Accessor for the output PMMR root (rules here are block height dependent).
pub fn output_root(&self) -> Hash { /// We assume the header version is consistent with the block height, validated
self.output_roots.output_root() /// as part of pipe::validate_header().
pub fn output_root(&self, header: &BlockHeader) -> Hash {
self.output_roots.root(header)
} }
/// Validate roots against the provided block header. /// Validate roots against the provided block header.
pub fn validate(&self, header: &BlockHeader) -> Result<(), Error> { pub fn validate(&self, header: &BlockHeader) -> Result<(), Error> {
debug!( debug!(
"validate roots: {} at {}, output_root: {}, output pmmr: {} (bitmap: {}, merged: {})", "validate roots: {} at {}, {} vs. {} (original: {}, merged: {})",
header.hash(), header.hash(),
header.height, header.height,
header.output_root, header.output_root,
self.output_roots.output_root(), self.output_root(header),
self.output_roots.bitmap_root, self.output_roots.pmmr_root,
self.output_roots.merged_root(header), self.output_roots.merged_root(header),
); );
if header.output_root != self.output_roots.pmmr_root { if header.output_root != self.output_root(header) {
Err(ErrorKind::InvalidRoot.into()) Err(ErrorKind::InvalidRoot.into())
} else if header.range_proof_root != self.rproof_root { } else if header.range_proof_root != self.rproof_root {
Err(ErrorKind::InvalidRoot.into()) Err(ErrorKind::InvalidRoot.into())
@ -232,15 +234,27 @@ pub struct OutputRoots {
} }
impl OutputRoots { impl OutputRoots {
/// The root of our output PMMR. The rules here are block height specific.
/// We use the merged root here for header version 3 and later.
/// We assume the header version is consistent with the block height, validated
/// as part of pipe::validate_header().
pub fn root(&self, header: &BlockHeader) -> Hash {
if header.version < HeaderVersion(3) {
self.output_root()
} else {
self.merged_root(header)
}
}
/// The root of the underlying output PMMR. /// The root of the underlying output PMMR.
pub fn output_root(&self) -> Hash { fn output_root(&self) -> Hash {
self.pmmr_root self.pmmr_root
} }
/// Hash the root of the output PMMR and the root of the bitmap accumulator /// Hash the root of the output PMMR and the root of the bitmap accumulator
/// together with the size of the output PMMR (for consistency with existing PMMR impl). /// together with the size of the output PMMR (for consistency with existing PMMR impl).
/// H(pmmr_size | pmmr_root | bitmap_root) /// H(pmmr_size | pmmr_root | bitmap_root)
pub fn merged_root(&self, header: &BlockHeader) -> Hash { fn merged_root(&self, header: &BlockHeader) -> Hash {
(self.pmmr_root, self.bitmap_root).hash_with_index(header.output_mmr_size) (self.pmmr_root, self.bitmap_root).hash_with_index(header.output_mmr_size)
} }
} }

View file

@ -133,12 +133,19 @@ pub const FLOONET_FIRST_HARD_FORK: u64 = 185_040;
/// Floonet second hard fork height, set to happen around 2019-12-19 /// Floonet second hard fork height, set to happen around 2019-12-19
pub const FLOONET_SECOND_HARD_FORK: u64 = 298_080; pub const FLOONET_SECOND_HARD_FORK: u64 = 298_080;
/// AutomatedTesting and UserTesting first hard fork height.
pub const TESTING_FIRST_HARD_FORK: u64 = 3;
/// AutomatedTesting and UserTesting second hard fork height.
pub const TESTING_SECOND_HARD_FORK: u64 = 6;
/// Compute possible block version at a given height, implements /// Compute possible block version at a given height, implements
/// 6 months interval scheduled hard forks for the first 2 years. /// 6 months interval scheduled hard forks for the first 2 years.
pub fn header_version(height: u64) -> HeaderVersion { pub fn header_version(height: u64) -> HeaderVersion {
let chain_type = global::CHAIN_TYPE.read().clone(); let chain_type = global::CHAIN_TYPE.read().clone();
let hf_interval = (1 + height / HARD_FORK_INTERVAL) as u16; let hf_interval = (1 + height / HARD_FORK_INTERVAL) as u16;
match chain_type { match chain_type {
global::ChainTypes::Mainnet => HeaderVersion(hf_interval),
global::ChainTypes::Floonet => { global::ChainTypes::Floonet => {
if height < FLOONET_FIRST_HARD_FORK { if height < FLOONET_FIRST_HARD_FORK {
(HeaderVersion(1)) (HeaderVersion(1))
@ -150,8 +157,17 @@ pub fn header_version(height: u64) -> HeaderVersion {
HeaderVersion(hf_interval) HeaderVersion(hf_interval)
} }
} }
// everything else just like mainnet global::ChainTypes::AutomatedTesting | global::ChainTypes::UserTesting => {
_ => HeaderVersion(hf_interval), if height < TESTING_FIRST_HARD_FORK {
(HeaderVersion(1))
} else if height < TESTING_SECOND_HARD_FORK {
(HeaderVersion(2))
} else if height < 3 * HARD_FORK_INTERVAL {
(HeaderVersion(3))
} else {
HeaderVersion(hf_interval)
}
}
} }
} }

View file

@ -176,7 +176,7 @@ impl Hashed for HeaderEntry {
} }
/// Some type safety around header versioning. /// Some type safety around header versioning.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Serialize)]
pub struct HeaderVersion(pub u16); pub struct HeaderVersion(pub u16);
impl From<HeaderVersion> for u16 { impl From<HeaderVersion> for u16 {