From 8317854b705d4fef19a17a251498432b55456ed6 Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Wed, 9 Nov 2016 11:05:44 -0800 Subject: [PATCH 1/3] Slight changes to transaction signature. The transaction fee gets signed (instead of the empty string) and the fee amount is attached to the transaction proof in blocks. This allows fee accounting and the tracking of fees within a block. --- core/src/core/block.rs | 6 ++---- core/src/core/transaction.rs | 41 +++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 3985705e..fbeea756 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -340,11 +340,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(()) } @@ -368,6 +365,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 33c715db..ba6b2a08 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -17,44 +17,58 @@ use core::Committed; use core::MerkleRow; use core::hash::{Hash, Hashed}; -use ser::{self, Reader, Writer, Readable, Writeable}; +use byteorder::{ByteOrder, BigEndian}; use secp::{self, Secp256k1, Message, Signature}; use secp::key::SecretKey; use secp::pedersen::{RangeProof, Commitment}; +use ser::{self, Reader, Writer, Readable, Writeable}; + /// The maximum number of inputs or outputs a transaction may have /// and be deserializable. pub const MAX_IN_OUT_LEN: u64 = 50000; -/// 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 { @@ -175,7 +189,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 { @@ -208,13 +222,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, }) } } @@ -368,6 +383,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::*; From 4e9aad639eb4bb2a0e9202813d6d1afcae5ace89 Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Wed, 9 Nov 2016 11:10:02 -0800 Subject: [PATCH 2/3] Formatting fixes. --- core/src/core/block.rs | 2 +- core/src/core/transaction.rs | 38 ++++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/core/src/core/block.rs b/core/src/core/block.rs index fbeea756..90cd5c2c 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -365,7 +365,7 @@ impl Block { let proof = TxProof { remainder: remainder, sig: sig.serialize_der(&secp), - fee: 0, + fee: 0, }; Ok((output, proof)) } diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index ba6b2a08..6d2baf49 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -29,15 +29,21 @@ use ser::{self, Reader, Writer, Readable, Writeable}; /// and be deserializable. pub const MAX_IN_OUT_LEN: u64 = 50000; -/// 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. +/// 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 { - /// 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. + /// 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, - /// The signature proving the remainder is a valid public key, which signs the transaction fee. + /// 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, + /// Fee originally included in the transaction this proof is for. + pub fee: u64, } impl Writeable for TxProof { @@ -54,19 +60,21 @@ impl Readable for TxProof { Ok(TxProof { remainder: Commitment::from_vec(remainder), sig: sig, - fee: fee, + 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. + /// 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) - } + let pubk = try!(self.remainder.to_pubkey(secp)); + let sig = try!(Signature::from_der(secp, &self.sig)); + secp.verify(&msg, &sig, &pubk) + } } /// A transaction @@ -229,7 +237,7 @@ impl Transaction { Ok(TxProof { remainder: rsum, sig: self.zerosig.clone(), - fee: self.fee, + fee: self.fee, }) } } @@ -384,9 +392,9 @@ pub fn merkle_inputs_outputs(inputs: &Vec, outputs: &Vec) -> Hash } fn u64_to_32bytes(n: u64) -> [u8; 32] { - let mut bytes = [0; 32]; - BigEndian::write_u64(&mut bytes[24..32], n); - bytes + let mut bytes = [0; 32]; + BigEndian::write_u64(&mut bytes[24..32], n); + bytes } #[cfg(test)] From 6e2a232ca33c410394373fdfcbbb6f4d8faaade8 Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Wed, 9 Nov 2016 13:25:40 -0800 Subject: [PATCH 3/3] Cleaned up total fees from block header. Unnecessary now. --- core/src/core/block.rs | 24 ++++++++---------------- core/src/core/hash.rs | 9 +++------ core/src/core/mod.rs | 6 ++++-- core/src/genesis.rs | 3 +-- core/src/pow/mod.rs | 3 --- core/src/ser.rs | 12 +++++++----- 6 files changed, 23 insertions(+), 34 deletions(-) diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 90cd5c2c..563d9675 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -36,7 +36,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, } @@ -50,7 +49,6 @@ impl Default for BlockHeader { td: 0, utxo_merkle: ZERO_HASH, tx_merkle: ZERO_HASH, - total_fees: 0, nonce: 0, pow: Proof::zero(), } @@ -66,8 +64,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)); @@ -118,14 +115,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 @@ -157,7 +152,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, }, @@ -179,7 +173,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) } } @@ -235,12 +229,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() }, @@ -255,6 +247,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. @@ -312,11 +308,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, diff --git a/core/src/core/hash.rs b/core/src/core/hash.rs index 770a45ae..8ae400c6 100644 --- a/core/src/core/hash.rs +++ b/core/src/core/hash.rs @@ -60,7 +60,7 @@ pub const ZERO_HASH: Hash = Hash([0; 32]); /// Serializer that outputs a hash of the serialized object pub struct HashWriter { - state: Keccak + state: Keccak, } impl HashWriter { @@ -71,9 +71,7 @@ impl HashWriter { impl Default for HashWriter { fn default() -> HashWriter { - HashWriter { - state: Keccak::new_sha3_256() - } + HashWriter { state: Keccak::new_sha3_256() } } } @@ -90,7 +88,7 @@ impl ser::Writer for HashWriter { /// A trait for types that have a canonical hash pub trait Hashed { - fn hash(&self) -> Hash; + fn hash(&self) -> Hash; } impl Hashed for W { @@ -112,4 +110,3 @@ impl Hashed for [u8] { Hash(ret) } } - diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index af5f7368..0340493b 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -156,8 +156,10 @@ impl Writeable for HPair { } } /// An iterator over hashes in a vector that pairs them to build a row in a -/// Merkle tree. If the vector has an odd number of hashes, it appends a zero hash -/// See https://bitcointalk.org/index.php?topic=102395.0 CVE-2012-2459 (block merkle calculation exploit) +/// Merkle tree. If the vector has an odd number of hashes, it appends a zero +/// hash +/// See https://bitcointalk.org/index.php?topic=102395.0 CVE-2012-2459 (block +/// merkle calculation exploit) /// for the argument against duplication of last hash struct HPairIter(Vec); impl Iterator for HPairIter { diff --git a/core/src/genesis.rs b/core/src/genesis.rs index 1fa92d7b..58a76d68 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 d2069721..5e339597 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -61,7 +61,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, @@ -78,7 +77,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) @@ -95,7 +93,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, diff --git a/core/src/ser.rs b/core/src/ser.rs index fbf0a240..795f1b63 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -49,9 +49,11 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::IOErr(ref e) => write!(f, "{}", e), - Error::UnexpectedData { expected: ref e, received: ref r } => write!(f, "expected {:?}, got {:?}", e, r), + Error::UnexpectedData { expected: ref e, received: ref r } => { + write!(f, "expected {:?}, got {:?}", e, r) + } Error::CorruptedData => f.write_str("corrupted data"), - Error::TooLargeReadErr(ref s) => f.write_str(&s) + Error::TooLargeReadErr(ref s) => f.write_str(&s), } } } @@ -60,7 +62,7 @@ impl error::Error for Error { fn cause(&self) -> Option<&error::Error> { match *self { Error::IOErr(ref e) => Some(e), - _ => None + _ => None, } } @@ -69,7 +71,7 @@ impl error::Error for Error { Error::IOErr(ref e) => error::Error::description(e), Error::UnexpectedData { expected: _, received: _ } => "unexpected data", Error::CorruptedData => "corrupted data", - Error::TooLargeReadErr(ref s) => s + Error::TooLargeReadErr(ref s) => s, } } } @@ -90,7 +92,7 @@ pub enum SerializationMode { /// Serialize the data that defines the object Hash, /// Serialize everything that a signer of the object should know - SigHash + SigHash, } /// Implementations defined how different numbers and binary structures are