mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
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:
parent
3a333ae00f
commit
0b21ee607a
7 changed files with 74 additions and 38 deletions
|
@ -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
|
||||||
|
|
|
@ -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(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue