Skip to content

Commit

Permalink
PIBD adaptation for MWC. No hardfork is required, bitmat hash is agre…
Browse files Browse the repository at this point in the history
…ed at PIBD start handshake stage.
  • Loading branch information
bayk committed Jul 9, 2024
1 parent ea21be6 commit ac0bd8d
Show file tree
Hide file tree
Showing 17 changed files with 840 additions and 390 deletions.
34 changes: 25 additions & 9 deletions chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1128,30 +1128,45 @@ impl Chain {
))
}

/// instantiate desegmenter for this header. Expected that handshake is done and as a result, header with bitmap_root_hash is known
pub fn create_desegmenter(
&self,
archive_header: &BlockHeader,
bitmap_root_hash: Hash,
) -> Result<(), Error> {
let desegmenter = self.init_desegmenter(archive_header, bitmap_root_hash)?;
*self.pibd_desegmenter.write() = Some(desegmenter);
Ok(())
}

/// instantiate desegmenter (in same lazy fashion as segmenter, though this should not be as
/// expensive an operation)
pub fn desegmenter(
pub fn get_desegmenter(
&self,
archive_header: &BlockHeader,
) -> Result<Arc<RwLock<Option<Desegmenter>>>, Error> {
) -> Arc<RwLock<Option<Desegmenter>>> {
// Use our cached desegmenter if we have one and the associated header matches.
if let Some(d) = self.pibd_desegmenter.write().as_ref() {
if d.header() == archive_header {
return Ok(self.pibd_desegmenter.clone());
return self.pibd_desegmenter.clone();
}
}
return Arc::new(RwLock::new(None));
}

let desegmenter = self.init_desegmenter(archive_header)?;
let mut cache = self.pibd_desegmenter.write();
*cache = Some(desegmenter.clone());

Ok(self.pibd_desegmenter.clone())
/// Reset desegmenter associated with this seesion
pub fn reset_desegmenter(&self) {
*self.pibd_desegmenter.write() = None
}

/// initialize a desegmenter, which is capable of extending the hashset by appending
/// PIBD segments of the three PMMR trees + Bitmap PMMR
/// header should be the same header as selected for the txhashset.zip archive
fn init_desegmenter(&self, header: &BlockHeader) -> Result<Desegmenter, Error> {
fn init_desegmenter(
&self,
header: &BlockHeader,
bitmap_root_hash: Hash,
) -> Result<Desegmenter, Error> {
debug!(
"init_desegmenter: initializing new desegmenter for {} at {}",
header.hash(),
Expand All @@ -1162,6 +1177,7 @@ impl Chain {
self.txhashset(),
self.header_pmmr.clone(),
header.clone(),
bitmap_root_hash,
self.genesis.header.clone(),
self.store.clone(),
))
Expand Down
5 changes: 3 additions & 2 deletions chain/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::core::ser;
use crate::keychain;
use crate::util::secp;
use crate::util::secp::pedersen::Commitment;
use grin_core::core::hash::Hash;
use grin_store as store;
use std::io;

Expand Down Expand Up @@ -179,8 +180,8 @@ pub enum Error {
#[error("Aborting PIBD error")]
AbortingPIBDError,
/// The segmenter is associated to a different block header
#[error("Segmenter header mismatch")]
SegmenterHeaderMismatch,
#[error("Segmenter header mismatch, available {0} at height {1}")]
SegmenterHeaderMismatch(Hash, u64),
/// Segment height not within allowed range
#[error("Invalid segment height")]
InvalidSegmentHeight,
Expand Down
3 changes: 3 additions & 0 deletions chain/src/pibd_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub const SEGMENT_REQUEST_TIMEOUT_SECS: i64 = 60;
/// will always be requested first)
pub const SEGMENT_REQUEST_COUNT: usize = 15;

/// Maximum stale requests per peer. If there are more requests, no new data will be requested
pub const STALE_REQUESTS_PER_PEER: u32 = 5;

/// If the syncer hasn't seen a max work peer that supports PIBD in this number of seconds
/// give up and revert back to the txhashset.zip download method
pub const TXHASHSET_ZIP_FALLBACK_TIME_SECS: i64 = 60;
27 changes: 8 additions & 19 deletions chain/src/txhashset/desegmenter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub struct Desegmenter {
txhashset: Arc<RwLock<TxHashSet>>,
header_pmmr: Arc<RwLock<txhashset::PMMRHandle<BlockHeader>>>,
archive_header: BlockHeader,
bitmap_root_hash: Hash, // bitmap root hash must come as a result of handshake process
store: Arc<store::ChainStore>,

genesis: BlockHeader,
Expand Down Expand Up @@ -78,6 +79,7 @@ impl Desegmenter {
txhashset: Arc<RwLock<TxHashSet>>,
header_pmmr: Arc<RwLock<txhashset::PMMRHandle<BlockHeader>>>,
archive_header: BlockHeader,
bitmap_root_hash: Hash,
genesis: BlockHeader,
store: Arc<store::ChainStore>,
) -> Desegmenter {
Expand All @@ -86,6 +88,7 @@ impl Desegmenter {
txhashset,
header_pmmr,
archive_header,
bitmap_root_hash,
store,
genesis,
bitmap_accumulator: BitmapAccumulator::new(),
Expand Down Expand Up @@ -653,19 +656,12 @@ impl Desegmenter {
}

/// Adds and validates a bitmap chunk
pub fn add_bitmap_segment(
&mut self,
segment: Segment<BitmapChunk>,
output_root_hash: Hash,
) -> Result<(), Error> {
pub fn add_bitmap_segment(&mut self, segment: Segment<BitmapChunk>) -> Result<(), Error> {
trace!("pibd_desegmenter: add bitmap segment");
segment.validate_with(
segment.validate(
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,
self.bitmap_root_hash,
)?;
trace!("pibd_desegmenter: adding segment to cache");
// All okay, add to our cached list of bitmap segments
Expand Down Expand Up @@ -778,24 +774,17 @@ impl Desegmenter {
}

/// Adds a output segment
pub fn add_output_segment(
&mut self,
segment: Segment<OutputIdentifier>,
_bitmap_root: Option<Hash>,
) -> Result<(), Error> {
pub fn add_output_segment(&mut self, segment: Segment<OutputIdentifier>) -> Result<(), Error> {
trace!("pibd_desegmenter: add output segment");
// TODO: This, something very wrong, probably need to reset entire body sync
// check bitmap root matches what we already have
/*if bitmap_root != Some(self.bitmap_accumulator.root()) {
}*/
segment.validate_with(
segment.validate(
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,
)?;
self.cache_output_segment(segment);
Ok(())
Expand Down
23 changes: 5 additions & 18 deletions chain/src/txhashset/segmenter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,31 +70,19 @@ impl Segmenter {
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(&Error::TxHashSetErr)?;
Ok(root)
}

/// The root of the bitmap snapshot PMMR.
fn bitmap_root(&self) -> Result<Hash, Error> {
pub fn bitmap_root(&self) -> Result<Hash, Error> {
let pmmr = self.bitmap_snapshot.readonly_pmmr();
let root = pmmr.root().map_err(&Error::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> {
pub fn bitmap_segment(&self, id: SegmentIdentifier) -> Result<Segment<BitmapChunk>, 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,
Expand All @@ -104,19 +92,18 @@ impl Segmenter {
segment.proof().size(),
now.elapsed().as_millis()
);
Ok((segment, output_root))
Ok(segment)
}

/// Create an output segment and return it with the corresponding bitmap root.
pub fn output_segment(
&self,
id: SegmentIdentifier,
) -> Result<(Segment<OutputIdentifier>, Hash), Error> {
) -> Result<Segment<OutputIdentifier>, 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,
Expand All @@ -126,7 +113,7 @@ impl Segmenter {
segment.proof().size(),
now.elapsed().as_millis()
);
Ok((segment, bitmap_root))
Ok(segment)
}

/// Create a rangeproof segment.
Expand Down
42 changes: 24 additions & 18 deletions chain/tests/test_pibd_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use grin_util as util;
#[macro_use]
extern crate log;

use grin_core::core::BlockHeader;
use std::path::Path;
use std::sync::Arc;
use std::{fs, io};
Expand Down Expand Up @@ -87,15 +88,16 @@ impl SegmenterResponder {
self.chain.clone()
}

pub fn get_bitmap_segment(&self, seg_id: SegmentIdentifier) -> (Segment<BitmapChunk>, Hash) {
pub fn get_bitmap_root_hash(&self) -> Hash {
self.chain.segmenter().unwrap().bitmap_root().unwrap()
}

pub fn get_bitmap_segment(&self, seg_id: SegmentIdentifier) -> Segment<BitmapChunk> {
let segmenter = self.chain.segmenter().unwrap();
segmenter.bitmap_segment(seg_id).unwrap()
}

pub fn get_output_segment(
&self,
seg_id: SegmentIdentifier,
) -> (Segment<OutputIdentifier>, Hash) {
pub fn get_output_segment(&self, seg_id: SegmentIdentifier) -> Segment<OutputIdentifier> {
let segmenter = self.chain.segmenter().unwrap();
segmenter.output_segment(seg_id).unwrap()
}
Expand Down Expand Up @@ -174,11 +176,17 @@ impl DesegmenterRequestor {
}
}

pub fn init_desegmenter(&mut self, bitmap_root_hash: Hash) {
let archive_header = self.chain.txhashset_archive_header_header_only().unwrap();
self.chain
.create_desegmenter(&archive_header, bitmap_root_hash);
}

// Emulate `continue_pibd` function, which would be called from state sync
// return whether is complete
pub fn continue_pibd(&mut self) -> bool {
let archive_header = self.chain.txhashset_archive_header_header_only().unwrap();
let desegmenter = self.chain.desegmenter(&archive_header).unwrap();
let desegmenter = self.chain.get_desegmenter(&archive_header);

// Apply segments... TODO: figure out how this should be called, might
// need to be a separate thread.
Expand All @@ -205,17 +213,15 @@ impl DesegmenterRequestor {
// Perform request and response
match seg_id.segment_type {
SegmentType::Bitmap => {
let (seg, output_root) =
self.responder.get_bitmap_segment(seg_id.identifier.clone());
let seg = self.responder.get_bitmap_segment(seg_id.identifier.clone());
if let Some(d) = desegmenter.write().as_mut() {
d.add_bitmap_segment(seg, output_root).unwrap();
d.add_bitmap_segment(seg).unwrap();
}
}
SegmentType::Output => {
let (seg, bitmap_root) =
self.responder.get_output_segment(seg_id.identifier.clone());
let seg = self.responder.get_output_segment(seg_id.identifier.clone());
if let Some(d) = desegmenter.write().as_mut() {
d.add_output_segment(seg, Some(bitmap_root)).unwrap();
d.add_output_segment(seg).unwrap();
}
}
SegmentType::RangeProof => {
Expand Down Expand Up @@ -279,6 +285,7 @@ fn test_pibd_copy_impl(
}

let src_responder = Arc::new(SegmenterResponder::new(src_root_dir, genesis.clone()));
let bitmap_root_hash = src_responder.get_bitmap_root_hash();
let mut dest_requestor =
DesegmenterRequestor::new(dest_root_dir, genesis.clone(), src_responder);

Expand All @@ -290,6 +297,8 @@ fn test_pibd_copy_impl(
}
}

dest_requestor.init_desegmenter(bitmap_root_hash);

// Perform until desegmenter reports it's done
while !dest_requestor.continue_pibd() {}

Expand Down Expand Up @@ -327,12 +336,9 @@ fn test_pibd_copy_real() {
let copy_headers_to_template = false;

// if testing against a real chain, insert location here
let src_root_dir = format!("/home/yeastplume/Projects/grin-project/servers/floo-1/chain_data");
let dest_template_dir = format!(
"/home/yeastplume/Projects/grin-project/servers/floo-pibd-1/chain_data_headers_only"
);
let dest_root_dir =
format!("/home/yeastplume/Projects/grin-project/servers/floo-pibd-1/chain_data_test_copy");
let src_root_dir = format!("/Users/bay/.mwc/_floo/chain_data");
let dest_template_dir = format!("/Users/bay/.mwc/_floo2/chain_data");
let dest_root_dir = format!("/Users/bay/.mwc/_floo2/chain_data");
if copy_headers_to_template {
clean_output_dir(&dest_template_dir);
test_pibd_copy_impl(false, &src_root_dir, &dest_template_dir, None);
Expand Down
Loading

0 comments on commit ac0bd8d

Please sign in to comment.