Initial import.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,308 @@
|
||||
//! 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Definition of the genesis block. Placeholder for now.
|
||||
|
||||
use time;
|
||||
|
||||
use core;
|
||||
|
||||
use tiny_keccak::Keccak;
|
||||
|
||||
// Genesis block definition. It has no rewards, no inputs, no outputs, no
|
||||
// fees and a height of zero.
|
||||
pub fn genesis() -> core::Block {
|
||||
let mut sha3 = Keccak::new_sha3_256();
|
||||
let mut empty_h = [0; 32];
|
||||
sha3.update(&[]);
|
||||
sha3.finalize(&mut empty_h);
|
||||
|
||||
core::Block {
|
||||
header: core::BlockHeader {
|
||||
height: 0,
|
||||
previous: core::ZERO_HASH,
|
||||
timestamp: time::Tm {
|
||||
tm_year: 1997,
|
||||
tm_mon: 7,
|
||||
tm_mday: 4,
|
||||
..time::empty_tm()
|
||||
},
|
||||
td: 0,
|
||||
utxo_merkle: core::Hash::from_vec(empty_h.to_vec()),
|
||||
tx_merkle: core::Hash::from_vec(empty_h.to_vec()),
|
||||
total_fees: 0,
|
||||
nonce: 0,
|
||||
pow: core::Proof::zero(), // TODO get actual PoW solution
|
||||
},
|
||||
inputs: vec![],
|
||||
outputs: vec![],
|
||||
proofs: vec![],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//! Implementation of the MimbleWimble paper.
|
||||
//! https://download.wpsoftware.net/bitcoin/wizardry/mimblewimble.txt
|
||||
|
||||
#![deny(non_upper_case_globals)]
|
||||
#![deny(non_camel_case_types)]
|
||||
#![deny(non_snake_case)]
|
||||
#![deny(unused_mut)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate crypto;
|
||||
extern crate rand;
|
||||
extern crate secp256k1zkp as secp;
|
||||
extern crate time;
|
||||
extern crate tiny_keccak;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod core;
|
||||
pub mod genesis;
|
||||
pub mod pow;
|
||||
pub mod ser;
|
||||
// mod chain;
|
||||
@@ -0,0 +1,59 @@
|
||||
//! Generic macros used here and there to simplify and make code more
|
||||
//! readable.
|
||||
|
||||
/// Eliminates some of the verbosity in having iter and collect
|
||||
/// around every map call.
|
||||
macro_rules! map_vec {
|
||||
($thing:expr, $mapfn:expr ) => {
|
||||
$thing.iter()
|
||||
.map($mapfn)
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as map_vec when the map closure returns Results. Makes sure the
|
||||
/// results are "pushed up" and wraps with a try.
|
||||
macro_rules! try_map_vec {
|
||||
($thing:expr, $mapfn:expr ) => {
|
||||
try!($thing.iter()
|
||||
.map($mapfn)
|
||||
.collect::<Result<Vec<_>, _>>());
|
||||
}
|
||||
}
|
||||
|
||||
/// Eliminates some of the verbosity in having iter and collect
|
||||
/// around every fitler_map call.
|
||||
macro_rules! filter_map_vec {
|
||||
($thing:expr, $mapfn:expr ) => {
|
||||
$thing.iter()
|
||||
.filter_map($mapfn)
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows the conversion of an expression that doesn't return anything to one
|
||||
/// that returns the provided identifier.
|
||||
/// Example:
|
||||
/// let foo = vec![1,2,3]
|
||||
/// println!(tee!(foo, foo.append(vec![3,4,5]))
|
||||
macro_rules! tee {
|
||||
($thing:ident, $thing_expr:expr) => {
|
||||
{
|
||||
$thing_expr;
|
||||
$thing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple equivalent of try! but for a Maybe<Error>. Motivated mostly by the
|
||||
/// io package and our serialization as an alternative to silly Result<(),
|
||||
/// Error>.
|
||||
#[macro_export]
|
||||
macro_rules! try_m {
|
||||
($trying:expr) => {
|
||||
let tried = $trying;
|
||||
if let Some(_) = tried {
|
||||
return tried;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
//! Implementation of Cuckoo Cycle designed by John Tromp. Ported to Rust from
|
||||
//! the C and Java code at https://github.com/tromp/cuckoo. Note that only the
|
||||
//! simple miner is included, mostly for testing purposes. John Tromp's Tomato
|
||||
//! miner will be much faster in almost every environment.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha256;
|
||||
|
||||
use pow::siphash::siphash24;
|
||||
|
||||
const PROOFSIZE: usize = 42;
|
||||
const MAXPATHLEN: usize = 8192;
|
||||
|
||||
/// A Cuckoo proof representing the nonces for a cycle of the right size.
|
||||
pub struct Proof([u32; PROOFSIZE]);
|
||||
|
||||
impl fmt::Debug for Proof {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(f, "Cuckoo("));
|
||||
for (i, val) in self.0[..].iter().enumerate() {
|
||||
try!(write!(f, "{:x}", val));
|
||||
if i < PROOFSIZE - 1 {
|
||||
write!(f, " ");
|
||||
}
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
impl PartialEq for Proof {
|
||||
fn eq(&self, other: &Proof) -> bool {
|
||||
self.0[..] == other.0[..]
|
||||
}
|
||||
}
|
||||
impl Eq for Proof {}
|
||||
impl Clone for Proof {
|
||||
#[inline]
|
||||
fn clone(&self) -> Proof {
|
||||
let mut cp = [0; PROOFSIZE];
|
||||
for (i, n) in self.0.iter().enumerate() {
|
||||
cp[i] = *n;
|
||||
}
|
||||
Proof(cp)
|
||||
}
|
||||
}
|
||||
|
||||
impl Proof {
|
||||
fn to_u64s(&self) -> Vec<u64> {
|
||||
let mut nonces = Vec::with_capacity(PROOFSIZE);
|
||||
for n in self.0.iter() {
|
||||
nonces.push(*n as u64);
|
||||
}
|
||||
nonces
|
||||
}
|
||||
}
|
||||
|
||||
/// An edge in the Cuckoo graph, simply references two u64 nodes.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
struct Edge {
|
||||
u: u64,
|
||||
v: u64,
|
||||
}
|
||||
|
||||
pub struct Cuckoo {
|
||||
mask: u64,
|
||||
size: u64,
|
||||
v: [u64; 4],
|
||||
}
|
||||
|
||||
impl Cuckoo {
|
||||
/// Initializes a new Cuckoo Cycle setup, using the provided byte array to
|
||||
/// generate a seed. In practice for PoW applications the byte array is a
|
||||
/// serialized block header.
|
||||
pub fn new(header: &[u8], sizeshift: u32) -> Cuckoo {
|
||||
let size = 1 << sizeshift;
|
||||
let mut hasher = Sha256::new();
|
||||
let mut hashed = [0; 32];
|
||||
hasher.input(header);
|
||||
hasher.result(&mut hashed);
|
||||
|
||||
let k0 = u8_to_u64(hashed, 0);
|
||||
let k1 = u8_to_u64(hashed, 8);
|
||||
let mut v = [0; 4];
|
||||
v[0] = k0 ^ 0x736f6d6570736575;
|
||||
v[1] = k1 ^ 0x646f72616e646f6d;
|
||||
v[2] = k0 ^ 0x6c7967656e657261;
|
||||
v[3] = k1 ^ 0x7465646279746573;
|
||||
// println!("{:?} {:?} {:?} {:?}", v[0], v[1], v[2], v[3]);
|
||||
Cuckoo {
|
||||
v: v,
|
||||
size: size,
|
||||
mask: (1 << sizeshift) / 2 - 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a node in the cuckoo graph generated from our seed. A node is
|
||||
/// simply materialized as a u64 from a nonce and an offset (generally 0 or
|
||||
/// 1).
|
||||
pub fn new_node(&self, nonce: u64, uorv: u64) -> u64 {
|
||||
return ((siphash24(self.v, 2 * nonce + uorv) & self.mask) << 1) | uorv;
|
||||
}
|
||||
|
||||
/// Creates a new edge in the cuckoo graph generated by our seed from a
|
||||
/// nonce. Generates two node coordinates from the nonce and links them
|
||||
/// together.
|
||||
pub fn new_edge(&self, nonce: u64) -> Edge {
|
||||
Edge {
|
||||
u: self.new_node(nonce, 0),
|
||||
v: self.new_node(nonce, 1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Assuming increasing nonces all smaller than easiness, verifies the
|
||||
/// nonces form a cycle in a Cuckoo graph. Each nonce generates an edge, we
|
||||
/// build the nodes on both side of that edge and count the connections.
|
||||
pub fn verify(&self, proof: Proof, ease: u64) -> bool {
|
||||
let easiness = ease * (self.size as u64) / 100;
|
||||
let nonces = proof.to_u64s();
|
||||
let mut us = [0; PROOFSIZE];
|
||||
let mut vs = [0; PROOFSIZE];
|
||||
for n in 0..PROOFSIZE {
|
||||
if nonces[n] >= easiness || (n != 0 && nonces[n] <= nonces[n - 1]) {
|
||||
return false;
|
||||
}
|
||||
us[n] = self.new_node(nonces[n], 0);
|
||||
vs[n] = self.new_node(nonces[n], 1);
|
||||
}
|
||||
let mut i = 0;
|
||||
let mut count = PROOFSIZE;
|
||||
loop {
|
||||
let mut j = i;
|
||||
for k in 0..PROOFSIZE {
|
||||
// find unique other j with same vs[j]
|
||||
if k != i && vs[k] == vs[i] {
|
||||
if j != i {
|
||||
return false;
|
||||
}
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
if j == i {
|
||||
return false;
|
||||
}
|
||||
i = j;
|
||||
for k in 0..PROOFSIZE {
|
||||
// find unique other i with same us[i]
|
||||
if k != j && us[k] == us[j] {
|
||||
if i != j {
|
||||
return false;
|
||||
}
|
||||
i = k;
|
||||
}
|
||||
}
|
||||
if i == j {
|
||||
return false;
|
||||
}
|
||||
count -= 2;
|
||||
if i == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
count == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
PathError,
|
||||
NoSolutionError,
|
||||
}
|
||||
|
||||
/// Miner for the Cuckoo Cycle algorithm. While the verifier will work for
|
||||
/// graph sizes up to a u64, the miner is limited to u32 to be more memory
|
||||
/// compact (so shift <= 32). Non-optimized for now and and so mostly used for
|
||||
/// tests, being impractical with sizes greater than 2^22.
|
||||
pub struct Miner {
|
||||
easiness: u64,
|
||||
size: usize,
|
||||
cuckoo: Cuckoo,
|
||||
graph: Vec<u32>,
|
||||
}
|
||||
|
||||
/// What type of cycle we have found?
|
||||
enum CycleSol {
|
||||
/// A cycle of the right length is a valid proof.
|
||||
ValidProof([u32; PROOFSIZE]),
|
||||
/// A cycle of the wrong length is great, but not a proof.
|
||||
InvalidCycle(usize),
|
||||
/// No cycles have been found.
|
||||
NoCycle,
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
pub fn new(header: &[u8], ease: u64, sizeshift: u32) -> Miner {
|
||||
let cuckoo = Cuckoo::new(header, sizeshift);
|
||||
let size = 1 << sizeshift;
|
||||
let graph = vec![0; size + 1];
|
||||
let easiness = ease * (size as u64) / 100;
|
||||
Miner {
|
||||
easiness: easiness,
|
||||
size: size,
|
||||
cuckoo: cuckoo,
|
||||
graph: graph,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mine(&mut self) -> Result<Proof, Error> {
|
||||
let mut us = [0; MAXPATHLEN];
|
||||
let mut vs = [0; MAXPATHLEN];
|
||||
// println!("{}", self.easiness);
|
||||
let mut m = 0;
|
||||
for nonce in 0..self.easiness {
|
||||
m += 1;
|
||||
// println!("- {}", nonce);
|
||||
us[0] = self.cuckoo.new_node(nonce, 0) as u32;
|
||||
vs[0] = self.cuckoo.new_node(nonce, 1) as u32;
|
||||
let u = self.graph[us[0] as usize];
|
||||
let v = self.graph[vs[0] as usize];
|
||||
if us[0] == 0 {
|
||||
continue; // ignore duplicate edges
|
||||
}
|
||||
// println!("{}, {}, {}, {}", us[0], vs[0], u, v);
|
||||
// println!(" ^{}, {}", us[0], vs[0]);
|
||||
// println!(" _{}, {}", u, v);
|
||||
let nu = try!(if nonce == 481921 {
|
||||
self.path_p(u, &mut us)
|
||||
} else {
|
||||
self.path(u, &mut us)
|
||||
}) as usize;
|
||||
let nv = try!(if nonce == 481921 {
|
||||
self.path_p(v, &mut vs)
|
||||
} else {
|
||||
self.path(v, &mut vs)
|
||||
}) as usize;
|
||||
// println!(" &{}, {}", nu, nv);
|
||||
|
||||
let sol = self.find_sol(nu, &us, nv, &vs);
|
||||
match sol {
|
||||
CycleSol::ValidProof(res) => return Ok(Proof(res)),
|
||||
CycleSol::InvalidCycle(_) => continue,
|
||||
CycleSol::NoCycle => {
|
||||
self.update_graph(nu, &us, nv, &vs);
|
||||
}
|
||||
}
|
||||
}
|
||||
// println!("== {}", m);
|
||||
Err(Error::NoSolutionError)
|
||||
}
|
||||
|
||||
fn path(&self, mut u: u32, us: &mut [u32]) -> Result<u32, Error> {
|
||||
let mut nu = 0;
|
||||
while u != 0 {
|
||||
nu += 1;
|
||||
if nu >= MAXPATHLEN {
|
||||
while nu != 0 && us[(nu - 1) as usize] != u {
|
||||
nu -= 1;
|
||||
}
|
||||
return Err(Error::PathError);
|
||||
}
|
||||
us[nu as usize] = u;
|
||||
u = self.graph[u as usize];
|
||||
}
|
||||
Ok(nu as u32)
|
||||
}
|
||||
|
||||
fn path_p(&self, mut u: u32, us: &mut [u32]) -> Result<u32, Error> {
|
||||
let mut nu = 0;
|
||||
while u != 0 {
|
||||
// println!("{}", u);
|
||||
nu += 1;
|
||||
if nu >= MAXPATHLEN {
|
||||
while nu != 0 && us[(nu - 1) as usize] != u {
|
||||
nu -= 1;
|
||||
}
|
||||
return Err(Error::PathError);
|
||||
}
|
||||
us[nu as usize] = u;
|
||||
u = self.graph[u as usize];
|
||||
}
|
||||
Ok(nu as u32)
|
||||
}
|
||||
|
||||
fn update_graph(&mut self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) {
|
||||
if nu < nv {
|
||||
while nu != 0 {
|
||||
nu -= 1;
|
||||
// self.graph[us[nu + 1] as usize] = us[nu];
|
||||
self.set_graph(us[nu + 1] as usize, us[nu]);
|
||||
}
|
||||
// self.graph[us[0] as usize] = vs[0];
|
||||
self.set_graph(us[0] as usize, vs[0]);
|
||||
} else {
|
||||
while nv != 0 {
|
||||
nv -= 1;
|
||||
// self.graph[vs[nv + 1] as usize] = vs[nv];
|
||||
self.set_graph(vs[nv + 1] as usize, vs[nv]);
|
||||
}
|
||||
// self.graph[vs[0] as usize] = us[0];
|
||||
self.set_graph(vs[0] as usize, us[0]);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_graph(&mut self, idx: usize, val: u32) {
|
||||
// println!("set {} = {}", idx, val);
|
||||
self.graph[idx] = val;
|
||||
}
|
||||
|
||||
fn find_sol(&self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) -> CycleSol {
|
||||
if us[nu] == vs[nv] {
|
||||
let min = cmp::min(nu, nv);
|
||||
nu -= min;
|
||||
nv -= min;
|
||||
while us[nu] != vs[nv] {
|
||||
nu += 1;
|
||||
nv += 1;
|
||||
}
|
||||
if nu + nv + 1 == PROOFSIZE {
|
||||
self.solution(&us, nu as u32, &vs, nv as u32)
|
||||
} else {
|
||||
CycleSol::InvalidCycle(nu + nv + 1)
|
||||
}
|
||||
} else {
|
||||
CycleSol::NoCycle
|
||||
}
|
||||
}
|
||||
|
||||
fn solution(&self, us: &[u32], mut nu: u32, vs: &[u32], mut nv: u32) -> CycleSol {
|
||||
let mut cycle = HashSet::new();
|
||||
cycle.insert(Edge {
|
||||
u: us[0] as u64,
|
||||
v: vs[0] as u64,
|
||||
});
|
||||
while nu != 0 {
|
||||
// u's in even position; v's in odd
|
||||
nu -= 1;
|
||||
cycle.insert(Edge {
|
||||
u: us[((nu + 1) & !1) as usize] as u64,
|
||||
v: us[(nu | 1) as usize] as u64,
|
||||
});
|
||||
}
|
||||
while nv != 0 {
|
||||
// u's in odd position; v's in even
|
||||
nv -= 1;
|
||||
cycle.insert(Edge {
|
||||
u: vs[(nv | 1) as usize] as u64,
|
||||
v: vs[((nv + 1) & !1) as usize] as u64,
|
||||
});
|
||||
}
|
||||
let mut n = 0;
|
||||
let mut sol = [0; PROOFSIZE];
|
||||
for nonce in 0..self.easiness {
|
||||
let edge = self.cuckoo.new_edge(nonce);
|
||||
if cycle.contains(&edge) {
|
||||
sol[n] = nonce as u32;
|
||||
n += 1;
|
||||
cycle.remove(&edge);
|
||||
}
|
||||
}
|
||||
return if n == PROOFSIZE {
|
||||
CycleSol::ValidProof(sol)
|
||||
} else {
|
||||
CycleSol::NoCycle
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Utility to transform a 8 bytes of a byte array into a u64.
|
||||
fn u8_to_u64(p: [u8; 32], i: usize) -> u64 {
|
||||
(p[i] as u64) | (p[i + 1] as u64) << 8 | (p[i + 2] as u64) << 16 | (p[i + 3] as u64) << 24 |
|
||||
(p[i + 4] as u64) << 32 | (p[i + 5] as u64) << 40 |
|
||||
(p[i + 6] as u64) << 48 | (p[i + 7] as u64) << 56
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
static V1: Proof = Proof([0xe13, 0x410c, 0x7974, 0x8317, 0xb016, 0xb992, 0xe3c8, 0x1038a,
|
||||
0x116f0, 0x15ed2, 0x165a2, 0x17793, 0x17dd1, 0x1f885, 0x20932,
|
||||
0x20936, 0x2171b, 0x28968, 0x2b184, 0x30b8e, 0x31d28, 0x35782,
|
||||
0x381ea, 0x38321, 0x3b414, 0x3e14b, 0x43615, 0x49a51, 0x4a319,
|
||||
0x58271, 0x5dbb9, 0x5dbcf, 0x62db4, 0x653d2, 0x655f6, 0x66382,
|
||||
0x7057d, 0x765b0, 0x79c7c, 0x83167, 0x86e7b, 0x8a5f4]);
|
||||
static V2: Proof = Proof([0x33b8, 0x3fd9, 0x8f2b, 0xba0d, 0x11e2d, 0x1d51d, 0x2786e, 0x29625,
|
||||
0x2a862, 0x2a972, 0x2e6d7, 0x319df, 0x37ce7, 0x3f771, 0x4373b,
|
||||
0x439b7, 0x48626, 0x49c7d, 0x4a6f1, 0x4a808, 0x4e518, 0x519e3,
|
||||
0x526bb, 0x54988, 0x564e9, 0x58a6c, 0x5a4dd, 0x63fa2, 0x68ad1,
|
||||
0x69e52, 0x6bf53, 0x70841, 0x76343, 0x763a4, 0x79681, 0x7d006,
|
||||
0x7d633, 0x7eebe, 0x7fe7c, 0x811fa, 0x863c1, 0x8b149]);
|
||||
static V3: Proof = Proof([0x24ae, 0x5180, 0x9f3d, 0xd379, 0x102c9, 0x15787, 0x16df4, 0x19509,
|
||||
0x19a78, 0x235a0, 0x24210, 0x24410, 0x2567f, 0x282c3, 0x2d986,
|
||||
0x2efde, 0x319d7, 0x334d7, 0x336dd, 0x34296, 0x35809, 0x3ad40,
|
||||
0x46d81, 0x48c92, 0x4b374, 0x4c353, 0x4fe4c, 0x50e4f, 0x53202,
|
||||
0x5d167, 0x6527c, 0x6a8b5, 0x6c70d, 0x76d90, 0x794f4, 0x7c411,
|
||||
0x7c5d4, 0x7f59f, 0x7fead, 0x872d8, 0x875b4, 0x95c6b]);
|
||||
|
||||
/// Find a 42-cycle on Cuckoo20 at 75% easiness and verifiy against a few
|
||||
/// known cycle proofs
|
||||
/// generated by other implementations.
|
||||
#[test]
|
||||
fn mine20_vectors() {
|
||||
let nonces1 = Miner::new(&[49], 75, 20).mine().unwrap();
|
||||
assert_eq!(V1, nonces1);
|
||||
|
||||
let nonces2 = Miner::new(&[50], 70, 20).mine().unwrap();
|
||||
assert_eq!(V2, nonces2);
|
||||
|
||||
let nonces3 = Miner::new(&[51], 70, 20).mine().unwrap();
|
||||
assert_eq!(V3, nonces3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate20_vectors() {
|
||||
assert!(Cuckoo::new(&[49], 20).verify(V1.clone(), 75));
|
||||
assert!(Cuckoo::new(&[50], 20).verify(V2.clone(), 70));
|
||||
assert!(Cuckoo::new(&[51], 20).verify(V3.clone(), 70));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_fail() {
|
||||
assert!(!Cuckoo::new(&[49], 20).verify(Proof([0; 42]), 75));
|
||||
assert!(!Cuckoo::new(&[50], 20).verify(V1.clone(), 75));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mine20_validate() {
|
||||
// cuckoo20
|
||||
for n in 1..5 {
|
||||
let h = [n; 32];
|
||||
let nonces = Miner::new(&h, 75, 20).mine().unwrap();
|
||||
assert!(Cuckoo::new(&h, 20).verify(nonces, 75));
|
||||
}
|
||||
// cuckoo18
|
||||
for n in 1..5 {
|
||||
let h = [n; 32];
|
||||
let nonces = Miner::new(&h, 75, 18).mine().unwrap();
|
||||
assert!(Cuckoo::new(&h, 18).verify(nonces, 75));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
//! Implementation of Cuckoo Cycle designed by John Tromp. Ported to Rust from
|
||||
//! the C and Java code at https://github.com/tromp/cuckoo. Note that only the
|
||||
//! simple miner is included, mostly for testing purposes. John Tromp's Tomato
|
||||
//! miner will be much faster in almost every environment.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha256;
|
||||
|
||||
use core::{Proof, PROOFSIZE};
|
||||
use pow::siphash::siphash24;
|
||||
|
||||
const MAXPATHLEN: usize = 8192;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
PathError,
|
||||
NoSolutionError,
|
||||
}
|
||||
|
||||
/// An edge in the Cuckoo graph, simply references two u64 nodes.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
struct Edge {
|
||||
u: u64,
|
||||
v: u64,
|
||||
}
|
||||
|
||||
pub struct Cuckoo {
|
||||
mask: u64,
|
||||
size: u64,
|
||||
v: [u64; 4],
|
||||
}
|
||||
|
||||
impl Cuckoo {
|
||||
/// Initializes a new Cuckoo Cycle setup, using the provided byte array to
|
||||
/// generate a seed. In practice for PoW applications the byte array is a
|
||||
/// serialized block header.
|
||||
pub fn new(header: &[u8], sizeshift: u32) -> Cuckoo {
|
||||
let size = 1 << sizeshift;
|
||||
let mut hasher = Sha256::new();
|
||||
let mut hashed = [0; 32];
|
||||
hasher.input(header);
|
||||
hasher.result(&mut hashed);
|
||||
|
||||
let k0 = u8_to_u64(hashed, 0);
|
||||
let k1 = u8_to_u64(hashed, 8);
|
||||
let mut v = [0; 4];
|
||||
v[0] = k0 ^ 0x736f6d6570736575;
|
||||
v[1] = k1 ^ 0x646f72616e646f6d;
|
||||
v[2] = k0 ^ 0x6c7967656e657261;
|
||||
v[3] = k1 ^ 0x7465646279746573;
|
||||
Cuckoo {
|
||||
v: v,
|
||||
size: size,
|
||||
mask: (1 << sizeshift) / 2 - 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a node in the cuckoo graph generated from our seed. A node is
|
||||
/// simply materialized as a u64 from a nonce and an offset (generally 0 or
|
||||
/// 1).
|
||||
pub fn new_node(&self, nonce: u64, uorv: u64) -> u64 {
|
||||
return ((siphash24(self.v, 2 * nonce + uorv) & self.mask) << 1) | uorv;
|
||||
}
|
||||
|
||||
/// Creates a new edge in the cuckoo graph generated by our seed from a
|
||||
/// nonce. Generates two node coordinates from the nonce and links them
|
||||
/// together.
|
||||
pub fn new_edge(&self, nonce: u64) -> Edge {
|
||||
Edge {
|
||||
u: self.new_node(nonce, 0),
|
||||
v: self.new_node(nonce, 1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Assuming increasing nonces all smaller than easiness, verifies the
|
||||
/// nonces form a cycle in a Cuckoo graph. Each nonce generates an edge, we
|
||||
/// build the nodes on both side of that edge and count the connections.
|
||||
pub fn verify(&self, proof: Proof, ease: u64) -> bool {
|
||||
let easiness = ease * (self.size as u64) / 100;
|
||||
let nonces = proof.to_u64s();
|
||||
let mut us = [0; PROOFSIZE];
|
||||
let mut vs = [0; PROOFSIZE];
|
||||
for n in 0..PROOFSIZE {
|
||||
if nonces[n] >= easiness || (n != 0 && nonces[n] <= nonces[n - 1]) {
|
||||
return false;
|
||||
}
|
||||
us[n] = self.new_node(nonces[n], 0);
|
||||
vs[n] = self.new_node(nonces[n], 1);
|
||||
}
|
||||
let mut i = 0;
|
||||
let mut count = PROOFSIZE;
|
||||
loop {
|
||||
let mut j = i;
|
||||
for k in 0..PROOFSIZE {
|
||||
// find unique other j with same vs[j]
|
||||
if k != i && vs[k] == vs[i] {
|
||||
if j != i {
|
||||
return false;
|
||||
}
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
if j == i {
|
||||
return false;
|
||||
}
|
||||
i = j;
|
||||
for k in 0..PROOFSIZE {
|
||||
// find unique other i with same us[i]
|
||||
if k != j && us[k] == us[j] {
|
||||
if i != j {
|
||||
return false;
|
||||
}
|
||||
i = k;
|
||||
}
|
||||
}
|
||||
if i == j {
|
||||
return false;
|
||||
}
|
||||
count -= 2;
|
||||
if i == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
count == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Miner for the Cuckoo Cycle algorithm. While the verifier will work for
|
||||
/// graph sizes up to a u64, the miner is limited to u32 to be more memory
|
||||
/// compact (so shift <= 32). Non-optimized for now and and so mostly used for
|
||||
/// tests, being impractical with sizes greater than 2^22.
|
||||
pub struct Miner {
|
||||
easiness: u64,
|
||||
cuckoo: Cuckoo,
|
||||
graph: Vec<u32>,
|
||||
}
|
||||
|
||||
/// What type of cycle we have found?
|
||||
enum CycleSol {
|
||||
/// A cycle of the right length is a valid proof.
|
||||
ValidProof([u32; PROOFSIZE]),
|
||||
/// A cycle of the wrong length is great, but not a proof.
|
||||
InvalidCycle(usize),
|
||||
/// No cycles have been found.
|
||||
NoCycle,
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
pub fn new(header: &[u8], ease: u32, sizeshift: u32) -> Miner {
|
||||
let cuckoo = Cuckoo::new(header, sizeshift);
|
||||
let size = 1 << sizeshift;
|
||||
let graph = vec![0; size + 1];
|
||||
let easiness = (ease as u64) * (size as u64) / 100;
|
||||
Miner {
|
||||
easiness: easiness,
|
||||
cuckoo: cuckoo,
|
||||
graph: graph,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mine(&mut self) -> Result<Proof, Error> {
|
||||
let mut us = [0; MAXPATHLEN];
|
||||
let mut vs = [0; MAXPATHLEN];
|
||||
for nonce in 0..self.easiness {
|
||||
us[0] = self.cuckoo.new_node(nonce, 0) as u32;
|
||||
vs[0] = self.cuckoo.new_node(nonce, 1) as u32;
|
||||
let u = self.graph[us[0] as usize];
|
||||
let v = self.graph[vs[0] as usize];
|
||||
if us[0] == 0 {
|
||||
continue; // ignore duplicate edges
|
||||
}
|
||||
let nu = try!(self.path(u, &mut us)) as usize;
|
||||
let nv = try!(self.path(v, &mut vs)) as usize;
|
||||
|
||||
let sol = self.find_sol(nu, &us, nv, &vs);
|
||||
match sol {
|
||||
CycleSol::ValidProof(res) => return Ok(Proof(res)),
|
||||
CycleSol::InvalidCycle(_) => continue,
|
||||
CycleSol::NoCycle => {
|
||||
self.update_graph(nu, &us, nv, &vs);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::NoSolutionError)
|
||||
}
|
||||
|
||||
fn path(&self, mut u: u32, us: &mut [u32]) -> Result<u32, Error> {
|
||||
let mut nu = 0;
|
||||
while u != 0 {
|
||||
nu += 1;
|
||||
if nu >= MAXPATHLEN {
|
||||
while nu != 0 && us[(nu - 1) as usize] != u {
|
||||
nu -= 1;
|
||||
}
|
||||
return Err(Error::PathError);
|
||||
}
|
||||
us[nu as usize] = u;
|
||||
u = self.graph[u as usize];
|
||||
}
|
||||
Ok(nu as u32)
|
||||
}
|
||||
|
||||
fn update_graph(&mut self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) {
|
||||
if nu < nv {
|
||||
while nu != 0 {
|
||||
nu -= 1;
|
||||
self.graph[us[nu + 1] as usize] = us[nu];
|
||||
}
|
||||
self.graph[us[0] as usize] = vs[0];
|
||||
} else {
|
||||
while nv != 0 {
|
||||
nv -= 1;
|
||||
self.graph[vs[nv + 1] as usize] = vs[nv];
|
||||
}
|
||||
self.graph[vs[0] as usize] = us[0];
|
||||
}
|
||||
}
|
||||
|
||||
fn find_sol(&self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) -> CycleSol {
|
||||
if us[nu] == vs[nv] {
|
||||
let min = cmp::min(nu, nv);
|
||||
nu -= min;
|
||||
nv -= min;
|
||||
while us[nu] != vs[nv] {
|
||||
nu += 1;
|
||||
nv += 1;
|
||||
}
|
||||
if nu + nv + 1 == PROOFSIZE {
|
||||
self.solution(&us, nu as u32, &vs, nv as u32)
|
||||
} else {
|
||||
CycleSol::InvalidCycle(nu + nv + 1)
|
||||
}
|
||||
} else {
|
||||
CycleSol::NoCycle
|
||||
}
|
||||
}
|
||||
|
||||
fn solution(&self, us: &[u32], mut nu: u32, vs: &[u32], mut nv: u32) -> CycleSol {
|
||||
let mut cycle = HashSet::new();
|
||||
cycle.insert(Edge {
|
||||
u: us[0] as u64,
|
||||
v: vs[0] as u64,
|
||||
});
|
||||
while nu != 0 {
|
||||
// u's in even position; v's in odd
|
||||
nu -= 1;
|
||||
cycle.insert(Edge {
|
||||
u: us[((nu + 1) & !1) as usize] as u64,
|
||||
v: us[(nu | 1) as usize] as u64,
|
||||
});
|
||||
}
|
||||
while nv != 0 {
|
||||
// u's in odd position; v's in even
|
||||
nv -= 1;
|
||||
cycle.insert(Edge {
|
||||
u: vs[(nv | 1) as usize] as u64,
|
||||
v: vs[((nv + 1) & !1) as usize] as u64,
|
||||
});
|
||||
}
|
||||
let mut n = 0;
|
||||
let mut sol = [0; PROOFSIZE];
|
||||
for nonce in 0..self.easiness {
|
||||
let edge = self.cuckoo.new_edge(nonce);
|
||||
if cycle.contains(&edge) {
|
||||
sol[n] = nonce as u32;
|
||||
n += 1;
|
||||
cycle.remove(&edge);
|
||||
}
|
||||
}
|
||||
return if n == PROOFSIZE {
|
||||
CycleSol::ValidProof(sol)
|
||||
} else {
|
||||
CycleSol::NoCycle
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Utility to transform a 8 bytes of a byte array into a u64.
|
||||
fn u8_to_u64(p: [u8; 32], i: usize) -> u64 {
|
||||
(p[i] as u64) | (p[i + 1] as u64) << 8 | (p[i + 2] as u64) << 16 | (p[i + 3] as u64) << 24 |
|
||||
(p[i + 4] as u64) << 32 | (p[i + 5] as u64) << 40 |
|
||||
(p[i + 6] as u64) << 48 | (p[i + 7] as u64) << 56
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use core::Proof;
|
||||
|
||||
static V1: Proof = Proof([0xe13, 0x410c, 0x7974, 0x8317, 0xb016, 0xb992, 0xe3c8, 0x1038a,
|
||||
0x116f0, 0x15ed2, 0x165a2, 0x17793, 0x17dd1, 0x1f885, 0x20932,
|
||||
0x20936, 0x2171b, 0x28968, 0x2b184, 0x30b8e, 0x31d28, 0x35782,
|
||||
0x381ea, 0x38321, 0x3b414, 0x3e14b, 0x43615, 0x49a51, 0x4a319,
|
||||
0x58271, 0x5dbb9, 0x5dbcf, 0x62db4, 0x653d2, 0x655f6, 0x66382,
|
||||
0x7057d, 0x765b0, 0x79c7c, 0x83167, 0x86e7b, 0x8a5f4]);
|
||||
static V2: Proof = Proof([0x33b8, 0x3fd9, 0x8f2b, 0xba0d, 0x11e2d, 0x1d51d, 0x2786e, 0x29625,
|
||||
0x2a862, 0x2a972, 0x2e6d7, 0x319df, 0x37ce7, 0x3f771, 0x4373b,
|
||||
0x439b7, 0x48626, 0x49c7d, 0x4a6f1, 0x4a808, 0x4e518, 0x519e3,
|
||||
0x526bb, 0x54988, 0x564e9, 0x58a6c, 0x5a4dd, 0x63fa2, 0x68ad1,
|
||||
0x69e52, 0x6bf53, 0x70841, 0x76343, 0x763a4, 0x79681, 0x7d006,
|
||||
0x7d633, 0x7eebe, 0x7fe7c, 0x811fa, 0x863c1, 0x8b149]);
|
||||
static V3: Proof = Proof([0x24ae, 0x5180, 0x9f3d, 0xd379, 0x102c9, 0x15787, 0x16df4, 0x19509,
|
||||
0x19a78, 0x235a0, 0x24210, 0x24410, 0x2567f, 0x282c3, 0x2d986,
|
||||
0x2efde, 0x319d7, 0x334d7, 0x336dd, 0x34296, 0x35809, 0x3ad40,
|
||||
0x46d81, 0x48c92, 0x4b374, 0x4c353, 0x4fe4c, 0x50e4f, 0x53202,
|
||||
0x5d167, 0x6527c, 0x6a8b5, 0x6c70d, 0x76d90, 0x794f4, 0x7c411,
|
||||
0x7c5d4, 0x7f59f, 0x7fead, 0x872d8, 0x875b4, 0x95c6b]);
|
||||
// cuckoo28 at 50% edges of letter 'u'
|
||||
static V4: Proof = Proof([0x1abd16, 0x7bb47e, 0x860253, 0xfad0b2, 0x121aa4d, 0x150a10b,
|
||||
0x20605cb, 0x20ae7e3, 0x235a9be, 0x2640f4a, 0x2724c36, 0x2a6d38c,
|
||||
0x2c50b28, 0x30850f2, 0x309668a, 0x30c85bd, 0x345f42c, 0x3901676,
|
||||
0x432838f, 0x472158a, 0x4d04e9d, 0x4d6a987, 0x4f577bf, 0x4fbc49c,
|
||||
0x593978d, 0x5acd98f, 0x5e60917, 0x6310602, 0x6385e88, 0x64f149c,
|
||||
0x66d472e, 0x68e4df9, 0x6b4a89c, 0x6bb751d, 0x6e09792, 0x6e57e1d,
|
||||
0x6ecfcdd, 0x70abddc, 0x7291dfd, 0x788069e, 0x79a15b1, 0x7d1a1e9]);
|
||||
|
||||
/// Find a 42-cycle on Cuckoo20 at 75% easiness and verifiy against a few
|
||||
/// known cycle proofs
|
||||
/// generated by other implementations.
|
||||
#[test]
|
||||
fn mine20_vectors() {
|
||||
let nonces1 = Miner::new(&[49], 75, 20).mine().unwrap();
|
||||
assert_eq!(V1, nonces1);
|
||||
|
||||
let nonces2 = Miner::new(&[50], 70, 20).mine().unwrap();
|
||||
assert_eq!(V2, nonces2);
|
||||
|
||||
let nonces3 = Miner::new(&[51], 70, 20).mine().unwrap();
|
||||
assert_eq!(V3, nonces3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate20_vectors() {
|
||||
assert!(Cuckoo::new(&[49], 20).verify(V1.clone(), 75));
|
||||
assert!(Cuckoo::new(&[50], 20).verify(V2.clone(), 70));
|
||||
assert!(Cuckoo::new(&[51], 20).verify(V3.clone(), 70));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate28_vectors() {
|
||||
assert!(Cuckoo::new(&[117], 28).verify(V4.clone(), 50));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_fail() {
|
||||
// edge checks
|
||||
assert!(!Cuckoo::new(&[49], 20).verify(Proof([0; 42]), 75));
|
||||
assert!(!Cuckoo::new(&[49], 20).verify(Proof([0xffff; 42]), 75));
|
||||
// wrong data for proof
|
||||
assert!(!Cuckoo::new(&[50], 20).verify(V1.clone(), 75));
|
||||
assert!(!Cuckoo::new(&[117], 20).verify(V4.clone(), 50));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mine20_validate() {
|
||||
// cuckoo20
|
||||
for n in 1..5 {
|
||||
let h = [n; 32];
|
||||
let nonces = Miner::new(&h, 75, 20).mine().unwrap();
|
||||
assert!(Cuckoo::new(&h, 20).verify(nonces, 75));
|
||||
}
|
||||
// cuckoo18
|
||||
for n in 1..5 {
|
||||
let h = [n; 32];
|
||||
let nonces = Miner::new(&h, 75, 18).mine().unwrap();
|
||||
assert!(Cuckoo::new(&h, 18).verify(nonces, 75));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
//! The proof of work needs to strike a balance between fast header
|
||||
//! verification to avoid DoS attacks and difficulty for block verifiers to
|
||||
//! build new blocks. In addition, mining new blocks should also be as
|
||||
//! difficult on high end custom-made hardware (ASICs) as on commodity hardware
|
||||
//! or smartphones. For this reason we use Cuckoo Cycles (see the cuckoo
|
||||
//! module for more information).
|
||||
//!
|
||||
//! Note that this miner implementation is here mostly for tests and
|
||||
//! reference. It's not optimized for speed.
|
||||
|
||||
mod siphash;
|
||||
mod cuckoo;
|
||||
|
||||
use time;
|
||||
|
||||
use core::{Block, BlockHeader, Hashed, Hash, Proof, PROOFSIZE};
|
||||
use pow::cuckoo::{Cuckoo, Miner, Error};
|
||||
|
||||
use ser;
|
||||
use ser::{Writeable, Writer, ser_vec};
|
||||
|
||||
/// Default Cuckoo Cycle size shift used is 28. We may decide to increase it.
|
||||
/// when difficuty increases.
|
||||
const SIZESHIFT: u32 = 28;
|
||||
|
||||
/// Default Cuckoo Cycle easiness, high enough to have good likeliness to find
|
||||
/// a solution.
|
||||
const EASINESS: u32 = 70;
|
||||
|
||||
/// Max target hash, lowest difficulty
|
||||
pub const MAX_TARGET: [u32; PROOFSIZE] =
|
||||
[0xfff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff];
|
||||
|
||||
/// Subset of a block header that goes into hashing for proof of work.
|
||||
/// Basically the whole thing minus the PoW solution itself and the total
|
||||
/// difficulty (yet unknown). We also add the count of every variable length
|
||||
/// elements in a header to make lying on those much harder.
|
||||
#[derive(Debug)]
|
||||
struct PowHeader {
|
||||
pub nonce: u64,
|
||||
pub height: u64,
|
||||
pub previous: Hash,
|
||||
pub timestamp: time::Tm,
|
||||
pub utxo_merkle: Hash,
|
||||
pub tx_merkle: Hash,
|
||||
pub total_fees: u64,
|
||||
pub n_in: u64,
|
||||
pub n_out: u64,
|
||||
pub n_proofs: u64,
|
||||
}
|
||||
|
||||
/// The binary definition of a PoW header is material for consensus as that's
|
||||
/// the data that gets hashed for PoW calculation. The nonce is written first
|
||||
/// to make incrementing from the serialized form trivial.
|
||||
impl Writeable for PowHeader {
|
||||
fn write(&self, writer: &mut Writer) -> Option<ser::Error> {
|
||||
try_m!(writer.write_u64(self.nonce));
|
||||
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));
|
||||
try_m!(writer.write_u64(self.n_in));
|
||||
try_m!(writer.write_u64(self.n_out));
|
||||
writer.write_u64(self.n_proofs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashed for PowHeader {
|
||||
fn bytes(&self) -> Vec<u8> {
|
||||
// no serialization errors are applicable in this specific case
|
||||
ser_vec(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl PowHeader {
|
||||
fn from_block(b: &Block) -> PowHeader {
|
||||
let ref h = b.header;
|
||||
PowHeader {
|
||||
nonce: h.nonce,
|
||||
height: h.height,
|
||||
previous: h.previous,
|
||||
timestamp: h.timestamp,
|
||||
utxo_merkle: h.utxo_merkle,
|
||||
tx_merkle: h.tx_merkle,
|
||||
total_fees: h.total_fees,
|
||||
n_in: b.inputs.len() as u64,
|
||||
n_out: b.outputs.len() as u64,
|
||||
n_proofs: b.proofs.len() as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the proof of work of a given header.
|
||||
pub fn verify(b: &Block, target: Proof) -> bool {
|
||||
verify_size(b, target, SIZESHIFT)
|
||||
}
|
||||
|
||||
/// Same as default verify function but uses the much easier Cuckoo20 (mostly
|
||||
/// for tests).
|
||||
pub fn verify20(b: &Block, target: Proof) -> bool {
|
||||
verify_size(b, target, 20)
|
||||
}
|
||||
|
||||
pub fn verify_size(b: &Block, target: Proof, sizeshift: u32) -> bool {
|
||||
let hash = PowHeader::from_block(b).hash();
|
||||
// make sure the hash is smaller than our target before going into more
|
||||
// expensive validation
|
||||
if target < b.header.pow {
|
||||
return false;
|
||||
}
|
||||
Cuckoo::new(hash.to_slice(), sizeshift).verify(b.header.pow, EASINESS as u64)
|
||||
}
|
||||
|
||||
/// Runs a naive single-threaded proof of work computation over the provided
|
||||
/// block, until the required difficulty target is reached. May take a
|
||||
/// while for a low target...
|
||||
pub fn pow(b: &Block, target: Proof) -> Result<(Proof, u64), Error> {
|
||||
pow_size(b, target, SIZESHIFT)
|
||||
}
|
||||
|
||||
/// Same as default pow function but uses the much easier Cuckoo20 (mostly for
|
||||
/// tests).
|
||||
pub fn pow20(b: &Block, target: Proof) -> Result<(Proof, u64), Error> {
|
||||
pow_size(b, target, 20)
|
||||
}
|
||||
|
||||
fn pow_size(b: &Block, target: Proof, sizeshift: u32) -> Result<(Proof, u64), Error> {
|
||||
let mut pow_header = PowHeader::from_block(b);
|
||||
let start_nonce = pow_header.nonce;
|
||||
|
||||
// try to find a cuckoo cycle on that header hash
|
||||
loop {
|
||||
// can be trivially optimized by avoiding re-serialization every time but this
|
||||
// is not meant as a fast miner implementation
|
||||
let pow_hash = pow_header.hash();
|
||||
|
||||
// if we found a cycle (not guaranteed) and the proof is lower that the target,
|
||||
// we're all good
|
||||
if let Ok(proof) = Miner::new(pow_hash.to_slice(), EASINESS, sizeshift).mine() {
|
||||
if proof <= target {
|
||||
return Ok((proof, pow_header.nonce));
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise increment the nonce
|
||||
pow_header.nonce += 1;
|
||||
|
||||
// and if we're back where we started, update the time (changes the hash as
|
||||
// well)
|
||||
if pow_header.nonce == start_nonce {
|
||||
pow_header.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use core::{BlockHeader, Hash, Proof};
|
||||
use std::time::Instant;
|
||||
use genesis;
|
||||
|
||||
#[test]
|
||||
fn genesis_pow() {
|
||||
let mut b = genesis::genesis();
|
||||
let (proof, nonce) = pow20(&b, Proof(MAX_TARGET)).unwrap();
|
||||
assert!(nonce > 0);
|
||||
assert!(proof < Proof(MAX_TARGET));
|
||||
b.header.pow = proof;
|
||||
b.header.nonce = nonce;
|
||||
assert!(verify20(&b, Proof(MAX_TARGET)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//! Simple implementation of the siphash 2-4 hashing function from
|
||||
//! Jean-Philippe Aumasson and Daniel J. Bernstein.
|
||||
|
||||
/// Implements siphash 2-4 specialized for a 4 u64 array key and a u64 nonce
|
||||
pub fn siphash24(v: [u64; 4], nonce: u64) -> u64 {
|
||||
let mut v0 = v[0];
|
||||
let mut v1 = v[1];
|
||||
let mut v2 = v[2];
|
||||
let mut v3 = v[3] ^ nonce;
|
||||
|
||||
// macro for left rotation
|
||||
macro_rules! rotl {
|
||||
($num:ident, $shift:expr) => {
|
||||
$num = ($num << $shift) | ($num >> (64 - $shift));
|
||||
}
|
||||
}
|
||||
|
||||
// macro for a single siphash round
|
||||
macro_rules! round {
|
||||
() => {
|
||||
v0 = v0.wrapping_add(v1);
|
||||
v2 = v2.wrapping_add(v3);
|
||||
rotl!(v1, 13);
|
||||
rotl!(v3, 16);
|
||||
v1 ^= v0;
|
||||
v3 ^= v2;
|
||||
rotl!(v0, 32);
|
||||
v2 = v2.wrapping_add(v1);
|
||||
v0 = v0.wrapping_add(v3);
|
||||
rotl!(v1, 17);
|
||||
rotl!(v3, 21);
|
||||
v1 ^= v2;
|
||||
v3 ^= v0;
|
||||
rotl!(v2, 32);
|
||||
}
|
||||
}
|
||||
|
||||
// 2 rounds
|
||||
round!();
|
||||
round!();
|
||||
|
||||
v0 ^= nonce;
|
||||
v2 ^= 0xff;
|
||||
|
||||
// and then 4 rounds, hence siphash 2-4
|
||||
round!();
|
||||
round!();
|
||||
round!();
|
||||
round!();
|
||||
|
||||
return v0 ^ v1 ^ v2 ^ v3;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
/// Some test vectors hoisted from the Java implementation (adjusted from
|
||||
/// the
|
||||
/// fact that the Java impl uses a long, aka a signed 64 bits number).
|
||||
#[test]
|
||||
fn hash_some() {
|
||||
assert_eq!(siphash24([1, 2, 3, 4], 10), 928382149599306901);
|
||||
assert_eq!(siphash24([1, 2, 3, 4], 111), 10524991083049122233);
|
||||
assert_eq!(siphash24([9, 7, 6, 7], 12), 1305683875471634734);
|
||||
assert_eq!(siphash24([9, 7, 6, 7], 10), 11589833042187638814);
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
//! Serialization and deserialization layer specialized for binary encoding.
|
||||
//! Ensures consistency and safety. Basically a minimal subset or
|
||||
//! rustc_serialize customized for our need.
|
||||
//!
|
||||
//! To use it simply implement `Writeable` or `Readable` and then use the
|
||||
//! `serialize` or `deserialize` functions on them as appropriate.
|
||||
|
||||
use std::io;
|
||||
use std::io::{Write, Read};
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
|
||||
|
||||
/// Possible errors deriving from serializing or deserializing.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Wraps an io error produced when reading or writing
|
||||
IOErr(io::Error),
|
||||
/// When asked to read too much data
|
||||
TooLargeReadErr(String),
|
||||
}
|
||||
|
||||
/// Useful trait to implement on types that can be translated to byte slices
|
||||
/// directly. Allows the use of `write_fixed_bytes` on them.
|
||||
pub trait AsFixedBytes {
|
||||
/// The slice representation of self
|
||||
fn as_fixed_bytes(&self) -> &[u8];
|
||||
}
|
||||
|
||||
/// Implementations defined how different numbers and binary structures are
|
||||
/// written to an underlying stream or container (depending on implementation).
|
||||
pub trait Writer {
|
||||
/// Writes a u32 as bytes
|
||||
fn write_u32(&mut self, n: u32) -> Option<Error>;
|
||||
/// Writes a u64 as bytes
|
||||
fn write_u64(&mut self, n: u64) -> Option<Error>;
|
||||
/// Writes a i64 as bytes
|
||||
fn write_i64(&mut self, n: i64) -> Option<Error>;
|
||||
/// Writes a variable length `Vec`, the length of the `Vec` is encoded as a
|
||||
/// prefix.
|
||||
fn write_vec(&mut self, vec: &mut Vec<u8>) -> Option<Error>;
|
||||
/// Writes a fixed number of bytes from something that can turn itself into
|
||||
/// a `&[u8]`. The reader is expected to know the actual length on read.
|
||||
fn write_fixed_bytes(&mut self, b32: &AsFixedBytes) -> Option<Error>;
|
||||
}
|
||||
|
||||
/// Implementations defined how different numbers and binary structures are
|
||||
/// read from an underlying stream or container (depending on implementation).
|
||||
pub trait Reader {
|
||||
/// Read a u32 from the underlying Read
|
||||
fn read_u32(&mut self) -> Result<u32, Error>;
|
||||
/// Read a u64 from the underlying Read
|
||||
fn read_u64(&mut self) -> Result<u64, Error>;
|
||||
/// Read a i32 from the underlying Read
|
||||
fn read_i64(&mut self) -> Result<i64, Error>;
|
||||
/// first before the data bytes.
|
||||
fn read_vec(&mut self) -> Result<Vec<u8>, Error>;
|
||||
/// Read a fixed number of bytes from the underlying reader.
|
||||
fn read_fixed_bytes(&mut self, length: usize) -> Result<Vec<u8>, Error>;
|
||||
}
|
||||
|
||||
/// Trait that every type that can be serialized as binary must implement.
|
||||
/// Writes directly to a Writer, a utility type thinly wrapping an
|
||||
/// underlying Write implementation.
|
||||
pub trait Writeable {
|
||||
/// Write the data held by this Writeable to the provided writer
|
||||
fn write(&self, writer: &mut Writer) -> Option<Error>;
|
||||
}
|
||||
|
||||
/// Trait that every type that can be deserialized from binary must implement.
|
||||
/// Reads directly to a Reader, a utility type thinly wrapping an
|
||||
/// underlying Read implementation.
|
||||
pub trait Readable<T> {
|
||||
/// Reads the data necessary to this Readable from the provided reader
|
||||
fn read(reader: &mut Reader) -> Result<T, Error>;
|
||||
}
|
||||
|
||||
/// Deserializes a Readeable from any std::io::Read implementation.
|
||||
pub fn deserialize<T: Readable<T>>(mut source: &mut Read) -> Result<T, Error> {
|
||||
let mut reader = BinReader { source: source };
|
||||
T::read(&mut reader)
|
||||
}
|
||||
|
||||
/// Serializes a Writeable into any std::io::Write implementation.
|
||||
pub fn serialize(mut sink: &mut Write, thing: &Writeable) -> Option<Error> {
|
||||
let mut writer = BinWriter { sink: sink };
|
||||
thing.write(&mut writer)
|
||||
}
|
||||
|
||||
/// Utility function to serialize a writeable directly in memory using a
|
||||
/// Vec<u8>.
|
||||
pub fn ser_vec(thing: &Writeable) -> Result<Vec<u8>, Error> {
|
||||
let mut vec = Vec::new();
|
||||
if let Some(err) = serialize(&mut vec, thing) {
|
||||
return Err(err);
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
struct BinReader<'a> {
|
||||
source: &'a mut Read,
|
||||
}
|
||||
|
||||
/// Utility wrapper for an underlying byte Reader. Defines higher level methods
|
||||
/// to read numbers, byte vectors, hashes, etc.
|
||||
impl<'a> Reader for BinReader<'a> {
|
||||
fn read_u32(&mut self) -> Result<u32, Error> {
|
||||
self.source.read_u32::<BigEndian>().map_err(Error::IOErr)
|
||||
}
|
||||
fn read_u64(&mut self) -> Result<u64, Error> {
|
||||
self.source.read_u64::<BigEndian>().map_err(Error::IOErr)
|
||||
}
|
||||
fn read_i64(&mut self) -> Result<i64, Error> {
|
||||
self.source.read_i64::<BigEndian>().map_err(Error::IOErr)
|
||||
}
|
||||
/// Read a variable size vector from the underlying Read. Expects a usize
|
||||
fn read_vec(&mut self) -> Result<Vec<u8>, Error> {
|
||||
let len = try!(self.read_u64());
|
||||
self.read_fixed_bytes(len as usize)
|
||||
}
|
||||
fn read_fixed_bytes(&mut self, length: usize) -> Result<Vec<u8>, Error> {
|
||||
// not reading more than 100k in a single read
|
||||
if length > 100000 {
|
||||
return Err(Error::TooLargeReadErr(format!("fixed bytes length too large: {}", length)));
|
||||
}
|
||||
let mut buf = vec![0; length];
|
||||
self.source.read_exact(&mut buf).map(move |_| buf).map_err(Error::IOErr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility wrapper for an underlying byte Writer. Defines higher level methods
|
||||
/// to write numbers, byte vectors, hashes, etc.
|
||||
struct BinWriter<'a> {
|
||||
sink: &'a mut Write,
|
||||
}
|
||||
|
||||
impl<'a> Writer for BinWriter<'a> {
|
||||
fn write_u32(&mut self, n: u32) -> Option<Error> {
|
||||
self.sink.write_u32::<BigEndian>(n).err().map(Error::IOErr)
|
||||
}
|
||||
|
||||
fn write_u64(&mut self, n: u64) -> Option<Error> {
|
||||
self.sink.write_u64::<BigEndian>(n).err().map(Error::IOErr)
|
||||
}
|
||||
|
||||
fn write_i64(&mut self, n: i64) -> Option<Error> {
|
||||
self.sink.write_i64::<BigEndian>(n).err().map(Error::IOErr)
|
||||
}
|
||||
|
||||
|
||||
fn write_vec(&mut self, vec: &mut Vec<u8>) -> Option<Error> {
|
||||
try_m!(self.write_u64(vec.len() as u64));
|
||||
self.sink.write_all(vec).err().map(Error::IOErr)
|
||||
}
|
||||
|
||||
fn write_fixed_bytes(&mut self, b32: &AsFixedBytes) -> Option<Error> {
|
||||
let bs = b32.as_fixed_bytes();
|
||||
self.sink.write_all(bs).err().map(Error::IOErr)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user