// Copyright 2021 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. //! Manages the reconsitution of a txhashset from segments produced by the //! segmenter use std::sync::Arc; use crate::core::core::hash::Hash; use crate::core::core::pmmr; use crate::core::core::{BlockHeader, OutputIdentifier, Segment, TxKernel}; use crate::error::Error; use crate::txhashset::{BitmapAccumulator, BitmapChunk, TxHashSet}; use crate::util::secp::pedersen::RangeProof; use crate::util::RwLock; use crate::store; use crate::txhashset; use croaring::Bitmap; /// Desegmenter for rebuilding a txhashset from PIBD segments #[derive(Clone)] pub struct Desegmenter { txhashset: Arc>, header_pmmr: Arc>>, archive_header: BlockHeader, store: Arc, bitmap_accumulator: BitmapAccumulator, bitmap_segments: Vec>, output_segments: Vec>, rangeproof_segments: Vec>, kernel_segments: Vec>, bitmap_mmr_leaf_count: u64, bitmap_mmr_size: u64, // In-memory 'raw' bitmap corresponding to contents of bitmap accumulator bitmap_cache: Option, } impl Desegmenter { /// Create a new segmenter based on the provided txhashset and the specified block header pub fn new( txhashset: Arc>, header_pmmr: Arc>>, archive_header: BlockHeader, store: Arc, ) -> Desegmenter { let mut retval = Desegmenter { txhashset, header_pmmr, archive_header, store, bitmap_accumulator: BitmapAccumulator::new(), bitmap_segments: vec![], output_segments: vec![], rangeproof_segments: vec![], kernel_segments: vec![], bitmap_mmr_leaf_count: 0, bitmap_mmr_size: 0, bitmap_cache: None, }; retval.calc_bitmap_mmr_sizes(); retval } /// Return reference to the header used for validation pub fn header(&self) -> &BlockHeader { &self.archive_header } /// Return size of bitmap mmr pub fn expected_bitmap_mmr_size(&self) -> u64 { self.bitmap_mmr_size } /// 'Finalize' the bitmap accumulator, storing an in-memory copy of the bitmap for /// use in further validation and setting the accumulator on the underlying txhashset /// TODO: Could be called automatically when we have the calculated number of /// required segments for the archive header /// TODO: Accumulator will likely need to be stored locally to deal with server /// being shut down and restarted pub fn finalize_bitmap(&mut self) -> Result<(), Error> { debug!( "pibd_desgmenter: caching bitmap - accumulator root: {}", self.bitmap_accumulator.root() ); self.bitmap_cache = Some(self.bitmap_accumulator.as_bitmap()?); // Set the txhashset's bitmap accumulator let mut header_pmmr = self.header_pmmr.write(); let mut txhashset = self.txhashset.write(); let mut batch = self.store.batch()?; txhashset::extending( &mut header_pmmr, &mut txhashset, &mut batch, |ext, _batch| { let extension = &mut ext.extension; // TODO: Unwrap extension.set_bitmap_accumulator(self.bitmap_accumulator.clone()); Ok(()) }, )?; Ok(()) } // Calculate and store number of leaves and positions in the bitmap mmr given the number of // outputs specified in the header. Should be called whenever the header changes fn calc_bitmap_mmr_sizes(&mut self) { // Number of leaves (BitmapChunks) self.bitmap_mmr_leaf_count = (pmmr::n_leaves(self.archive_header.output_mmr_size) + 1023) / 1024; debug!( "pibd_desgmenter - expected number of leaves in bitmap MMR: {}", self.bitmap_mmr_leaf_count ); // Total size of Bitmap PMMR self.bitmap_mmr_size = pmmr::peaks(self.bitmap_mmr_leaf_count) .last() .unwrap_or(&pmmr::insertion_to_pmmr_index(self.bitmap_mmr_leaf_count)) .clone(); debug!( "pibd_desgmenter - expected size of bitmap MMR: {}", self.bitmap_mmr_size ); } /// Adds and validates a bitmap chunk /// TODO: Still experimenting, this expects chunks received to be in order pub fn add_bitmap_segment( &mut self, segment: Segment, output_root_hash: Hash, ) -> Result<(), Error> { debug!("pibd_desegmenter: add bitmap segment"); segment.validate_with( self.bitmap_mmr_size, // Last MMR pos at the height being validated, in this case of the bitmap root None, self.archive_header.output_root, // Output root we're checking for self.archive_header.output_mmr_size, output_root_hash, // Other root true, )?; // All okay, add leaves to bitmap accumulator let (_sid, _hash_pos, _hashes, _leaf_pos, leaf_data, _proof) = segment.parts(); for chunk in leaf_data.into_iter() { self.bitmap_accumulator.append_chunk(chunk)?; } Ok(()) } /// Adds a output segment /// TODO: Still experimenting, expects chunks received to be in order pub fn add_output_segment(&self, segment: Segment) -> Result<(), Error> { debug!("pibd_desegmenter: add output segment"); segment.validate_with( self.archive_header.output_mmr_size, // Last MMR pos at the height being validated self.bitmap_cache.as_ref(), self.archive_header.output_root, // Output root we're checking for self.archive_header.output_mmr_size, self.bitmap_accumulator.root(), // Other root false, )?; let mut header_pmmr = self.header_pmmr.write(); let mut txhashset = self.txhashset.write(); let mut batch = self.store.batch()?; txhashset::extending( &mut header_pmmr, &mut txhashset, &mut batch, |ext, _batch| { let extension = &mut ext.extension; extension.apply_output_segment(segment)?; Ok(()) }, )?; Ok(()) } /// Adds a Rangeproof segment /// TODO: Still experimenting, expects chunks received to be in order pub fn add_rangeproof_segment(&self, segment: Segment) -> Result<(), Error> { debug!("pibd_desegmenter: add rangeproof segment"); segment.validate( self.archive_header.output_mmr_size, // Last MMR pos at the height being validated self.bitmap_cache.as_ref(), self.archive_header.range_proof_root, // Range proof root we're checking for )?; let mut header_pmmr = self.header_pmmr.write(); let mut txhashset = self.txhashset.write(); let mut batch = self.store.batch()?; txhashset::extending( &mut header_pmmr, &mut txhashset, &mut batch, |ext, _batch| { let extension = &mut ext.extension; extension.apply_rangeproof_segment(segment)?; Ok(()) }, )?; Ok(()) } /// Adds a Kernel segment /// TODO: Still experimenting, expects chunks received to be in order pub fn add_kernel_segment(&self, segment: Segment) -> Result<(), Error> { debug!("pibd_desegmenter: add kernel segment"); segment.validate( self.archive_header.kernel_mmr_size, // Last MMR pos at the height being validated None, self.archive_header.kernel_root, // Kernel root we're checking for )?; let mut header_pmmr = self.header_pmmr.write(); let mut txhashset = self.txhashset.write(); let mut batch = self.store.batch()?; txhashset::extending( &mut header_pmmr, &mut txhashset, &mut batch, |ext, _batch| { let extension = &mut ext.extension; extension.apply_kernel_segment(segment)?; Ok(()) }, )?; Ok(()) } }