Fix is_unspent to consider MMR and not only index

The method `is_unspent` hadn't been fixed with the other sumtree
functions to check the MMR before deciding whether something is
really unspent. This is now fixed and also checks the output
hash is the one we expect.
This commit is contained in:
Ignotus Peverell 2017-12-25 00:07:50 +00:00
parent 2dac10a690
commit 707a2073a5
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
3 changed files with 32 additions and 21 deletions

View file

@ -120,7 +120,7 @@ impl UtxoHandler {
let outputs = block let outputs = block
.outputs .outputs
.iter() .iter()
.filter(|c|self.chain.is_unspent(&c.commit).unwrap()) .filter(|c|self.chain.is_unspent(&c.commit, &c.switch_commit_hash).unwrap())
.map(|k|OutputSwitch::from_output(k, &header)) .map(|k|OutputSwitch::from_output(k, &header))
.collect(); .collect();
BlockOutputs { BlockOutputs {

View file

@ -23,7 +23,7 @@ use util::secp::pedersen::{Commitment, RangeProof};
use core::core::SumCommit; use core::core::SumCommit;
use core::core::pmmr::{HashSum, NoSum}; use core::core::pmmr::{HashSum, NoSum};
use core::core::{Block, BlockHeader, Output, TxKernel}; use core::core::{Block, BlockHeader, Output, TxKernel, SwitchCommitHash};
use core::core::target::Difficulty; use core::core::target::Difficulty;
use core::core::hash::Hash; use core::core::hash::Hash;
use grin_store::Error::NotFoundErr; use grin_store::Error::NotFoundErr;
@ -360,21 +360,26 @@ impl Chain {
/// way that's consistent with the current chain state and more /// way that's consistent with the current chain state and more
/// specifically the current winning fork. /// specifically the current winning fork.
pub fn get_unspent(&self, output_ref: &Commitment) -> Result<Output, Error> { pub fn get_unspent(&self, output_ref: &Commitment) -> Result<Output, Error> {
let sumtrees = self.sumtrees.read().unwrap(); match self.store.get_output_by_commit(output_ref) {
let is_unspent = sumtrees.is_unspent(output_ref)?; Ok(out) => {
if is_unspent { let mut sumtrees = self.sumtrees.write().unwrap();
self.store if sumtrees.is_unspent(output_ref, &out.switch_commit_hash())? {
.get_output_by_commit(output_ref) Ok(out)
.map_err(|e| Error::StoreErr(e, "chain get unspent".to_owned())) } else {
} else { Err(Error::OutputNotFound)
Err(Error::OutputNotFound) }
}
Err(NotFoundErr) => Err(Error::OutputNotFound),
Err(e) => Err(Error::StoreErr(e, "chain get unspent".to_owned())),
} }
} }
/// Checks whether an output is unspent /// Checks whether an output is unspent
pub fn is_unspent(&self, output_ref: &Commitment) -> Result<bool, Error> { pub fn is_unspent(&self, output_ref: &Commitment, switch: &SwitchCommitHash)
let sumtrees = self.sumtrees.read().unwrap(); -> Result<bool, Error> {
sumtrees.is_unspent(output_ref)
let mut sumtrees = self.sumtrees.write().unwrap();
sumtrees.is_unspent(output_ref, switch)
} }
/// Sets the sumtree roots on a brand new block by applying the block on the /// Sets the sumtree roots on a brand new block by applying the block on the

View file

@ -22,8 +22,8 @@ use std::sync::Arc;
use util::secp::pedersen::{RangeProof, Commitment}; use util::secp::pedersen::{RangeProof, Commitment};
use core::core::{Block, SumCommit, TxKernel}; use core::core::{Block, SumCommit, SwitchCommitHash, TxKernel};
use core::core::pmmr::{Backend, HashSum, NoSum, Summable, PMMR}; use core::core::pmmr::{HashSum, NoSum, Summable, PMMR};
use core::core::hash::Hashed; use core::core::hash::Hashed;
use grin_store; use grin_store;
use grin_store::sumtree::PMMRBackend; use grin_store::sumtree::PMMRBackend;
@ -90,16 +90,22 @@ impl SumTrees {
} }
/// Whether a given commitment exists in the Output MMR and it's unspent /// Whether a given commitment exists in the Output MMR and it's unspent
pub fn is_unspent(&self, commit: &Commitment) -> Result<bool, Error> { pub fn is_unspent(&mut self, commit: &Commitment, switch: &SwitchCommitHash)
-> Result<bool, Error> {
let rpos = self.commit_index.get_output_pos(commit); let rpos = self.commit_index.get_output_pos(commit);
match rpos { match rpos {
Ok(pos) => { Ok(pos) => {
// checking the position is within the MMR, the commit index could be let output_pmmr = PMMR::at(
// returning rewound data &mut self.output_pmmr_h.backend,
if pos > self.output_pmmr_h.last_pos { self.output_pmmr_h.last_pos
Ok(false) );
if let Some(hs) = output_pmmr.get(pos) {
let hashsum = HashSum::from_summable(
pos, &SumCommit{commit: commit.clone()}, Some(switch));
Ok(hs.hash == hashsum.hash)
} else { } else {
Ok(self.output_pmmr_h.backend.get(pos).is_some()) Ok(false)
} }
} }
Err(grin_store::Error::NotFoundErr) => Ok(false), Err(grin_store::Error::NotFoundErr) => Ok(false),