diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 009d975cf..b7826ac13 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -35,7 +35,6 @@ pub struct BlockHeader { pub td: u64, // total difficulty up to this block pub utxo_merkle: Hash, pub tx_merkle: Hash, - pub total_fees: u64, pub nonce: u64, pub pow: Proof, } @@ -49,7 +48,6 @@ impl Default for BlockHeader { td: 0, utxo_merkle: ZERO_HASH, tx_merkle: ZERO_HASH, - total_fees: 0, nonce: 0, pow: Proof::zero(), } @@ -65,8 +63,7 @@ impl Writeable for BlockHeader { [write_fixed_bytes, &self.previous], [write_i64, self.timestamp.to_timespec().sec], [write_fixed_bytes, &self.utxo_merkle], - [write_fixed_bytes, &self.tx_merkle], - [write_u64, self.total_fees]); + [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)); @@ -117,14 +114,12 @@ impl Writeable for Block { /// from a binary stream. impl Readable for Block { fn read(reader: &mut Reader) -> Result { - let (height, previous, timestamp, utxo_merkle, tx_merkle, total_fees, nonce) = - ser_multiread!(reader, + let (height, previous, timestamp, utxo_merkle, tx_merkle, nonce) = ser_multiread!(reader, read_u64, read_32_bytes, read_i64, read_32_bytes, read_32_bytes, - read_u64, read_u64); // cuckoo cycle of 42 nodes @@ -156,7 +151,6 @@ impl Readable for Block { td: td, utxo_merkle: Hash::from_vec(utxo_merkle), tx_merkle: Hash::from_vec(tx_merkle), - total_fees: total_fees, pow: Proof(pow), nonce: nonce, }, @@ -178,7 +172,7 @@ impl Committed for Block { &self.outputs } fn overage(&self) -> i64 { - (REWARD as i64) - (self.header.total_fees as i64) + (REWARD as i64) - (self.total_fees() as i64) } } @@ -234,12 +228,10 @@ impl Block { outputs.sort_by_key(|out| out.hash()); // calculate the overall Merkle tree and fees - let fees = txs.iter().map(|tx| tx.fee).sum(); Ok(Block { header: BlockHeader { height: prev.height + 1, - total_fees: fees, timestamp: time::now(), ..Default::default() }, @@ -254,6 +246,10 @@ impl Block { self.header.hash() } + pub fn total_fees(&self) -> u64 { + self.proofs.iter().map(|p| p.fee).sum() + } + /// Matches any output with a potential spending input, eliminating them /// from the block. Provides a simple way to compact the block. The /// elimination is stable with respect to inputs and outputs order. @@ -311,11 +307,7 @@ impl Block { Block { // compact will fix the merkle tree - header: BlockHeader { - total_fees: self.header.total_fees + other.header.total_fees, - pow: self.header.pow.clone(), - ..self.header - }, + header: BlockHeader { pow: self.header.pow.clone(), ..self.header }, inputs: all_inputs, outputs: all_outputs, proofs: all_proofs, @@ -339,11 +331,8 @@ impl Block { } // verify all signatures with the commitment as pk - let msg = try!(Message::from_slice(&[0; 32])); for proof in &self.proofs { - let pubk = try!(proof.remainder.to_pubkey(secp)); - let sig = try!(Signature::from_der(secp, &proof.sig)); - try!(secp.verify(&msg, &sig, &pubk)); + try!(proof.verify(secp)); } Ok(()) } @@ -367,6 +356,7 @@ impl Block { let proof = TxProof { remainder: remainder, sig: sig.serialize_der(&secp), + fee: 0, }; Ok((output, proof)) } diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index a221ebf2c..91852e165 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -14,6 +14,7 @@ //! Transactions +use byteorder::{ByteOrder, BigEndian}; use secp::{self, Secp256k1, Message, Signature}; use secp::key::SecretKey; use secp::pedersen::{RangeProof, Commitment}; @@ -24,34 +25,54 @@ use core::MerkleRow; use core::hash::{Hash, Hashed}; use ser::{self, Reader, Writer, Readable, Writeable}; -/// A proof that a transaction did not create (or remove) funds. Includes both -/// the transaction's Pedersen commitment and the signature that guarantees -/// that the commitment amounts to zero. +/// A proof that a transaction sums to zero. Includes both the transaction's +/// Pedersen commitment and the signature, that guarantees that the commitments +/// amount to zero. The signature signs the fee, which is retained for +/// signature validation. #[derive(Debug, Clone)] pub struct TxProof { - /// temporarily public + /// Remainder of the sum of all transaction commitments. If the transaction + /// is well formed, amounts components should sum to zero and the remainder + /// is hence a valid public key. pub remainder: Commitment, - /// temporarily public + /// The signature proving the remainder is a valid public key, which signs + /// the transaction fee. pub sig: Vec, + /// Fee originally included in the transaction this proof is for. + pub fee: u64, } impl Writeable for TxProof { fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { try!(writer.write_fixed_bytes(&self.remainder)); - writer.write_bytes(&self.sig) + try!(writer.write_bytes(&self.sig)); + writer.write_u64(self.fee) } } impl Readable for TxProof { fn read(reader: &mut Reader) -> Result { - let (remainder, sig) = ser_multiread!(reader, read_33_bytes, read_vec); + let (remainder, sig, fee) = ser_multiread!(reader, read_33_bytes, read_vec, read_u64); Ok(TxProof { remainder: Commitment::from_vec(remainder), sig: sig, + fee: fee, }) } } +impl TxProof { + /// Verify the transaction proof validity. Entails handling the commitment + /// as a public key and checking the signature verifies with the fee as + /// message. + pub fn verify(&self, secp: &Secp256k1) -> Result<(), secp::Error> { + let msg = try!(Message::from_slice(&u64_to_32bytes(self.fee))); + let pubk = try!(self.remainder.to_pubkey(secp)); + let sig = try!(Signature::from_der(secp, &self.sig)); + secp.verify(&msg, &sig, &pubk) + } +} + /// A transaction #[derive(Debug)] pub struct Transaction { @@ -172,7 +193,7 @@ impl Transaction { // and sign with the remainder so the signature can be checked to match with // the k.G commitment leftover, that should also be the pubkey - let msg = try!(Message::from_slice(&[0; 32])); + let msg = try!(Message::from_slice(&u64_to_32bytes(self.fee))); let sig = try!(secp.sign(&msg, &remainder)); Ok(Transaction { @@ -205,13 +226,14 @@ impl Transaction { // pretend the sum is a public key (which it is, being of the form r.G) and // verify the transaction sig with it let pubk = try!(rsum.to_pubkey(secp)); - let msg = try!(Message::from_slice(&[0; 32])); + let msg = try!(Message::from_slice(&u64_to_32bytes(self.fee))); let sig = try!(Signature::from_der(secp, &self.zerosig)); try!(secp.verify(&msg, &sig, &pubk)); Ok(TxProof { remainder: rsum, sig: self.zerosig.clone(), + fee: self.fee, }) } } @@ -365,6 +387,12 @@ pub fn merkle_inputs_outputs(inputs: &Vec, outputs: &Vec) -> Hash MerkleRow::new(all_hs).root() } +fn u64_to_32bytes(n: u64) -> [u8; 32] { + let mut bytes = [0; 32]; + BigEndian::write_u64(&mut bytes[24..32], n); + bytes +} + #[cfg(test)] mod test { use super::*; diff --git a/core/src/genesis.rs b/core/src/genesis.rs index 1fa92d7bb..58a76d68f 100644 --- a/core/src/genesis.rs +++ b/core/src/genesis.rs @@ -31,7 +31,7 @@ pub fn genesis() -> core::Block { core::Block { header: core::BlockHeader { height: 0, - previous: core::hash::ZERO_HASH, + previous: core::hash::Hash([0xff; 32]), timestamp: time::Tm { tm_year: 1997, tm_mon: 7, @@ -41,7 +41,6 @@ pub fn genesis() -> core::Block { td: 0, utxo_merkle: core::hash::Hash::from_vec(empty_h.to_vec()), tx_merkle: core::hash::Hash::from_vec(empty_h.to_vec()), - total_fees: 0, nonce: 0, pow: core::Proof::zero(), // TODO get actual PoW solution }, diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 574249c5b..936a0eddf 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -47,7 +47,6 @@ struct PowHeader { pub timestamp: time::Tm, pub utxo_merkle: Hash, pub tx_merkle: Hash, - pub total_fees: u64, pub n_in: u64, pub n_out: u64, pub n_proofs: u64, @@ -64,7 +63,6 @@ impl Writeable for PowHeader { 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.total_fees)); try!(writer.write_u64(self.n_in)); try!(writer.write_u64(self.n_out)); writer.write_u64(self.n_proofs) @@ -81,7 +79,6 @@ impl PowHeader { timestamp: h.timestamp, utxo_merkle: h.utxo_merkle, tx_merkle: h.tx_merkle, - total_fees: h.total_fees, n_in: b.inputs.len() as u64, n_out: b.outputs.len() as u64, n_proofs: b.proofs.len() as u64,