Simplify block marker metadata (#985)

* wip BlockMarker struct, get rid of PMMRMetadata
* use rewind to init the txhashet correctly on startup, we do not need to track pos via metadata (we have block markers), we do not need to open the txhashset with specific pos (we have rewind)
* better logging on init
* keep rewinding and validating on init, to find a good block
* use validate_roots on chain init
This commit is contained in:
Antioch Peverell
2018-04-24 15:53:01 -04:00
committed by Ignotus Peverell
parent 55f6e3e63f
commit e22d025dc8
11 changed files with 278 additions and 412 deletions
+71 -57
View File
@@ -166,56 +166,71 @@ impl Chain {
// check if we have a head in store, otherwise the genesis block is it
let head = store.head();
let txhashset_md = match head {
Ok(h) => {
// Add the height to the metadata for the use of the rewind log, as this isn't
// stored
if let Ok(mut ts) = store.get_block_pmmr_file_metadata(&h.last_block_h) {
ts.output_file_md.block_height = h.height;
ts.rproof_file_md.block_height = h.height;
ts.kernel_file_md.block_height = h.height;
Some(ts)
} else {
debug!(LOGGER, "metadata not found for {} @ {}", h.height, h.hash());
None
// open the txhashset, creating a new one if necessary
let mut txhashset = txhashset::TxHashSet::open(db_root.clone(), store.clone())?;
match head {
Ok(head) => {
let mut head = head;
loop {
// Use current chain tip if we have one.
// Note: We are rewinding and validating against a writeable extension.
// If validation is successful we will truncate the backend files
// to match the provided block header.
let header = store.get_block_header(&head.last_block_h)?;
let res = txhashset::extending(&mut txhashset, |extension| {
debug!(
LOGGER,
"chain: init: rewinding and validating before we start... {} at {}",
header.hash(),
header.height,
);
extension.rewind(&header)?;
extension.validate_roots(&header)?;
Ok(())
});
if res.is_ok() {
break;
} else {
// We may have corrupted the MMR backend files
// last time we stopped the node.
// If this appears to be the case
// revert the head to the previous header and try again
let _ = store.delete_block(&header.hash());
let prev_header = store.get_block_header(&head.prev_block_h)?;
let _ = store.setup_height(&prev_header, &head)?;
head = Tip::from_block(&prev_header);
store.save_head(&head)?;
}
}
}
Err(NotFoundErr) => None,
Err(e) => return Err(Error::StoreErr(e, "chain init load head".to_owned())),
};
let mut txhashset =
txhashset::TxHashSet::open(db_root.clone(), store.clone(), txhashset_md)?;
let head = store.head();
let head = match head {
Ok(h) => h,
Err(NotFoundErr) => {
let tip = Tip::from_block(&genesis.header);
store.save_block(&genesis)?;
store.setup_height(&genesis.header, &tip)?;
if genesis.kernels.len() > 0 {
txhashset::extending(&mut txhashset, |extension| {
extension.apply_block(&genesis)
})?;
}
txhashset::extending(&mut txhashset, |extension| {
extension.apply_block(&genesis)?;
Ok(())
})?;
// saving a new tip based on genesis
store.save_head(&tip)?;
info!(
LOGGER,
"Saved genesis block: {:?}, nonce: {:?}, pow: {:?}",
"chain: init: saved genesis block: {:?}, nonce: {:?}, pow: {:?}",
genesis.hash(),
genesis.header.nonce,
genesis.header.pow,
);
pipe::save_pmmr_metadata(&tip, &txhashset, store.clone())?;
tip
}
Err(e) => return Err(Error::StoreErr(e, "chain init load head".to_owned())),
};
// Now reload the chain head (either existing head or genesis from above)
let head = store.head()?;
// Initialize header_head and sync_head as necessary for chain init.
store.init_head()?;
@@ -224,7 +239,7 @@ impl Chain {
"Chain init: {} @ {} [{}]",
head.total_difficulty.into_num(),
head.height,
head.last_block_h
head.last_block_h,
);
Ok(Chain {
@@ -434,9 +449,10 @@ impl Chain {
// Now create an extension from the txhashset and validate
// against the latest block header.
// We will rewind the extension internally to the pos for
// the block header to ensure the view is consistent.
// Rewind the extension to the specified header to ensure the view is
// consistent.
txhashset::extending_readonly(&mut txhashset, |extension| {
extension.rewind(&header)?;
extension.validate(&header, skip_rproofs)
})
}
@@ -502,18 +518,14 @@ impl Chain {
/// at the provided block hash.
pub fn txhashset_read(&self, h: Hash) -> Result<(u64, u64, File), Error> {
// get the indexes for the block
let out_index: u64;
let kernel_index: u64;
{
let marker = {
let txhashset = self.txhashset.read().unwrap();
let (oi, ki) = txhashset.indexes_at(&h)?;
out_index = oi;
kernel_index = ki;
}
txhashset.indexes_at(&h)?
};
// prepares the zip and return the corresponding Read
let txhashset_reader = txhashset::zip_read(self.db_root.clone())?;
Ok((out_index, kernel_index, txhashset_reader))
Ok((marker.output_pos, marker.kernel_pos, txhashset_reader))
}
/// Writes a reading view on a txhashset state that's been provided to us.
@@ -539,16 +551,22 @@ impl Chain {
// write the block marker so we can safely rewind to
// the pos for that block when we validate the extension below
self.store
.save_block_marker(&h, &(rewind_to_output, rewind_to_kernel))?;
let marker = BlockMarker {
output_pos: rewind_to_output,
kernel_pos: rewind_to_kernel,
};
self.store.save_block_marker(&h, &marker)?;
debug!(
LOGGER,
"Going to validate new txhashset, might take some time..."
);
let mut txhashset =
txhashset::TxHashSet::open(self.db_root.clone(), self.store.clone(), None)?;
let mut txhashset = txhashset::TxHashSet::open(self.db_root.clone(), self.store.clone())?;
// Note: we are validating against a writeable extension.
txhashset::extending(&mut txhashset, |extension| {
extension.rewind(&header)?;
extension.validate(&header, false)?;
extension.rebuild_index()?;
Ok(())
@@ -621,7 +639,6 @@ impl Chain {
match self.store.get_block(&current.hash()) {
Ok(b) => {
self.store.delete_block(&b.hash())?;
self.store.delete_block_pmmr_file_metadata(&b.hash())?;
self.store.delete_block_marker(&b.hash())?;
}
Err(NotFoundErr) => {
@@ -733,6 +750,13 @@ impl Chain {
.map_err(|e| Error::StoreErr(e, "chain get header".to_owned()))
}
/// Get the block marker for the specified block hash.
pub fn get_block_marker(&self, bh: &Hash) -> Result<BlockMarker, Error> {
self.store
.get_block_marker(bh)
.map_err(|e| Error::StoreErr(e, "chain get block marker".to_owned()))
}
/// Gets the block header at the provided height
pub fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, Error> {
self.store
@@ -778,16 +802,6 @@ impl Chain {
.map_err(|e| Error::StoreErr(e, "chain block exists".to_owned()))
}
/// Retrieve the file index metadata for a given block
pub fn get_block_pmmr_file_metadata(
&self,
h: &Hash,
) -> Result<PMMRFileMetadataCollection, Error> {
self.store
.get_block_pmmr_file_metadata(h)
.map_err(|e| Error::StoreErr(e, "retrieve block pmmr metadata".to_owned()))
}
/// Rebuilds height index. Reachable as endpoint POST /chain/height-index
pub fn rebuild_header_by_height(&self) -> Result<(), Error> {
let head = self.head_header()?;
+1 -21
View File
@@ -112,27 +112,7 @@ pub fn process_block(b: &Block, mut ctx: BlockContext) -> Result<Option<Tip>, Er
Ok(h)
});
match result {
Ok(t) => {
save_pmmr_metadata(&Tip::from_block(&b.header), &txhashset, ctx.store.clone())?;
Ok(t)
}
Err(e) => Err(e),
}
}
/// Save pmmr index location for a given block
pub fn save_pmmr_metadata(
t: &Tip,
txhashset: &txhashset::TxHashSet,
store: Arc<ChainStore>,
) -> Result<(), Error> {
// Save pmmr file metadata for this block
let block_file_md = txhashset.last_file_metadata();
store
.save_block_pmmr_file_metadata(&t.last_block_h, &block_file_md)
.map_err(|e| Error::StoreErr(e, "saving pmmr file metadata".to_owned()))?;
Ok(())
result
}
/// Process the block header.
+12 -26
View File
@@ -37,7 +37,6 @@ const SYNC_HEAD_PREFIX: u8 = 's' as u8;
const HEADER_HEIGHT_PREFIX: u8 = '8' as u8;
const COMMIT_POS_PREFIX: u8 = 'c' as u8;
const BLOCK_MARKER_PREFIX: u8 = 'm' as u8;
const BLOCK_PMMR_FILE_METADATA_PREFIX: u8 = 'p' as u8;
/// An implementation of the ChainStore trait backed by a simple key-value
/// store.
@@ -163,7 +162,17 @@ impl ChainStore for ChainKVStore {
self.db.delete(&to_key(BLOCK_PREFIX, &mut bh.to_vec())[..])
}
// We are on the current chain if -
// * the header by height index matches the header, and
// * we are not ahead of the current head
fn is_on_current_chain(&self, header: &BlockHeader) -> Result<(), Error> {
let head = self.head()?;
// check we are not out ahead of the current head
if header.height > head.height {
return Err(Error::NotFoundErr);
}
let header_at_height = self.get_header_by_height(header.height)?;
if header.hash() == header_at_height.hash() {
Ok(())
@@ -212,12 +221,12 @@ impl ChainStore for ChainKVStore {
.delete(&to_key(COMMIT_POS_PREFIX, &mut commit.to_vec()))
}
fn save_block_marker(&self, bh: &Hash, marker: &(u64, u64)) -> Result<(), Error> {
fn save_block_marker(&self, bh: &Hash, marker: &BlockMarker) -> Result<(), Error> {
self.db
.put_ser(&to_key(BLOCK_MARKER_PREFIX, &mut bh.to_vec())[..], &marker)
}
fn get_block_marker(&self, bh: &Hash) -> Result<(u64, u64), Error> {
fn get_block_marker(&self, bh: &Hash) -> Result<BlockMarker, Error> {
option_to_not_found(
self.db
.get_ser(&to_key(BLOCK_MARKER_PREFIX, &mut bh.to_vec())),
@@ -229,29 +238,6 @@ impl ChainStore for ChainKVStore {
.delete(&to_key(BLOCK_MARKER_PREFIX, &mut bh.to_vec()))
}
fn save_block_pmmr_file_metadata(
&self,
h: &Hash,
md: &PMMRFileMetadataCollection,
) -> Result<(), Error> {
self.db.put_ser(
&to_key(BLOCK_PMMR_FILE_METADATA_PREFIX, &mut h.to_vec())[..],
&md,
)
}
fn get_block_pmmr_file_metadata(&self, h: &Hash) -> Result<PMMRFileMetadataCollection, Error> {
option_to_not_found(
self.db
.get_ser(&to_key(BLOCK_PMMR_FILE_METADATA_PREFIX, &mut h.to_vec())),
)
}
fn delete_block_pmmr_file_metadata(&self, h: &Hash) -> Result<(), Error> {
self.db
.delete(&to_key(BLOCK_PMMR_FILE_METADATA_PREFIX, &mut h.to_vec())[..])
}
/// Maintain consistency of the "header_by_height" index by traversing back
/// through the current chain and updating "header_by_height" until we reach
/// a block_header that is consistent with its height (everything prior to
+53 -81
View File
@@ -33,10 +33,10 @@ use core::core::hash::{Hash, Hashed};
use core::ser::{PMMRIndexHashable, PMMRable};
use grin_store;
use grin_store::pmmr::{PMMRBackend, PMMRFileMetadata};
use grin_store::pmmr::PMMRBackend;
use grin_store::types::prune_noop;
use keychain::BlindingFactor;
use types::{ChainStore, Error, PMMRFileMetadataCollection, TxHashSetRoots};
use types::{BlockMarker, ChainStore, Error, TxHashSetRoots};
use util::{zip, LOGGER};
const TXHASHSET_SUBDIR: &'static str = "txhashset";
@@ -57,25 +57,16 @@ impl<T> PMMRHandle<T>
where
T: PMMRable + ::std::fmt::Debug,
{
fn new(
root_dir: String,
file_name: &str,
index_md: Option<PMMRFileMetadata>,
) -> Result<PMMRHandle<T>, Error> {
fn new(root_dir: String, file_name: &str) -> Result<PMMRHandle<T>, Error> {
let path = Path::new(&root_dir).join(TXHASHSET_SUBDIR).join(file_name);
fs::create_dir_all(path.clone())?;
let be = PMMRBackend::new(path.to_str().unwrap().to_string(), index_md)?;
let be = PMMRBackend::new(path.to_str().unwrap().to_string())?;
let sz = be.unpruned_size()?;
Ok(PMMRHandle {
backend: be,
last_pos: sz,
})
}
/// Return last written positions of hash file and data file
pub fn last_file_positions(&self) -> PMMRFileMetadata {
self.backend.last_file_positions()
}
}
/// An easy to manipulate structure holding the 3 sum trees necessary to
@@ -99,11 +90,7 @@ pub struct TxHashSet {
impl TxHashSet {
/// Open an existing or new set of backends for the TxHashSet
pub fn open(
root_dir: String,
commit_index: Arc<ChainStore>,
last_file_positions: Option<PMMRFileMetadataCollection>,
) -> Result<TxHashSet, Error> {
pub fn open(root_dir: String, commit_index: Arc<ChainStore>) -> Result<TxHashSet, Error> {
let output_file_path: PathBuf = [&root_dir, TXHASHSET_SUBDIR, OUTPUT_SUBDIR]
.iter()
.collect();
@@ -119,21 +106,11 @@ impl TxHashSet {
.collect();
fs::create_dir_all(kernel_file_path.clone())?;
let mut output_md = None;
let mut rproof_md = None;
let mut kernel_md = None;
if let Some(p) = last_file_positions {
output_md = Some(p.output_file_md);
rproof_md = Some(p.rproof_file_md);
kernel_md = Some(p.kernel_file_md);
}
Ok(TxHashSet {
output_pmmr_h: PMMRHandle::new(root_dir.clone(), OUTPUT_SUBDIR, output_md)?,
rproof_pmmr_h: PMMRHandle::new(root_dir.clone(), RANGE_PROOF_SUBDIR, rproof_md)?,
kernel_pmmr_h: PMMRHandle::new(root_dir.clone(), KERNEL_SUBDIR, kernel_md)?,
commit_index: commit_index,
output_pmmr_h: PMMRHandle::new(root_dir.clone(), OUTPUT_SUBDIR)?,
rproof_pmmr_h: PMMRHandle::new(root_dir.clone(), RANGE_PROOF_SUBDIR)?,
kernel_pmmr_h: PMMRHandle::new(root_dir.clone(), KERNEL_SUBDIR)?,
commit_index,
})
}
@@ -212,19 +189,10 @@ impl TxHashSet {
}
/// Output and kernel MMR indexes at the end of the provided block
pub fn indexes_at(&self, bh: &Hash) -> Result<(u64, u64), Error> {
pub fn indexes_at(&self, bh: &Hash) -> Result<BlockMarker, Error> {
self.commit_index.get_block_marker(bh).map_err(&From::from)
}
/// Last file positions of Output set.. hash file,data file
pub fn last_file_metadata(&self) -> PMMRFileMetadataCollection {
PMMRFileMetadataCollection::new(
self.output_pmmr_h.last_file_positions(),
self.rproof_pmmr_h.last_file_positions(),
self.kernel_pmmr_h.last_file_positions(),
)
}
/// Get sum tree roots
/// TODO: Return data instead of hashes
pub fn roots(&mut self) -> (Hash, Hash, Hash) {
@@ -314,6 +282,7 @@ where
let sizes: (u64, u64, u64);
let res: Result<T, Error>;
let rollback: bool;
{
let commit_index = trees.commit_index.clone();
@@ -327,6 +296,7 @@ where
}
sizes = extension.sizes();
}
match res {
Err(e) => {
debug!(LOGGER, "Error returned, discarding txhashset extension.");
@@ -367,7 +337,7 @@ pub struct Extension<'a> {
commit_index: Arc<ChainStore>,
new_output_commits: HashMap<Commitment, u64>,
new_block_markers: HashMap<Hash, (u64, u64)>,
new_block_markers: HashMap<Hash, BlockMarker>,
rollback: bool,
}
@@ -427,11 +397,11 @@ impl<'a> Extension<'a> {
}
// finally, recording the PMMR positions after this block for future rewind
let last_output_pos = self.output_pmmr.unpruned_size();
let last_kernel_pos = self.kernel_pmmr.unpruned_size();
self.new_block_markers
.insert(b.hash(), (last_output_pos, last_kernel_pos));
let marker = BlockMarker {
output_pos: self.output_pmmr.unpruned_size(),
kernel_pos: self.kernel_pmmr.unpruned_size(),
};
self.new_block_markers.insert(b.hash(), marker);
Ok(())
}
@@ -440,8 +410,8 @@ impl<'a> Extension<'a> {
for (commit, pos) in &self.new_output_commits {
self.commit_index.save_output_pos(commit, *pos)?;
}
for (bh, tag) in &self.new_block_markers {
self.commit_index.save_block_marker(bh, tag)?;
for (bh, marker) in &self.new_block_markers {
self.commit_index.save_block_marker(bh, marker)?;
}
Ok(())
}
@@ -566,35 +536,28 @@ impl<'a> Extension<'a> {
pub fn rewind(&mut self, block_header: &BlockHeader) -> Result<(), Error> {
let hash = block_header.hash();
let height = block_header.height;
debug!(LOGGER, "Rewind to header at {} [{}]", height, hash); // keep this
debug!(LOGGER, "Rewind to header {} @ {}", height, hash);
// rewind each MMR
let (out_pos_rew, kern_pos_rew) = self.commit_index.get_block_marker(&hash)?;
self.rewind_pos(height, out_pos_rew, kern_pos_rew)?;
// rewind our MMRs to the appropriate pos
// based on block height and block marker
let marker = self.commit_index.get_block_marker(&hash)?;
self.rewind_to_marker(height, &marker)?;
Ok(())
}
/// Rewinds the MMRs to the provided positions, given the output and
/// kernel we want to rewind to.
fn rewind_pos(
&mut self,
height: u64,
out_pos_rew: u64,
kern_pos_rew: u64,
) -> Result<(), Error> {
debug!(
LOGGER,
"Rewind txhashset to output pos: {}, kernel pos: {}", out_pos_rew, kern_pos_rew,
);
fn rewind_to_marker(&mut self, height: u64, marker: &BlockMarker) -> Result<(), Error> {
debug!(LOGGER, "Rewind txhashset to {}, {:?}", height, marker);
self.output_pmmr
.rewind(out_pos_rew, height as u32)
.rewind(marker.output_pos, height as u32)
.map_err(&Error::TxHashSetErr)?;
self.rproof_pmmr
.rewind(out_pos_rew, height as u32)
.rewind(marker.output_pos, height as u32)
.map_err(&Error::TxHashSetErr)?;
self.kernel_pmmr
.rewind(kern_pos_rew, height as u32)
.rewind(marker.kernel_pos, height as u32)
.map_err(&Error::TxHashSetErr)?;
Ok(())
@@ -618,15 +581,26 @@ impl<'a> Extension<'a> {
}
}
/// Validate the txhashset state against the provided block header.
/// Rewinds to that pos for the header first so we see a consistent
/// view of the world.
/// Note: this is an expensive operation and sets force_rollback
/// so the extension is read-only.
pub fn validate(&mut self, header: &BlockHeader, skip_rproofs: bool) -> Result<(), Error> {
// rewind to the provided header for a consistent view
&self.rewind(header)?;
/// Validate the various MMR roots against the block header.
pub fn validate_roots(&self, header: &BlockHeader) -> Result<(), Error> {
// If we are validating the genesis block then
// we have no outputs or kernels.
// So we are done here.
if header.height == 0 {
return Ok(());
}
let roots = self.roots();
if roots.output_root != header.output_root || roots.rproof_root != header.range_proof_root
|| roots.kernel_root != header.kernel_root
{
return Err(Error::InvalidRoot);
}
Ok(())
}
/// Validate the txhashset state against the provided block header.
pub fn validate(&mut self, header: &BlockHeader, skip_rproofs: bool) -> Result<(), Error> {
// validate all hashes and sums within the trees
if let Err(e) = self.output_pmmr.validate() {
return Err(Error::InvalidTxHashSet(e));
@@ -638,12 +612,10 @@ impl<'a> Extension<'a> {
return Err(Error::InvalidTxHashSet(e));
}
// validate the tree roots against the block header
let roots = self.roots();
if roots.output_root != header.output_root || roots.rproof_root != header.range_proof_root
|| roots.kernel_root != header.kernel_root
{
return Err(Error::InvalidRoot);
self.validate_roots(header)?;
if header.height == 0 {
return Ok(());
}
// the real magicking: the sum of all kernel excess should equal the sum
+40 -75
View File
@@ -25,7 +25,6 @@ use core::core::{block, transaction, Block, BlockHeader};
use core::ser::{self, Readable, Reader, Writeable, Writer};
use grin_store as store;
use grin_store;
use grin_store::pmmr::PMMRFileMetadata;
use keychain;
bitflags! {
@@ -321,31 +320,14 @@ pub trait ChainStore: Send + Sync {
/// Saves a marker associated with a block recording the MMR positions of
/// its last elements.
fn save_block_marker(&self, bh: &Hash, marker: &(u64, u64)) -> Result<(), store::Error>;
fn save_block_marker(&self, bh: &Hash, marker: &BlockMarker) -> Result<(), store::Error>;
/// Retrieves a block marker from a block hash.
fn get_block_marker(&self, bh: &Hash) -> Result<(u64, u64), store::Error>;
fn get_block_marker(&self, bh: &Hash) -> Result<BlockMarker, store::Error>;
/// Deletes a block marker associated with the provided hash
fn delete_block_marker(&self, bh: &Hash) -> Result<(), store::Error>;
/// Saves information about the last written PMMR file positions for each
/// committed block
fn save_block_pmmr_file_metadata(
&self,
h: &Hash,
md: &PMMRFileMetadataCollection,
) -> Result<(), store::Error>;
/// Retrieves stored pmmr file metadata information for a given block
fn get_block_pmmr_file_metadata(
&self,
h: &Hash,
) -> Result<PMMRFileMetadataCollection, store::Error>;
/// Delete stored pmmr file metadata information for a given block
fn delete_block_pmmr_file_metadata(&self, h: &Hash) -> Result<(), store::Error>;
/// Saves the provided block header at the corresponding height. Also check
/// the consistency of the height chain in store by assuring previous
/// headers are also at their respective heights.
@@ -355,61 +337,6 @@ pub trait ChainStore: Send + Sync {
fn build_by_height_index(&self, header: &BlockHeader, force: bool) -> Result<(), store::Error>;
}
/// Single serializable struct to hold metadata about all PMMR file position
/// for a given block
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct PMMRFileMetadataCollection {
/// file metadata for the output file
pub output_file_md: PMMRFileMetadata,
/// file metadata for the rangeproof file
pub rproof_file_md: PMMRFileMetadata,
/// file metadata for the kernel file
pub kernel_file_md: PMMRFileMetadata,
}
impl Writeable for PMMRFileMetadataCollection {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
self.output_file_md.write(writer)?;
self.rproof_file_md.write(writer)?;
self.kernel_file_md.write(writer)?;
Ok(())
}
}
impl Readable for PMMRFileMetadataCollection {
fn read(reader: &mut Reader) -> Result<PMMRFileMetadataCollection, ser::Error> {
Ok(PMMRFileMetadataCollection {
output_file_md: PMMRFileMetadata::read(reader)?,
rproof_file_md: PMMRFileMetadata::read(reader)?,
kernel_file_md: PMMRFileMetadata::read(reader)?,
})
}
}
impl PMMRFileMetadataCollection {
/// Return empty with all file positions = 0
pub fn empty() -> PMMRFileMetadataCollection {
PMMRFileMetadataCollection {
output_file_md: PMMRFileMetadata::empty(),
rproof_file_md: PMMRFileMetadata::empty(),
kernel_file_md: PMMRFileMetadata::empty(),
}
}
/// Helper to create a new collection
pub fn new(
output_md: PMMRFileMetadata,
rproof_md: PMMRFileMetadata,
kernel_md: PMMRFileMetadata,
) -> PMMRFileMetadataCollection {
PMMRFileMetadataCollection {
output_file_md: output_md,
rproof_file_md: rproof_md,
kernel_file_md: kernel_md,
}
}
}
/// Bridge between the chain pipeline and the rest of the system. Handles
/// downstream processing of valid blocks by the rest of the system, most
/// importantly the broadcasting of blocks to our peers.
@@ -421,6 +348,44 @@ pub trait ChainAdapter {
/// Dummy adapter used as a placeholder for real implementations
pub struct NoopAdapter {}
impl ChainAdapter for NoopAdapter {
fn block_accepted(&self, _: &Block, _: Options) {}
}
/// The output and kernel positions that define the size of the MMRs for a
/// particular block.
#[derive(Debug, Clone)]
pub struct BlockMarker {
/// The output (and rangeproof) MMR position of the final output in the
/// block
pub output_pos: u64,
/// The kernel position of the final kernel in the block
pub kernel_pos: u64,
}
impl Writeable for BlockMarker {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u64(self.output_pos)?;
writer.write_u64(self.kernel_pos)?;
Ok(())
}
}
impl Readable for BlockMarker {
fn read(reader: &mut Reader) -> Result<BlockMarker, ser::Error> {
Ok(BlockMarker {
output_pos: reader.read_u64()?,
kernel_pos: reader.read_u64()?,
})
}
}
impl Default for BlockMarker {
fn default() -> BlockMarker {
BlockMarker {
output_pos: 0,
kernel_pos: 0,
}
}
}