0-based positions, bag peaks right-to-left with mmr size as index (#863)

* 0-based positions, bag peaks right-to-left with mmr size as index
* fix remaining instances of hash_with_index
* fix pmmr test
This commit is contained in:
John Tromp 2018-03-26 04:26:11 +02:00 committed by Ignotus Peverell
parent ccf0c11da6
commit a327427178
3 changed files with 79 additions and 71 deletions

View file

@ -146,7 +146,7 @@ impl TxHashSet {
let output_pmmr: PMMR<OutputIdentifier, _> =
PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos);
if let Some(hash) = output_pmmr.get_hash(pos) {
if hash == output_id.hash_with_index(pos) {
if hash == output_id.hash_with_index(pos-1) {
Ok(hash)
} else {
Err(Error::TxHashSetErr(format!("txhashset hash mismatch")))
@ -386,7 +386,7 @@ impl<'a> Extension<'a> {
let commit = input.commitment();
let pos_res = self.get_output_pos(&commit);
if let Ok(pos) = pos_res {
let output_id_hash = OutputIdentifier::from_input(input).hash_with_index(pos);
let output_id_hash = OutputIdentifier::from_input(input).hash_with_index(pos-1);
if let Some(read_hash) = self.output_pmmr.get_hash(pos) {
// check hash from pmmr matches hash from input (or corresponding output)
// if not then the input is not being honest about
@ -398,7 +398,7 @@ impl<'a> Extension<'a> {
|| output_id_hash
!= read_elem
.expect("no output at position")
.hash_with_index(pos)
.hash_with_index(pos-1)
{
return Err(Error::TxHashSetErr(format!("output pmmr hash mismatch")));
}

View file

@ -37,7 +37,7 @@
use std::clone::Clone;
use std::marker::PhantomData;
use core::hash::{Hash, Hashed};
use core::hash::{Hash};
use ser;
use ser::{Readable, Reader, Writeable, Writer};
use ser::{PMMRIndexHashable, PMMRable};
@ -111,6 +111,8 @@ pub struct MerkleProof {
pub root: Hash,
/// The hash of the element in the tree we care about
pub node: Hash,
/// The size of the full Merkle tree
pub mmr_size: u64,
/// The full list of peak hashes in the MMR
pub peaks: Vec<Hash>,
/// The sibling (hash, pos) along the path of the tree
@ -124,6 +126,7 @@ impl Writeable for MerkleProof {
writer,
[write_fixed_bytes, &self.root],
[write_fixed_bytes, &self.node],
[write_u64, self.mmr_size],
[write_u64, self.peaks.len() as u64],
[write_u64, self.path.len() as u64]
);
@ -140,7 +143,7 @@ impl Readable for MerkleProof {
let root = Hash::read(reader)?;
let node = Hash::read(reader)?;
let (peaks_len, path_len) = ser_multiread!(reader, read_u64, read_u64);
let (mmr_size, peaks_len, path_len) = ser_multiread!(reader, read_u64, read_u64, read_u64);
if peaks_len > MAX_PEAKS || path_len > MAX_PATH {
return Err(ser::Error::CorruptedData);
@ -161,6 +164,7 @@ impl Readable for MerkleProof {
Ok(MerkleProof {
root,
node,
mmr_size,
peaks,
path,
})
@ -180,6 +184,7 @@ impl MerkleProof {
MerkleProof {
root: Hash::default(),
node: Hash::default(),
mmr_size: 0,
peaks: vec![],
path: vec![],
}
@ -215,10 +220,10 @@ impl MerkleProof {
}
let mut bagged = None;
for peak in self.peaks.iter().cloned() {
bagged = match (bagged, peak) {
(None, rhs) => Some(rhs),
(Some(lhs), rhs) => Some(lhs.hash_with(rhs)),
for peak in self.peaks.iter().rev() {
bagged = match bagged {
None => Some(*peak),
Some(rhs) => Some((*peak,rhs).hash_with_index(self.mmr_size)),
}
}
return bagged == Some(self.root);
@ -231,14 +236,15 @@ impl MerkleProof {
// hash our node and sibling together (noting left/right position of the
// sibling)
let parent = if is_left_sibling(sibling_pos) {
(sibling, self.node).hash_with_index(parent_pos)
(sibling, self.node).hash_with_index(parent_pos-1)
} else {
(self.node, sibling).hash_with_index(parent_pos)
(self.node, sibling).hash_with_index(parent_pos-1)
};
let proof = MerkleProof {
root: self.root,
node: parent,
mmr_size: self.mmr_size,
peaks: self.peaks.clone(),
path,
};
@ -307,10 +313,10 @@ where
/// tree and "bags" them to get a single peak.
pub fn root(&self) -> Hash {
let mut res = None;
for peak in self.peaks() {
res = match (res, peak) {
(None, rhash) => Some(rhash),
(Some(lhash), rhash) => Some(lhash.hash_with(rhash)),
for peak in self.peaks().iter().rev() {
res = match res {
None => Some(*peak),
Some(rhash) => Some((*peak,rhash).hash_with_index(self.unpruned_size())),
}
}
res.expect("no root, invalid tree")
@ -332,6 +338,8 @@ where
let node = self.get_hash(pos)
.ok_or(format!("no element at pos {}", pos))?;
let mmr_size = self.unpruned_size();
let family_branch = family_branch(pos, self.last_pos);
let path = family_branch
@ -350,6 +358,7 @@ where
let proof = MerkleProof {
root,
node,
mmr_size,
peaks,
path,
};
@ -361,7 +370,7 @@ where
/// the same time if applicable.
pub fn push(&mut self, elmt: T) -> Result<u64, String> {
let elmt_pos = self.last_pos + 1;
let mut current_hash = elmt.hash_with_index(elmt_pos);
let mut current_hash = elmt.hash_with_index(elmt_pos-1);
let mut to_append = vec![(current_hash, Some(elmt))];
let mut height = 0;
@ -381,7 +390,7 @@ where
height += 1;
pos += 1;
current_hash = (left_hash, current_hash).hash_with_index(pos);
current_hash = (left_hash, current_hash).hash_with_index(pos-1);
to_append.push((current_hash.clone(), None));
}
@ -531,7 +540,7 @@ where
if let Some(right_child_hs) = self.get_from_file(right_pos) {
// hash the two child nodes together with parent_pos and compare
let (parent_pos, _) = family(left_pos);
if (left_child_hs, right_child_hs).hash_with_index(parent_pos) != hash {
if (left_child_hs, right_child_hs).hash_with_index(parent_pos-1) != hash {
return Err(format!(
"Invalid MMR, hash of parent at {} does \
not match children.",
@ -1426,78 +1435,77 @@ mod test {
// one element
pmmr.push(elems[0]).unwrap();
pmmr.dump(false);
let pos_1 = elems[0].hash_with_index(1);
assert_eq!(pmmr.peaks(), vec![pos_1]);
assert_eq!(pmmr.root(), pos_1);
let pos_0 = elems[0].hash_with_index(0);
assert_eq!(pmmr.peaks(), vec![pos_0]);
assert_eq!(pmmr.root(), pos_0);
assert_eq!(pmmr.unpruned_size(), 1);
// two elements
pmmr.push(elems[1]).unwrap();
pmmr.dump(false);
let pos_2 = elems[1].hash_with_index(2);
let pos_3 = (pos_1, pos_2).hash_with_index(3);
assert_eq!(pmmr.peaks(), vec![pos_3]);
assert_eq!(pmmr.root(), pos_3);
let pos_1 = elems[1].hash_with_index(1);
let pos_2 = (pos_0, pos_1).hash_with_index(2);
assert_eq!(pmmr.peaks(), vec![pos_2]);
assert_eq!(pmmr.root(), pos_2);
assert_eq!(pmmr.unpruned_size(), 3);
// three elements
pmmr.push(elems[2]).unwrap();
pmmr.dump(false);
let pos_4 = elems[2].hash_with_index(4);
assert_eq!(pmmr.peaks(), vec![pos_3, pos_4]);
let root = pos_3 + pos_4;
assert_eq!(pmmr.root(), pos_3 + pos_4);
let pos_3 = elems[2].hash_with_index(3);
assert_eq!(pmmr.peaks(), vec![pos_2, pos_3]);
assert_eq!(pmmr.root(), (pos_2, pos_3).hash_with_index(4));
assert_eq!(pmmr.unpruned_size(), 4);
// four elements
pmmr.push(elems[3]).unwrap();
pmmr.dump(false);
let pos_5 = elems[3].hash_with_index(5);
let pos_6 = (pos_4, pos_5).hash_with_index(6);
let pos_7 = (pos_3, pos_6).hash_with_index(7);
assert_eq!(pmmr.peaks(), vec![pos_7]);
assert_eq!(pmmr.root(), pos_7);
let pos_4 = elems[3].hash_with_index(4);
let pos_5 = (pos_3, pos_4).hash_with_index(5);
let pos_6 = (pos_2, pos_5).hash_with_index(6);
assert_eq!(pmmr.peaks(), vec![pos_6]);
assert_eq!(pmmr.root(), pos_6);
assert_eq!(pmmr.unpruned_size(), 7);
// five elements
pmmr.push(elems[4]).unwrap();
pmmr.dump(false);
let pos_8 = elems[4].hash_with_index(8);
assert_eq!(pmmr.peaks(), vec![pos_7, pos_8]);
assert_eq!(pmmr.root(), pos_7 + pos_8);
let pos_7 = elems[4].hash_with_index(7);
assert_eq!(pmmr.peaks(), vec![pos_6, pos_7]);
assert_eq!(pmmr.root(), (pos_6, pos_7).hash_with_index(8));
assert_eq!(pmmr.unpruned_size(), 8);
// six elements
pmmr.push(elems[5]).unwrap();
let pos_9 = elems[5].hash_with_index(9);
let pos_10 = (pos_8, pos_9).hash_with_index(10);
assert_eq!(pmmr.peaks(), vec![pos_7, pos_10]);
assert_eq!(pmmr.root(), pos_7 + pos_10);
let pos_8 = elems[5].hash_with_index(8);
let pos_9 = (pos_7, pos_8).hash_with_index(9);
assert_eq!(pmmr.peaks(), vec![pos_6, pos_9]);
assert_eq!(pmmr.root(), (pos_6, pos_9).hash_with_index(10));
assert_eq!(pmmr.unpruned_size(), 10);
// seven elements
pmmr.push(elems[6]).unwrap();
let pos_11 = elems[6].hash_with_index(11);
assert_eq!(pmmr.peaks(), vec![pos_7, pos_10, pos_11]);
assert_eq!(pmmr.root(), pos_7 + pos_10 + pos_11);
let pos_10 = elems[6].hash_with_index(10);
assert_eq!(pmmr.peaks(), vec![pos_6, pos_9, pos_10]);
assert_eq!(pmmr.root(), (pos_6, (pos_9, pos_10).hash_with_index(11)).hash_with_index(11));
assert_eq!(pmmr.unpruned_size(), 11);
// 001001200100123
// eight elements
pmmr.push(elems[7]).unwrap();
let pos_12 = elems[7].hash_with_index(12);
let pos_13 = (pos_11, pos_12).hash_with_index(13);
let pos_14 = (pos_10, pos_13).hash_with_index(14);
let pos_15 = (pos_7, pos_14).hash_with_index(15);
assert_eq!(pmmr.peaks(), vec![pos_15]);
assert_eq!(pmmr.root(), pos_15);
let pos_11 = elems[7].hash_with_index(11);
let pos_12 = (pos_10, pos_11).hash_with_index(12);
let pos_13 = (pos_9, pos_12).hash_with_index(13);
let pos_14 = (pos_6, pos_13).hash_with_index(14);
assert_eq!(pmmr.peaks(), vec![pos_14]);
assert_eq!(pmmr.root(), pos_14);
assert_eq!(pmmr.unpruned_size(), 15);
// nine elements
pmmr.push(elems[8]).unwrap();
let pos_16 = elems[8].hash_with_index(16);
assert_eq!(pmmr.peaks(), vec![pos_15, pos_16]);
assert_eq!(pmmr.root(), pos_15 + pos_16);
let pos_15 = elems[8].hash_with_index(15);
assert_eq!(pmmr.peaks(), vec![pos_14, pos_15]);
assert_eq!(pmmr.root(), (pos_14, pos_15).hash_with_index(16));
assert_eq!(pmmr.unpruned_size(), 16);
}

View file

@ -38,35 +38,35 @@ fn pmmr_append() {
backend.sync().unwrap();
// check the resulting backend store and the computation of the root
let node_hash = elems[0].hash_with_index(1);
let node_hash = elems[0].hash_with_index(0);
assert_eq!(backend.get_hash(1).unwrap(), node_hash);
// 0010012001001230
let pos_1 = elems[0].hash_with_index(1);
let pos_2 = elems[1].hash_with_index(2);
let pos_3 = (pos_1, pos_2).hash_with_index(3);
let pos_0 = elems[0].hash_with_index(0);
let pos_1 = elems[1].hash_with_index(1);
let pos_2 = (pos_0, pos_1).hash_with_index(2);
let pos_4 = elems[2].hash_with_index(4);
let pos_5 = elems[3].hash_with_index(5);
let pos_6 = (pos_4, pos_5).hash_with_index(6);
let pos_7 = (pos_3, pos_6).hash_with_index(7);
let pos_3 = elems[2].hash_with_index(3);
let pos_4 = elems[3].hash_with_index(4);
let pos_5 = (pos_3, pos_4).hash_with_index(5);
let pos_6 = (pos_2, pos_5).hash_with_index(6);
let pos_8 = elems[4].hash_with_index(8);
let pos_9 = elems[5].hash_with_index(9);
let pos_10 = (pos_8, pos_9).hash_with_index(10);
let pos_7 = elems[4].hash_with_index(7);
let pos_8 = elems[5].hash_with_index(8);
let pos_9 = (pos_7, pos_8).hash_with_index(9);
let pos_11 = elems[6].hash_with_index(11);
let pos_12 = elems[7].hash_with_index(12);
let pos_13 = (pos_11, pos_12).hash_with_index(13);
let pos_14 = (pos_10, pos_13).hash_with_index(14);
let pos_15 = (pos_7, pos_14).hash_with_index(15);
let pos_10 = elems[6].hash_with_index(10);
let pos_11 = elems[7].hash_with_index(11);
let pos_12 = (pos_10, pos_11).hash_with_index(12);
let pos_13 = (pos_9, pos_12).hash_with_index(13);
let pos_14 = (pos_6, pos_13).hash_with_index(14);
let pos_16 = elems[8].hash_with_index(16);
let pos_15 = elems[8].hash_with_index(15);
{
let pmmr: PMMR<TestElem, _> = PMMR::at(&mut backend, mmr_size);
assert_eq!(pmmr.root(), pos_15 + pos_16);
assert_eq!(pmmr.root(), (pos_14, pos_15).hash_with_index(16));
}
teardown(data_dir);