diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 4f75af2d2..90e32910a 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -74,18 +74,18 @@ impl Writeable for BlockHeader { [write_u64, self.height], [write_fixed_bytes, &self.previous], [write_i64, self.timestamp.to_timespec().sec], - [write_u8, self.cuckoo_len]); - ser_multiwrite!(writer, + [write_u8, self.cuckoo_len], [write_fixed_bytes, &self.utxo_merkle], [write_fixed_bytes, &self.tx_merkle]); - // make sure to not introduce any variable length data before the nonce to - // avoid complicating PoW - try!(writer.write_u64(self.nonce)); - // proof - try!(self.pow.write(writer)); - // block and total difficulty + + try!(writer.write_u64(self.nonce)); try!(self.difficulty.write(writer)); - self.total_difficulty.write(writer) + try!(self.total_difficulty.write(writer)); + + if writer.serialization_mode() != ser::SerializationMode::Hash { + try!(self.pow.write(writer)); + } + Ok(()) } } @@ -97,10 +97,10 @@ impl Readable for BlockHeader { let (timestamp, cuckoo_len) = ser_multiread!(reader, read_i64, read_u8); let utxo_merkle = try!(Hash::read(reader)); let tx_merkle = try!(Hash::read(reader)); - let nonce = try!(reader.read_u64()); - let pow = try!(Proof::read(reader)); let difficulty = try!(Difficulty::read(reader)); let total_difficulty = try!(Difficulty::read(reader)); + let nonce = try!(reader.read_u64()); + let pow = try!(Proof::read(reader)); Ok(BlockHeader { height: height, @@ -132,25 +132,29 @@ pub struct Block { pub proofs: Vec, } -/// Implementation of Writeable for a block, defines how to write the full -/// block as binary. +/// Implementation of Writeable for a block, defines how to write the block to a +/// binary writer. Differentiates between writing the block for the purpose of +/// full serialization and the one of just extracting a hash. impl Writeable for Block { fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { try!(self.header.write(writer)); - ser_multiwrite!(writer, - [write_u64, self.inputs.len() as u64], - [write_u64, self.outputs.len() as u64], - [write_u64, self.proofs.len() as u64]); - for inp in &self.inputs { - try!(inp.write(writer)); - } - for out in &self.outputs { - try!(out.write(writer)); - } - for proof in &self.proofs { - try!(proof.write(writer)); - } + if writer.serialization_mode() != ser::SerializationMode::Hash { + ser_multiwrite!(writer, + [write_u64, self.inputs.len() as u64], + [write_u64, self.outputs.len() as u64], + [write_u64, self.proofs.len() as u64]); + + for inp in &self.inputs { + try!(inp.write(writer)); + } + for out in &self.outputs { + try!(out.write(writer)); + } + for proof in &self.proofs { + try!(proof.write(writer)); + } + } Ok(()) } } diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 5817e71a3..07986d3e4 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -36,110 +36,59 @@ use pow::cuckoo::{Cuckoo, Miner, Error}; use ser; use ser::{Writeable, Writer}; -/// Subset of a block header that goes into hashing for proof of work. -/// Basically the whole thing minus the PoW solution itself and the total -/// difficulty (yet unknown). We also add the count of every variable length -/// elements in a header to make lying on those much harder. -#[derive(Debug)] -pub struct PowHeader { - pub nonce: u64, - pub height: u64, - pub previous: Hash, - pub timestamp: time::Tm, - pub utxo_merkle: Hash, - pub tx_merkle: Hash, - pub n_in: u64, - pub n_out: u64, - pub n_proofs: u64, -} - -/// The binary definition of a PoW header is material for consensus as that's -/// the data that gets hashed for PoW calculation. The nonce is written first -/// to make incrementing from the serialized form trivial. -impl Writeable for PowHeader { - fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { - try!(writer.write_u64(self.nonce)); - try!(writer.write_u64(self.height)); - try!(writer.write_fixed_bytes(&self.previous)); - try!(writer.write_i64(self.timestamp.to_timespec().sec)); - try!(writer.write_fixed_bytes(&self.utxo_merkle)); - try!(writer.write_fixed_bytes(&self.tx_merkle)); - try!(writer.write_u64(self.n_in)); - try!(writer.write_u64(self.n_out)); - writer.write_u64(self.n_proofs) - } -} - -impl PowHeader { - pub fn from_block(b: &Block) -> PowHeader { - let ref h = b.header; - PowHeader { - nonce: h.nonce, - height: h.height, - previous: h.previous, - timestamp: h.timestamp, - utxo_merkle: h.utxo_merkle, - tx_merkle: h.tx_merkle, - n_in: b.inputs.len() as u64, - n_out: b.outputs.len() as u64, - n_proofs: b.proofs.len() as u64, - } - } -} - /// Validates the proof of work of a given header. pub fn verify(b: &Block) -> bool { verify_size(b, b.header.cuckoo_len as u32) } pub fn verify_size(b: &Block, cuckoo_sz: u32) -> bool { - let hash = PowHeader::from_block(b).hash(); // make sure the pow hash shows a difficulty at least as large as the target // difficulty if b.header.difficulty > b.header.pow.to_difficulty() { return false; } - Cuckoo::new(hash.to_slice(), cuckoo_sz).verify(b.header.pow, EASINESS as u64) + Cuckoo::new(b.hash().to_slice(), cuckoo_sz).verify(b.header.pow, EASINESS as u64) } /// Runs a naive single-threaded proof of work computation over the provided /// block, until the required difficulty target is reached. May take a /// while for a low target... -pub fn pow(b: &Block, diff: Difficulty) -> Result<(Proof, u64), Error> { - pow_size(b, diff, b.header.cuckoo_len as u32) +pub fn pow(b: &mut Block, diff: Difficulty) -> Result<(), Error> { + let cuckoo_len = b.header.cuckoo_len as u32; + pow_size(b, diff, cuckoo_len) } /// Same as default pow function but uses the much easier Cuckoo20 (mostly for /// tests). -pub fn pow20(b: &Block, diff: Difficulty) -> Result<(Proof, u64), Error> { +pub fn pow20(b: &mut Block, diff: Difficulty) -> Result<(), Error> { pow_size(b, diff, 20) } -pub fn pow_size(b: &Block, diff: Difficulty, sizeshift: u32) -> Result<(Proof, u64), Error> { - let mut pow_header = PowHeader::from_block(b); - let start_nonce = pow_header.nonce; +pub fn pow_size(b: &mut Block, diff: Difficulty, sizeshift: u32) -> Result<(), Error> { + let start_nonce = b.header.nonce; // try to find a cuckoo cycle on that header hash loop { // can be trivially optimized by avoiding re-serialization every time but this // is not meant as a fast miner implementation - let pow_hash = pow_header.hash(); + let pow_hash = b.hash(); // if we found a cycle (not guaranteed) and the proof hash is higher that the // diff, we're all good if let Ok(proof) = Miner::new(pow_hash.to_slice(), EASINESS, sizeshift).mine() { if proof.to_difficulty() >= diff { - return Ok((proof, pow_header.nonce)); + b.header.pow = proof; + return Ok(()); } } // otherwise increment the nonce - pow_header.nonce += 1; + b.header.nonce += 1; // and if we're back where we started, update the time (changes the hash as // well) - if pow_header.nonce == start_nonce { - pow_header.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 }); + if b.header.nonce == start_nonce { + b.header.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 }); } } } @@ -154,12 +103,10 @@ mod test { #[test] fn genesis_pow() { let mut b = genesis::genesis(); - let (proof, nonce) = pow20(&b, Difficulty::one()).unwrap(); - assert!(nonce > 0); - assert!(proof.to_difficulty() >= Difficulty::one()); - b.header.pow = proof; - b.header.nonce = nonce; - b.header.cuckoo_len = 20; - assert!(verify(&b)); + b.header.nonce = 310; + pow20(&mut b, Difficulty::one()).unwrap(); + assert!(b.header.nonce != 310); + assert!(b.header.pow.to_difficulty() >= Difficulty::one()); + assert!(verify_size(&b, 20)); } }