Take the 'Sum' out of 'Sumtree' (#702)
* beginning to remove sum * continuing to remove sumtree sums * finished removing sums from pmmr core * renamed sumtree files, and completed changes+test updates in core and store * updating grin/chain to include removelogs * integration of flatfile structure, changes to chain/sumtree to start using them * tests on chain, core and store passing * cleaning up api and tests * formatting * flatfiles stored as part of PMMR backend instead * all compiling and tests running * documentation * added remove + pruning to flatfiles * remove unneeded enum * adding sumtree root struct
This commit is contained in:
+14
-17
@@ -20,21 +20,18 @@ use std::fs::File;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use util::secp::pedersen::RangeProof;
|
||||
|
||||
use core::core::{Input, OutputIdentifier, SumCommit};
|
||||
use core::core::hash::Hashed;
|
||||
use core::core::pmmr::{HashSum, NoSum};
|
||||
use core::core::{Input, OutputIdentifier, OutputStoreable, TxKernel};
|
||||
use core::core::hash::{Hash, Hashed};
|
||||
use core::global;
|
||||
|
||||
use core::core::{Block, BlockHeader, TxKernel};
|
||||
use core::core::{Block, BlockHeader};
|
||||
use core::core::target::Difficulty;
|
||||
use core::core::hash::Hash;
|
||||
use grin_store::Error::NotFoundErr;
|
||||
use pipe;
|
||||
use store;
|
||||
use sumtree;
|
||||
use types::*;
|
||||
use util::secp::pedersen::RangeProof;
|
||||
use util::LOGGER;
|
||||
|
||||
|
||||
@@ -428,9 +425,9 @@ impl Chain {
|
||||
Ok(extension.roots())
|
||||
})?;
|
||||
|
||||
b.header.utxo_root = roots.0.hash;
|
||||
b.header.range_proof_root = roots.1.hash;
|
||||
b.header.kernel_root = roots.2.hash;
|
||||
b.header.utxo_root = roots.utxo_root;
|
||||
b.header.range_proof_root = roots.rproof_root;
|
||||
b.header.kernel_root = roots.kernel_root;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -438,9 +435,9 @@ impl Chain {
|
||||
pub fn get_sumtree_roots(
|
||||
&self,
|
||||
) -> (
|
||||
HashSum<SumCommit>,
|
||||
HashSum<NoSum<RangeProof>>,
|
||||
HashSum<NoSum<TxKernel>>,
|
||||
Hash,
|
||||
Hash,
|
||||
Hash,
|
||||
) {
|
||||
let mut sumtrees = self.sumtrees.write().unwrap();
|
||||
sumtrees.roots()
|
||||
@@ -507,7 +504,7 @@ impl Chain {
|
||||
{
|
||||
let mut head = self.head.lock().unwrap();
|
||||
*head = Tip::from_block(&header);
|
||||
self.store.save_body_head(&head)?;
|
||||
let _ = self.store.save_body_head(&head);
|
||||
self.store.save_header_height(&header)?;
|
||||
}
|
||||
|
||||
@@ -517,19 +514,19 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// returns the last n nodes inserted into the utxo sum tree
|
||||
pub fn get_last_n_utxo(&self, distance: u64) -> Vec<HashSum<SumCommit>> {
|
||||
pub fn get_last_n_utxo(&self, distance: u64) -> Vec<(Hash, Option<OutputStoreable>)> {
|
||||
let mut sumtrees = self.sumtrees.write().unwrap();
|
||||
sumtrees.last_n_utxo(distance)
|
||||
}
|
||||
|
||||
/// as above, for rangeproofs
|
||||
pub fn get_last_n_rangeproof(&self, distance: u64) -> Vec<HashSum<NoSum<RangeProof>>> {
|
||||
pub fn get_last_n_rangeproof(&self, distance: u64) -> Vec<(Hash, Option<RangeProof>)> {
|
||||
let mut sumtrees = self.sumtrees.write().unwrap();
|
||||
sumtrees.last_n_rangeproof(distance)
|
||||
}
|
||||
|
||||
/// as above, for kernels
|
||||
pub fn get_last_n_kernel(&self, distance: u64) -> Vec<HashSum<NoSum<TxKernel>>> {
|
||||
pub fn get_last_n_kernel(&self, distance: u64) -> Vec<(Hash, Option<TxKernel>)> {
|
||||
let mut sumtrees = self.sumtrees.write().unwrap();
|
||||
sumtrees.last_n_kernel(distance)
|
||||
}
|
||||
|
||||
+6
-6
@@ -305,28 +305,28 @@ fn validate_block(
|
||||
// apply the new block to the MMR trees and check the new root hashes
|
||||
ext.apply_block(&b)?;
|
||||
|
||||
let (utxo_root, rproof_root, kernel_root) = ext.roots();
|
||||
if utxo_root.hash != b.header.utxo_root || rproof_root.hash != b.header.range_proof_root
|
||||
|| kernel_root.hash != b.header.kernel_root
|
||||
let roots = ext.roots();
|
||||
if roots.utxo_root != b.header.utxo_root || roots.rproof_root != b.header.range_proof_root
|
||||
|| roots.kernel_root != b.header.kernel_root
|
||||
{
|
||||
ext.dump(false);
|
||||
|
||||
debug!(
|
||||
LOGGER,
|
||||
"validate_block: utxo roots - {:?}, {:?}",
|
||||
utxo_root.hash,
|
||||
roots.utxo_root,
|
||||
b.header.utxo_root,
|
||||
);
|
||||
debug!(
|
||||
LOGGER,
|
||||
"validate_block: rproof roots - {:?}, {:?}",
|
||||
rproof_root.hash,
|
||||
roots.rproof_root,
|
||||
b.header.range_proof_root,
|
||||
);
|
||||
debug!(
|
||||
LOGGER,
|
||||
"validate_block: kernel roots - {:?}, {:?}",
|
||||
kernel_root.hash,
|
||||
roots.kernel_root,
|
||||
b.header.kernel_root,
|
||||
);
|
||||
|
||||
|
||||
+136
-118
@@ -1,4 +1,3 @@
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -26,26 +25,26 @@ use util::static_secp_instance;
|
||||
use util::secp::pedersen::{RangeProof, Commitment};
|
||||
|
||||
use core::consensus::reward;
|
||||
use core::core::{Block, BlockHeader, SumCommit, Input, Output, OutputIdentifier, OutputFeatures, TxKernel};
|
||||
use core::core::pmmr::{self, HashSum, NoSum, Summable, PMMR};
|
||||
use core::core::hash::Hashed;
|
||||
use core::ser;
|
||||
use core::core::{Block, BlockHeader, Input, Output, OutputIdentifier,
|
||||
OutputFeatures, OutputStoreable, TxKernel};
|
||||
use core::core::pmmr::{self, PMMR};
|
||||
use core::core::hash::{Hash, Hashed};
|
||||
use core::ser::{self, PMMRable};
|
||||
|
||||
use grin_store;
|
||||
use grin_store::sumtree::{PMMRBackend, AppendOnlyFile};
|
||||
use types::ChainStore;
|
||||
use types::Error;
|
||||
use grin_store::pmmr::PMMRBackend;
|
||||
use types::{ChainStore, SumTreeRoots, Error};
|
||||
use util::{LOGGER, zip};
|
||||
|
||||
const SUMTREES_SUBDIR: &'static str = "sumtrees";
|
||||
const UTXO_SUBDIR: &'static str = "utxo";
|
||||
const RANGE_PROOF_SUBDIR: &'static str = "rangeproof";
|
||||
const KERNEL_SUBDIR: &'static str = "kernel";
|
||||
const KERNEL_FILE: &'static str = "kernel_full_data.bin";
|
||||
const SUMTREES_ZIP: &'static str = "sumtrees_snapshot.zip";
|
||||
|
||||
struct PMMRHandle<T>
|
||||
where
|
||||
T: Summable + Clone,
|
||||
T: PMMRable,
|
||||
{
|
||||
backend: PMMRBackend<T>,
|
||||
last_pos: u64,
|
||||
@@ -53,7 +52,7 @@ where
|
||||
|
||||
impl<T> PMMRHandle<T>
|
||||
where
|
||||
T: Summable + Clone,
|
||||
T: PMMRable,
|
||||
{
|
||||
fn new(root_dir: String, file_name: &str) -> Result<PMMRHandle<T>, Error> {
|
||||
let path = Path::new(&root_dir).join(SUMTREES_SUBDIR).join(file_name);
|
||||
@@ -70,20 +69,17 @@ where
|
||||
/// An easy to manipulate structure holding the 3 sum trees necessary to
|
||||
/// validate blocks and capturing the UTXO set, the range proofs and the
|
||||
/// kernels. Also handles the index of Commitments to positions in the
|
||||
/// output and range proof sum trees.
|
||||
/// output and range proof pmmr trees.
|
||||
///
|
||||
/// Note that the index is never authoritative, only the trees are
|
||||
/// guaranteed to indicate whether an output is spent or not. The index
|
||||
/// may have commitments that have already been spent, even with
|
||||
/// pruning enabled.
|
||||
///
|
||||
/// In addition of the sumtrees, this maintains the full list of kernel
|
||||
/// data so it can be easily packaged for sync or validation.
|
||||
|
||||
pub struct SumTrees {
|
||||
output_pmmr_h: PMMRHandle<SumCommit>,
|
||||
rproof_pmmr_h: PMMRHandle<NoSum<RangeProof>>,
|
||||
kernel_pmmr_h: PMMRHandle<NoSum<TxKernel>>,
|
||||
kernel_file: AppendOnlyFile,
|
||||
utxo_pmmr_h: PMMRHandle<OutputStoreable>,
|
||||
rproof_pmmr_h: PMMRHandle<RangeProof>,
|
||||
kernel_pmmr_h: PMMRHandle<TxKernel>,
|
||||
|
||||
// chain store used as index of commitments to MMR positions
|
||||
commit_index: Arc<ChainStore>,
|
||||
@@ -92,16 +88,20 @@ pub struct SumTrees {
|
||||
impl SumTrees {
|
||||
/// Open an existing or new set of backends for the SumTrees
|
||||
pub fn open(root_dir: String, commit_index: Arc<ChainStore>) -> Result<SumTrees, Error> {
|
||||
let mut kernel_file_path: PathBuf = [&root_dir, SUMTREES_SUBDIR, KERNEL_SUBDIR].iter().collect();
|
||||
|
||||
let utxo_file_path: PathBuf = [&root_dir, SUMTREES_SUBDIR, UTXO_SUBDIR].iter().collect();
|
||||
fs::create_dir_all(utxo_file_path.clone())?;
|
||||
|
||||
let rproof_file_path: PathBuf = [&root_dir, SUMTREES_SUBDIR, RANGE_PROOF_SUBDIR].iter().collect();
|
||||
fs::create_dir_all(rproof_file_path.clone())?;
|
||||
|
||||
let kernel_file_path: PathBuf = [&root_dir, SUMTREES_SUBDIR, KERNEL_SUBDIR].iter().collect();
|
||||
fs::create_dir_all(kernel_file_path.clone())?;
|
||||
kernel_file_path.push(KERNEL_FILE);
|
||||
let kernel_file = AppendOnlyFile::open(kernel_file_path.to_str().unwrap().to_owned())?;
|
||||
|
||||
Ok(SumTrees {
|
||||
output_pmmr_h: PMMRHandle::new(root_dir.clone(), UTXO_SUBDIR)?,
|
||||
utxo_pmmr_h: PMMRHandle::new(root_dir.clone(), UTXO_SUBDIR)?,
|
||||
rproof_pmmr_h: PMMRHandle::new(root_dir.clone(), RANGE_PROOF_SUBDIR)?,
|
||||
kernel_pmmr_h: PMMRHandle::new(root_dir.clone(), KERNEL_SUBDIR)?,
|
||||
kernel_file: kernel_file,
|
||||
commit_index: commit_index,
|
||||
})
|
||||
}
|
||||
@@ -109,19 +109,19 @@ impl SumTrees {
|
||||
/// Check is an output is unspent.
|
||||
/// We look in the index to find the output MMR pos.
|
||||
/// Then we check the entry in the output MMR and confirm the hash matches.
|
||||
pub fn is_unspent(&mut self, output: &OutputIdentifier) -> Result<(), Error> {
|
||||
match self.commit_index.get_output_pos(&output.commit) {
|
||||
pub fn is_unspent(&mut self, output_id: &OutputIdentifier) -> Result<(), Error> {
|
||||
match self.commit_index.get_output_pos(&output_id.commit) {
|
||||
Ok(pos) => {
|
||||
let output_pmmr = PMMR::at(
|
||||
&mut self.output_pmmr_h.backend,
|
||||
self.output_pmmr_h.last_pos,
|
||||
let output_pmmr:PMMR<OutputStoreable, _> = PMMR::at(
|
||||
&mut self.utxo_pmmr_h.backend,
|
||||
self.utxo_pmmr_h.last_pos,
|
||||
);
|
||||
if let Some(HashSum { hash, sum: _ }) = output_pmmr.get(pos) {
|
||||
let sum_commit = output.as_sum_commit();
|
||||
let hash_sum = HashSum::from_summable(pos, &sum_commit);
|
||||
if hash == hash_sum.hash {
|
||||
if let Some((hash, _)) = output_pmmr.get(pos, false) {
|
||||
println!("Getting output ID hash");
|
||||
if hash == output_id.hash() {
|
||||
Ok(())
|
||||
} else {
|
||||
println!("MISMATCH BECAUSE THE BLOODY THING MISMATCHES");
|
||||
Err(Error::SumTreeErr(format!("sumtree hash mismatch")))
|
||||
}
|
||||
} else {
|
||||
@@ -164,20 +164,21 @@ impl SumTrees {
|
||||
|
||||
/// returns the last N nodes inserted into the tree (i.e. the 'bottom'
|
||||
/// nodes at level 0
|
||||
pub fn last_n_utxo(&mut self, distance: u64) -> Vec<HashSum<SumCommit>> {
|
||||
let output_pmmr = PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos);
|
||||
output_pmmr.get_last_n_insertions(distance)
|
||||
/// TODO: These need to return the actual data from the flat-files instead of hashes now
|
||||
pub fn last_n_utxo(&mut self, distance: u64) -> Vec<(Hash, Option<OutputStoreable>)> {
|
||||
let utxo_pmmr:PMMR<OutputStoreable, _> = PMMR::at(&mut self.utxo_pmmr_h.backend, self.utxo_pmmr_h.last_pos);
|
||||
utxo_pmmr.get_last_n_insertions(distance)
|
||||
}
|
||||
|
||||
/// as above, for range proofs
|
||||
pub fn last_n_rangeproof(&mut self, distance: u64) -> Vec<HashSum<NoSum<RangeProof>>> {
|
||||
let rproof_pmmr = PMMR::at(&mut self.rproof_pmmr_h.backend, self.rproof_pmmr_h.last_pos);
|
||||
pub fn last_n_rangeproof(&mut self, distance: u64) -> Vec<(Hash, Option<RangeProof>)> {
|
||||
let rproof_pmmr:PMMR<RangeProof, _> = PMMR::at(&mut self.rproof_pmmr_h.backend, self.rproof_pmmr_h.last_pos);
|
||||
rproof_pmmr.get_last_n_insertions(distance)
|
||||
}
|
||||
|
||||
/// as above, for kernels
|
||||
pub fn last_n_kernel(&mut self, distance: u64) -> Vec<HashSum<NoSum<TxKernel>>> {
|
||||
let kernel_pmmr = PMMR::at(&mut self.kernel_pmmr_h.backend, self.kernel_pmmr_h.last_pos);
|
||||
pub fn last_n_kernel(&mut self, distance: u64) -> Vec<(Hash, Option<TxKernel>)> {
|
||||
let kernel_pmmr:PMMR<TxKernel, _> = PMMR::at(&mut self.kernel_pmmr_h.backend, self.kernel_pmmr_h.last_pos);
|
||||
kernel_pmmr.get_last_n_insertions(distance)
|
||||
}
|
||||
|
||||
@@ -186,17 +187,19 @@ impl SumTrees {
|
||||
indexes_at(block, self.commit_index.deref())
|
||||
}
|
||||
|
||||
|
||||
/// Get sum tree roots
|
||||
/// TODO: Return data instead of hashes
|
||||
pub fn roots(
|
||||
&mut self,
|
||||
) -> (
|
||||
HashSum<SumCommit>,
|
||||
HashSum<NoSum<RangeProof>>,
|
||||
HashSum<NoSum<TxKernel>>,
|
||||
Hash,
|
||||
Hash,
|
||||
Hash,
|
||||
) {
|
||||
let output_pmmr = PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos);
|
||||
let rproof_pmmr = PMMR::at(&mut self.rproof_pmmr_h.backend, self.rproof_pmmr_h.last_pos);
|
||||
let kernel_pmmr = PMMR::at(&mut self.kernel_pmmr_h.backend, self.kernel_pmmr_h.last_pos);
|
||||
let output_pmmr:PMMR<OutputStoreable, _> = PMMR::at(&mut self.utxo_pmmr_h.backend, self.utxo_pmmr_h.last_pos);
|
||||
let rproof_pmmr:PMMR<RangeProof, _> = PMMR::at(&mut self.rproof_pmmr_h.backend, self.rproof_pmmr_h.last_pos);
|
||||
let kernel_pmmr:PMMR<TxKernel, _> = PMMR::at(&mut self.kernel_pmmr_h.backend, self.kernel_pmmr_h.last_pos);
|
||||
(output_pmmr.root(), rproof_pmmr.root(), kernel_pmmr.root())
|
||||
}
|
||||
}
|
||||
@@ -231,26 +234,23 @@ where
|
||||
match res {
|
||||
Err(e) => {
|
||||
debug!(LOGGER, "Error returned, discarding sumtree extension.");
|
||||
trees.output_pmmr_h.backend.discard();
|
||||
trees.utxo_pmmr_h.backend.discard();
|
||||
trees.rproof_pmmr_h.backend.discard();
|
||||
trees.kernel_pmmr_h.backend.discard();
|
||||
trees.kernel_file.discard();
|
||||
Err(e)
|
||||
}
|
||||
Ok(r) => {
|
||||
if rollback {
|
||||
debug!(LOGGER, "Rollbacking sumtree extension.");
|
||||
trees.output_pmmr_h.backend.discard();
|
||||
trees.utxo_pmmr_h.backend.discard();
|
||||
trees.rproof_pmmr_h.backend.discard();
|
||||
trees.kernel_pmmr_h.backend.discard();
|
||||
trees.kernel_file.discard();
|
||||
} else {
|
||||
debug!(LOGGER, "Committing sumtree extension.");
|
||||
trees.output_pmmr_h.backend.sync()?;
|
||||
trees.utxo_pmmr_h.backend.sync()?;
|
||||
trees.rproof_pmmr_h.backend.sync()?;
|
||||
trees.kernel_pmmr_h.backend.sync()?;
|
||||
trees.kernel_file.flush()?;
|
||||
trees.output_pmmr_h.last_pos = sizes.0;
|
||||
trees.utxo_pmmr_h.last_pos = sizes.0;
|
||||
trees.rproof_pmmr_h.last_pos = sizes.1;
|
||||
trees.kernel_pmmr_h.last_pos = sizes.2;
|
||||
}
|
||||
@@ -265,11 +265,10 @@ where
|
||||
/// reversible manner within a unit of work provided by the `extending`
|
||||
/// function.
|
||||
pub struct Extension<'a> {
|
||||
output_pmmr: PMMR<'a, SumCommit, PMMRBackend<SumCommit>>,
|
||||
rproof_pmmr: PMMR<'a, NoSum<RangeProof>, PMMRBackend<NoSum<RangeProof>>>,
|
||||
kernel_pmmr: PMMR<'a, NoSum<TxKernel>, PMMRBackend<NoSum<TxKernel>>>,
|
||||
utxo_pmmr: PMMR<'a, OutputStoreable, PMMRBackend<OutputStoreable>>,
|
||||
rproof_pmmr: PMMR<'a, RangeProof, PMMRBackend<RangeProof>>,
|
||||
kernel_pmmr: PMMR<'a, TxKernel, PMMRBackend<TxKernel>>,
|
||||
|
||||
kernel_file: &'a mut AppendOnlyFile,
|
||||
commit_index: Arc<ChainStore>,
|
||||
new_output_commits: HashMap<Commitment, u64>,
|
||||
new_kernel_excesses: HashMap<Commitment, u64>,
|
||||
@@ -284,9 +283,9 @@ impl<'a> Extension<'a> {
|
||||
) -> Extension<'a> {
|
||||
|
||||
Extension {
|
||||
output_pmmr: PMMR::at(
|
||||
&mut trees.output_pmmr_h.backend,
|
||||
trees.output_pmmr_h.last_pos,
|
||||
utxo_pmmr: PMMR::at(
|
||||
&mut trees.utxo_pmmr_h.backend,
|
||||
trees.utxo_pmmr_h.last_pos,
|
||||
),
|
||||
rproof_pmmr: PMMR::at(
|
||||
&mut trees.rproof_pmmr_h.backend,
|
||||
@@ -296,10 +295,10 @@ impl<'a> Extension<'a> {
|
||||
&mut trees.kernel_pmmr_h.backend,
|
||||
trees.kernel_pmmr_h.last_pos,
|
||||
),
|
||||
kernel_file: &mut trees.kernel_file,
|
||||
commit_index: commit_index,
|
||||
new_output_commits: HashMap::new(),
|
||||
new_kernel_excesses: HashMap::new(),
|
||||
|
||||
rollback: false,
|
||||
}
|
||||
}
|
||||
@@ -354,15 +353,14 @@ impl<'a> Extension<'a> {
|
||||
fn apply_input(&mut self, input: &Input, height: u64) -> Result<(), Error> {
|
||||
let commit = input.commitment();
|
||||
let pos_res = self.get_output_pos(&commit);
|
||||
let output_id_hash = OutputIdentifier::from_input(input).hash();
|
||||
if let Ok(pos) = pos_res {
|
||||
if let Some(HashSum { hash, sum: _ }) = self.output_pmmr.get(pos) {
|
||||
let sum_commit = SumCommit::from_input(&input);
|
||||
|
||||
// check hash from pmmr matches hash from input
|
||||
if let Some((read_hash, read_elem)) = self.utxo_pmmr.get(pos, true) {
|
||||
// check hash from pmmr matches hash from input (or corresponding output)
|
||||
// if not then the input is not being honest about
|
||||
// what it is attempting to spend...
|
||||
let hash_sum = HashSum::from_summable(pos, &sum_commit);
|
||||
if hash != hash_sum.hash {
|
||||
if output_id_hash != read_hash ||
|
||||
output_id_hash != read_elem.expect("no output at position").hash() {
|
||||
return Err(Error::SumTreeErr(format!("output pmmr hash mismatch")));
|
||||
}
|
||||
|
||||
@@ -379,9 +377,10 @@ impl<'a> Extension<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Now prune the output_pmmr and rproof_pmmr.
|
||||
// Now prune the utxo_pmmr, rproof_pmmr and their storage.
|
||||
// Input is not valid if we cannot prune successfully (to spend an unspent output).
|
||||
match self.output_pmmr.prune(pos, height as u32) {
|
||||
// TODO: rm log, skip list for utxo and range proofs
|
||||
match self.utxo_pmmr.prune(pos, height as u32) {
|
||||
Ok(true) => {
|
||||
self.rproof_pmmr
|
||||
.prune(pos, height as u32)
|
||||
@@ -398,7 +397,6 @@ impl<'a> Extension<'a> {
|
||||
|
||||
fn apply_output(&mut self, out: &Output) -> Result<(), Error> {
|
||||
let commit = out.commitment();
|
||||
let sum_commit = SumCommit::from_output(out);
|
||||
|
||||
if let Ok(pos) = self.get_output_pos(&commit) {
|
||||
// we need to check whether the commitment is in the current MMR view
|
||||
@@ -406,27 +404,24 @@ impl<'a> Extension<'a> {
|
||||
// (non-historical node will have a much smaller one)
|
||||
// note that this doesn't show the commitment *never* existed, just
|
||||
// that this is not an existing unspent commitment right now
|
||||
if let Some(c) = self.output_pmmr.get(pos) {
|
||||
let hash_sum = HashSum::from_summable(pos, &sum_commit);
|
||||
|
||||
if let Some((hash, _)) = self.utxo_pmmr.get(pos, false) {
|
||||
// processing a new fork so we may get a position on the old
|
||||
// fork that exists but matches a different node
|
||||
// filtering that case out
|
||||
if c.hash == hash_sum.hash {
|
||||
if hash == OutputStoreable::from_output(out).hash() {
|
||||
return Err(Error::DuplicateCommitment(commit));
|
||||
}
|
||||
}
|
||||
}
|
||||
// push new outputs commitments in their MMR and save them in the index
|
||||
let pos = self.output_pmmr
|
||||
.push(sum_commit)
|
||||
// push new outputs in their MMR and save them in the index
|
||||
let pos = self.utxo_pmmr
|
||||
.push(OutputStoreable::from_output(out))
|
||||
.map_err(&Error::SumTreeErr)?;
|
||||
|
||||
self.new_output_commits.insert(out.commitment(), pos);
|
||||
|
||||
// push range proofs in their MMR
|
||||
// push range proofs in their MMR and file
|
||||
self.rproof_pmmr
|
||||
.push(NoSum(out.proof))
|
||||
.push(out.proof)
|
||||
.map_err(&Error::SumTreeErr)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -434,9 +429,8 @@ impl<'a> Extension<'a> {
|
||||
fn apply_kernel(&mut self, kernel: &TxKernel) -> Result<(), Error> {
|
||||
if let Ok(pos) = self.get_kernel_pos(&kernel.excess) {
|
||||
// same as outputs
|
||||
if let Some(k) = self.kernel_pmmr.get(pos) {
|
||||
let hashsum = HashSum::from_summable(pos, &NoSum(kernel));
|
||||
if k.hash == hashsum.hash {
|
||||
if let Some((h,_)) = self.kernel_pmmr.get(pos, false) {
|
||||
if h == kernel.hash() {
|
||||
return Err(Error::DuplicateKernel(kernel.excess.clone()));
|
||||
}
|
||||
}
|
||||
@@ -444,10 +438,9 @@ impl<'a> Extension<'a> {
|
||||
|
||||
// push kernels in their MMR and file
|
||||
let pos = self.kernel_pmmr
|
||||
.push(NoSum(kernel.clone()))
|
||||
.push(kernel.clone())
|
||||
.map_err(&Error::SumTreeErr)?;
|
||||
self.new_kernel_excesses.insert(kernel.excess, pos);
|
||||
self.kernel_file.append(&mut ser::ser_vec(&kernel).unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -465,13 +458,6 @@ impl<'a> Extension<'a> {
|
||||
// rewind each MMR
|
||||
let (out_pos_rew, kern_pos_rew) = indexes_at(block, self.commit_index.deref())?;
|
||||
self.rewind_pos(block.header.height, out_pos_rew, kern_pos_rew)?;
|
||||
|
||||
// rewind the kernel file store, the position is the number of kernels
|
||||
// multiplied by their size
|
||||
// the number of kernels is the number of leaves in the MMR
|
||||
let pos = pmmr::n_leaves(kern_pos_rew);
|
||||
self.kernel_file.rewind(pos * (TxKernel::size() as u64));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -485,7 +471,7 @@ impl<'a> Extension<'a> {
|
||||
kern_pos_rew,
|
||||
);
|
||||
|
||||
self.output_pmmr
|
||||
self.utxo_pmmr
|
||||
.rewind(out_pos_rew, height as u32)
|
||||
.map_err(&Error::SumTreeErr)?;
|
||||
self.rproof_pmmr
|
||||
@@ -495,7 +481,6 @@ impl<'a> Extension<'a> {
|
||||
.rewind(kern_pos_rew, height as u32)
|
||||
.map_err(&Error::SumTreeErr)?;
|
||||
|
||||
self.dump(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -515,26 +500,23 @@ impl<'a> Extension<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Current root hashes and sums (if applicable) for the UTXO, range proof
|
||||
/// and kernel sum trees.
|
||||
pub fn roots(
|
||||
&self,
|
||||
) -> (
|
||||
HashSum<SumCommit>,
|
||||
HashSum<NoSum<RangeProof>>,
|
||||
HashSum<NoSum<TxKernel>>,
|
||||
) {
|
||||
(
|
||||
self.output_pmmr.root(),
|
||||
self.rproof_pmmr.root(),
|
||||
self.kernel_pmmr.root(),
|
||||
)
|
||||
) -> SumTreeRoots {
|
||||
SumTreeRoots {
|
||||
utxo_root: self.utxo_pmmr.root(),
|
||||
rproof_root: self.rproof_pmmr.root(),
|
||||
kernel_root: self.kernel_pmmr.root(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate the current sumtree state against a block header
|
||||
pub fn validate(&self, header: &BlockHeader) -> Result<(), Error> {
|
||||
// validate all hashes and sums within the trees
|
||||
if let Err(e) = self.output_pmmr.validate() {
|
||||
if let Err(e) = self.utxo_pmmr.validate() {
|
||||
return Err(Error::InvalidSumtree(e));
|
||||
}
|
||||
if let Err(e) = self.rproof_pmmr.validate() {
|
||||
@@ -545,9 +527,9 @@ impl<'a> Extension<'a> {
|
||||
}
|
||||
|
||||
// validate the tree roots against the block header
|
||||
let (utxo_root, rproof_root, kernel_root) = self.roots();
|
||||
if utxo_root.hash != header.utxo_root || rproof_root.hash != header.range_proof_root
|
||||
|| kernel_root.hash != header.kernel_root
|
||||
let roots = self.roots();
|
||||
if roots.utxo_root != header.utxo_root || roots.rproof_root != header.range_proof_root
|
||||
|| roots.kernel_root != header.kernel_root
|
||||
{
|
||||
return Err(Error::InvalidRoot);
|
||||
}
|
||||
@@ -574,11 +556,11 @@ impl<'a> Extension<'a> {
|
||||
/// by iterating over the whole MMR data. This is a costly operation
|
||||
/// performed only when we receive a full new chain state.
|
||||
pub fn rebuild_index(&self) -> Result<(), Error> {
|
||||
for n in 1..self.output_pmmr.unpruned_size()+1 {
|
||||
for n in 1..self.utxo_pmmr.unpruned_size()+1 {
|
||||
// non-pruned leaves only
|
||||
if pmmr::bintree_postorder_height(n) == 0 {
|
||||
if let Some(hs) = self.output_pmmr.get(n) {
|
||||
self.commit_index.save_output_pos(&hs.sum.commit, n)?;
|
||||
if let Some((_, out)) = self.utxo_pmmr.get(n, true) {
|
||||
self.commit_index.save_output_pos(&out.expect("not a leaf node").commit, n)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,7 +576,7 @@ impl<'a> Extension<'a> {
|
||||
/// version only prints the UTXO tree.
|
||||
pub fn dump(&self, short: bool) {
|
||||
debug!(LOGGER, "-- outputs --");
|
||||
self.output_pmmr.dump(short);
|
||||
self.utxo_pmmr.dump(short);
|
||||
if !short {
|
||||
debug!(LOGGER, "-- range proofs --");
|
||||
self.rproof_pmmr.dump(short);
|
||||
@@ -606,7 +588,7 @@ impl<'a> Extension<'a> {
|
||||
// Sizes of the sum trees, used by `extending` on rollback.
|
||||
fn sizes(&self) -> (u64, u64, u64) {
|
||||
(
|
||||
self.output_pmmr.unpruned_size(),
|
||||
self.utxo_pmmr.unpruned_size(),
|
||||
self.rproof_pmmr.unpruned_size(),
|
||||
self.kernel_pmmr.unpruned_size(),
|
||||
)
|
||||
@@ -619,7 +601,7 @@ impl<'a> Extension<'a> {
|
||||
let mmr_sz = self.kernel_pmmr.unpruned_size();
|
||||
let count = pmmr::n_leaves(mmr_sz);
|
||||
|
||||
let mut kernel_file = File::open(self.kernel_file.path())?;
|
||||
let mut kernel_file = File::open(self.kernel_pmmr.data_file_path())?;
|
||||
let first: TxKernel = ser::deserialize(&mut kernel_file)?;
|
||||
first.verify()?;
|
||||
let mut sum_kernel = first.excess;
|
||||
@@ -651,14 +633,14 @@ impl<'a> Extension<'a> {
|
||||
let mut sum_utxo = None;
|
||||
let mut utxo_count = 0;
|
||||
let secp = static_secp_instance();
|
||||
for n in 1..self.output_pmmr.unpruned_size()+1 {
|
||||
for n in 1..self.utxo_pmmr.unpruned_size()+1 {
|
||||
if pmmr::bintree_postorder_height(n) == 0 {
|
||||
if let Some(hs) = self.output_pmmr.get(n) {
|
||||
if let Some((_,output)) = self.utxo_pmmr.get(n, true) {
|
||||
if n == 1 {
|
||||
sum_utxo = Some(hs.sum.commit);
|
||||
sum_utxo = Some(output.expect("not a leaf node").commit);
|
||||
} else {
|
||||
let secp = secp.lock().unwrap();
|
||||
sum_utxo = Some(secp.commit_sum(vec![sum_utxo.unwrap(), hs.sum.commit], vec![])?);
|
||||
sum_utxo = Some(secp.commit_sum(vec![sum_utxo.unwrap(), output.expect("not a leaf node").commit], vec![])?);
|
||||
}
|
||||
utxo_count += 1;
|
||||
}
|
||||
@@ -669,6 +651,42 @@ impl<'a> Extension<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/*fn store_element<T>(file_store: &mut FlatFileStore<T>, data: T)
|
||||
-> Result<(), String>
|
||||
where
|
||||
T: ser::Readable + ser::Writeable + Clone
|
||||
{
|
||||
file_store.append(vec![data])
|
||||
}
|
||||
|
||||
fn read_element_at_pmmr_index<T>(file_store: &FlatFileStore<T>, pos: u64) -> Option<T>
|
||||
where
|
||||
T: ser::Readable + ser::Writeable + Clone
|
||||
{
|
||||
let leaf_index = pmmr::leaf_index(pos);
|
||||
// flat files are zero indexed
|
||||
file_store.get(leaf_index - 1)
|
||||
}
|
||||
|
||||
fn _remove_element_at_pmmr_index<T>(file_store: &mut FlatFileStore<T>, pos: u64)
|
||||
-> Result<(), String>
|
||||
where
|
||||
T: ser::Readable + ser::Writeable + Clone
|
||||
{
|
||||
let leaf_index = pmmr::leaf_index(pos);
|
||||
// flat files are zero indexed
|
||||
file_store.remove(vec![leaf_index - 1])
|
||||
}
|
||||
|
||||
fn rewind_to_pmmr_index<T>(file_store: &mut FlatFileStore<T>, pos: u64) -> Result<(), String>
|
||||
where
|
||||
T: ser::Readable + ser::Writeable + Clone
|
||||
{
|
||||
let leaf_index = pmmr::leaf_index(pos);
|
||||
// flat files are zero indexed
|
||||
file_store.rewind(leaf_index - 1)
|
||||
}*/
|
||||
|
||||
/// Output and kernel MMR indexes at the end of the provided block
|
||||
fn indexes_at(block: &Block, commit_index: &ChainStore) -> Result<(u64, u64), Error> {
|
||||
let out_idx = match block.outputs.last() {
|
||||
|
||||
@@ -40,6 +40,17 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper to hold the roots of the sumtrees in order to keep them
|
||||
/// readable
|
||||
pub struct SumTreeRoots {
|
||||
/// UTXO root
|
||||
pub utxo_root: Hash,
|
||||
/// Range Proof root
|
||||
pub rproof_root: Hash,
|
||||
/// Kernel root
|
||||
pub kernel_root: Hash,
|
||||
}
|
||||
|
||||
/// Errors
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -81,6 +92,8 @@ pub enum Error {
|
||||
InvalidSumtree(String),
|
||||
/// Internal issue when trying to save or load data from store
|
||||
StoreErr(grin_store::Error, String),
|
||||
/// Internal issue when trying to save or load data from append only files
|
||||
FileReadErr(String),
|
||||
/// Error serializing or deserializing a type
|
||||
SerErr(ser::Error),
|
||||
/// Error with the sumtrees
|
||||
|
||||
Reference in New Issue
Block a user