compact block needs a random nonce in it (used to generate short_ids) (#709)

This commit is contained in:
Antioch Peverell 2018-02-14 09:19:24 -05:00 committed by GitHub
parent 6fb139670e
commit 8bdf0be73e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 28 deletions

View file

@ -15,8 +15,7 @@
//! Blocks and blockheaders
use time;
use util;
use util::{secp, static_secp_instance};
use rand::{thread_rng, Rng};
use std::collections::HashSet;
use core::{
@ -39,11 +38,13 @@ use core::id::ShortIdentifiable;
use core::target::Difficulty;
use core::transaction;
use ser::{self, Readable, Reader, Writeable, Writer, WriteableSorted, read_and_verify_sorted};
use util::kernel_sig_msg;
use util::LOGGER;
use global;
use keychain;
use keychain::BlindingFactor;
use util;
use util::kernel_sig_msg;
use util::LOGGER;
use util::{secp, static_secp_instance};
/// Errors thrown by Block validation
#[derive(Debug, Clone, PartialEq)]
@ -221,6 +222,8 @@ impl Readable for BlockHeader {
pub struct CompactBlock {
/// The header with metadata and commitments to the rest of the data
pub header: BlockHeader,
/// Nonce for connection specific short_ids
pub nonce: u64,
/// List of full outputs - specifically the coinbase output(s)
pub out_full: Vec<Output>,
/// List of full kernels - specifically the coinbase kernel(s)
@ -235,10 +238,10 @@ pub struct CompactBlock {
impl Writeable for CompactBlock {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
try!(self.header.write(writer));
if writer.serialization_mode() != ser::SerializationMode::Hash {
ser_multiwrite!(
writer,
[write_u64, self.nonce],
[write_u64, self.out_full.len() as u64],
[write_u64, self.kern_full.len() as u64],
[write_u64, self.kern_ids.len() as u64]
@ -263,8 +266,8 @@ impl Readable for CompactBlock {
fn read(reader: &mut Reader) -> Result<CompactBlock, ser::Error> {
let header = try!(BlockHeader::read(reader));
let (out_full_len, kern_full_len, kern_id_len) =
ser_multiread!(reader, read_u64, read_u64, read_u64);
let (nonce, out_full_len, kern_full_len, kern_id_len) =
ser_multiread!(reader, read_u64, read_u64, read_u64, read_u64);
let out_full = read_and_verify_sorted(reader, out_full_len as u64)?;
let kern_full = read_and_verify_sorted(reader, kern_full_len as u64)?;
@ -272,6 +275,7 @@ impl Readable for CompactBlock {
Ok(CompactBlock {
header,
nonce,
out_full,
kern_full,
kern_ids,
@ -447,24 +451,27 @@ impl Block {
/// Generate the compact block representation.
pub fn as_compact_block(&self) -> CompactBlock {
let header = self.header.clone();
let block_hash = self.hash();
let nonce = thread_rng().next_u64();
// concatenate the nonce with our block_header to build the hash
let hash = (self, nonce).hash();
let mut out_full = self.outputs
.iter()
.filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
.cloned()
.collect::<Vec<_>>();
let mut kern_full = self.kernels
.iter()
.filter(|x| x.features.contains(KernelFeatures::COINBASE_KERNEL))
.cloned()
.collect::<Vec<_>>();
let mut kern_ids = self.kernels
.iter()
.filter(|x| !x.features.contains(KernelFeatures::COINBASE_KERNEL))
.map(|x| x.short_id(&block_hash))
.collect::<Vec<_>>();
let mut kern_full = vec![];
let mut kern_ids = vec![];
for k in &self.kernels {
if k.features.contains(KernelFeatures::COINBASE_KERNEL) {
kern_full.push(k.clone());
} else {
kern_ids.push(k.short_id(&hash));
}
}
// sort all the lists
out_full.sort();
@ -473,6 +480,7 @@ impl Block {
CompactBlock {
header,
nonce,
out_full,
kern_full,
kern_ids,
@ -1081,7 +1089,7 @@ mod test {
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
assert_eq!(
vec.len(),
5_708,
5_716,
);
}
@ -1094,7 +1102,7 @@ mod test {
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
assert_eq!(
vec.len(),
5_714,
5_722,
);
}
@ -1138,7 +1146,7 @@ mod test {
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
assert_eq!(
vec.len(),
5_768,
5_776,
);
}
@ -1147,9 +1155,10 @@ mod test {
let keychain = Keychain::from_random_seed().unwrap();
let tx1 = tx1i2o();
let b = new_block(vec![&tx1], &keychain);
let cb = b.as_compact_block();
let hash = (b.header, cb.nonce).hash();
assert_eq!(cb.out_full.len(), 1);
assert_eq!(cb.kern_full.len(), 1);
assert_eq!(cb.kern_ids.len(), 1);
@ -1160,7 +1169,7 @@ mod test {
.iter()
.find(|x| !x.features.contains(KernelFeatures::COINBASE_KERNEL))
.unwrap()
.short_id(&b.hash())
.short_id(&hash)
);
}
@ -1179,6 +1188,7 @@ mod test {
fn serialize_deserialize_compact_block() {
let b = CompactBlock {
header: BlockHeader::default(),
nonce: 0,
out_full: vec![],
kern_full: vec![],
kern_ids: vec![ShortId::zero()],

View file

@ -30,8 +30,9 @@ pub const SHORT_ID_SIZE: usize = 6;
/// A trait for types that have a short_id (inputs/outputs/kernels)
pub trait ShortIdentifiable {
/// The short_id of the instance.
fn short_id(&self, block_hash: &Hash) -> ShortId;
/// The short_id of a kernel uses a hash built from the block_header *and* a
/// connection specific nonce to minimize the effect of collisions.
fn short_id(&self, hash: &Hash) -> ShortId;
}
impl<H: Hashed> ShortIdentifiable for H {
@ -42,14 +43,14 @@ impl<H: Hashed> ShortIdentifiable for H {
/// * self.hash() passing in the siphasher24 instance
/// * drop the 2 most significant bytes (to return a 6 byte short_id)
///
fn short_id(&self, block_hash: &Hash) -> ShortId {
fn short_id(&self, hash: &Hash) -> ShortId {
// we "use" core::hash::Hash in the outer namespace
// so doing this here in the fn to minimize collateral damage/confusion
use std::hash::Hasher;
// extract k0/k1 from the block_hash
let k0 = LittleEndian::read_u64(&block_hash.0[0..8]);
let k1 = LittleEndian::read_u64(&block_hash.0[8..16]);
let k0 = LittleEndian::read_u64(&hash.0[0..8]);
let k1 = LittleEndian::read_u64(&hash.0[8..16]);
// initialize a siphasher24 with k0/k1
let mut sip_hasher = SipHasher24::new_with_keys(k0, k1);