mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Merge pull request #4 from merope07/cleanup-oct2016
Modularize several data structures in core
This commit is contained in:
commit
0855d7b41e
19 changed files with 1367 additions and 1062 deletions
|
@ -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.
|
//! The block chain itself, validates and accepts new blocks, handles reorgs.
|
||||||
|
|
||||||
#![deny(non_upper_case_globals)]
|
#![deny(non_upper_case_globals)]
|
||||||
|
|
|
@ -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.
|
//! Implementation of the chain block acceptance (or refusal) pipeline.
|
||||||
|
|
||||||
use secp;
|
use secp;
|
||||||
|
|
|
@ -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
|
//! Implements storage primitives required by the chain
|
||||||
|
|
||||||
use byteorder::{WriteBytesExt, BigEndian};
|
use byteorder::{WriteBytesExt, BigEndian};
|
||||||
|
|
|
@ -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.
|
//! Base types that the block chain pipeline requires.
|
||||||
|
|
||||||
use core::core::{Hash, Block};
|
use core::core::{Hash, Block};
|
||||||
|
|
|
@ -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_core;
|
||||||
extern crate grin_chain;
|
extern crate grin_chain;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
|
470
core/src/core/block.rs
Normal file
470
core/src/core/block.rs
Normal file
|
@ -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::transaction::merkle_inputs_outputs;
|
||||||
|
use core::{PROOFSIZE, REWARD};
|
||||||
|
use core::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
|
use core::transaction::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<ser::Error> {
|
||||||
|
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<u8> {
|
||||||
|
// 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<Input>,
|
||||||
|
pub outputs: Vec<Output>,
|
||||||
|
pub proofs: Vec<TxProof>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<ser::Error> {
|
||||||
|
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<Block> for Block {
|
||||||
|
fn read(reader: &mut Reader) -> Result<Block, ser::Error> {
|
||||||
|
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<Input> {
|
||||||
|
&self.inputs
|
||||||
|
}
|
||||||
|
fn outputs_committed(&self) -> &Vec<Output> {
|
||||||
|
&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<Block, secp::Error> {
|
||||||
|
|
||||||
|
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::<Vec<Output>>();
|
||||||
|
|
||||||
|
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<R: Rng>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
83
core/src/core/hash.rs
Normal file
83
core/src/core/hash.rs
Normal file
|
@ -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<u8>) -> 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<u8> {
|
||||||
|
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<u8>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sha3(data: Vec<u8>) -> [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<u8> {
|
||||||
|
self.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,34 @@
|
||||||
|
// 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
|
//! Core types
|
||||||
|
|
||||||
|
pub mod block;
|
||||||
|
pub mod hash;
|
||||||
|
pub mod transaction;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[macro_use]
|
#[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};
|
use ser::{Writeable, Writer, Error, ser_vec};
|
||||||
|
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use secp;
|
use secp;
|
||||||
use secp::{Secp256k1, Signature, Message};
|
use secp::{Secp256k1, Signature, Message};
|
||||||
|
@ -18,64 +37,15 @@ use secp::pedersen::*;
|
||||||
|
|
||||||
use tiny_keccak::Keccak;
|
use tiny_keccak::Keccak;
|
||||||
|
|
||||||
|
/// The block subsidy amount
|
||||||
pub const REWARD: u64 = 1_000_000_000;
|
pub const REWARD: u64 = 1_000_000_000;
|
||||||
|
|
||||||
|
/// Block interval, in seconds
|
||||||
pub const BLOCK_TIME_SEC: u8 = 15;
|
pub const BLOCK_TIME_SEC: u8 = 15;
|
||||||
|
|
||||||
|
/// Cuckoo-cycle proof size (cycle length)
|
||||||
pub const PROOFSIZE: usize = 42;
|
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<u8>) -> 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<u8> {
|
|
||||||
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<u8>;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sha3(data: Vec<u8>) -> [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
|
/// Implemented by types that hold inputs and outputs including Pedersen
|
||||||
/// commitments. Handles the collection of the commitments as well as their
|
/// commitments. Handles the collection of the commitments as well as their
|
||||||
/// summing, taking potential explicit overages of fees into account.
|
/// summing, taking potential explicit overages of fees into account.
|
||||||
|
@ -119,14 +89,6 @@ pub trait Committed {
|
||||||
fn overage(&self) -> i64;
|
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<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Proof of work
|
/// Proof of work
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
|
@ -189,520 +151,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<Error> {
|
|
||||||
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<u8> {
|
|
||||||
// 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<Input>,
|
|
||||||
pub outputs: Vec<Output>,
|
|
||||||
pub proofs: Vec<TxProof>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides all information from a block that allows the calculation of total
|
|
||||||
/// Pedersen commitment.
|
|
||||||
impl Committed for Block {
|
|
||||||
fn inputs_committed(&self) -> &Vec<Input> {
|
|
||||||
&self.inputs
|
|
||||||
}
|
|
||||||
fn outputs_committed(&self) -> &Vec<Output> {
|
|
||||||
&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<Block, secp::Error> {
|
|
||||||
|
|
||||||
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::<Vec<Output>>();
|
|
||||||
|
|
||||||
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<Hash>,
|
|
||||||
pub fee: u64,
|
|
||||||
pub zerosig: Vec<u8>,
|
|
||||||
pub inputs: Vec<Input>,
|
|
||||||
pub outputs: Vec<Output>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Committed for Transaction {
|
|
||||||
fn inputs_committed(&self) -> &Vec<Input> {
|
|
||||||
&self.inputs
|
|
||||||
}
|
|
||||||
fn outputs_committed(&self) -> &Vec<Output> {
|
|
||||||
&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<Input>, outputs: Vec<Output>, 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<Transaction, secp::Error> {
|
|
||||||
// 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<SecretKey, secp::Error> {
|
|
||||||
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<TxProof, secp::Error> {
|
|
||||||
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<Commitment> {
|
|
||||||
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<SecretKey> {
|
|
||||||
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<u8> {
|
|
||||||
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<Commitment> {
|
|
||||||
match self {
|
|
||||||
&Output::BlindOutput { commit, .. } => Some(commit),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn proof(&self) -> Option<RangeProof> {
|
|
||||||
match self {
|
|
||||||
&Output::BlindOutput { proof, .. } => Some(proof),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn blinding_factor(&self) -> Option<SecretKey> {
|
|
||||||
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<u8> {
|
|
||||||
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<Input>, outputs: &Vec<Output>) -> 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
|
/// Two hashes that will get hashed together in a Merkle tree to build the next
|
||||||
/// level up.
|
/// level up.
|
||||||
struct HPair(Hash, Hash);
|
struct HPair(Hash, Hash);
|
||||||
|
@ -736,7 +184,7 @@ impl MerkleRow {
|
||||||
}
|
}
|
||||||
fn root(&self) -> Hash {
|
fn root(&self) -> Hash {
|
||||||
if self.0.len() == 0 {
|
if self.0.len() == 0 {
|
||||||
Hash(sha3(vec![]))
|
vec![].hash()
|
||||||
} else if self.0.len() == 1 {
|
} else if self.0.len() == 1 {
|
||||||
self.0[0].hash()
|
self.0[0].hash()
|
||||||
} else {
|
} else {
|
||||||
|
@ -748,6 +196,7 @@ impl MerkleRow {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use super::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
use secp;
|
use secp;
|
||||||
use secp::Secp256k1;
|
use secp::Secp256k1;
|
||||||
use secp::key::SecretKey;
|
use secp::key::SecretKey;
|
||||||
|
@ -758,104 +207,6 @@ mod test {
|
||||||
secp::Secp256k1::with_caps(secp::ContextFlag::Commit)
|
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();
|
|
||||||
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]
|
#[test]
|
||||||
#[should_panic(expected = "InvalidSecretKey")]
|
#[should_panic(expected = "InvalidSecretKey")]
|
||||||
fn zero_commit() {
|
fn zero_commit() {
|
||||||
|
@ -921,60 +272,8 @@ mod test {
|
||||||
b.verify(&secp).unwrap();
|
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
|
// utility producing a transaction with 2 inputs and a single outputs
|
||||||
fn tx2i1o<R: Rng>(secp: &Secp256k1, rng: &mut R) -> Transaction {
|
pub fn tx2i1o<R: Rng>(secp: &Secp256k1, rng: &mut R) -> Transaction {
|
||||||
let outh = ZERO_HASH;
|
let outh = ZERO_HASH;
|
||||||
Transaction::new(vec![Input::OvertInput {
|
Transaction::new(vec![Input::OvertInput {
|
||||||
output: outh,
|
output: outh,
|
||||||
|
@ -994,7 +293,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility producing a transaction with a single input and output
|
// utility producing a transaction with a single input and output
|
||||||
fn tx1i1o<R: Rng>(secp: &Secp256k1, rng: &mut R) -> Transaction {
|
pub fn tx1i1o<R: Rng>(secp: &Secp256k1, rng: &mut R) -> Transaction {
|
||||||
let outh = ZERO_HASH;
|
let outh = ZERO_HASH;
|
||||||
Transaction::new(vec![Input::OvertInput {
|
Transaction::new(vec![Input::OvertInput {
|
||||||
output: outh,
|
output: outh,
|
||||||
|
@ -1007,22 +306,4 @@ mod test {
|
||||||
}],
|
}],
|
||||||
1)
|
1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility producing a transaction that spends the above
|
|
||||||
fn txspend1i1o<R: Rng>(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!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,308 +0,0 @@
|
||||||
//! 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;
|
|
||||||
use ser::*;
|
|
||||||
|
|
||||||
use secp::Signature;
|
|
||||||
use secp::key::SecretKey;
|
|
||||||
use secp::pedersen::{Commitment, RangeProof};
|
|
||||||
|
|
||||||
const MAX_IN_OUT_LEN: u64 = 50000;
|
|
||||||
|
|
||||||
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<u8>);
|
|
||||||
|
|
||||||
impl AsFixedBytes for core::Hash {
|
|
||||||
fn as_fixed_bytes(&self) -> &[u8] {
|
|
||||||
self.to_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsFixedBytes for RangeProof {
|
|
||||||
fn as_fixed_bytes(&self) -> &[u8] {
|
|
||||||
&self.bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<Error> {
|
|
||||||
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<Error> {
|
|
||||||
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<Error> {
|
|
||||||
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<Error> {
|
|
||||||
try_m!(writer.write_fixed_bytes(&self.remainder));
|
|
||||||
writer.write_vec(&mut self.sig.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<Error> {
|
|
||||||
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<core::Input> for core::Input {
|
|
||||||
fn read(reader: &mut Reader) -> Result<core::Input, Error> {
|
|
||||||
reader.read_fixed_bytes(32)
|
|
||||||
.map(|h| core::Input::BareInput { output: core::Hash::from_vec(h) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation of Readable for a transaction Output, defines how to read
|
|
||||||
/// an Output from a binary stream.
|
|
||||||
impl Readable<core::Output> for core::Output {
|
|
||||||
fn read(reader: &mut Reader) -> Result<core::Output, Error> {
|
|
||||||
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<core::Transaction> for core::Transaction {
|
|
||||||
fn read(reader: &mut Reader) -> Result<core::Transaction, Error> {
|
|
||||||
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<core::TxProof> for core::TxProof {
|
|
||||||
fn read(reader: &mut Reader) -> Result<core::TxProof, Error> {
|
|
||||||
let remainder = try!(reader.read_fixed_bytes(33));
|
|
||||||
let sig = try!(reader.read_vec());
|
|
||||||
Ok(core::TxProof {
|
|
||||||
remainder: Commitment::from_vec(remainder),
|
|
||||||
sig: sig,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation of Readable for a block, defines how to read a full block
|
|
||||||
/// from a binary stream.
|
|
||||||
impl Readable<core::Block> for core::Block {
|
|
||||||
fn read(reader: &mut Reader) -> Result<core::Block, Error> {
|
|
||||||
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: core::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),
|
|
||||||
total_fees: total_fees,
|
|
||||||
pow: core::Proof(pow),
|
|
||||||
nonce: nonce,
|
|
||||||
},
|
|
||||||
inputs: inputs,
|
|
||||||
outputs: outputs,
|
|
||||||
proofs: proofs,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use ser::{serialize, deserialize};
|
|
||||||
use secp;
|
|
||||||
use secp::*;
|
|
||||||
use secp::key::*;
|
|
||||||
use core::*;
|
|
||||||
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<R: Rng>(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)
|
|
||||||
}
|
|
||||||
}
|
|
553
core/src/core/transaction.rs
Normal file
553
core/src/core/transaction.rs
Normal file
|
@ -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<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Writeable for TxProof {
|
||||||
|
fn write(&self, writer: &mut Writer) -> Option<ser::Error> {
|
||||||
|
try_m!(writer.write_fixed_bytes(&self.remainder));
|
||||||
|
writer.write_vec(&mut self.sig.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Readable<TxProof> for TxProof {
|
||||||
|
fn read(reader: &mut Reader) -> Result<TxProof, ser::Error> {
|
||||||
|
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<Hash>,
|
||||||
|
pub fee: u64,
|
||||||
|
pub zerosig: Vec<u8>,
|
||||||
|
pub inputs: Vec<Input>,
|
||||||
|
pub outputs: Vec<Output>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<ser::Error> {
|
||||||
|
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<Transaction> for Transaction {
|
||||||
|
fn read(reader: &mut Reader) -> Result<Transaction, ser::Error> {
|
||||||
|
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<Input> {
|
||||||
|
&self.inputs
|
||||||
|
}
|
||||||
|
fn outputs_committed(&self) -> &Vec<Output> {
|
||||||
|
&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<Input>, outputs: Vec<Output>, 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<Transaction, secp::Error> {
|
||||||
|
// 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<SecretKey, secp::Error> {
|
||||||
|
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<TxProof, secp::Error> {
|
||||||
|
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<ser::Error> {
|
||||||
|
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<Input> for Input {
|
||||||
|
fn read(reader: &mut Reader) -> Result<Input, ser::Error> {
|
||||||
|
reader.read_fixed_bytes(32)
|
||||||
|
.map(|h| Input::BareInput { output: Hash::from_vec(h) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub fn commitment(&self) -> Option<Commitment> {
|
||||||
|
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<SecretKey> {
|
||||||
|
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<u8> {
|
||||||
|
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<ser::Error> {
|
||||||
|
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<Output> for Output {
|
||||||
|
fn read(reader: &mut Reader) -> Result<Output, ser::Error> {
|
||||||
|
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<Commitment> {
|
||||||
|
match self {
|
||||||
|
&Output::BlindOutput { commit, .. } => Some(commit),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn proof(&self) -> Option<RangeProof> {
|
||||||
|
match self {
|
||||||
|
&Output::BlindOutput { proof, .. } => Some(proof),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn blinding_factor(&self) -> Option<SecretKey> {
|
||||||
|
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<u8> {
|
||||||
|
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<Input>, outputs: &Vec<Output>) -> 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
//! Definition of the genesis block. Placeholder for now.
|
||||||
|
|
||||||
use time;
|
use time;
|
||||||
|
@ -17,7 +31,7 @@ pub fn genesis() -> core::Block {
|
||||||
core::Block {
|
core::Block {
|
||||||
header: core::BlockHeader {
|
header: core::BlockHeader {
|
||||||
height: 0,
|
height: 0,
|
||||||
previous: core::ZERO_HASH,
|
previous: core::hash::ZERO_HASH,
|
||||||
timestamp: time::Tm {
|
timestamp: time::Tm {
|
||||||
tm_year: 1997,
|
tm_year: 1997,
|
||||||
tm_mon: 7,
|
tm_mon: 7,
|
||||||
|
@ -25,8 +39,8 @@ pub fn genesis() -> core::Block {
|
||||||
..time::empty_tm()
|
..time::empty_tm()
|
||||||
},
|
},
|
||||||
td: 0,
|
td: 0,
|
||||||
utxo_merkle: core::Hash::from_vec(empty_h.to_vec()),
|
utxo_merkle: core::hash::Hash::from_vec(empty_h.to_vec()),
|
||||||
tx_merkle: core::Hash::from_vec(empty_h.to_vec()),
|
tx_merkle: core::hash::Hash::from_vec(empty_h.to_vec()),
|
||||||
total_fees: 0,
|
total_fees: 0,
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
pow: core::Proof::zero(), // TODO get actual PoW solution
|
pow: core::Proof::zero(), // TODO get actual PoW solution
|
||||||
|
|
|
@ -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.
|
//! Implementation of the MimbleWimble paper.
|
||||||
//! https://download.wpsoftware.net/bitcoin/wizardry/mimblewimble.txt
|
//! https://download.wpsoftware.net/bitcoin/wizardry/mimblewimble.txt
|
||||||
|
|
||||||
|
|
|
@ -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
|
//! Generic macros used here and there to simplify and make code more
|
||||||
//! readable.
|
//! readable.
|
||||||
|
|
||||||
|
|
|
@ -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
|
//! 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
|
//! 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
|
//! simple miner is included, mostly for testing purposes. John Tromp's Tomato
|
||||||
|
|
|
@ -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
|
//! The proof of work needs to strike a balance between fast header
|
||||||
//! verification to avoid DoS attacks and difficulty for block verifiers to
|
//! verification to avoid DoS attacks and difficulty for block verifiers to
|
||||||
//! build new blocks. In addition, mining new blocks should also be as
|
//! build new blocks. In addition, mining new blocks should also be as
|
||||||
|
@ -13,7 +27,8 @@ mod cuckoo;
|
||||||
|
|
||||||
use time;
|
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 pow::cuckoo::{Cuckoo, Miner, Error};
|
||||||
|
|
||||||
use ser;
|
use ser;
|
||||||
|
@ -161,7 +176,8 @@ fn pow_size(b: &Block, target: Proof, sizeshift: u32) -> Result<(Proof, u64), Er
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use core::{BlockHeader, Hash, Proof};
|
use core::{BlockHeader, Proof};
|
||||||
|
use core::hash::Hash;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use genesis;
|
use genesis;
|
||||||
|
|
||||||
|
|
|
@ -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
|
//! Simple implementation of the siphash 2-4 hashing function from
|
||||||
//! Jean-Philippe Aumasson and Daniel J. Bernstein.
|
//! Jean-Philippe Aumasson and Daniel J. Bernstein.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
//! Serialization and deserialization layer specialized for binary encoding.
|
||||||
//! Ensures consistency and safety. Basically a minimal subset or
|
//! Ensures consistency and safety. Basically a minimal subset or
|
||||||
//! rustc_serialize customized for our need.
|
//! rustc_serialize customized for our need.
|
||||||
|
@ -156,3 +170,31 @@ impl<'a> Writer for BinWriter<'a> {
|
||||||
self.sink.write_all(bs).err().map(Error::IOErr)
|
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<u8>);
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
//! Main crate putting together all the other crates that compose Grin into a
|
||||||
//! binary.
|
//! binary.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
//! Storage of core types using RocksDB.
|
||||||
|
|
||||||
#![deny(non_upper_case_globals)]
|
#![deny(non_upper_case_globals)]
|
||||||
|
|
Loading…
Reference in a new issue