From 9644fb6a73997ac6a1c4ff66ddc7c5ba1c48afe3 Mon Sep 17 00:00:00 2001 From: Merope Riddle Date: Sat, 22 Oct 2016 18:35:48 +0000 Subject: [PATCH 1/6] Add apache 2.0 license header to all source files --- chain/src/lib.rs | 14 ++++++++++++++ chain/src/pipe.rs | 14 ++++++++++++++ chain/src/store.rs | 14 ++++++++++++++ chain/src/types.rs | 14 ++++++++++++++ chain/tests/mine_simple_chain.rs | 14 ++++++++++++++ core/src/core/mod.rs | 14 ++++++++++++++ core/src/core/ser.rs | 14 ++++++++++++++ core/src/genesis.rs | 14 ++++++++++++++ core/src/lib.rs | 14 ++++++++++++++ core/src/macros.rs | 14 ++++++++++++++ core/src/pow/cuckoo.rs | 14 ++++++++++++++ core/src/pow/mod.rs | 14 ++++++++++++++ core/src/pow/siphash.rs | 14 ++++++++++++++ core/src/ser.rs | 14 ++++++++++++++ grin/src/main.rs | 14 ++++++++++++++ store/src/lib.rs | 14 ++++++++++++++ 16 files changed, 224 insertions(+) diff --git a/chain/src/lib.rs b/chain/src/lib.rs index 1325037c5..c0c6b7090 100644 --- a/chain/src/lib.rs +++ b/chain/src/lib.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! The block chain itself, validates and accepts new blocks, handles reorgs. #![deny(non_upper_case_globals)] diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index 9b92a1bb0..050fceaae 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Implementation of the chain block acceptance (or refusal) pipeline. use secp; diff --git a/chain/src/store.rs b/chain/src/store.rs index 08e69eeb2..4b5ef9cd6 100644 --- a/chain/src/store.rs +++ b/chain/src/store.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Implements storage primitives required by the chain use byteorder::{WriteBytesExt, BigEndian}; diff --git a/chain/src/types.rs b/chain/src/types.rs index 086dd2661..142af6f63 100644 --- a/chain/src/types.rs +++ b/chain/src/types.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Base types that the block chain pipeline requires. use core::core::{Hash, Block}; diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 742ec6233..7fb1cdb3b 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + extern crate grin_core; extern crate grin_chain; extern crate rand; diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 0a40350d7..fd828615e 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Core types #[allow(dead_code)] diff --git a/core/src/core/ser.rs b/core/src/core/ser.rs index 34d40ef95..aabe3cc62 100644 --- a/core/src/core/ser.rs +++ b/core/src/core/ser.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Binary stream serialization and deserialzation for core types from trusted //! Write or Read implementations. Issues like starvation or too big sends are //! expected to be handled upstream. diff --git a/core/src/genesis.rs b/core/src/genesis.rs index 9cbe8a47c..02a225dd2 100644 --- a/core/src/genesis.rs +++ b/core/src/genesis.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Definition of the genesis block. Placeholder for now. use time; diff --git a/core/src/lib.rs b/core/src/lib.rs index 805bc26ba..9d234104d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Implementation of the MimbleWimble paper. //! https://download.wpsoftware.net/bitcoin/wizardry/mimblewimble.txt diff --git a/core/src/macros.rs b/core/src/macros.rs index 7c9988f5e..5a480dad5 100644 --- a/core/src/macros.rs +++ b/core/src/macros.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Generic macros used here and there to simplify and make code more //! readable. diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index 10888a82f..6d0a4615c 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Implementation of Cuckoo Cycle designed by John Tromp. Ported to Rust from //! the C and Java code at https://github.com/tromp/cuckoo. Note that only the //! simple miner is included, mostly for testing purposes. John Tromp's Tomato diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index d795d8661..7b41878c3 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! The proof of work needs to strike a balance between fast header //! verification to avoid DoS attacks and difficulty for block verifiers to //! build new blocks. In addition, mining new blocks should also be as diff --git a/core/src/pow/siphash.rs b/core/src/pow/siphash.rs index 8d1c148cd..d1339dda2 100644 --- a/core/src/pow/siphash.rs +++ b/core/src/pow/siphash.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Simple implementation of the siphash 2-4 hashing function from //! Jean-Philippe Aumasson and Daniel J. Bernstein. diff --git a/core/src/ser.rs b/core/src/ser.rs index 0851140b4..a0c9a607a 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Serialization and deserialization layer specialized for binary encoding. //! Ensures consistency and safety. Basically a minimal subset or //! rustc_serialize customized for our need. diff --git a/grin/src/main.rs b/grin/src/main.rs index a78f88a63..8e6a261aa 100644 --- a/grin/src/main.rs +++ b/grin/src/main.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Main crate putting together all the other crates that compose Grin into a //! binary. diff --git a/store/src/lib.rs b/store/src/lib.rs index 60a1b613c..d70cfe189 100644 --- a/store/src/lib.rs +++ b/store/src/lib.rs @@ -1,3 +1,17 @@ +// Copyright 2016 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Storage of core types using RocksDB. #![deny(non_upper_case_globals)] From 7e2b271865e9056f31c9493e22c1958642e4f39c Mon Sep 17 00:00:00 2001 From: Merope Riddle Date: Sat, 22 Oct 2016 21:57:55 +0000 Subject: [PATCH 2/6] add doccomments for consensus comments --- core/src/core/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index fd828615e..6644a0565 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -32,8 +32,13 @@ use secp::pedersen::*; use tiny_keccak::Keccak; +/// The block subsidy amount pub const REWARD: u64 = 1_000_000_000; + +/// Block interval, in seconds pub const BLOCK_TIME_SEC: u8 = 15; + +/// Cuckoo-cycle proof size (cycle length) pub const PROOFSIZE: usize = 42; /// A hash to uniquely (or close enough) identify one of the main blockchain From 88b07791fa16bf96657d47da756838061c91a40f Mon Sep 17 00:00:00 2001 From: Merope Riddle Date: Sat, 22 Oct 2016 22:08:53 +0000 Subject: [PATCH 3/6] core: move Hash into its own module --- core/src/core/hash.rs | 83 +++++++++++++++++++++++++++++++++++++++++++ core/src/core/mod.rs | 60 +++---------------------------- core/src/core/ser.rs | 13 +++---- core/src/genesis.rs | 6 ++-- core/src/pow/mod.rs | 6 ++-- 5 files changed, 102 insertions(+), 66 deletions(-) create mode 100644 core/src/core/hash.rs diff --git a/core/src/core/hash.rs b/core/src/core/hash.rs new file mode 100644 index 000000000..0ed193809 --- /dev/null +++ b/core/src/core/hash.rs @@ -0,0 +1,83 @@ +// Copyright 2016 The Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Hash Function +//! +//! Primary hash function used in the protocol +//! + +use std::fmt; + +use tiny_keccak::Keccak; + +/// A hash to uniquely (or close enough) identify one of the main blockchain +/// constructs. Used pervasively for blocks, transactions and ouputs. +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub struct Hash(pub [u8; 32]); + +impl fmt::Display for Hash { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for i in self.0[..].iter().cloned() { + try!(write!(f, "{:02x}", i)); + } + Ok(()) + } +} + +impl Hash { + /// Creates a new hash from a vector + pub fn from_vec(v: Vec) -> Hash { + let mut a = [0; 32]; + for i in 0..a.len() { + a[i] = v[i]; + } + Hash(a) + } + /// Converts the hash to a byte vector + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } + /// Converts the hash to a byte slice + pub fn to_slice(&self) -> &[u8] { + &self.0 + } +} + +pub const ZERO_HASH: Hash = Hash([0; 32]); + +/// A trait for types that get their hash (double SHA256) from their byte +/// serialzation. +pub trait Hashed { + fn hash(&self) -> Hash { + let data = self.bytes(); + Hash(sha3(data)) + } + + fn bytes(&self) -> Vec; +} + +fn sha3(data: Vec) -> [u8; 32] { + let mut sha3 = Keccak::new_sha3_256(); + let mut buf = [0; 32]; + sha3.update(&data); + sha3.finalize(&mut buf); + buf +} + +impl Hashed for [u8] { + fn bytes(&self) -> Vec { + self.to_owned() + } +} + diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 6644a0565..6cf5a78bb 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -14,9 +14,12 @@ //! Core types +pub mod hash; #[allow(dead_code)] #[macro_use] mod ser; + +use self::hash::{Hash, Hashed, ZERO_HASH}; use ser::{Writeable, Writer, Error, ser_vec}; use time; @@ -41,60 +44,6 @@ pub const BLOCK_TIME_SEC: u8 = 15; /// Cuckoo-cycle proof size (cycle length) pub const PROOFSIZE: usize = 42; -/// A hash to uniquely (or close enough) identify one of the main blockchain -/// constructs. Used pervasively for blocks, transactions and ouputs. -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub struct Hash(pub [u8; 32]); - -impl fmt::Display for Hash { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for i in self.0[..].iter().cloned() { - try!(write!(f, "{:02x}", i)); - } - Ok(()) - } -} - -impl Hash { - /// Creates a new hash from a vector - pub fn from_vec(v: Vec) -> Hash { - let mut a = [0; 32]; - for i in 0..a.len() { - a[i] = v[i]; - } - Hash(a) - } - /// Converts the hash to a byte vector - pub fn to_vec(&self) -> Vec { - self.0.to_vec() - } - /// Converts the hash to a byte slice - pub fn to_slice(&self) -> &[u8] { - &self.0 - } -} - -pub const ZERO_HASH: Hash = Hash([0; 32]); - -/// A trait for types that get their hash (double SHA256) from their byte -/// serialzation. -pub trait Hashed { - fn hash(&self) -> Hash { - let data = self.bytes(); - Hash(sha3(data)) - } - - fn bytes(&self) -> Vec; -} - -fn sha3(data: Vec) -> [u8; 32] { - let mut sha3 = Keccak::new_sha3_256(); - let mut buf = [0; 32]; - sha3.update(&data); - sha3.finalize(&mut buf); - buf -} - /// Implemented by types that hold inputs and outputs including Pedersen /// commitments. Handles the collection of the commitments as well as their /// summing, taking potential explicit overages of fees into account. @@ -755,7 +704,7 @@ impl MerkleRow { } fn root(&self) -> Hash { if self.0.len() == 0 { - Hash(sha3(vec![])) + vec![].hash() } else if self.0.len() == 1 { self.0[0].hash() } else { @@ -767,6 +716,7 @@ impl MerkleRow { #[cfg(test)] mod test { use super::*; + use super::hash::{Hash, Hashed, ZERO_HASH}; use secp; use secp::Secp256k1; use secp::key::SecretKey; diff --git a/core/src/core/ser.rs b/core/src/core/ser.rs index aabe3cc62..0a1c1124d 100644 --- a/core/src/core/ser.rs +++ b/core/src/core/ser.rs @@ -19,7 +19,7 @@ use time; use std::io::{Write, Read}; -use core; +use core::{self, hash}; use ser::*; use secp::Signature; @@ -43,7 +43,7 @@ impl_slice_bytes!(Signature); impl_slice_bytes!(Commitment); impl_slice_bytes!(Vec); -impl AsFixedBytes for core::Hash { +impl AsFixedBytes for hash::Hash { fn as_fixed_bytes(&self) -> &[u8] { self.to_slice() } @@ -124,7 +124,7 @@ impl Writeable for core::Block { impl Readable for core::Input { fn read(reader: &mut Reader) -> Result { reader.read_fixed_bytes(32) - .map(|h| core::Input::BareInput { output: core::Hash::from_vec(h) }) + .map(|h| core::Input::BareInput { output: hash::Hash::from_vec(h) }) } } @@ -210,14 +210,14 @@ impl Readable for core::Block { Ok(core::Block { header: core::BlockHeader { height: height, - previous: core::Hash::from_vec(previous), + previous: hash::Hash::from_vec(previous), timestamp: time::at_utc(time::Timespec { sec: timestamp, nsec: 0, }), td: td, - utxo_merkle: core::Hash::from_vec(utxo_merkle), - tx_merkle: core::Hash::from_vec(tx_merkle), + utxo_merkle: hash::Hash::from_vec(utxo_merkle), + tx_merkle: hash::Hash::from_vec(tx_merkle), total_fees: total_fees, pow: core::Proof(pow), nonce: nonce, @@ -237,6 +237,7 @@ mod test { use secp::*; use secp::key::*; use core::*; + use core::hash::ZERO_HASH; use rand::Rng; use rand::os::OsRng; diff --git a/core/src/genesis.rs b/core/src/genesis.rs index 02a225dd2..1fa92d7bb 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::ZERO_HASH, + previous: core::hash::ZERO_HASH, timestamp: time::Tm { tm_year: 1997, tm_mon: 7, @@ -39,8 +39,8 @@ pub fn genesis() -> core::Block { ..time::empty_tm() }, td: 0, - utxo_merkle: core::Hash::from_vec(empty_h.to_vec()), - tx_merkle: core::Hash::from_vec(empty_h.to_vec()), + 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 7b41878c3..df1808666 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -27,7 +27,8 @@ mod cuckoo; use time; -use core::{Block, BlockHeader, Hashed, Hash, Proof, PROOFSIZE}; +use core::{Block, BlockHeader, Proof, PROOFSIZE}; +use core::hash::{Hash, Hashed}; use pow::cuckoo::{Cuckoo, Miner, Error}; use ser; @@ -175,7 +176,8 @@ fn pow_size(b: &Block, target: Proof, sizeshift: u32) -> Result<(Proof, u64), Er #[cfg(test)] mod test { use super::*; - use core::{BlockHeader, Hash, Proof}; + use core::{BlockHeader, Proof}; + use core::hash::Hash; use std::time::Instant; use genesis; From 2ea58d4478fe56320d5b3af149a266d404493d46 Mon Sep 17 00:00:00 2001 From: Merope Riddle Date: Sat, 22 Oct 2016 22:54:42 +0000 Subject: [PATCH 4/6] core: move Block and BlockHeader into their own module --- core/src/core/block.rs | 470 +++++++++++++++++++++++++++++++++++++++++ core/src/core/mod.rs | 357 +------------------------------ core/src/core/ser.rs | 74 +------ 3 files changed, 475 insertions(+), 426 deletions(-) create mode 100644 core/src/core/block.rs diff --git a/core/src/core/block.rs b/core/src/core/block.rs new file mode 100644 index 000000000..4febce271 --- /dev/null +++ b/core/src/core/block.rs @@ -0,0 +1,470 @@ +// Copyright 2016 The Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Blocks and blockheaders + +use time; +use secp; +use secp::{Secp256k1, Signature, Message}; +use secp::key::SecretKey; +use std::collections::HashSet; + +use core::Committed; +use core::{Input, Output, Proof, TxProof, Transaction}; +use core::merkle_inputs_outputs; +use core::{PROOFSIZE, REWARD}; +use core::hash::{Hash, Hashed, ZERO_HASH}; +use core::ser::MAX_IN_OUT_LEN; +use ser::{self, Readable, Reader, Writeable, Writer, ser_vec}; + +/// Block header, fairly standard compared to other blockchains. +pub struct BlockHeader { + pub height: u64, + pub previous: Hash, + pub timestamp: time::Tm, + 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, +} + +impl Default for BlockHeader { + fn default() -> BlockHeader { + BlockHeader { + height: 0, + previous: ZERO_HASH, + timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }), + td: 0, + utxo_merkle: ZERO_HASH, + tx_merkle: ZERO_HASH, + total_fees: 0, + nonce: 0, + pow: Proof::zero(), + } + } +} + +// Only Writeable implementation is required for hashing, which is part of +// core. Readable is in the ser package. +impl Writeable for BlockHeader { + fn write(&self, writer: &mut Writer) -> Option { + try_m!(writer.write_u64(self.height)); + try_m!(writer.write_fixed_bytes(&self.previous)); + try_m!(writer.write_i64(self.timestamp.to_timespec().sec)); + try_m!(writer.write_fixed_bytes(&self.utxo_merkle)); + try_m!(writer.write_fixed_bytes(&self.tx_merkle)); + try_m!(writer.write_u64(self.total_fees)); + // make sure to not introduce any variable length data before the nonce to + // avoid complicating PoW + try_m!(writer.write_u64(self.nonce)); + // cuckoo cycle of 42 nodes + for n in 0..42 { + try_m!(writer.write_u32(self.pow.0[n])); + } + writer.write_u64(self.td) + } +} + +impl Hashed for BlockHeader { + fn bytes(&self) -> Vec { + // no serialization errors are applicable in this specific case + ser_vec(self).unwrap() + } +} + +/// A block as expressed in the MimbleWimble protocol. The reward is +/// non-explicit, assumed to be deductible from block height (similar to +/// bitcoin's schedule) and expressed as a global transaction fee (added v.H), +/// additive to the total of fees ever collected. +pub struct Block { + // hash_mem: Hash, + pub header: BlockHeader, + pub inputs: Vec, + pub outputs: Vec, + pub proofs: Vec, +} + +/// Implementation of Writeable for a block, defines how to write the full +/// block as binary. +impl Writeable for Block { + fn write(&self, writer: &mut Writer) -> Option { + try_m!(self.header.write(writer)); + + try_m!(writer.write_u64(self.inputs.len() as u64)); + try_m!(writer.write_u64(self.outputs.len() as u64)); + try_m!(writer.write_u64(self.proofs.len() as u64)); + for inp in &self.inputs { + try_m!(inp.write(writer)); + } + for out in &self.outputs { + try_m!(out.write(writer)); + } + for proof in &self.proofs { + try_m!(proof.write(writer)); + } + None + } +} + +/// Implementation of Readable for a block, defines how to read a full block +/// from a binary stream. +impl Readable for Block { + fn read(reader: &mut Reader) -> Result { + let height = try!(reader.read_u64()); + let previous = try!(reader.read_fixed_bytes(32)); + let timestamp = try!(reader.read_i64()); + let utxo_merkle = try!(reader.read_fixed_bytes(32)); + let tx_merkle = try!(reader.read_fixed_bytes(32)); + let total_fees = try!(reader.read_u64()); + let nonce = try!(reader.read_u64()); + // cuckoo cycle of 42 nodes + let mut pow = [0; PROOFSIZE]; + for n in 0..PROOFSIZE { + pow[n] = try!(reader.read_u32()); + } + let td = try!(reader.read_u64()); + + let input_len = try!(reader.read_u64()); + let output_len = try!(reader.read_u64()); + let proof_len = try!(reader.read_u64()); + if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN || proof_len > MAX_IN_OUT_LEN { + return Err(ser::Error::TooLargeReadErr("Too many inputs, outputs or proofs.".to_string())); + } + + let inputs = try!((0..input_len).map(|_| Input::read(reader)).collect()); + let outputs = try!((0..output_len).map(|_| Output::read(reader)).collect()); + let proofs = try!((0..proof_len).map(|_| TxProof::read(reader)).collect()); + Ok(Block { + header: BlockHeader { + height: height, + previous: Hash::from_vec(previous), + timestamp: time::at_utc(time::Timespec { + sec: timestamp, + nsec: 0, + }), + 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, + }, + inputs: inputs, + outputs: outputs, + proofs: proofs, + ..Default::default() + }) + } +} + +/// Provides all information from a block that allows the calculation of total +/// Pedersen commitment. +impl Committed for Block { + fn inputs_committed(&self) -> &Vec { + &self.inputs + } + fn outputs_committed(&self) -> &Vec { + &self.outputs + } + fn overage(&self) -> i64 { + (REWARD as i64) - (self.header.total_fees as i64) + } +} + +/// Default properties for a block, everything zeroed out and empty vectors. +impl Default for Block { + fn default() -> Block { + Block { + header: Default::default(), + inputs: vec![], + outputs: vec![], + proofs: vec![], + } + } +} + +impl Block { + /// Builds a new block from the header of the previous block, a vector of + /// transactions and the private key that will receive the reward. Checks + /// that all transactions are valid and calculates the Merkle tree. + pub fn new(prev: BlockHeader, + txs: Vec<&mut Transaction>, + reward_key: SecretKey) + -> Result { + + let secp = Secp256k1::with_caps(secp::ContextFlag::Commit); + let (reward_out, reward_proof) = try!(Block::reward_output(reward_key, &secp)); + + // note: the following reads easily but may not be the most efficient due to + // repeated iterations, revisit if a problem + + // validate each transaction and gather their proofs + let mut proofs = try_map_vec!(txs, |tx| tx.verify_sig(&secp)); + proofs.push(reward_proof); + + // build vectors with all inputs and all outputs, ordering them by hash + // needs to be a fold so we don't end up with a vector of vectors and we + // want to fullt own the refs (not just a pointer like flat_map). + let mut inputs = txs.iter() + .fold(vec![], |mut acc, ref tx| { + let mut inputs = tx.inputs.clone(); + acc.append(&mut inputs); + acc + }); + let mut outputs = txs.iter() + .fold(vec![], |mut acc, ref tx| { + let mut outputs = tx.outputs.clone(); + acc.append(&mut outputs); + acc + }); + outputs.push(reward_out); + + inputs.sort_by_key(|inp| inp.hash()); + 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() + }, + inputs: inputs, + outputs: outputs, + proofs: proofs, + } + .compact()) + } + + pub fn hash(&self) -> Hash { + self.header.hash() + } + + /// 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. + pub fn compact(&self) -> Block { + // the chosen ones + let mut new_inputs = vec![]; + + // build a set of all output hashes + let mut out_set = HashSet::new(); + for out in &self.outputs { + out_set.insert(out.hash()); + } + // removes from the set any hash referenced by an input, keeps the inputs that + // don't have a match + for inp in &self.inputs { + if !out_set.remove(&inp.output_hash()) { + new_inputs.push(*inp); + } + } + // we got ourselves a keep list in that set + let new_outputs = self.outputs + .iter() + .filter(|out| out_set.contains(&(out.hash()))) + .map(|&out| out) + .collect::>(); + + let tx_merkle = merkle_inputs_outputs(&new_inputs, &new_outputs); + + Block { + header: BlockHeader { + tx_merkle: tx_merkle, + pow: self.header.pow.clone(), + ..self.header + }, + inputs: new_inputs, + outputs: new_outputs, + proofs: self.proofs.clone(), + } + } + + // Merges the 2 blocks, essentially appending the inputs, outputs and proofs. + // Also performs a compaction on the result. + pub fn merge(&self, other: Block) -> Block { + let mut all_inputs = self.inputs.clone(); + all_inputs.append(&mut other.inputs.clone()); + + let mut all_outputs = self.outputs.clone(); + all_outputs.append(&mut other.outputs.clone()); + + let mut all_proofs = self.proofs.clone(); + all_proofs.append(&mut other.proofs.clone()); + + all_inputs.sort_by_key(|inp| inp.hash()); + all_outputs.sort_by_key(|out| out.hash()); + + 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 + }, + inputs: all_inputs, + outputs: all_outputs, + proofs: all_proofs, + } + .compact() + } + + /// Checks the block is valid by verifying the overall commitments sums and + /// proofs. + pub fn verify(&self, secp: &Secp256k1) -> Result<(), secp::Error> { + // sum all inputs and outs commitments + let io_sum = try!(self.sum_commitments(secp)); + // sum all proofs commitments + let proof_commits = map_vec!(self.proofs, |proof| proof.remainder); + let proof_sum = try!(secp.commit_sum(proof_commits, vec![])); + + // both should be the same + if proof_sum != io_sum { + // TODO more specific error + return Err(secp::Error::IncorrectCommitSum); + } + + // 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)); + } + Ok(()) + } + + // Builds the blinded output and related signature proof for the block reward. + fn reward_output(skey: secp::key::SecretKey, + secp: &Secp256k1) + -> Result<(Output, TxProof), secp::Error> { + let msg = try!(secp::Message::from_slice(&[0; 32])); + let sig = try!(secp.sign(&msg, &skey)); + let output = Output::OvertOutput { + value: REWARD, + blindkey: skey, + } + .blind(&secp); + + let over_commit = try!(secp.commit_value(REWARD as u64)); + let out_commit = output.commitment().unwrap(); + let remainder = try!(secp.commit_sum(vec![over_commit], vec![out_commit])); + + let proof = TxProof { + remainder: remainder, + sig: sig.serialize_der(&secp), + }; + Ok((output, proof)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use core::{Input, Output, Transaction}; + use core::hash::{Hash, Hashed}; + use core::test::{tx1i1o, tx2i1o}; + + use secp::{self, Secp256k1}; + use secp::key::SecretKey; + use rand::Rng; + use rand::os::OsRng; + + fn new_secp() -> Secp256k1 { + secp::Secp256k1::with_caps(secp::ContextFlag::Commit) + } + + // utility to create a block without worrying about the key or previous header + fn new_block(txs: Vec<&mut Transaction>, secp: &Secp256k1) -> Block { + let mut rng = OsRng::new().unwrap(); + let skey = SecretKey::new(secp, &mut rng); + Block::new(BlockHeader::default(), txs, skey).unwrap() + } + + // utility producing a transaction that spends the above + fn txspend1i1o(secp: &Secp256k1, rng: &mut R, oout: Output, outh: Hash) -> Transaction { + if let Output::OvertOutput { blindkey, value } = oout { + Transaction::new(vec![Input::OvertInput { + output: outh, + value: value, + blindkey: blindkey, + }], + vec![Output::OvertOutput { + value: 3, + blindkey: SecretKey::new(secp, rng), + }], + 1) + } else { + panic!(); + } + } + + #[test] + // builds a block with a tx spending another and check if merging occurred + fn compactable_block() { + let mut rng = OsRng::new().unwrap(); + let ref secp = new_secp(); + + let tx1 = tx2i1o(secp, &mut rng); + let mut btx1 = tx1.blind(&secp).unwrap(); + + let tx2 = tx1i1o(secp, &mut rng); + let mut btx2 = tx2.blind(&secp).unwrap(); + + // spending tx2 + let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash()); + let mut btx3 = spending.blind(&secp).unwrap(); + let b = new_block(vec![&mut btx1, &mut btx2, &mut btx3], secp); + + // block should have been automatically compacted (including reward output) and + // should still be valid + b.verify(&secp).unwrap(); + assert_eq!(b.inputs.len(), 3); + assert_eq!(b.outputs.len(), 3); + } + + #[test] + // builds 2 different blocks with a tx spending another and check if merging + // occurs + fn mergeable_blocks() { + let mut rng = OsRng::new().unwrap(); + let ref secp = new_secp(); + + let tx1 = tx2i1o(secp, &mut rng); + let mut btx1 = tx1.blind(&secp).unwrap(); + + let tx2 = tx1i1o(secp, &mut rng); + let mut btx2 = tx2.blind(&secp).unwrap(); + + // spending tx2 + let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash()); + let mut btx3 = spending.blind(&secp).unwrap(); + + let b1 = new_block(vec![&mut btx1, &mut btx2], secp); + b1.verify(&secp).unwrap(); + let b2 = new_block(vec![&mut btx3], secp); + b2.verify(&secp).unwrap(); + + // block should have been automatically compacted and should still be valid + let b3 = b1.merge(b2); + assert_eq!(b3.inputs.len(), 3); + assert_eq!(b3.outputs.len(), 4); + } +} + diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 6cf5a78bb..6d025421c 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -14,11 +14,13 @@ //! Core types +pub mod block; pub mod hash; #[allow(dead_code)] #[macro_use] mod ser; +pub use self::block::{Block, BlockHeader}; use self::hash::{Hash, Hashed, ZERO_HASH}; use ser::{Writeable, Writer, Error, ser_vec}; @@ -26,7 +28,6 @@ use time; use std::fmt; use std::cmp::Ordering; -use std::collections::HashSet; use secp; use secp::{Secp256k1, Signature, Message}; @@ -157,279 +158,6 @@ impl Proof { } } -/// Block header, fairly standard compared to other blockchains. -pub struct BlockHeader { - pub height: u64, - pub previous: Hash, - pub timestamp: time::Tm, - 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, -} - -impl Default for BlockHeader { - fn default() -> BlockHeader { - BlockHeader { - height: 0, - previous: ZERO_HASH, - timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }), - td: 0, - utxo_merkle: ZERO_HASH, - tx_merkle: ZERO_HASH, - total_fees: 0, - nonce: 0, - pow: Proof::zero(), - } - } -} - -// Only Writeable implementation is required for hashing, which is part of -// core. Readable is in the ser package. -impl Writeable for BlockHeader { - fn write(&self, writer: &mut Writer) -> Option { - try_m!(writer.write_u64(self.height)); - try_m!(writer.write_fixed_bytes(&self.previous)); - try_m!(writer.write_i64(self.timestamp.to_timespec().sec)); - try_m!(writer.write_fixed_bytes(&self.utxo_merkle)); - try_m!(writer.write_fixed_bytes(&self.tx_merkle)); - try_m!(writer.write_u64(self.total_fees)); - // make sure to not introduce any variable length data before the nonce to - // avoid complicating PoW - try_m!(writer.write_u64(self.nonce)); - // cuckoo cycle of 42 nodes - for n in 0..42 { - try_m!(writer.write_u32(self.pow.0[n])); - } - writer.write_u64(self.td) - } -} - -impl Hashed for BlockHeader { - fn bytes(&self) -> Vec { - // no serialization errors are applicable in this specific case - ser_vec(self).unwrap() - } -} - -/// A block as expressed in the MimbleWimble protocol. The reward is -/// non-explicit, assumed to be deductible from block height (similar to -/// bitcoin's schedule) and expressed as a global transaction fee (added v.H), -/// additive to the total of fees ever collected. -pub struct Block { - // hash_mem: Hash, - pub header: BlockHeader, - pub inputs: Vec, - pub outputs: Vec, - pub proofs: Vec, -} - -/// Provides all information from a block that allows the calculation of total -/// Pedersen commitment. -impl Committed for Block { - fn inputs_committed(&self) -> &Vec { - &self.inputs - } - fn outputs_committed(&self) -> &Vec { - &self.outputs - } - fn overage(&self) -> i64 { - (REWARD as i64) - (self.header.total_fees as i64) - } -} - -/// Default properties for a block, everything zeroed out and empty vectors. -impl Default for Block { - fn default() -> Block { - Block { - header: Default::default(), - inputs: vec![], - outputs: vec![], - proofs: vec![], - } - } -} - -impl Block { - /// Builds a new block from the header of the previous block, a vector of - /// transactions and the private key that will receive the reward. Checks - /// that all transactions are valid and calculates the Merkle tree. - pub fn new(prev: BlockHeader, - txs: Vec<&mut Transaction>, - reward_key: SecretKey) - -> Result { - - let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); - let (reward_out, reward_proof) = try!(Block::reward_output(reward_key, &secp)); - - // note: the following reads easily but may not be the most efficient due to - // repeated iterations, revisit if a problem - - // validate each transaction and gather their proofs - let mut proofs = try_map_vec!(txs, |tx| tx.verify_sig(&secp)); - proofs.push(reward_proof); - - // build vectors with all inputs and all outputs, ordering them by hash - // needs to be a fold so we don't end up with a vector of vectors and we - // want to fullt own the refs (not just a pointer like flat_map). - let mut inputs = txs.iter() - .fold(vec![], |mut acc, ref tx| { - let mut inputs = tx.inputs.clone(); - acc.append(&mut inputs); - acc - }); - let mut outputs = txs.iter() - .fold(vec![], |mut acc, ref tx| { - let mut outputs = tx.outputs.clone(); - acc.append(&mut outputs); - acc - }); - outputs.push(reward_out); - - inputs.sort_by_key(|inp| inp.hash()); - 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() - }, - inputs: inputs, - outputs: outputs, - proofs: proofs, - } - .compact()) - } - - pub fn hash(&self) -> Hash { - self.header.hash() - } - - /// 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. - pub fn compact(&self) -> Block { - // the chosen ones - let mut new_inputs = vec![]; - - // build a set of all output hashes - let mut out_set = HashSet::new(); - for out in &self.outputs { - out_set.insert(out.hash()); - } - // removes from the set any hash referenced by an input, keeps the inputs that - // don't have a match - for inp in &self.inputs { - if !out_set.remove(&inp.output_hash()) { - new_inputs.push(*inp); - } - } - // we got ourselves a keep list in that set - let new_outputs = self.outputs - .iter() - .filter(|out| out_set.contains(&(out.hash()))) - .map(|&out| out) - .collect::>(); - - let tx_merkle = merkle_inputs_outputs(&new_inputs, &new_outputs); - - Block { - header: BlockHeader { - tx_merkle: tx_merkle, - pow: self.header.pow.clone(), - ..self.header - }, - inputs: new_inputs, - outputs: new_outputs, - proofs: self.proofs.clone(), - } - } - - // Merges the 2 blocks, essentially appending the inputs, outputs and proofs. - // Also performs a compaction on the result. - pub fn merge(&self, other: Block) -> Block { - let mut all_inputs = self.inputs.clone(); - all_inputs.append(&mut other.inputs.clone()); - - let mut all_outputs = self.outputs.clone(); - all_outputs.append(&mut other.outputs.clone()); - - let mut all_proofs = self.proofs.clone(); - all_proofs.append(&mut other.proofs.clone()); - - all_inputs.sort_by_key(|inp| inp.hash()); - all_outputs.sort_by_key(|out| out.hash()); - - 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 - }, - inputs: all_inputs, - outputs: all_outputs, - proofs: all_proofs, - } - .compact() - } - - /// Checks the block is valid by verifying the overall commitments sums and - /// proofs. - pub fn verify(&self, secp: &Secp256k1) -> Result<(), secp::Error> { - // sum all inputs and outs commitments - let io_sum = try!(self.sum_commitments(secp)); - // sum all proofs commitments - let proof_commits = map_vec!(self.proofs, |proof| proof.remainder); - let proof_sum = try!(secp.commit_sum(proof_commits, vec![])); - - // both should be the same - if proof_sum != io_sum { - // TODO more specific error - return Err(secp::Error::IncorrectCommitSum); - } - - // 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)); - } - Ok(()) - } - - // Builds the blinded output and related signature proof for the block reward. - fn reward_output(skey: secp::key::SecretKey, - secp: &Secp256k1) - -> Result<(Output, TxProof), secp::Error> { - let msg = try!(secp::Message::from_slice(&[0; 32])); - let sig = try!(secp.sign(&msg, &skey)); - let output = Output::OvertOutput { - value: REWARD, - blindkey: skey, - } - .blind(&secp); - - let over_commit = try!(secp.commit_value(REWARD as u64)); - let out_commit = output.commitment().unwrap(); - let remainder = try!(secp.commit_sum(vec![over_commit], vec![out_commit])); - - let proof = TxProof { - remainder: remainder, - sig: sig.serialize_der(&secp), - }; - Ok((output, proof)) - } -} - #[derive(Debug)] pub struct Transaction { hash_mem: Option, @@ -727,13 +455,6 @@ mod test { secp::Secp256k1::with_caps(secp::ContextFlag::Commit) } - // utility to create a block without worrying about the key or previous header - fn new_block(txs: Vec<&mut Transaction>, secp: &Secp256k1) -> Block { - let mut rng = OsRng::new().unwrap(); - let skey = SecretKey::new(secp, &mut rng); - Block::new(BlockHeader::default(), txs, skey).unwrap() - } - #[test] fn blind_overt_output() { let ref secp = new_secp(); @@ -890,60 +611,8 @@ mod test { b.verify(&secp).unwrap(); } - #[test] - // builds a block with a tx spending another and check if merging occurred - fn compactable_block() { - let mut rng = OsRng::new().unwrap(); - let ref secp = new_secp(); - - let tx1 = tx2i1o(secp, &mut rng); - let mut btx1 = tx1.blind(&secp).unwrap(); - - let tx2 = tx1i1o(secp, &mut rng); - let mut btx2 = tx2.blind(&secp).unwrap(); - - // spending tx2 - let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash()); - let mut btx3 = spending.blind(&secp).unwrap(); - let b = new_block(vec![&mut btx1, &mut btx2, &mut btx3], secp); - - // block should have been automatically compacted (including reward output) and - // should still be valid - b.verify(&secp).unwrap(); - assert_eq!(b.inputs.len(), 3); - assert_eq!(b.outputs.len(), 3); - } - - #[test] - // builds 2 different blocks with a tx spending another and check if merging - // occurs - fn mergeable_blocks() { - let mut rng = OsRng::new().unwrap(); - let ref secp = new_secp(); - - let tx1 = tx2i1o(secp, &mut rng); - let mut btx1 = tx1.blind(&secp).unwrap(); - - let tx2 = tx1i1o(secp, &mut rng); - let mut btx2 = tx2.blind(&secp).unwrap(); - - // spending tx2 - let spending = txspend1i1o(secp, &mut rng, tx2.outputs[0], btx2.outputs[0].hash()); - let mut btx3 = spending.blind(&secp).unwrap(); - - let b1 = new_block(vec![&mut btx1, &mut btx2], secp); - b1.verify(&secp).unwrap(); - let b2 = new_block(vec![&mut btx3], secp); - b2.verify(&secp).unwrap(); - - // block should have been automatically compacted and should still be valid - let b3 = b1.merge(b2); - assert_eq!(b3.inputs.len(), 3); - assert_eq!(b3.outputs.len(), 4); - } - // utility producing a transaction with 2 inputs and a single outputs - fn tx2i1o(secp: &Secp256k1, rng: &mut R) -> Transaction { + pub fn tx2i1o(secp: &Secp256k1, rng: &mut R) -> Transaction { let outh = ZERO_HASH; Transaction::new(vec![Input::OvertInput { output: outh, @@ -963,7 +632,7 @@ mod test { } // utility producing a transaction with a single input and output - fn tx1i1o(secp: &Secp256k1, rng: &mut R) -> Transaction { + pub fn tx1i1o(secp: &Secp256k1, rng: &mut R) -> Transaction { let outh = ZERO_HASH; Transaction::new(vec![Input::OvertInput { output: outh, @@ -976,22 +645,4 @@ mod test { }], 1) } - - // utility producing a transaction that spends the above - fn txspend1i1o(secp: &Secp256k1, rng: &mut R, oout: Output, outh: Hash) -> Transaction { - if let Output::OvertOutput { blindkey, value } = oout { - Transaction::new(vec![Input::OvertInput { - output: outh, - value: value, - blindkey: blindkey, - }], - vec![Output::OvertOutput { - value: 3, - blindkey: SecretKey::new(secp, rng), - }], - 1) - } else { - panic!(); - } - } } diff --git a/core/src/core/ser.rs b/core/src/core/ser.rs index 0a1c1124d..4651d1b59 100644 --- a/core/src/core/ser.rs +++ b/core/src/core/ser.rs @@ -26,7 +26,7 @@ use secp::Signature; use secp::key::SecretKey; use secp::pedersen::{Commitment, RangeProof}; -const MAX_IN_OUT_LEN: u64 = 50000; +pub const MAX_IN_OUT_LEN: u64 = 50000; macro_rules! impl_slice_bytes { ($byteable: ty) => { @@ -97,28 +97,6 @@ impl Writeable for core::TxProof { } } -/// Implementation of Writeable for a block, defines how to write the full -/// block as binary. -impl Writeable for core::Block { - fn write(&self, writer: &mut Writer) -> Option { - try_m!(self.header.write(writer)); - - try_m!(writer.write_u64(self.inputs.len() as u64)); - try_m!(writer.write_u64(self.outputs.len() as u64)); - try_m!(writer.write_u64(self.proofs.len() as u64)); - for inp in &self.inputs { - try_m!(inp.write(writer)); - } - for out in &self.outputs { - try_m!(out.write(writer)); - } - for proof in &self.proofs { - try_m!(proof.write(writer)); - } - None - } -} - /// Implementation of Readable for a transaction Input, defines how to read /// an Input from a binary stream. impl Readable for core::Input { @@ -179,56 +157,6 @@ impl Readable for core::TxProof { } } -/// Implementation of Readable for a block, defines how to read a full block -/// from a binary stream. -impl Readable for core::Block { - fn read(reader: &mut Reader) -> Result { - let height = try!(reader.read_u64()); - let previous = try!(reader.read_fixed_bytes(32)); - let timestamp = try!(reader.read_i64()); - let utxo_merkle = try!(reader.read_fixed_bytes(32)); - let tx_merkle = try!(reader.read_fixed_bytes(32)); - let total_fees = try!(reader.read_u64()); - let nonce = try!(reader.read_u64()); - // cuckoo cycle of 42 nodes - let mut pow = [0; core::PROOFSIZE]; - for n in 0..core::PROOFSIZE { - pow[n] = try!(reader.read_u32()); - } - let td = try!(reader.read_u64()); - - let input_len = try!(reader.read_u64()); - let output_len = try!(reader.read_u64()); - let proof_len = try!(reader.read_u64()); - if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN || proof_len > MAX_IN_OUT_LEN { - return Err(Error::TooLargeReadErr("Too many inputs, outputs or proofs.".to_string())); - } - - let inputs = try!((0..input_len).map(|_| core::Input::read(reader)).collect()); - let outputs = try!((0..output_len).map(|_| core::Output::read(reader)).collect()); - let proofs = try!((0..proof_len).map(|_| core::TxProof::read(reader)).collect()); - Ok(core::Block { - header: core::BlockHeader { - height: height, - previous: hash::Hash::from_vec(previous), - timestamp: time::at_utc(time::Timespec { - sec: timestamp, - nsec: 0, - }), - td: td, - utxo_merkle: hash::Hash::from_vec(utxo_merkle), - tx_merkle: hash::Hash::from_vec(tx_merkle), - total_fees: total_fees, - pow: core::Proof(pow), - nonce: nonce, - }, - inputs: inputs, - outputs: outputs, - proofs: proofs, - ..Default::default() - }) - } -} #[cfg(test)] mod test { From 82adc54971092974c4c4ffbb4cc986d9aaf5841d Mon Sep 17 00:00:00 2001 From: Merope Riddle Date: Sun, 23 Oct 2016 13:18:44 +0000 Subject: [PATCH 5/6] core: separate Transaction into its own module --- core/src/core/block.rs | 4 +- core/src/core/mod.rs | 342 +--------------------- core/src/core/ser.rs | 196 ------------- core/src/core/transaction.rs | 553 +++++++++++++++++++++++++++++++++++ 4 files changed, 557 insertions(+), 538 deletions(-) create mode 100644 core/src/core/transaction.rs diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 4febce271..873741cf0 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -22,10 +22,10 @@ use std::collections::HashSet; use core::Committed; use core::{Input, Output, Proof, TxProof, Transaction}; -use core::merkle_inputs_outputs; +use core::transaction::merkle_inputs_outputs; use core::{PROOFSIZE, REWARD}; use core::hash::{Hash, Hashed, ZERO_HASH}; -use core::ser::MAX_IN_OUT_LEN; +use core::transaction::MAX_IN_OUT_LEN; use ser::{self, Readable, Reader, Writeable, Writer, ser_vec}; /// Block header, fairly standard compared to other blockchains. diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 6d025421c..6be1e7c1d 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -16,11 +16,13 @@ pub mod block; pub mod hash; +pub mod transaction; #[allow(dead_code)] #[macro_use] mod ser; pub use self::block::{Block, BlockHeader}; +pub use self::transaction::{Transaction, Input, Output, TxProof}; use self::hash::{Hash, Hashed, ZERO_HASH}; use ser::{Writeable, Writer, Error, ser_vec}; @@ -88,14 +90,6 @@ pub trait Committed { fn overage(&self) -> i64; } -/// 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. -#[derive(Debug, Clone)] -pub struct TxProof { - remainder: Commitment, - sig: Vec, -} /// Proof of work #[derive(Copy)] @@ -158,247 +152,6 @@ impl Proof { } } -#[derive(Debug)] -pub struct Transaction { - hash_mem: Option, - pub fee: u64, - pub zerosig: Vec, - pub inputs: Vec, - pub outputs: Vec, -} - -impl Committed for Transaction { - fn inputs_committed(&self) -> &Vec { - &self.inputs - } - fn outputs_committed(&self) -> &Vec { - &self.outputs - } - fn overage(&self) -> i64 { - -(self.fee as i64) - } -} - -impl Default for Transaction { - fn default() -> Transaction { - Transaction::empty() - } -} - -impl Transaction { - /// Creates a new empty transaction (no inputs or outputs, zero fee). - pub fn empty() -> Transaction { - Transaction { - hash_mem: None, - fee: 0, - zerosig: vec![], - inputs: vec![], - outputs: vec![], - } - } - - /// Creates a new transaction initialized with the provided inputs, - /// outputs and fee. - pub fn new(inputs: Vec, outputs: Vec, fee: u64) -> Transaction { - Transaction { - hash_mem: None, - fee: fee, - zerosig: vec![], - inputs: inputs, - outputs: outputs, - } - } - - /// The hash of a transaction is the Merkle tree of its inputs and outputs - /// hashes. None of the rest is required. - fn hash(&mut self) -> Hash { - if let None = self.hash_mem { - self.hash_mem = Some(merkle_inputs_outputs(&self.inputs, &self.outputs)); - } - self.hash_mem.unwrap() - } - - /// Takes a transaction and fully blinds it. Following the MW - /// algorithm: calculates the commitments for each inputs and outputs - /// using the values and blinding factors, takes the blinding factors - /// remainder and uses it for an empty signature. - pub fn blind(&self, secp: &Secp256k1) -> Result { - // we compute the sum of blinding factors to get the k remainder - let remainder = try!(self.blind_sum(secp)); - - // next, blind the inputs and outputs if they haven't been yet - let blind_inputs = map_vec!(self.inputs, |inp| inp.blind(secp)); - let blind_outputs = map_vec!(self.outputs, |out| out.blind(secp)); - - // 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 sig = try!(secp.sign(&msg, &remainder)); - - Ok(Transaction { - hash_mem: None, - fee: self.fee, - zerosig: sig.serialize_der(secp), - inputs: blind_inputs, - outputs: blind_outputs, - }) - } - - /// Compute the sum of blinding factors on all overt inputs and outputs - /// of the transaction to get the k remainder. - pub fn blind_sum(&self, secp: &Secp256k1) -> Result { - let inputs_blinding_fact = filter_map_vec!(self.inputs, |inp| inp.blinding_factor()); - let outputs_blinding_fact = filter_map_vec!(self.outputs, |out| out.blinding_factor()); - - secp.blind_sum(inputs_blinding_fact, outputs_blinding_fact) - } - - /// The verification for a MimbleWimble transaction involves getting the - /// remainder of summing all commitments and using it as a public key - /// to verify the embedded signature. The rational is that if the values - /// sum to zero as they should in r.G + v.H then only k.G the remainder - /// of the sum of r.G should be left. And r.G is the definition of a - /// public key generated using r as a private key. - pub fn verify_sig(&self, secp: &Secp256k1) -> Result { - let rsum = try!(self.sum_commitments(secp)); - - // 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 sig = try!(Signature::from_der(secp, &self.zerosig)); - try!(secp.verify(&msg, &sig, &pubk)); - - Ok(TxProof { - remainder: rsum, - sig: self.zerosig.clone(), - }) - } -} - -/// A transaction input, mostly a reference to an output being spent by the -/// transaction. -#[derive(Debug, Copy, Clone)] -pub enum Input { - BareInput { output: Hash }, - BlindInput { output: Hash, commit: Commitment }, - OvertInput { - output: Hash, - value: u64, - blindkey: SecretKey, - }, -} -impl Input { - pub fn commitment(&self) -> Option { - match self { - &Input::BlindInput { commit, .. } => Some(commit), - _ => None, - } - } - pub fn blind(&self, secp: &Secp256k1) -> Input { - match self { - &Input::OvertInput { output, value, blindkey } => { - let commit = secp.commit(value, blindkey).unwrap(); - Input::BlindInput { - output: output, - commit: commit, - } - } - _ => *self, - } - } - pub fn blinding_factor(&self) -> Option { - match self { - &Input::OvertInput { blindkey, .. } => Some(blindkey), - _ => None, - } - } - pub fn output_hash(&self) -> Hash { - match self { - &Input::BlindInput { output, .. } => output, - &Input::OvertInput { output, .. } => output, - &Input::BareInput { output, .. } => output, - } - } -} - -/// The hash of an input is the hash of the output hash it references. -impl Hashed for Input { - fn bytes(&self) -> Vec { - self.output_hash().to_vec() - } -} - -#[derive(Debug, Copy, Clone)] -pub enum Output { - BlindOutput { - commit: Commitment, - proof: RangeProof, - }, - OvertOutput { value: u64, blindkey: SecretKey }, -} -impl Output { - pub fn commitment(&self) -> Option { - match self { - &Output::BlindOutput { commit, .. } => Some(commit), - _ => None, - } - } - pub fn proof(&self) -> Option { - match self { - &Output::BlindOutput { proof, .. } => Some(proof), - _ => None, - } - } - pub fn blinding_factor(&self) -> Option { - match self { - &Output::OvertOutput { blindkey, .. } => Some(blindkey), - _ => None, - } - } - pub fn blind(&self, secp: &Secp256k1) -> Output { - match self { - &Output::OvertOutput { value, blindkey } => { - let commit = secp.commit(value, blindkey).unwrap(); - let rproof = secp.range_proof(0, value, blindkey, commit); - Output::BlindOutput { - commit: commit, - proof: rproof, - } - } - _ => *self, - } - } - /// Validates the range proof using the commitment - pub fn verify_proof(&self, secp: &Secp256k1) -> Result<(), secp::Error> { - match self { - &Output::BlindOutput { commit, proof } => { - secp.verify_range_proof(commit, proof).map(|_| ()) - } - _ => Ok(()), - } - } -} - -/// The hash of an output is the hash of its commitment. -impl Hashed for Output { - fn bytes(&self) -> Vec { - if let &Output::BlindOutput { commit, .. } = self { - return commit.bytes().to_vec(); - } else { - panic!("cannot hash an overt output"); - } - } -} - -/// Utility function to calculate the Merkle root of vectors of inputs and -/// outputs. -pub fn merkle_inputs_outputs(inputs: &Vec, outputs: &Vec) -> Hash { - let mut all_hs = map_vec!(inputs, |inp| inp.hash()); - all_hs.append(&mut map_vec!(outputs, |out| out.hash())); - MerkleRow::new(all_hs).root() -} - /// Two hashes that will get hashed together in a Merkle tree to build the next /// level up. struct HPair(Hash, Hash); @@ -455,97 +208,6 @@ mod test { secp::Secp256k1::with_caps(secp::ContextFlag::Commit) } - #[test] - fn blind_overt_output() { - let ref secp = new_secp(); - let mut rng = OsRng::new().unwrap(); - - let oo = Output::OvertOutput { - value: 42, - blindkey: SecretKey::new(secp, &mut rng), - }; - if let Output::BlindOutput { commit, proof } = oo.blind(secp) { - // checks the blind output is sane and verifies - assert!(commit.len() > 0); - assert!(proof.bytes().len() > 5000); - secp.verify_range_proof(commit, proof).unwrap(); - - // checks that changing the value changes the proof and commitment - let oo2 = Output::OvertOutput { - value: 32, - blindkey: SecretKey::new(secp, &mut rng), - }; - if let Output::BlindOutput { commit: c2, proof: p2 } = oo2.blind(secp) { - assert!(c2 != commit); - assert!(p2.bytes() != proof.bytes()); - secp.verify_range_proof(c2, p2).unwrap(); - - // checks that swapping the proofs fails the validation - if let Ok(_) = secp.verify_range_proof(commit, p2) { - panic!("verification successful on wrong proof"); - } - } else { - panic!("not a blind output"); - } - } else { - panic!("not a blind output"); - } - } - - #[test] - fn hash_output() { - let ref secp = new_secp(); - let mut rng = OsRng::new().unwrap(); - - let oo = Output::OvertOutput { - value: 42, - blindkey: SecretKey::new(secp, &mut rng), - } - .blind(secp); - let oo2 = Output::OvertOutput { - value: 32, - blindkey: SecretKey::new(secp, &mut rng), - } - .blind(secp); - let h = oo.hash(); - assert!(h != ZERO_HASH); - let h2 = oo2.hash(); - assert!(h != h2); - } - - #[test] - fn blind_tx() { - let ref secp = new_secp(); - let mut rng = OsRng::new().unwrap(); - - let tx = tx2i1o(secp, &mut rng); - let btx = tx.blind(&secp).unwrap(); - btx.verify_sig(&secp).unwrap(); // unwrap will panic if invalid - - // checks that the range proof on our blind output is sufficiently hiding - if let Output::BlindOutput { proof, .. } = btx.outputs[0] { - let info = secp.range_proof_info(proof); - assert!(info.min == 0); - assert!(info.max == u64::max_value()); - } - } - - #[test] - fn tx_hash_diff() { - let ref secp = new_secp(); - let mut rng = OsRng::new().unwrap(); - - let tx1 = tx2i1o(secp, &mut rng); - let mut btx1 = tx1.blind(&secp).unwrap(); - - let tx2 = tx1i1o(secp, &mut rng); - let mut btx2 = tx2.blind(&secp).unwrap(); - - if btx1.hash() == btx2.hash() { - panic!("diff txs have same hash") - } - } - #[test] #[should_panic(expected = "InvalidSecretKey")] fn zero_commit() { diff --git a/core/src/core/ser.rs b/core/src/core/ser.rs index 4651d1b59..f7d726b4d 100644 --- a/core/src/core/ser.rs +++ b/core/src/core/ser.rs @@ -26,8 +26,6 @@ use secp::Signature; use secp::key::SecretKey; use secp::pedersen::{Commitment, RangeProof}; -pub const MAX_IN_OUT_LEN: u64 = 50000; - macro_rules! impl_slice_bytes { ($byteable: ty) => { impl AsFixedBytes for $byteable { @@ -55,197 +53,3 @@ impl AsFixedBytes for RangeProof { } } -/// Implementation of Writeable for a transaction Input, defines how to write -/// an Input as binary. -impl Writeable for core::Input { - fn write(&self, writer: &mut Writer) -> Option { - writer.write_fixed_bytes(&self.output_hash()) - } -} - -/// Implementation of Writeable for a transaction Output, defines how to write -/// an Output as binary. -impl Writeable for core::Output { - fn write(&self, writer: &mut Writer) -> Option { - try_m!(writer.write_fixed_bytes(&self.commitment().unwrap())); - writer.write_vec(&mut self.proof().unwrap().bytes().to_vec()) - } -} - -/// Implementation of Writeable for a fully blinded transaction, defines how to -/// write the transaction as binary. -impl Writeable for core::Transaction { - fn write(&self, writer: &mut Writer) -> Option { - try_m!(writer.write_u64(self.fee)); - try_m!(writer.write_vec(&mut self.zerosig.clone())); - try_m!(writer.write_u64(self.inputs.len() as u64)); - try_m!(writer.write_u64(self.outputs.len() as u64)); - for inp in &self.inputs { - try_m!(inp.write(writer)); - } - for out in &self.outputs { - try_m!(out.write(writer)); - } - None - } -} - -impl Writeable for core::TxProof { - fn write(&self, writer: &mut Writer) -> Option { - try_m!(writer.write_fixed_bytes(&self.remainder)); - writer.write_vec(&mut self.sig.clone()) - } -} - -/// Implementation of Readable for a transaction Input, defines how to read -/// an Input from a binary stream. -impl Readable for core::Input { - fn read(reader: &mut Reader) -> Result { - reader.read_fixed_bytes(32) - .map(|h| core::Input::BareInput { output: hash::Hash::from_vec(h) }) - } -} - -/// Implementation of Readable for a transaction Output, defines how to read -/// an Output from a binary stream. -impl Readable for core::Output { - fn read(reader: &mut Reader) -> Result { - let commit = try!(reader.read_fixed_bytes(33)); - let proof = try!(reader.read_vec()); - Ok(core::Output::BlindOutput { - commit: Commitment::from_vec(commit), - proof: RangeProof::from_vec(proof), - }) - } -} - -/// Implementation of Readable for a transaction, defines how to read a full -/// transaction from a binary stream. -impl Readable for core::Transaction { - fn read(reader: &mut Reader) -> Result { - let fee = try!(reader.read_u64()); - let zerosig = try!(reader.read_vec()); - let input_len = try!(reader.read_u64()); - let output_len = try!(reader.read_u64()); - - // in case a facetious miner sends us more than what we can allocate - if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN { - return Err(Error::TooLargeReadErr("Too many inputs or outputs.".to_string())); - } - - let inputs = try!((0..input_len).map(|_| core::Input::read(reader)).collect()); - let outputs = try!((0..output_len).map(|_| core::Output::read(reader)).collect()); - - Ok(core::Transaction { - fee: fee, - zerosig: zerosig, - inputs: inputs, - outputs: outputs, - ..Default::default() - }) - } -} - -impl Readable for core::TxProof { - fn read(reader: &mut Reader) -> Result { - let remainder = try!(reader.read_fixed_bytes(33)); - let sig = try!(reader.read_vec()); - Ok(core::TxProof { - remainder: Commitment::from_vec(remainder), - sig: sig, - }) - } -} - - -#[cfg(test)] -mod test { - use ser::{serialize, deserialize}; - use secp; - use secp::*; - use secp::key::*; - use core::*; - use core::hash::ZERO_HASH; - use rand::Rng; - use rand::os::OsRng; - - fn new_secp() -> Secp256k1 { - secp::Secp256k1::with_caps(secp::ContextFlag::Commit) - } - - #[test] - fn simple_tx_ser() { - let mut rng = OsRng::new().unwrap(); - let ref secp = new_secp(); - - let tx = tx2i1o(secp, &mut rng); - let btx = tx.blind(&secp).unwrap(); - let mut vec = Vec::new(); - if let Some(e) = serialize(&mut vec, &btx) { - panic!(e); - } - assert!(vec.len() > 5320); - assert!(vec.len() < 5340); - } - - #[test] - fn simple_tx_ser_deser() { - let mut rng = OsRng::new().unwrap(); - let ref secp = new_secp(); - - let tx = tx2i1o(secp, &mut rng); - let mut btx = tx.blind(&secp).unwrap(); - let mut vec = Vec::new(); - if let Some(e) = serialize(&mut vec, &btx) { - panic!(e); - } - // let mut dtx = Transaction::read(&mut BinReader { source: &mut &vec[..] - // }).unwrap(); - let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); - assert_eq!(dtx.fee, 1); - assert_eq!(dtx.inputs.len(), 2); - assert_eq!(dtx.outputs.len(), 1); - assert_eq!(btx.hash(), dtx.hash()); - } - - #[test] - fn tx_double_ser_deser() { - // checks serializing doesn't mess up the tx and produces consistent results - let mut rng = OsRng::new().unwrap(); - let ref secp = new_secp(); - - let tx = tx2i1o(secp, &mut rng); - let mut btx = tx.blind(&secp).unwrap(); - - let mut vec = Vec::new(); - assert!(serialize(&mut vec, &btx).is_none()); - let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); - - let mut vec2 = Vec::new(); - assert!(serialize(&mut vec2, &btx).is_none()); - let mut dtx2: Transaction = deserialize(&mut &vec2[..]).unwrap(); - - assert_eq!(btx.hash(), dtx.hash()); - assert_eq!(dtx.hash(), dtx2.hash()); - } - - // utility producing a transaction with 2 inputs and a single outputs - fn tx2i1o(secp: &Secp256k1, rng: &mut R) -> Transaction { - let outh = ZERO_HASH; - Transaction::new(vec![Input::OvertInput { - output: outh, - value: 10, - blindkey: SecretKey::new(secp, rng), - }, - Input::OvertInput { - output: outh, - value: 11, - blindkey: SecretKey::new(secp, rng), - }], - vec![Output::OvertOutput { - value: 20, - blindkey: SecretKey::new(secp, rng), - }], - 1) - } -} diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs new file mode 100644 index 000000000..581a0efe8 --- /dev/null +++ b/core/src/core/transaction.rs @@ -0,0 +1,553 @@ +// Copyright 2016 The Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Transactions + +use core::Committed; +use core::MerkleRow; +use core::hash::{Hashed, Hash}; +use ser::{self, Reader, Writer, Readable, Writeable}; + +use secp::{self, Secp256k1, Message, Signature}; +use secp::key::SecretKey; +use secp::pedersen::{RangeProof, Commitment}; + +/// 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. +#[derive(Debug, Clone)] +pub struct TxProof { + /// temporarily public + pub remainder: Commitment, + /// temporarily public + pub sig: Vec, +} + +impl Writeable for TxProof { + fn write(&self, writer: &mut Writer) -> Option { + try_m!(writer.write_fixed_bytes(&self.remainder)); + writer.write_vec(&mut self.sig.clone()) + } +} + +impl Readable for TxProof { + fn read(reader: &mut Reader) -> Result { + let remainder = try!(reader.read_fixed_bytes(33)); + let sig = try!(reader.read_vec()); + Ok(TxProof { + remainder: Commitment::from_vec(remainder), + sig: sig, + }) + } +} + +/// A transaction +#[derive(Debug)] +pub struct Transaction { + hash_mem: Option, + pub fee: u64, + pub zerosig: Vec, + pub inputs: Vec, + pub outputs: Vec, +} + +/// Implementation of Writeable for a fully blinded transaction, defines how to +/// write the transaction as binary. +impl Writeable for Transaction { + fn write(&self, writer: &mut Writer) -> Option { + try_m!(writer.write_u64(self.fee)); + try_m!(writer.write_vec(&mut self.zerosig.clone())); + try_m!(writer.write_u64(self.inputs.len() as u64)); + try_m!(writer.write_u64(self.outputs.len() as u64)); + for inp in &self.inputs { + try_m!(inp.write(writer)); + } + for out in &self.outputs { + try_m!(out.write(writer)); + } + None + } +} + +/// Implementation of Readable for a transaction, defines how to read a full +/// transaction from a binary stream. +impl Readable for Transaction { + fn read(reader: &mut Reader) -> Result { + let fee = try!(reader.read_u64()); + let zerosig = try!(reader.read_vec()); + let input_len = try!(reader.read_u64()); + let output_len = try!(reader.read_u64()); + + // in case a facetious miner sends us more than what we can allocate + if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN { + return Err(ser::Error::TooLargeReadErr("Too many inputs or outputs.".to_string())); + } + + let inputs = try!((0..input_len).map(|_| Input::read(reader)).collect()); + let outputs = try!((0..output_len).map(|_| Output::read(reader)).collect()); + + Ok(Transaction { + fee: fee, + zerosig: zerosig, + inputs: inputs, + outputs: outputs, + ..Default::default() + }) + } +} + + +impl Committed for Transaction { + fn inputs_committed(&self) -> &Vec { + &self.inputs + } + fn outputs_committed(&self) -> &Vec { + &self.outputs + } + fn overage(&self) -> i64 { + -(self.fee as i64) + } +} + +impl Default for Transaction { + fn default() -> Transaction { + Transaction::empty() + } +} + +impl Transaction { + /// Creates a new empty transaction (no inputs or outputs, zero fee). + pub fn empty() -> Transaction { + Transaction { + hash_mem: None, + fee: 0, + zerosig: vec![], + inputs: vec![], + outputs: vec![], + } + } + + /// Creates a new transaction initialized with the provided inputs, + /// outputs and fee. + pub fn new(inputs: Vec, outputs: Vec, fee: u64) -> Transaction { + Transaction { + hash_mem: None, + fee: fee, + zerosig: vec![], + inputs: inputs, + outputs: outputs, + } + } + + /// The hash of a transaction is the Merkle tree of its inputs and outputs + /// hashes. None of the rest is required. + fn hash(&mut self) -> Hash { + if let None = self.hash_mem { + self.hash_mem = Some(merkle_inputs_outputs(&self.inputs, &self.outputs)); + } + self.hash_mem.unwrap() + } + + /// Takes a transaction and fully blinds it. Following the MW + /// algorithm: calculates the commitments for each inputs and outputs + /// using the values and blinding factors, takes the blinding factors + /// remainder and uses it for an empty signature. + pub fn blind(&self, secp: &Secp256k1) -> Result { + // we compute the sum of blinding factors to get the k remainder + let remainder = try!(self.blind_sum(secp)); + + // next, blind the inputs and outputs if they haven't been yet + let blind_inputs = map_vec!(self.inputs, |inp| inp.blind(secp)); + let blind_outputs = map_vec!(self.outputs, |out| out.blind(secp)); + + // 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 sig = try!(secp.sign(&msg, &remainder)); + + Ok(Transaction { + hash_mem: None, + fee: self.fee, + zerosig: sig.serialize_der(secp), + inputs: blind_inputs, + outputs: blind_outputs, + }) + } + + /// Compute the sum of blinding factors on all overt inputs and outputs + /// of the transaction to get the k remainder. + pub fn blind_sum(&self, secp: &Secp256k1) -> Result { + let inputs_blinding_fact = filter_map_vec!(self.inputs, |inp| inp.blinding_factor()); + let outputs_blinding_fact = filter_map_vec!(self.outputs, |out| out.blinding_factor()); + + secp.blind_sum(inputs_blinding_fact, outputs_blinding_fact) + } + + /// The verification for a MimbleWimble transaction involves getting the + /// remainder of summing all commitments and using it as a public key + /// to verify the embedded signature. The rational is that if the values + /// sum to zero as they should in r.G + v.H then only k.G the remainder + /// of the sum of r.G should be left. And r.G is the definition of a + /// public key generated using r as a private key. + pub fn verify_sig(&self, secp: &Secp256k1) -> Result { + let rsum = try!(self.sum_commitments(secp)); + + // 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 sig = try!(Signature::from_der(secp, &self.zerosig)); + try!(secp.verify(&msg, &sig, &pubk)); + + Ok(TxProof { + remainder: rsum, + sig: self.zerosig.clone(), + }) + } +} + +/// A transaction input, mostly a reference to an output being spent by the +/// transaction. +#[derive(Debug, Copy, Clone)] +pub enum Input { + BareInput { output: Hash }, + BlindInput { output: Hash, commit: Commitment }, + OvertInput { + output: Hash, + value: u64, + blindkey: SecretKey, + }, +} + +/// Implementation of Writeable for a transaction Input, defines how to write +/// an Input as binary. +impl Writeable for Input { + fn write(&self, writer: &mut Writer) -> Option { + writer.write_fixed_bytes(&self.output_hash()) + } +} + +/// Implementation of Readable for a transaction Input, defines how to read +/// an Input from a binary stream. +impl Readable for Input { + fn read(reader: &mut Reader) -> Result { + reader.read_fixed_bytes(32) + .map(|h| Input::BareInput { output: Hash::from_vec(h) }) + } +} + +impl Input { + pub fn commitment(&self) -> Option { + match self { + &Input::BlindInput { commit, .. } => Some(commit), + _ => None, + } + } + pub fn blind(&self, secp: &Secp256k1) -> Input { + match self { + &Input::OvertInput { output, value, blindkey } => { + let commit = secp.commit(value, blindkey).unwrap(); + Input::BlindInput { + output: output, + commit: commit, + } + } + _ => *self, + } + } + pub fn blinding_factor(&self) -> Option { + match self { + &Input::OvertInput { blindkey, .. } => Some(blindkey), + _ => None, + } + } + pub fn output_hash(&self) -> Hash { + match self { + &Input::BlindInput { output, .. } => output, + &Input::OvertInput { output, .. } => output, + &Input::BareInput { output, .. } => output, + } + } +} + +/// The hash of an input is the hash of the output hash it references. +impl Hashed for Input { + fn bytes(&self) -> Vec { + self.output_hash().to_vec() + } +} + +#[derive(Debug, Copy, Clone)] +pub enum Output { + BlindOutput { + commit: Commitment, + proof: RangeProof, + }, + OvertOutput { value: u64, blindkey: SecretKey }, +} + +/// Implementation of Writeable for a transaction Output, defines how to write +/// an Output as binary. +impl Writeable for Output { + fn write(&self, writer: &mut Writer) -> Option { + try_m!(writer.write_fixed_bytes(&self.commitment().unwrap())); + writer.write_vec(&mut self.proof().unwrap().bytes().to_vec()) + } +} + +/// Implementation of Readable for a transaction Output, defines how to read +/// an Output from a binary stream. +impl Readable for Output { + fn read(reader: &mut Reader) -> Result { + let commit = try!(reader.read_fixed_bytes(33)); + let proof = try!(reader.read_vec()); + Ok(Output::BlindOutput { + commit: Commitment::from_vec(commit), + proof: RangeProof::from_vec(proof), + }) + } +} + +impl Output { + pub fn commitment(&self) -> Option { + match self { + &Output::BlindOutput { commit, .. } => Some(commit), + _ => None, + } + } + pub fn proof(&self) -> Option { + match self { + &Output::BlindOutput { proof, .. } => Some(proof), + _ => None, + } + } + pub fn blinding_factor(&self) -> Option { + match self { + &Output::OvertOutput { blindkey, .. } => Some(blindkey), + _ => None, + } + } + pub fn blind(&self, secp: &Secp256k1) -> Output { + match self { + &Output::OvertOutput { value, blindkey } => { + let commit = secp.commit(value, blindkey).unwrap(); + let rproof = secp.range_proof(0, value, blindkey, commit); + Output::BlindOutput { + commit: commit, + proof: rproof, + } + } + _ => *self, + } + } + /// Validates the range proof using the commitment + pub fn verify_proof(&self, secp: &Secp256k1) -> Result<(), secp::Error> { + match self { + &Output::BlindOutput { commit, proof } => { + secp.verify_range_proof(commit, proof).map(|_| ()) + } + _ => Ok(()), + } + } +} + +/// The hash of an output is the hash of its commitment. +impl Hashed for Output { + fn bytes(&self) -> Vec { + if let &Output::BlindOutput { commit, .. } = self { + return commit.bytes().to_vec(); + } else { + panic!("cannot hash an overt output"); + } + } +} + +/// Utility function to calculate the Merkle root of vectors of inputs and +/// outputs. +pub fn merkle_inputs_outputs(inputs: &Vec, outputs: &Vec) -> Hash { + let mut all_hs = map_vec!(inputs, |inp| inp.hash()); + all_hs.append(&mut map_vec!(outputs, |out| out.hash())); + MerkleRow::new(all_hs).root() +} + +#[cfg(test)] +mod test { + use super::*; + use core::hash::Hashed; + use core::hash::ZERO_HASH; + use core::test::{tx1i1o, tx2i1o}; + use ser::{deserialize, serialize}; + + use secp::{self, Secp256k1}; + use secp::key::SecretKey; + use rand::Rng; + use rand::os::OsRng; + + fn new_secp() -> Secp256k1 { + secp::Secp256k1::with_caps(secp::ContextFlag::Commit) + } + + #[test] + fn simple_tx_ser() { + let mut rng = OsRng::new().unwrap(); + let ref secp = new_secp(); + + let tx = tx2i1o(secp, &mut rng); + let btx = tx.blind(&secp).unwrap(); + let mut vec = Vec::new(); + if let Some(e) = serialize(&mut vec, &btx) { + panic!(e); + } + assert!(vec.len() > 5320); + assert!(vec.len() < 5340); + } + + #[test] + fn simple_tx_ser_deser() { + let mut rng = OsRng::new().unwrap(); + let ref secp = new_secp(); + + let tx = tx2i1o(secp, &mut rng); + let mut btx = tx.blind(&secp).unwrap(); + let mut vec = Vec::new(); + if let Some(e) = serialize(&mut vec, &btx) { + panic!(e); + } + // let mut dtx = Transaction::read(&mut BinReader { source: &mut &vec[..] + // }).unwrap(); + let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); + assert_eq!(dtx.fee, 1); + assert_eq!(dtx.inputs.len(), 2); + assert_eq!(dtx.outputs.len(), 1); + assert_eq!(btx.hash(), dtx.hash()); + } + + #[test] + fn tx_double_ser_deser() { + // checks serializing doesn't mess up the tx and produces consistent results + let mut rng = OsRng::new().unwrap(); + let ref secp = new_secp(); + + let tx = tx2i1o(secp, &mut rng); + let mut btx = tx.blind(&secp).unwrap(); + + let mut vec = Vec::new(); + assert!(serialize(&mut vec, &btx).is_none()); + let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); + + let mut vec2 = Vec::new(); + assert!(serialize(&mut vec2, &btx).is_none()); + let mut dtx2: Transaction = deserialize(&mut &vec2[..]).unwrap(); + + assert_eq!(btx.hash(), dtx.hash()); + assert_eq!(dtx.hash(), dtx2.hash()); + } + + #[test] + fn blind_overt_output() { + let ref secp = new_secp(); + let mut rng = OsRng::new().unwrap(); + + let oo = Output::OvertOutput { + value: 42, + blindkey: SecretKey::new(secp, &mut rng), + }; + if let Output::BlindOutput { commit, proof } = oo.blind(secp) { + // checks the blind output is sane and verifies + assert!(commit.len() > 0); + assert!(proof.bytes().len() > 5000); + secp.verify_range_proof(commit, proof).unwrap(); + + // checks that changing the value changes the proof and commitment + let oo2 = Output::OvertOutput { + value: 32, + blindkey: SecretKey::new(secp, &mut rng), + }; + if let Output::BlindOutput { commit: c2, proof: p2 } = oo2.blind(secp) { + assert!(c2 != commit); + assert!(p2.bytes() != proof.bytes()); + secp.verify_range_proof(c2, p2).unwrap(); + + // checks that swapping the proofs fails the validation + if let Ok(_) = secp.verify_range_proof(commit, p2) { + panic!("verification successful on wrong proof"); + } + } else { + panic!("not a blind output"); + } + } else { + panic!("not a blind output"); + } + } + + #[test] + fn hash_output() { + let ref secp = new_secp(); + let mut rng = OsRng::new().unwrap(); + + let oo = Output::OvertOutput { + value: 42, + blindkey: SecretKey::new(secp, &mut rng), + } + .blind(secp); + let oo2 = Output::OvertOutput { + value: 32, + blindkey: SecretKey::new(secp, &mut rng), + } + .blind(secp); + let h = oo.hash(); + assert!(h != ZERO_HASH); + let h2 = oo2.hash(); + assert!(h != h2); + } + + #[test] + fn blind_tx() { + let ref secp = new_secp(); + let mut rng = OsRng::new().unwrap(); + + let tx = tx2i1o(secp, &mut rng); + let btx = tx.blind(&secp).unwrap(); + btx.verify_sig(&secp).unwrap(); // unwrap will panic if invalid + + // checks that the range proof on our blind output is sufficiently hiding + if let Output::BlindOutput { proof, .. } = btx.outputs[0] { + let info = secp.range_proof_info(proof); + assert!(info.min == 0); + assert!(info.max == u64::max_value()); + } + } + + #[test] + fn tx_hash_diff() { + let ref secp = new_secp(); + let mut rng = OsRng::new().unwrap(); + + let tx1 = tx2i1o(secp, &mut rng); + let mut btx1 = tx1.blind(&secp).unwrap(); + + let tx2 = tx1i1o(secp, &mut rng); + let mut btx2 = tx2.blind(&secp).unwrap(); + + if btx1.hash() == btx2.hash() { + panic!("diff txs have same hash") + } + } +} + + From 4b51610d9aea5fcfd048004d34df909664f49689 Mon Sep 17 00:00:00 2001 From: Merope Riddle Date: Sun, 23 Oct 2016 14:08:41 +0000 Subject: [PATCH 6/6] core: move remaining contents of core/ser.rs into ser.rs --- core/src/core/mod.rs | 1 - core/src/core/ser.rs | 55 -------------------------------------------- core/src/ser.rs | 28 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 56 deletions(-) delete mode 100644 core/src/core/ser.rs diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 6be1e7c1d..cffccadb2 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -19,7 +19,6 @@ pub mod hash; pub mod transaction; #[allow(dead_code)] #[macro_use] -mod ser; pub use self::block::{Block, BlockHeader}; pub use self::transaction::{Transaction, Input, Output, TxProof}; diff --git a/core/src/core/ser.rs b/core/src/core/ser.rs deleted file mode 100644 index f7d726b4d..000000000 --- a/core/src/core/ser.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Binary stream serialization and deserialzation for core types from trusted -//! Write or Read implementations. Issues like starvation or too big sends are -//! expected to be handled upstream. - -use time; - -use std::io::{Write, Read}; -use core::{self, hash}; -use ser::*; - -use secp::Signature; -use secp::key::SecretKey; -use secp::pedersen::{Commitment, RangeProof}; - -macro_rules! impl_slice_bytes { - ($byteable: ty) => { - impl AsFixedBytes for $byteable { - fn as_fixed_bytes(&self) -> &[u8] { - &self[..] - } - } - } -} - -impl_slice_bytes!(SecretKey); -impl_slice_bytes!(Signature); -impl_slice_bytes!(Commitment); -impl_slice_bytes!(Vec); - -impl AsFixedBytes for hash::Hash { - fn as_fixed_bytes(&self) -> &[u8] { - self.to_slice() - } -} - -impl AsFixedBytes for RangeProof { - fn as_fixed_bytes(&self) -> &[u8] { - &self.bytes() - } -} - diff --git a/core/src/ser.rs b/core/src/ser.rs index a0c9a607a..3e08f3835 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -170,3 +170,31 @@ impl<'a> Writer for BinWriter<'a> { self.sink.write_all(bs).err().map(Error::IOErr) } } + +macro_rules! impl_slice_bytes { + ($byteable: ty) => { + impl AsFixedBytes for $byteable { + fn as_fixed_bytes(&self) -> &[u8] { + &self[..] + } + } + } +} + +impl_slice_bytes!(::secp::key::SecretKey); +impl_slice_bytes!(::secp::Signature); +impl_slice_bytes!(::secp::pedersen::Commitment); +impl_slice_bytes!(Vec); + +impl AsFixedBytes for ::core::hash::Hash { + fn as_fixed_bytes(&self) -> &[u8] { + self.to_slice() + } +} + +impl AsFixedBytes for ::secp::pedersen::RangeProof { + fn as_fixed_bytes(&self) -> &[u8] { + &self.bytes() + } +} +