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, _> = let output_pmmr: PMMR<OutputIdentifier, _> =
PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos); PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos);
if let Some(hash) = output_pmmr.get_hash(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) Ok(hash)
} else { } else {
Err(Error::TxHashSetErr(format!("txhashset hash mismatch"))) Err(Error::TxHashSetErr(format!("txhashset hash mismatch")))
@ -386,7 +386,7 @@ impl<'a> Extension<'a> {
let commit = input.commitment(); let commit = input.commitment();
let pos_res = self.get_output_pos(&commit); let pos_res = self.get_output_pos(&commit);
if let Ok(pos) = pos_res { 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) { if let Some(read_hash) = self.output_pmmr.get_hash(pos) {
// 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
@ -398,7 +398,7 @@ impl<'a> Extension<'a> {
|| output_id_hash || output_id_hash
!= read_elem != read_elem
.expect("no output at position") .expect("no output at position")
.hash_with_index(pos) .hash_with_index(pos-1)
{ {
return Err(Error::TxHashSetErr(format!("output pmmr hash mismatch"))); return Err(Error::TxHashSetErr(format!("output pmmr hash mismatch")));
} }

View file

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

View file

@ -38,35 +38,35 @@ 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_with_index(1); let node_hash = elems[0].hash_with_index(0);
assert_eq!(backend.get_hash(1).unwrap(), node_hash); assert_eq!(backend.get_hash(1).unwrap(), node_hash);
// 0010012001001230 // 0010012001001230
let pos_1 = elems[0].hash_with_index(1); let pos_0 = elems[0].hash_with_index(0);
let pos_2 = elems[1].hash_with_index(2); let pos_1 = elems[1].hash_with_index(1);
let pos_3 = (pos_1, pos_2).hash_with_index(3); let pos_2 = (pos_0, pos_1).hash_with_index(2);
let pos_4 = elems[2].hash_with_index(4); let pos_3 = elems[2].hash_with_index(3);
let pos_5 = elems[3].hash_with_index(5); let pos_4 = elems[3].hash_with_index(4);
let pos_6 = (pos_4, pos_5).hash_with_index(6); let pos_5 = (pos_3, pos_4).hash_with_index(5);
let pos_7 = (pos_3, pos_6).hash_with_index(7); let pos_6 = (pos_2, pos_5).hash_with_index(6);
let pos_8 = elems[4].hash_with_index(8); let pos_7 = elems[4].hash_with_index(7);
let pos_9 = elems[5].hash_with_index(9); let pos_8 = elems[5].hash_with_index(8);
let pos_10 = (pos_8, pos_9).hash_with_index(10); let pos_9 = (pos_7, pos_8).hash_with_index(9);
let pos_11 = elems[6].hash_with_index(11); let pos_10 = elems[6].hash_with_index(10);
let pos_12 = elems[7].hash_with_index(12); let pos_11 = elems[7].hash_with_index(11);
let pos_13 = (pos_11, pos_12).hash_with_index(13); let pos_12 = (pos_10, pos_11).hash_with_index(12);
let pos_14 = (pos_10, pos_13).hash_with_index(14); let pos_13 = (pos_9, pos_12).hash_with_index(13);
let pos_15 = (pos_7, pos_14).hash_with_index(15); 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); 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); teardown(data_dir);