mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Include index as part of pmmr element hash (#748)
* storing index as part of pmmr element hash * factor out hash_with_index into generic trait impl * tighten up trait bounds for PMMRable
This commit is contained in:
parent
63c5795eb9
commit
90f844d382
5 changed files with 48 additions and 24 deletions
|
@ -30,7 +30,7 @@ use core::core::{Block, BlockHeader, Input, Output, OutputFeatures, OutputIdenti
|
||||||
OutputStoreable, TxKernel};
|
OutputStoreable, TxKernel};
|
||||||
use core::core::pmmr::{self, MerkleProof, PMMR};
|
use core::core::pmmr::{self, MerkleProof, PMMR};
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
use core::ser::{self, PMMRable};
|
use core::ser::{self, PMMRable, PMMRIndexHashable};
|
||||||
|
|
||||||
use grin_store;
|
use grin_store;
|
||||||
use grin_store::pmmr::{PMMRBackend, PMMRFileMetadata};
|
use grin_store::pmmr::{PMMRBackend, PMMRFileMetadata};
|
||||||
|
@ -141,7 +141,7 @@ impl SumTrees {
|
||||||
let output_pmmr: PMMR<OutputStoreable, _> =
|
let output_pmmr: PMMR<OutputStoreable, _> =
|
||||||
PMMR::at(&mut self.utxo_pmmr_h.backend, self.utxo_pmmr_h.last_pos);
|
PMMR::at(&mut self.utxo_pmmr_h.backend, self.utxo_pmmr_h.last_pos);
|
||||||
if let Some((hash, _)) = output_pmmr.get(pos, false) {
|
if let Some((hash, _)) = output_pmmr.get(pos, false) {
|
||||||
if hash == output_id.hash() {
|
if hash == output_id.hash_with_index(pos) {
|
||||||
Ok(hash)
|
Ok(hash)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::SumTreeErr(format!("sumtree hash mismatch")))
|
Err(Error::SumTreeErr(format!("sumtree hash mismatch")))
|
||||||
|
@ -348,14 +348,14 @@ impl<'a> Extension<'a> {
|
||||||
fn apply_input(&mut self, input: &Input, height: u64) -> Result<(), Error> {
|
fn apply_input(&mut self, input: &Input, height: u64) -> Result<(), Error> {
|
||||||
let commit = input.commitment();
|
let commit = input.commitment();
|
||||||
let pos_res = self.get_output_pos(&commit);
|
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 Ok(pos) = pos_res {
|
||||||
|
let output_id_hash = OutputIdentifier::from_input(input).hash_with_index(pos);
|
||||||
if let Some((read_hash, read_elem)) = self.utxo_pmmr.get(pos, true) {
|
if let Some((read_hash, read_elem)) = self.utxo_pmmr.get(pos, true) {
|
||||||
// check hash from pmmr matches hash from input (or corresponding output)
|
// check hash from pmmr matches hash from input (or corresponding output)
|
||||||
// if not then the input is not being honest about
|
// if not then the input is not being honest about
|
||||||
// what it is attempting to spend...
|
// what it is attempting to spend...
|
||||||
if output_id_hash != read_hash
|
if output_id_hash != read_hash
|
||||||
|| output_id_hash != read_elem.expect("no output at position").hash()
|
|| output_id_hash != read_elem.expect("no output at position").hash_with_index(pos)
|
||||||
{
|
{
|
||||||
return Err(Error::SumTreeErr(format!("output pmmr hash mismatch")));
|
return Err(Error::SumTreeErr(format!("output pmmr hash mismatch")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ use std::marker::PhantomData;
|
||||||
use core::hash::{Hash, Hashed};
|
use core::hash::{Hash, Hashed};
|
||||||
use ser;
|
use ser;
|
||||||
use ser::{Readable, Reader, Writeable, Writer};
|
use ser::{Readable, Reader, Writeable, Writer};
|
||||||
use ser::PMMRable;
|
use ser::{PMMRable, PMMRIndexHashable};
|
||||||
use util;
|
use util;
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ where
|
||||||
/// the same time if applicable.
|
/// the same time if applicable.
|
||||||
pub fn push(&mut self, elmt: T) -> Result<u64, String> {
|
pub fn push(&mut self, elmt: T) -> Result<u64, String> {
|
||||||
let elmt_pos = self.last_pos + 1;
|
let elmt_pos = self.last_pos + 1;
|
||||||
let mut current_hash = elmt.hash();
|
let mut current_hash = elmt.hash_with_index(elmt_pos);
|
||||||
let mut to_append = vec![(current_hash, Some(elmt))];
|
let mut to_append = vec![(current_hash, Some(elmt))];
|
||||||
let mut height = 0;
|
let mut height = 0;
|
||||||
let mut pos = elmt_pos;
|
let mut pos = elmt_pos;
|
||||||
|
@ -917,6 +917,7 @@ mod test {
|
||||||
use ser::{Error, Readable, Writeable};
|
use ser::{Error, Readable, Writeable};
|
||||||
use core::{Reader, Writer};
|
use core::{Reader, Writer};
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
|
use ser::{PMMRable, PMMRIndexHashable};
|
||||||
|
|
||||||
/// Simple MMR backend implementation based on a Vector. Pruning does not
|
/// Simple MMR backend implementation based on a Vector. Pruning does not
|
||||||
/// compact the Vec itself.
|
/// compact the Vec itself.
|
||||||
|
@ -1169,7 +1170,7 @@ mod test {
|
||||||
fn len() -> usize {
|
fn len() -> usize {
|
||||||
16
|
16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Writeable for TestElem {
|
impl Writeable for TestElem {
|
||||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
|
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
|
||||||
|
@ -1305,28 +1306,28 @@ mod test {
|
||||||
|
|
||||||
// one element
|
// one element
|
||||||
pmmr.push(elems[0]).unwrap();
|
pmmr.push(elems[0]).unwrap();
|
||||||
let node_hash = elems[0].hash();
|
let node_hash = elems[0].hash_with_index(1);
|
||||||
assert_eq!(pmmr.root(), node_hash,);
|
assert_eq!(pmmr.root(), node_hash);
|
||||||
assert_eq!(pmmr.unpruned_size(), 1);
|
assert_eq!(pmmr.unpruned_size(), 1);
|
||||||
pmmr.dump(false);
|
pmmr.dump(false);
|
||||||
|
|
||||||
// two elements
|
// two elements
|
||||||
pmmr.push(elems[1]).unwrap();
|
pmmr.push(elems[1]).unwrap();
|
||||||
let sum2 = elems[0].hash() + elems[1].hash();
|
let sum2 = elems[0].hash_with_index(1) + elems[1].hash_with_index(2);
|
||||||
pmmr.dump(false);
|
pmmr.dump(false);
|
||||||
assert_eq!(pmmr.root(), sum2);
|
assert_eq!(pmmr.root(), sum2);
|
||||||
assert_eq!(pmmr.unpruned_size(), 3);
|
assert_eq!(pmmr.unpruned_size(), 3);
|
||||||
|
|
||||||
// three elements
|
// three elements
|
||||||
pmmr.push(elems[2]).unwrap();
|
pmmr.push(elems[2]).unwrap();
|
||||||
let sum3 = sum2 + elems[2].hash();
|
let sum3 = sum2 + elems[2].hash_with_index(4);
|
||||||
pmmr.dump(false);
|
pmmr.dump(false);
|
||||||
assert_eq!(pmmr.root(), sum3);
|
assert_eq!(pmmr.root(), sum3);
|
||||||
assert_eq!(pmmr.unpruned_size(), 4);
|
assert_eq!(pmmr.unpruned_size(), 4);
|
||||||
|
|
||||||
// four elements
|
// four elements
|
||||||
pmmr.push(elems[3]).unwrap();
|
pmmr.push(elems[3]).unwrap();
|
||||||
let sum_one = elems[2].hash() + elems[3].hash();
|
let sum_one = elems[2].hash_with_index(4) + elems[3].hash_with_index(5);
|
||||||
let sum4 = sum2 + sum_one;
|
let sum4 = sum2 + sum_one;
|
||||||
pmmr.dump(false);
|
pmmr.dump(false);
|
||||||
assert_eq!(pmmr.root(), sum4);
|
assert_eq!(pmmr.root(), sum4);
|
||||||
|
@ -1334,33 +1335,34 @@ mod test {
|
||||||
|
|
||||||
// five elements
|
// five elements
|
||||||
pmmr.push(elems[4]).unwrap();
|
pmmr.push(elems[4]).unwrap();
|
||||||
let sum3 = sum4 + elems[4].hash();
|
let sum3 = sum4 + elems[4].hash_with_index(8);
|
||||||
pmmr.dump(false);
|
pmmr.dump(false);
|
||||||
assert_eq!(pmmr.root(), sum3);
|
assert_eq!(pmmr.root(), sum3);
|
||||||
assert_eq!(pmmr.unpruned_size(), 8);
|
assert_eq!(pmmr.unpruned_size(), 8);
|
||||||
|
|
||||||
// six elements
|
// six elements
|
||||||
pmmr.push(elems[5]).unwrap();
|
pmmr.push(elems[5]).unwrap();
|
||||||
let sum6 = sum4 + (elems[4].hash() + elems[5].hash());
|
let sum6 = sum4 + (elems[4].hash_with_index(8) + elems[5].hash_with_index(9));
|
||||||
assert_eq!(pmmr.root(), sum6.clone());
|
assert_eq!(pmmr.root(), sum6.clone());
|
||||||
assert_eq!(pmmr.unpruned_size(), 10);
|
assert_eq!(pmmr.unpruned_size(), 10);
|
||||||
|
|
||||||
// seven elements
|
// seven elements
|
||||||
pmmr.push(elems[6]).unwrap();
|
pmmr.push(elems[6]).unwrap();
|
||||||
let sum7 = sum6 + elems[6].hash();
|
let sum7 = sum6 + elems[6].hash_with_index(11);
|
||||||
assert_eq!(pmmr.root(), sum7);
|
assert_eq!(pmmr.root(), sum7);
|
||||||
assert_eq!(pmmr.unpruned_size(), 11);
|
assert_eq!(pmmr.unpruned_size(), 11);
|
||||||
|
|
||||||
// eight elements
|
// eight elements
|
||||||
pmmr.push(elems[7]).unwrap();
|
pmmr.push(elems[7]).unwrap();
|
||||||
let sum8 =
|
let sum8 =
|
||||||
sum4 + ((elems[4].hash() + elems[5].hash()) + (elems[6].hash() + elems[7].hash()));
|
sum4 + ((elems[4].hash_with_index(8) + elems[5].hash_with_index(9))
|
||||||
|
+ (elems[6].hash_with_index(11) + elems[7].hash_with_index(12)));
|
||||||
assert_eq!(pmmr.root(), sum8);
|
assert_eq!(pmmr.root(), sum8);
|
||||||
assert_eq!(pmmr.unpruned_size(), 15);
|
assert_eq!(pmmr.unpruned_size(), 15);
|
||||||
|
|
||||||
// nine elements
|
// nine elements
|
||||||
pmmr.push(elems[8]).unwrap();
|
pmmr.push(elems[8]).unwrap();
|
||||||
let sum9 = sum8 + elems[8].hash();
|
let sum9 = sum8 + elems[8].hash_with_index(16);
|
||||||
assert_eq!(pmmr.root(), sum9);
|
assert_eq!(pmmr.root(), sum9);
|
||||||
assert_eq!(pmmr.unpruned_size(), 16);
|
assert_eq!(pmmr.unpruned_size(), 16);
|
||||||
}
|
}
|
||||||
|
|
|
@ -938,6 +938,8 @@ pub struct OutputIdentifier {
|
||||||
pub commit: Commitment,
|
pub commit: Commitment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl OutputIdentifier {
|
impl OutputIdentifier {
|
||||||
/// Build a new output_identifier.
|
/// Build a new output_identifier.
|
||||||
pub fn new(features: OutputFeatures, commit: &Commitment) -> OutputIdentifier {
|
pub fn new(features: OutputFeatures, commit: &Commitment) -> OutputIdentifier {
|
||||||
|
@ -973,6 +975,13 @@ impl OutputIdentifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure this is implemented to centralize hashing with indexes
|
||||||
|
impl PMMRable for OutputIdentifier {
|
||||||
|
fn len() -> usize {
|
||||||
|
1 + secp::constants::PEDERSEN_COMMITMENT_SIZE + SWITCH_COMMIT_HASH_SIZE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Writeable for OutputIdentifier {
|
impl Writeable for OutputIdentifier {
|
||||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
||||||
writer.write_u8(self.features.bits())?;
|
writer.write_u8(self.features.bits())?;
|
||||||
|
|
|
@ -25,7 +25,7 @@ use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
|
||||||
use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE};
|
use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE};
|
||||||
use consensus;
|
use consensus;
|
||||||
use consensus::VerifySortOrder;
|
use consensus::VerifySortOrder;
|
||||||
use core::hash::Hashed;
|
use core::hash::{Hashed, Hash};
|
||||||
use core::transaction::{SwitchCommitHash, SWITCH_COMMIT_HASH_SIZE};
|
use core::transaction::{SwitchCommitHash, SWITCH_COMMIT_HASH_SIZE};
|
||||||
use util::secp::pedersen::Commitment;
|
use util::secp::pedersen::Commitment;
|
||||||
use util::secp::pedersen::RangeProof;
|
use util::secp::pedersen::RangeProof;
|
||||||
|
@ -549,11 +549,23 @@ impl Writeable for [u8; 4] {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for types that can serialize and report their size
|
/// Trait for types that can serialize and report their size
|
||||||
pub trait PMMRable: Readable + Writeable + Hashed + Clone {
|
pub trait PMMRable: Readable + Writeable + Clone {
|
||||||
/// Length in bytes
|
/// Length in bytes
|
||||||
fn len() -> usize;
|
fn len() -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic trait to ensure PMMR elements can be hashed with an index
|
||||||
|
pub trait PMMRIndexHashable {
|
||||||
|
/// Hash with a given index
|
||||||
|
fn hash_with_index(&self, index: u64) -> Hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PMMRable> PMMRIndexHashable for T {
|
||||||
|
fn hash_with_index(&self, index: u64) -> Hash {
|
||||||
|
(index, self).hash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Useful marker trait on types that can be sized byte slices
|
/// Useful marker trait on types that can be sized byte slices
|
||||||
pub trait AsFixedBytes: Sized + AsRef<[u8]> {
|
pub trait AsFixedBytes: Sized + AsRef<[u8]> {
|
||||||
/// The length in bytes
|
/// The length in bytes
|
||||||
|
|
|
@ -37,13 +37,14 @@ fn pmmr_append() {
|
||||||
backend.sync().unwrap();
|
backend.sync().unwrap();
|
||||||
|
|
||||||
// check the resulting backend store and the computation of the root
|
// check the resulting backend store and the computation of the root
|
||||||
let node_hash = elems[0].hash();
|
let node_hash = elems[0].hash_with_index(1);
|
||||||
assert_eq!(backend.get(1, false).expect("").0, node_hash);
|
assert_eq!(backend.get(1, false).expect("").0, node_hash);
|
||||||
|
|
||||||
let sum2 = elems[0].hash() + elems[1].hash();
|
let sum2 = elems[0].hash_with_index(1) + elems[1].hash_with_index(2);
|
||||||
let sum4 = sum2 + (elems[2].hash() + elems[3].hash());
|
let sum4 = sum2 + (elems[2].hash_with_index(4) + elems[3].hash_with_index(5));
|
||||||
let sum8 = sum4 + ((elems[4].hash() + elems[5].hash()) + (elems[6].hash() + elems[7].hash()));
|
let sum8 = sum4 + ((elems[4].hash_with_index(8) + elems[5].hash_with_index(9))
|
||||||
let sum9 = sum8 + elems[8].hash();
|
+ (elems[6].hash_with_index(11) + elems[7].hash_with_index(12)));
|
||||||
|
let sum9 = sum8 + elems[8].hash_with_index(16);
|
||||||
|
|
||||||
{
|
{
|
||||||
let pmmr: PMMR<TestElem, _> = PMMR::at(&mut backend, mmr_size);
|
let pmmr: PMMR<TestElem, _> = PMMR::at(&mut backend, mmr_size);
|
||||||
|
|
Loading…
Reference in a new issue