mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
add segmenter for generating segments from txhashset with consistent rewind (#3482)
* add segmenter for generating segments from txhashset with consistent rewind * rework segmenter to take a txhashset wrapped in rwlock rework our rewindable pmmr so we can convert to readonly easily * placeholder code for rewinding readonly txhashset extension to build a rangeproof segment * segment creation for outputs/rangeproofs/kernels/bitmaps * placeholder segment impl * commit * rework segmenter to use a cached bitmap (rewind is expensive) * cache segmenter instance based on current archive header * integrate the real segment and segment identifier with our segmenter * exercise the segmenter code on chain init * wrap accumulator in an arc, no need to clone each time
This commit is contained in:
parent
5282ecb12f
commit
cba3137338
11 changed files with 335 additions and 76 deletions
|
@ -20,7 +20,7 @@ use crate::core::core::merkle_proof::MerkleProof;
|
|||
use crate::core::core::verifier_cache::VerifierCache;
|
||||
use crate::core::core::{
|
||||
Block, BlockHeader, BlockSums, Committed, Inputs, KernelFeatures, Output, OutputIdentifier,
|
||||
Transaction, TxKernel,
|
||||
SegmentIdentifier, Transaction, TxKernel,
|
||||
};
|
||||
use crate::core::global;
|
||||
use crate::core::pow;
|
||||
|
@ -29,12 +29,13 @@ use crate::error::{Error, ErrorKind};
|
|||
use crate::pipe;
|
||||
use crate::store;
|
||||
use crate::txhashset;
|
||||
use crate::txhashset::{PMMRHandle, TxHashSet};
|
||||
use crate::txhashset::{PMMRHandle, Segmenter, TxHashSet};
|
||||
use crate::types::{
|
||||
BlockStatus, ChainAdapter, CommitPos, NoStatus, Options, Tip, TxHashsetWriteStatus,
|
||||
};
|
||||
use crate::util::secp::pedersen::{Commitment, RangeProof};
|
||||
use crate::{util::RwLock, ChainStore};
|
||||
use crate::util::RwLock;
|
||||
use crate::ChainStore;
|
||||
use grin_core::ser;
|
||||
use grin_store::Error::NotFoundErr;
|
||||
use std::fs::{self, File};
|
||||
|
@ -152,6 +153,7 @@ pub struct Chain {
|
|||
header_pmmr: Arc<RwLock<txhashset::PMMRHandle<BlockHeader>>>,
|
||||
sync_pmmr: Arc<RwLock<txhashset::PMMRHandle<BlockHeader>>>,
|
||||
verifier_cache: Arc<RwLock<dyn VerifierCache>>,
|
||||
pibd_segmenter: Arc<RwLock<Option<Segmenter>>>,
|
||||
// POW verification function
|
||||
pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>,
|
||||
archive_mode: bool,
|
||||
|
@ -217,6 +219,7 @@ impl Chain {
|
|||
txhashset: Arc::new(RwLock::new(txhashset)),
|
||||
header_pmmr: Arc::new(RwLock::new(header_pmmr)),
|
||||
sync_pmmr: Arc::new(RwLock::new(sync_pmmr)),
|
||||
pibd_segmenter: Arc::new(RwLock::new(None)),
|
||||
pow_verifier,
|
||||
verifier_cache,
|
||||
archive_mode,
|
||||
|
@ -225,6 +228,22 @@ impl Chain {
|
|||
|
||||
chain.log_heads()?;
|
||||
|
||||
// Temporarily exercising the initialization process.
|
||||
// Note: This is *really* slow because we are starting from cold.
|
||||
//
|
||||
// This is not required as we will lazily initialize our segmenter as required
|
||||
// once we start receiving PIBD segment requests.
|
||||
// In reality we will do this based on PIBD segment requests.
|
||||
// Initialization (once per 12 hour period) will not be this slow once lmdb and PMMRs
|
||||
// are warmed up.
|
||||
{
|
||||
let segmenter = chain.segmenter()?;
|
||||
let _ = segmenter.kernel_segment(SegmentIdentifier { height: 9, idx: 0 });
|
||||
let _ = segmenter.bitmap_segment(SegmentIdentifier { height: 9, idx: 0 });
|
||||
let _ = segmenter.output_segment(SegmentIdentifier { height: 11, idx: 0 });
|
||||
let _ = segmenter.rangeproof_segment(SegmentIdentifier { height: 7, idx: 0 });
|
||||
}
|
||||
|
||||
Ok(chain)
|
||||
}
|
||||
|
||||
|
@ -815,6 +834,64 @@ impl Chain {
|
|||
})
|
||||
}
|
||||
|
||||
/// The segmenter is responsible for generation PIBD segments.
|
||||
/// We cache a segmenter instance based on the current archve period (new period every 12 hours).
|
||||
/// This allows us to efficiently generate bitmap segments for the current archive period.
|
||||
///
|
||||
/// It is a relatively expensive operation to initializa and cache a new segmenter instance
|
||||
/// as this involves rewinding the txhashet by approx 720 blocks (12 hours).
|
||||
///
|
||||
/// Caller is responsible for only doing this when required.
|
||||
/// Caller should verify a peer segment request is valid before calling this for example.
|
||||
///
|
||||
pub fn segmenter(&self) -> Result<Segmenter, Error> {
|
||||
// The archive header corresponds to the data we will segment.
|
||||
let ref archive_header = self.txhashset_archive_header()?;
|
||||
|
||||
// Use our cached segmenter if we have one and the associated header matches.
|
||||
if let Some(x) = self.pibd_segmenter.read().as_ref() {
|
||||
if x.header() == archive_header {
|
||||
return Ok(x.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// We have no cached segmenter or the cached segmenter is no longer useful.
|
||||
// Initialize a new segment, cache it and return it.
|
||||
let segmenter = self.init_segmenter(archive_header)?;
|
||||
let mut cache = self.pibd_segmenter.write();
|
||||
*cache = Some(segmenter.clone());
|
||||
|
||||
return Ok(segmenter);
|
||||
}
|
||||
|
||||
/// This is an expensive rewind to recreate bitmap state but we only need to do this once.
|
||||
/// Caller is responsible for "caching" the segmenter (per archive period) for reuse.
|
||||
fn init_segmenter(&self, header: &BlockHeader) -> Result<Segmenter, Error> {
|
||||
let now = Instant::now();
|
||||
debug!(
|
||||
"init_segmenter: initializing new segmenter for {} at {}",
|
||||
header.hash(),
|
||||
header.height
|
||||
);
|
||||
|
||||
let mut header_pmmr = self.header_pmmr.write();
|
||||
let mut txhashset = self.txhashset.write();
|
||||
|
||||
let bitmap_snapshot =
|
||||
txhashset::extending_readonly(&mut header_pmmr, &mut txhashset, |ext, batch| {
|
||||
ext.extension.rewind(header, batch)?;
|
||||
Ok(ext.extension.bitmap_accumulator())
|
||||
})?;
|
||||
|
||||
debug!("init_segmenter: done, took {}ms", now.elapsed().as_millis());
|
||||
|
||||
Ok(Segmenter::new(
|
||||
self.txhashset(),
|
||||
Arc::new(bitmap_snapshot),
|
||||
header.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
/// To support the ability to download the txhashset from multiple peers in parallel,
|
||||
/// the peers must all agree on the exact binary representation of the txhashset.
|
||||
/// This means compacting and rewinding to the exact same header.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
//! Error types for chain
|
||||
use crate::core::core::pmmr::segment;
|
||||
use crate::core::core::{block, committed, transaction};
|
||||
use crate::core::ser;
|
||||
use crate::keychain;
|
||||
|
@ -149,6 +150,9 @@ pub enum ErrorKind {
|
|||
/// Error during chain sync
|
||||
#[fail(display = "Sync error")]
|
||||
SyncError(String),
|
||||
/// PIBD segment related error
|
||||
#[fail(display = "Segment error")]
|
||||
SegmentError(segment::SegmentError),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
|
@ -273,6 +277,14 @@ impl From<ser::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<segment::SegmentError> for Error {
|
||||
fn from(error: segment::SegmentError) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::SegmentError(error)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<secp::Error> for Error {
|
||||
fn from(e: secp::Error) -> Error {
|
||||
Error {
|
||||
|
|
|
@ -17,10 +17,12 @@
|
|||
|
||||
mod bitmap_accumulator;
|
||||
mod rewindable_kernel_view;
|
||||
mod segmenter;
|
||||
mod txhashset;
|
||||
mod utxo_view;
|
||||
|
||||
pub use self::bitmap_accumulator::*;
|
||||
pub use self::rewindable_kernel_view::*;
|
||||
pub use self::segmenter::*;
|
||||
pub use self::txhashset::*;
|
||||
pub use self::utxo_view::*;
|
||||
|
|
|
@ -50,7 +50,7 @@ impl BitmapAccumulator {
|
|||
/// Crate a new empty bitmap accumulator.
|
||||
pub fn new() -> BitmapAccumulator {
|
||||
BitmapAccumulator {
|
||||
backend: VecBackend::new_hash_only(),
|
||||
backend: VecBackend::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,9 +176,12 @@ impl BitmapAccumulator {
|
|||
|
||||
/// The root hash of the bitmap accumulator MMR.
|
||||
pub fn root(&self) -> Hash {
|
||||
self.readonly_pmmr().root().expect("no root, invalid tree")
|
||||
}
|
||||
|
||||
/// Readonly access to our internal data.
|
||||
pub fn readonly_pmmr(&self) -> ReadonlyPMMR<BitmapChunk, VecBackend<BitmapChunk>> {
|
||||
ReadonlyPMMR::at(&self.backend, self.backend.size())
|
||||
.root()
|
||||
.expect("no root, invalid tree")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
//! Lightweight readonly view into kernel MMR for convenience.
|
||||
|
||||
use crate::core::core::pmmr::RewindablePMMR;
|
||||
use crate::core::core::pmmr::{ReadablePMMR, ReadonlyPMMR, RewindablePMMR};
|
||||
use crate::core::core::{BlockHeader, TxKernel};
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use grin_store::pmmr::PMMRBackend;
|
||||
|
@ -54,7 +54,10 @@ impl<'a> RewindableKernelView<'a> {
|
|||
/// fast sync where a reorg past the horizon could allow a whole rewrite of
|
||||
/// the kernel set.
|
||||
pub fn validate_root(&self) -> Result<(), Error> {
|
||||
let root = self.pmmr.root().map_err(|_| ErrorKind::InvalidRoot)?;
|
||||
let root = self
|
||||
.readonly_pmmr()
|
||||
.root()
|
||||
.map_err(|_| ErrorKind::InvalidRoot)?;
|
||||
if root != self.header.kernel_root {
|
||||
return Err(ErrorKind::InvalidTxHashSet(format!(
|
||||
"Kernel root at {} does not match",
|
||||
|
@ -64,4 +67,9 @@ impl<'a> RewindableKernelView<'a> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Readonly view of our internal data.
|
||||
pub fn readonly_pmmr(&self) -> ReadonlyPMMR<TxKernel, PMMRBackend<TxKernel>> {
|
||||
self.pmmr.as_readonly()
|
||||
}
|
||||
}
|
||||
|
|
149
chain/src/txhashset/segmenter.rs
Normal file
149
chain/src/txhashset/segmenter.rs
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2020 The Grin Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Generation of the various necessary segments requested during PIBD.
|
||||
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use crate::core::core::hash::Hash;
|
||||
use crate::core::core::pmmr::ReadablePMMR;
|
||||
use crate::core::core::{BlockHeader, OutputIdentifier, Segment, SegmentIdentifier, TxKernel};
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::txhashset::{BitmapAccumulator, BitmapChunk, TxHashSet};
|
||||
use crate::util::secp::pedersen::RangeProof;
|
||||
use crate::util::RwLock;
|
||||
|
||||
/// Segmenter for generating PIBD segments.
|
||||
#[derive(Clone)]
|
||||
pub struct Segmenter {
|
||||
txhashset: Arc<RwLock<TxHashSet>>,
|
||||
bitmap_snapshot: Arc<BitmapAccumulator>,
|
||||
header: BlockHeader,
|
||||
}
|
||||
|
||||
impl Segmenter {
|
||||
/// Create a new segmenter based on the provided txhashset.
|
||||
pub fn new(
|
||||
txhashset: Arc<RwLock<TxHashSet>>,
|
||||
bitmap_snapshot: Arc<BitmapAccumulator>,
|
||||
header: BlockHeader,
|
||||
) -> Segmenter {
|
||||
Segmenter {
|
||||
txhashset,
|
||||
bitmap_snapshot,
|
||||
header,
|
||||
}
|
||||
}
|
||||
|
||||
/// Header associated with this segmenter instance.
|
||||
/// The bitmap "snapshot" corresponds to rewound state at this header.
|
||||
pub fn header(&self) -> &BlockHeader {
|
||||
&self.header
|
||||
}
|
||||
|
||||
/// Create a kernel segment.
|
||||
pub fn kernel_segment(&self, id: SegmentIdentifier) -> Result<Segment<TxKernel>, Error> {
|
||||
let now = Instant::now();
|
||||
let txhashset = self.txhashset.read();
|
||||
let kernel_pmmr = txhashset.kernel_pmmr_at(&self.header);
|
||||
let segment = Segment::from_pmmr(id, &kernel_pmmr, false)?;
|
||||
debug!(
|
||||
"kernel_segment: id: ({}, {}), leaves: {}, hashes: {}, proof hashes: {}, took {}ms",
|
||||
segment.id().height,
|
||||
segment.id().idx,
|
||||
segment.leaf_iter().count(),
|
||||
segment.hash_iter().count(),
|
||||
segment.proof().size(),
|
||||
now.elapsed().as_millis()
|
||||
);
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
/// The root of the output PMMR based on size from the header.
|
||||
fn output_root(&self) -> Result<Hash, Error> {
|
||||
let txhashset = self.txhashset.read();
|
||||
let pmmr = txhashset.output_pmmr_at(&self.header);
|
||||
let root = pmmr.root().map_err(&ErrorKind::TxHashSetErr)?;
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
/// The root of the bitmap snapshot PMMR.
|
||||
fn bitmap_root(&self) -> Result<Hash, Error> {
|
||||
let pmmr = self.bitmap_snapshot.readonly_pmmr();
|
||||
let root = pmmr.root().map_err(&ErrorKind::TxHashSetErr)?;
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
/// Create a utxo bitmap segment based on our bitmap "snapshot" and return it with
|
||||
/// the corresponding output root.
|
||||
pub fn bitmap_segment(
|
||||
&self,
|
||||
id: SegmentIdentifier,
|
||||
) -> Result<(Segment<BitmapChunk>, Hash), Error> {
|
||||
let now = Instant::now();
|
||||
let bitmap_pmmr = self.bitmap_snapshot.readonly_pmmr();
|
||||
let segment = Segment::from_pmmr(id, &bitmap_pmmr, false)?;
|
||||
let output_root = self.output_root()?;
|
||||
debug!(
|
||||
"bitmap_segment: id: ({}, {}), leaves: {}, hashes: {}, proof hashes: {}, took {}ms",
|
||||
segment.id().height,
|
||||
segment.id().idx,
|
||||
segment.leaf_iter().count(),
|
||||
segment.hash_iter().count(),
|
||||
segment.proof().size(),
|
||||
now.elapsed().as_millis()
|
||||
);
|
||||
Ok((segment, output_root))
|
||||
}
|
||||
|
||||
/// Create an output segment and return it with the corresponding bitmap root.
|
||||
pub fn output_segment(
|
||||
&self,
|
||||
id: SegmentIdentifier,
|
||||
) -> Result<(Segment<OutputIdentifier>, Hash), Error> {
|
||||
let now = Instant::now();
|
||||
let txhashset = self.txhashset.read();
|
||||
let output_pmmr = txhashset.output_pmmr_at(&self.header);
|
||||
let segment = Segment::from_pmmr(id, &output_pmmr, true)?;
|
||||
let bitmap_root = self.bitmap_root()?;
|
||||
debug!(
|
||||
"output_segment: id: ({}, {}), leaves: {}, hashes: {}, proof hashes: {}, took {}ms",
|
||||
segment.id().height,
|
||||
segment.id().idx,
|
||||
segment.leaf_iter().count(),
|
||||
segment.hash_iter().count(),
|
||||
segment.proof().size(),
|
||||
now.elapsed().as_millis()
|
||||
);
|
||||
Ok((segment, bitmap_root))
|
||||
}
|
||||
|
||||
/// Create a rangeproof segment.
|
||||
pub fn rangeproof_segment(&self, id: SegmentIdentifier) -> Result<Segment<RangeProof>, Error> {
|
||||
let now = Instant::now();
|
||||
let txhashset = self.txhashset.read();
|
||||
let pmmr = txhashset.rangeproof_pmmr_at(&self.header);
|
||||
let segment = Segment::from_pmmr(id, &pmmr, true)?;
|
||||
debug!(
|
||||
"rangeproof_segment: id: ({}, {}), leaves: {}, hashes: {}, proof hashes: {}, took {}ms",
|
||||
segment.id().height,
|
||||
segment.id().idx,
|
||||
segment.leaf_iter().count(),
|
||||
segment.hash_iter().count(),
|
||||
segment.proof().size(),
|
||||
now.elapsed().as_millis()
|
||||
);
|
||||
Ok(segment)
|
||||
}
|
||||
}
|
|
@ -19,14 +19,16 @@ use crate::core::consensus::WEEK_HEIGHT;
|
|||
use crate::core::core::committed::Committed;
|
||||
use crate::core::core::hash::{Hash, Hashed};
|
||||
use crate::core::core::merkle_proof::MerkleProof;
|
||||
use crate::core::core::pmmr::{self, Backend, ReadablePMMR, ReadonlyPMMR, RewindablePMMR, PMMR};
|
||||
use crate::core::core::pmmr::{
|
||||
self, Backend, ReadablePMMR, ReadonlyPMMR, RewindablePMMR, VecBackend, PMMR,
|
||||
};
|
||||
use crate::core::core::{Block, BlockHeader, KernelFeatures, Output, OutputIdentifier, TxKernel};
|
||||
use crate::core::global;
|
||||
use crate::core::ser::{PMMRable, ProtocolVersion};
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::linked_list::{ListIndex, PruneableListIndex, RewindableListIndex};
|
||||
use crate::store::{self, Batch, ChainStore};
|
||||
use crate::txhashset::bitmap_accumulator::BitmapAccumulator;
|
||||
use crate::txhashset::bitmap_accumulator::{BitmapAccumulator, BitmapChunk};
|
||||
use crate::txhashset::{RewindableKernelView, UTXOView};
|
||||
use crate::types::{CommitPos, OutputRoots, Tip, TxHashSetRoots, TxHashsetWriteStatus};
|
||||
use crate::util::secp::pedersen::{Commitment, RangeProof};
|
||||
|
@ -301,6 +303,30 @@ impl TxHashSet {
|
|||
.get_last_n_insertions(distance)
|
||||
}
|
||||
|
||||
/// Efficient view into the kernel PMMR based on size in header.
|
||||
pub fn kernel_pmmr_at(
|
||||
&self,
|
||||
header: &BlockHeader,
|
||||
) -> ReadonlyPMMR<TxKernel, PMMRBackend<TxKernel>> {
|
||||
ReadonlyPMMR::at(&self.kernel_pmmr_h.backend, header.kernel_mmr_size)
|
||||
}
|
||||
|
||||
/// Efficient view into the output PMMR based on size in header.
|
||||
pub fn output_pmmr_at(
|
||||
&self,
|
||||
header: &BlockHeader,
|
||||
) -> ReadonlyPMMR<OutputIdentifier, PMMRBackend<OutputIdentifier>> {
|
||||
ReadonlyPMMR::at(&self.output_pmmr_h.backend, header.output_mmr_size)
|
||||
}
|
||||
|
||||
/// Efficient view into the rangeproof PMMR based on size in header.
|
||||
pub fn rangeproof_pmmr_at(
|
||||
&self,
|
||||
header: &BlockHeader,
|
||||
) -> ReadonlyPMMR<RangeProof, PMMRBackend<RangeProof>> {
|
||||
ReadonlyPMMR::at(&self.rproof_pmmr_h.backend, header.output_mmr_size)
|
||||
}
|
||||
|
||||
/// Convenience function to query the db for a header by its hash.
|
||||
pub fn get_block_header(&self, hash: &Hash) -> Result<BlockHeader, Error> {
|
||||
Ok(self.commit_index.get_block_header(&hash)?)
|
||||
|
@ -1069,11 +1095,33 @@ impl<'a> Extension<'a> {
|
|||
pub fn utxo_view(&'a self, header_ext: &'a HeaderExtension<'a>) -> UTXOView<'a> {
|
||||
UTXOView::new(
|
||||
header_ext.pmmr.readonly_pmmr(),
|
||||
self.output_pmmr.readonly_pmmr(),
|
||||
self.rproof_pmmr.readonly_pmmr(),
|
||||
self.output_readonly_pmmr(),
|
||||
self.rproof_readonly_pmmr(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Readonly view of our output data.
|
||||
pub fn output_readonly_pmmr(
|
||||
&self,
|
||||
) -> ReadonlyPMMR<OutputIdentifier, PMMRBackend<OutputIdentifier>> {
|
||||
self.output_pmmr.readonly_pmmr()
|
||||
}
|
||||
|
||||
/// Take a snapshot of our bitmap accumulator
|
||||
pub fn bitmap_accumulator(&self) -> BitmapAccumulator {
|
||||
self.bitmap_accumulator.clone()
|
||||
}
|
||||
|
||||
/// Readonly view of our bitmap accumulator data.
|
||||
pub fn bitmap_readonly_pmmr(&self) -> ReadonlyPMMR<BitmapChunk, VecBackend<BitmapChunk>> {
|
||||
self.bitmap_accumulator.readonly_pmmr()
|
||||
}
|
||||
|
||||
/// Readonly view of our rangeproof data.
|
||||
pub fn rproof_readonly_pmmr(&self) -> ReadonlyPMMR<RangeProof, PMMRBackend<RangeProof>> {
|
||||
self.rproof_pmmr.readonly_pmmr()
|
||||
}
|
||||
|
||||
/// Apply a new block to the current txhashet extension (output, rangeproof, kernel MMRs).
|
||||
/// Returns a vec of commit_pos representing the pos and height of the outputs spent
|
||||
/// by this block.
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
|
||||
use std::marker;
|
||||
|
||||
use crate::core::hash::{Hash, ZERO_HASH};
|
||||
use crate::core::pmmr::{bintree_postorder_height, is_leaf, peaks, Backend};
|
||||
use crate::ser::{PMMRIndexHashable, PMMRable};
|
||||
use crate::core::pmmr::{bintree_postorder_height, Backend, ReadonlyPMMR};
|
||||
use crate::ser::PMMRable;
|
||||
|
||||
/// Rewindable (but still readonly) view of a PMMR.
|
||||
pub struct RewindablePMMR<'a, T, B>
|
||||
|
@ -49,11 +48,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Reference to the underlying storage backend.
|
||||
pub fn backend(&'a self) -> &dyn Backend<T> {
|
||||
self.backend
|
||||
}
|
||||
|
||||
/// Build a new readonly PMMR pre-initialized to
|
||||
/// last_pos with the provided backend.
|
||||
pub fn at(backend: &'a B, last_pos: u64) -> RewindablePMMR<'_, T, B> {
|
||||
|
@ -74,62 +68,14 @@ where
|
|||
while bintree_postorder_height(pos + 1) > 0 {
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
self.last_pos = pos;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the data element at provided position in the MMR.
|
||||
pub fn get_data(&self, pos: u64) -> Option<T::E> {
|
||||
if pos > self.last_pos {
|
||||
// If we are beyond the rhs of the MMR return None.
|
||||
None
|
||||
} else if is_leaf(pos) {
|
||||
// If we are a leaf then get data from the backend.
|
||||
self.backend.get_data(pos)
|
||||
} else {
|
||||
// If we are not a leaf then return None as only leaves have data.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the MMR empty?
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.last_pos == 0
|
||||
}
|
||||
|
||||
/// Computes the root of the MMR. Find all the peaks in the current
|
||||
/// tree and "bags" them to get a single peak.
|
||||
pub fn root(&self) -> Result<Hash, String> {
|
||||
if self.is_empty() {
|
||||
return Ok(ZERO_HASH);
|
||||
}
|
||||
let mut res = None;
|
||||
for peak in self.peaks().iter().rev() {
|
||||
res = match res {
|
||||
None => Some(*peak),
|
||||
Some(rhash) => Some((*peak, rhash).hash_with_index(self.unpruned_size())),
|
||||
}
|
||||
}
|
||||
res.ok_or_else(|| "no root, invalid tree".to_owned())
|
||||
}
|
||||
|
||||
/// Returns a vec of the peaks of this MMR.
|
||||
pub fn peaks(&self) -> Vec<Hash> {
|
||||
let peaks_pos = peaks(self.last_pos);
|
||||
peaks_pos
|
||||
.into_iter()
|
||||
.filter_map(|pi| {
|
||||
// here we want to get from underlying hash file
|
||||
// as the pos *may* have been "removed"
|
||||
self.backend.get_from_file(pi)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Total size of the tree, including intermediary nodes and ignoring any
|
||||
/// pruning.
|
||||
pub fn unpruned_size(&self) -> u64 {
|
||||
self.last_pos
|
||||
/// Allows conversion of a "rewindable" PMMR into a "readonly" PMMR.
|
||||
/// Intended usage is to create a rewindable PMMR, rewind it,
|
||||
/// then convert to "readonly" and read from it.
|
||||
pub fn as_readonly(&self) -> ReadonlyPMMR<'a, T, B> {
|
||||
ReadonlyPMMR::at(&self.backend, self.last_pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@ impl<T> Segment<T> {
|
|||
(first, last)
|
||||
}
|
||||
|
||||
/// TODO - binary_search_by_key() here (can we assume these are sorted by pos?)
|
||||
fn get_hash(&self, pos: u64) -> Result<Hash, SegmentError> {
|
||||
self.hash_pos
|
||||
.iter()
|
||||
|
@ -153,6 +154,16 @@ impl<T> Segment<T> {
|
|||
.zip(&self.hashes)
|
||||
.map(|(&p, &h)| (p, h))
|
||||
}
|
||||
|
||||
/// Segment proof
|
||||
pub fn proof(&self) -> &SegmentProof {
|
||||
&self.proof
|
||||
}
|
||||
|
||||
/// Segment identifier
|
||||
pub fn id(&self) -> SegmentIdentifier {
|
||||
self.identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Segment<T>
|
||||
|
@ -539,6 +550,11 @@ impl SegmentProof {
|
|||
Ok(proof)
|
||||
}
|
||||
|
||||
/// Size of the proof in hashes.
|
||||
pub fn size(&self) -> usize {
|
||||
self.hashes.len()
|
||||
}
|
||||
|
||||
/// Reconstruct PMMR root using this proof
|
||||
pub fn reconstruct_root(
|
||||
&self,
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
use grin_core as core;
|
||||
use grin_store as store;
|
||||
use grin_util as util;
|
||||
use store::PrefixIterator;
|
||||
|
||||
use crate::core::global;
|
||||
use crate::core::ser::{self, Readable, Reader, Writeable, Writer};
|
||||
|
|
|
@ -28,8 +28,7 @@ extern crate lazy_static;
|
|||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
// Re-export so only has to be included once
|
||||
pub use parking_lot::Mutex;
|
||||
pub use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
pub use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
// Re-export so only has to be included once
|
||||
pub use secp256k1zkp as secp;
|
||||
|
|
Loading…
Reference in a new issue