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:
committed by
Ignotus Peverell
parent
55f6e3e63f
commit
e22d025dc8
+71
-57
@@ -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(¤t.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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user