Checkpoint commit
27 files changed, 3944 insertions(+), 3812 deletions(-) :} I've started doing whitespace changes as well, I want everything to be 4-space tabs from now on.
This commit is contained in:
parent
811df8a713
commit
200e0fe8e3
|
@ -22,8 +22,8 @@
|
||||||
|
|
||||||
use std::num::{Zero, from_u64};
|
use std::num::{Zero, from_u64};
|
||||||
|
|
||||||
use util::error;
|
use util;
|
||||||
use util::error::Error::{SpvBadTarget, SpvBadProofOfWork};
|
use util::Error::{SpvBadTarget, SpvBadProofOfWork};
|
||||||
use util::hash::Sha256dHash;
|
use util::hash::Sha256dHash;
|
||||||
use util::uint::Uint256;
|
use util::uint::Uint256;
|
||||||
use network::encodable::{ConsensusEncodable, VarInt};
|
use network::encodable::{ConsensusEncodable, VarInt};
|
||||||
|
@ -97,7 +97,7 @@ impl BlockHeader {
|
||||||
/// Performs an SPV validation of a block, which confirms that the proof-of-work
|
/// Performs an SPV validation of a block, which confirms that the proof-of-work
|
||||||
/// is correct, but does not verify that the transactions are valid or encoded
|
/// is correct, but does not verify that the transactions are valid or encoded
|
||||||
/// correctly.
|
/// correctly.
|
||||||
pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), error::Error> {
|
pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), util::Error> {
|
||||||
let ref target = self.target();
|
let ref target = self.target();
|
||||||
if target != required_target {
|
if target != required_target {
|
||||||
return Err(SpvBadTarget);
|
return Err(SpvBadTarget);
|
||||||
|
@ -137,7 +137,7 @@ impl_consensus_encoding!(LoneBlockHeader, header, tx_count);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use blockdata::block::Block;
|
use blockdata::block::Block;
|
||||||
|
@ -151,8 +151,8 @@ mod tests {
|
||||||
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
||||||
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
||||||
|
|
||||||
let decode: IoResult<Block> = deserialize(some_block.clone());
|
let decode: io::Result<Block> = deserialize(some_block.clone());
|
||||||
let bad_decode: IoResult<Block> = deserialize(cutoff_block);
|
let bad_decode: io::Result<Block> = deserialize(cutoff_block);
|
||||||
|
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
assert!(bad_decode.is_err());
|
assert!(bad_decode.is_err());
|
||||||
|
|
|
@ -33,8 +33,8 @@ use network::constants::Network;
|
||||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||||
use network::serialize::{BitcoinHash, SimpleDecoder, SimpleEncoder};
|
use network::serialize::{BitcoinHash, SimpleDecoder, SimpleEncoder};
|
||||||
use util::BitArray;
|
use util::BitArray;
|
||||||
use util::error;
|
use util;
|
||||||
use util::error::Error::{BlockNotFound, DuplicateHash, PrevHashNotFound};
|
use util::Error::{BlockNotFound, DuplicateHash, PrevHashNotFound};
|
||||||
use util::uint::Uint256;
|
use util::uint::Uint256;
|
||||||
use util::hash::Sha256dHash;
|
use util::hash::Sha256dHash;
|
||||||
use util::patricia_tree::PatriciaTree;
|
use util::patricia_tree::PatriciaTree;
|
||||||
|
@ -373,7 +373,7 @@ impl Blockchain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec<Transaction>, has_txdata: bool) -> Result<(), error::Error> {
|
fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec<Transaction>, has_txdata: bool) -> Result<(), util::Error> {
|
||||||
match self.tree.lookup_mut(hash, 256) {
|
match self.tree.lookup_mut(hash, 256) {
|
||||||
Some(existing_block) => {
|
Some(existing_block) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -413,26 +413,26 @@ impl Blockchain {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Locates a block in the chain and overwrites its txdata
|
/// Locates a block in the chain and overwrites its txdata
|
||||||
pub fn add_txdata(&mut self, block: Block) -> Result<(), error::Error> {
|
pub fn add_txdata(&mut self, block: Block) -> Result<(), util::Error> {
|
||||||
self.replace_txdata(&block.header.bitcoin_hash().into_le(), block.txdata, true)
|
self.replace_txdata(&block.header.bitcoin_hash().into_le(), block.txdata, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Locates a block in the chain and removes its txdata
|
/// Locates a block in the chain and removes its txdata
|
||||||
pub fn remove_txdata(&mut self, hash: Sha256dHash) -> Result<(), error::Error> {
|
pub fn remove_txdata(&mut self, hash: Sha256dHash) -> Result<(), util::Error> {
|
||||||
self.replace_txdata(&hash.into_le(), vec![], false)
|
self.replace_txdata(&hash.into_le(), vec![], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a block header to the chain
|
/// Adds a block header to the chain
|
||||||
pub fn add_header(&mut self, header: BlockHeader) -> Result<(), error::Error> {
|
pub fn add_header(&mut self, header: BlockHeader) -> Result<(), util::Error> {
|
||||||
self.real_add_block(Block { header: header, txdata: vec![] }, false)
|
self.real_add_block(Block { header: header, txdata: vec![] }, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a block to the chain
|
/// Adds a block to the chain
|
||||||
pub fn add_block(&mut self, block: Block) -> Result<(), error::Error> {
|
pub fn add_block(&mut self, block: Block) -> Result<(), util::Error> {
|
||||||
self.real_add_block(block, true)
|
self.real_add_block(block, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn real_add_block(&mut self, block: Block, has_txdata: bool) -> Result<(), error::Error> {
|
fn real_add_block(&mut self, block: Block, has_txdata: bool) -> Result<(), util::Error> {
|
||||||
// get_prev optimizes the common case where we are extending the best tip
|
// get_prev optimizes the common case where we are extending the best tip
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_prev<'a>(chain: &'a Blockchain, hash: Sha256dHash) -> Option<NodePtr> {
|
fn get_prev<'a>(chain: &'a Blockchain, hash: Sha256dHash) -> Option<NodePtr> {
|
||||||
|
@ -618,7 +618,7 @@ impl Blockchain {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
|
|
||||||
use blockdata::blockchain::Blockchain;
|
use blockdata::blockchain::Blockchain;
|
||||||
use blockdata::constants::genesis_block;
|
use blockdata::constants::genesis_block;
|
||||||
|
@ -632,7 +632,7 @@ mod tests {
|
||||||
genesis_block(Bitcoin).header.bitcoin_hash());
|
genesis_block(Bitcoin).header.bitcoin_hash());
|
||||||
|
|
||||||
let serial = serialize(&empty_chain);
|
let serial = serialize(&empty_chain);
|
||||||
let deserial: IoResult<Blockchain> = deserialize(serial.unwrap());
|
let deserial: io::Result<Blockchain> = deserialize(serial.unwrap());
|
||||||
|
|
||||||
assert!(deserial.is_ok());
|
assert!(deserial.is_ok());
|
||||||
let read_chain = deserial.unwrap();
|
let read_chain = deserial.unwrap();
|
||||||
|
|
|
@ -24,6 +24,7 @@ use serde;
|
||||||
|
|
||||||
// Heavy stick to translate between opcode types
|
// Heavy stick to translate between opcode types
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||||
|
@ -602,6 +603,8 @@ impl All {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
display_from_debug!(All);
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for All {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for All {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<All, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<All, D::Error> {
|
||||||
|
@ -646,6 +649,8 @@ pub enum Class {
|
||||||
Ordinary(Ordinary)
|
Ordinary(Ordinary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
display_from_debug!(Class);
|
||||||
|
|
||||||
impl serde::Serialize for Class {
|
impl serde::Serialize for Class {
|
||||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
where S: serde::Serializer,
|
where S: serde::Serializer,
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -180,7 +180,7 @@ impl TxIn {
|
||||||
}
|
}
|
||||||
match stack.pop() {
|
match stack.pop() {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
if !read_scriptbool(v.as_slice()) {
|
if !read_scriptbool(&v[..]) {
|
||||||
return Err(Error::ScriptReturnedFalse);
|
return Err(Error::ScriptReturnedFalse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ impl TxIn {
|
||||||
}
|
}
|
||||||
match p2sh_stack.pop() {
|
match p2sh_stack.pop() {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
if !read_scriptbool(v.as_slice()) {
|
if !read_scriptbool(&v[..]) {
|
||||||
return Err(Error::P2shScriptReturnedFalse);
|
return Err(Error::P2shScriptReturnedFalse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ impl Transaction {
|
||||||
err.map(|e| trace.error = Some(Error::OutputScriptFailure(e)));
|
err.map(|e| trace.error = Some(Error::OutputScriptFailure(e)));
|
||||||
match stack.pop() {
|
match stack.pop() {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
if !read_scriptbool(v.as_slice()) {
|
if !read_scriptbool(&v[..]) {
|
||||||
trace.error = Some(Error::ScriptReturnedFalse);
|
trace.error = Some(Error::ScriptReturnedFalse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ impl Transaction {
|
||||||
err.map(|e| trace.error = Some(Error::P2shScriptFailure(e)));
|
err.map(|e| trace.error = Some(Error::P2shScriptFailure(e)));
|
||||||
match p2sh_stack.pop() {
|
match p2sh_stack.pop() {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
if !read_scriptbool(v.as_slice()) {
|
if !read_scriptbool(&v[..]) {
|
||||||
trace.error = Some(Error::P2shScriptReturnedFalse);
|
trace.error = Some(Error::P2shScriptReturnedFalse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,7 +295,7 @@ impl Transaction {
|
||||||
impl BitcoinHash for Transaction {
|
impl BitcoinHash for Transaction {
|
||||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||||
use network::serialize::serialize;
|
use network::serialize::serialize;
|
||||||
Sha256dHash::from_data(serialize(self).unwrap().as_slice())
|
Sha256dHash::from_data(&serialize(self).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +307,7 @@ impl_consensus_encoding!(Transaction, version, input, output, lock_time);
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Transaction, TxIn};
|
use super::{Transaction, TxIn};
|
||||||
|
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
|
|
||||||
use network::serialize::BitcoinHash;
|
use network::serialize::BitcoinHash;
|
||||||
use network::serialize::deserialize;
|
use network::serialize::deserialize;
|
||||||
|
@ -315,14 +315,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_txin() {
|
fn test_txin() {
|
||||||
let txin: IoResult<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
|
let txin: io::Result<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
|
||||||
assert!(txin.is_ok());
|
assert!(txin.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transaction() {
|
fn test_transaction() {
|
||||||
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||||
let tx: IoResult<Transaction> = deserialize(hex_tx);
|
let tx: io::Result<Transaction> = deserialize(hex_tx);
|
||||||
assert!(tx.is_ok());
|
assert!(tx.is_ok());
|
||||||
let realtx = tx.unwrap();
|
let realtx = tx.unwrap();
|
||||||
// All these tests aren't really needed because if they fail, the hash check at the end
|
// All these tests aren't really needed because if they fail, the hash check at the end
|
||||||
|
|
|
@ -423,7 +423,7 @@ impl UtxoSet {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::prelude::*;
|
use std::prelude::*;
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use super::{UtxoSet, ValidationLevel};
|
use super::{UtxoSet, ValidationLevel};
|
||||||
|
@ -472,7 +472,7 @@ mod tests {
|
||||||
// Serialize/deserialize the resulting UTXO set
|
// Serialize/deserialize the resulting UTXO set
|
||||||
let serial = serialize(&empty_set).unwrap();
|
let serial = serialize(&empty_set).unwrap();
|
||||||
|
|
||||||
let deserial: IoResult<UtxoSet> = deserialize(serial.clone());
|
let deserial: io::Result<UtxoSet> = deserialize(serial.clone());
|
||||||
assert!(deserial.is_ok());
|
assert!(deserial.is_ok());
|
||||||
|
|
||||||
// Check that all outputs are there
|
// Check that all outputs are there
|
||||||
|
@ -492,7 +492,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let deserial_again: IoResult<UtxoSet> = deserialize(serial);
|
let deserial_again: io::Result<UtxoSet> = deserialize(serial);
|
||||||
let mut read_again = deserial_again.unwrap();
|
let mut read_again = deserial_again.unwrap();
|
||||||
assert!(read_again.rewind(&new_block));
|
assert!(read_again.rewind(&new_block));
|
||||||
assert_eq!(read_again.n_utxos(), 0);
|
assert_eq!(read_again.n_utxos(), 0);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -13,109 +13,81 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
macro_rules! impl_consensus_encoding {
|
macro_rules! impl_consensus_encoding {
|
||||||
($thing:ident, $($field:ident),+) => (
|
($thing:ident, $($field:ident),+) => (
|
||||||
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $thing {
|
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
$( try!(self.$field.consensus_encode(s)); )+
|
$( try!(self.$field.consensus_encode(s)); )+
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $thing {
|
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<$thing, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<$thing, D::Error> {
|
||||||
use network::encodable::ConsensusDecodable;
|
use network::encodable::ConsensusDecodable;
|
||||||
Ok($thing {
|
Ok($thing {
|
||||||
$( $field: try!(ConsensusDecodable::consensus_decode(d)), )+
|
$( $field: try!(ConsensusDecodable::consensus_decode(d)), )+
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_newtype_consensus_encoding {
|
macro_rules! impl_newtype_consensus_encoding {
|
||||||
($thing:ident) => (
|
($thing:ident) => (
|
||||||
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $thing {
|
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
let &$thing(ref data) = self;
|
let &$thing(ref data) = self;
|
||||||
data.consensus_encode(s)
|
data.consensus_encode(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $thing {
|
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<$thing, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<$thing, D::Error> {
|
||||||
Ok($thing(try!(ConsensusDecodable::consensus_decode(d))))
|
Ok($thing(try!(ConsensusDecodable::consensus_decode(d))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_array_newtype {
|
macro_rules! impl_array_newtype {
|
||||||
($thing:ident, $ty:ty, $len:expr) => {
|
($thing:ident, $ty:ty, $len:expr) => {
|
||||||
impl $thing {
|
impl $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Provides an immutable view into the object
|
/// Converts the object to a raw pointer
|
||||||
pub fn as_slice<'a>(&'a self) -> &'a [$ty] {
|
pub fn as_ptr(&self) -> *const $ty {
|
||||||
let &$thing(ref dat) = self;
|
let &$thing(ref dat) = self;
|
||||||
dat.as_slice()
|
dat.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Provides an immutable view into the object from index `s` inclusive to `e` exclusive
|
/// Converts the object to a mutable raw pointer
|
||||||
pub fn slice<'a>(&'a self, s: usize, e: usize) -> &'a [$ty] {
|
pub fn as_mut_ptr(&mut self) -> *mut $ty {
|
||||||
let &$thing(ref dat) = self;
|
let &mut $thing(ref mut dat) = self;
|
||||||
dat.slice(s, e)
|
dat.as_mut_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Provides an immutable view into the object, up to index `n` exclusive
|
/// Returns the length of the object as an array
|
||||||
pub fn slice_to<'a>(&'a self, n: usize) -> &'a [$ty] {
|
pub fn len(&self) -> usize { $len }
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
dat.slice_to(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
/// Constructs a new object from raw data
|
||||||
/// Provides an immutable view into the object, starting from index `n`
|
pub fn from_slice(data: &[$ty]) -> $thing {
|
||||||
pub fn slice_from<'a>(&'a self, n: usize) -> &'a [$ty] {
|
assert_eq!(data.len(), $len);
|
||||||
let &$thing(ref dat) = self;
|
unsafe {
|
||||||
dat.slice_from(n)
|
use std::intrinsics::copy_nonoverlapping;
|
||||||
}
|
use std::mem;
|
||||||
|
let mut ret: $thing = mem::uninitialized();
|
||||||
#[inline]
|
copy_nonoverlapping(data.as_ptr(),
|
||||||
/// Converts the object to a raw pointer
|
ret.as_mut_ptr(),
|
||||||
pub fn as_ptr(&self) -> *const $ty {
|
mem::size_of::<$thing>());
|
||||||
let &$thing(ref dat) = self;
|
ret
|
||||||
dat.as_ptr()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Converts the object to a mutable raw pointer
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut $ty {
|
|
||||||
let &$thing(ref mut dat) = self;
|
|
||||||
dat.as_mut_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Returns the length of the object as an array
|
|
||||||
pub fn len(&self) -> usize { $len }
|
|
||||||
|
|
||||||
/// Constructs a new object from raw data
|
|
||||||
pub fn from_slice(data: &[$ty]) -> $thing {
|
|
||||||
assert_eq!(data.len(), $len);
|
|
||||||
unsafe {
|
|
||||||
use std::intrinsics::copy_nonoverlapping;
|
|
||||||
use std::mem;
|
|
||||||
let mut ret: $thing = mem::uninitialized();
|
|
||||||
copy_nonoverlapping(ret.as_mut_ptr(),
|
|
||||||
data.as_ptr(),
|
|
||||||
mem::size_of::<$thing>());
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Index<usize> for $thing {
|
impl ::std::ops::Index<usize> for $thing {
|
||||||
type Output = $ty;
|
type Output = $ty;
|
||||||
|
@ -133,7 +105,7 @@ macro_rules! impl_array_newtype {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
|
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
|
||||||
let &$thing(ref dat) = self;
|
let &$thing(ref dat) = self;
|
||||||
&dat[index.start..index.end]
|
&dat[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +115,7 @@ macro_rules! impl_array_newtype {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
|
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
|
||||||
let &$thing(ref dat) = self;
|
let &$thing(ref dat) = self;
|
||||||
&dat[..index.end]
|
&dat[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +125,7 @@ macro_rules! impl_array_newtype {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
|
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
|
||||||
let &$thing(ref dat) = self;
|
let &$thing(ref dat) = self;
|
||||||
&dat[index.start..]
|
&dat[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,78 +139,108 @@ macro_rules! impl_array_newtype {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for $thing {
|
impl PartialEq for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eq(&self, other: &$thing) -> bool {
|
fn eq(&self, other: &$thing) -> bool {
|
||||||
self.as_slice() == other.as_slice()
|
&self[..] == &other[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for $thing {}
|
impl Eq for $thing {}
|
||||||
|
|
||||||
impl Clone for $thing {
|
impl Clone for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> $thing {
|
fn clone(&self) -> $thing {
|
||||||
$thing::from_slice(self.as_slice())
|
$thing::from_slice(&self[..])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::hash::Hash for $thing {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H>(&self, state: &mut H)
|
||||||
|
where H: ::std::hash::Hasher
|
||||||
|
{
|
||||||
|
(&self[..]).hash(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_slice<H>(data: &[$thing], state: &mut H)
|
||||||
|
where H: ::std::hash::Hasher
|
||||||
|
{
|
||||||
|
for d in data.iter() {
|
||||||
|
(&d[..]).hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_array_newtype_encodable {
|
macro_rules! impl_array_newtype_encodable {
|
||||||
($thing:ident, $ty:ty, $len:expr) => {
|
($thing:ident, $ty:ty, $len:expr) => {
|
||||||
|
|
||||||
impl ::serde::Deserialize for $thing {
|
impl ::serde::Deserialize for $thing {
|
||||||
fn deserialize<D>(d: &mut D) -> Result<$thing, D::Error>
|
fn deserialize<D>(d: &mut D) -> Result<$thing, D::Error>
|
||||||
where D: ::serde::Deserializer
|
where D: ::serde::Deserializer
|
||||||
{
|
{
|
||||||
// We have to define the Visitor struct inside the function
|
// We have to define the Visitor struct inside the function
|
||||||
// to make it local ... what we really need is that it's
|
// to make it local ... what we really need is that it's
|
||||||
// local to the macro, but this is Close Enough.
|
// local to the macro, but this is Close Enough.
|
||||||
struct Visitor {
|
struct Visitor {
|
||||||
marker: ::std::marker::PhantomData<$thing>,
|
marker: ::std::marker::PhantomData<$thing>,
|
||||||
}
|
}
|
||||||
impl ::serde::de::Visitor for Visitor {
|
impl ::serde::de::Visitor for Visitor {
|
||||||
type Value = $thing;
|
type Value = $thing;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn visit_seq<V>(&mut self, mut v: V) -> Result<$thing, V::Error>
|
fn visit_seq<V>(&mut self, mut v: V) -> Result<$thing, V::Error>
|
||||||
where V: ::serde::de::SeqVisitor
|
where V: ::serde::de::SeqVisitor
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
let mut ret: [$ty; $len] = mem::uninitialized();
|
let mut ret: [$ty; $len] = mem::uninitialized();
|
||||||
for i in 0..$len {
|
for i in 0..$len {
|
||||||
ret[i] = try!(v.visit());
|
ret[i] = match try!(v.visit()) {
|
||||||
}
|
Some(c) => c,
|
||||||
Ok($thing(ret))
|
None => return Err(::serde::de::Error::end_of_stream_error())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok($thing(ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin actual function
|
||||||
|
d.visit(Visitor { marker: ::std::marker::PhantomData })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin actual function
|
impl ::serde::Serialize for $thing {
|
||||||
d.visit(Visitor { marker: ::std::marker::PhantomData })
|
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||||
}
|
where S: ::serde::Serializer
|
||||||
|
{
|
||||||
|
let &$thing(ref dat) = self;
|
||||||
|
(&dat[..]).serialize(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::serde::Serialize for $thing {
|
|
||||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
|
||||||
where S: ::serde::Serializer
|
|
||||||
{
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
(&dat[..]).serialize(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_array_newtype_show {
|
macro_rules! impl_array_newtype_show {
|
||||||
($thing:ident) => {
|
($thing:ident) => {
|
||||||
impl ::std::fmt::Debug for $thing {
|
impl ::std::fmt::Debug for $thing {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
write!(f, concat!(stringify!($thing), "({})"), self.as_slice())
|
write!(f, concat!(stringify!($thing), "({:?})"), &self[..])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! display_from_debug {
|
||||||
|
($thing:ident) => {
|
||||||
|
impl ::std::fmt::Display for $thing {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||||
|
::std::fmt::Debug::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,14 @@ macro_rules! user_enum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for $name {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
f.pad(match *self {
|
||||||
|
$($elem => $txt),*
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ::serde::Deserialize for $name {
|
impl ::serde::Deserialize for $name {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deserialize<D>(d: &mut D) -> Result<$name, D::Error>
|
fn deserialize<D>(d: &mut D) -> Result<$name, D::Error>
|
||||||
|
|
|
@ -64,8 +64,8 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Address {
|
||||||
impl fmt::Debug for Address {
|
impl fmt::Debug for Address {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
// TODO: render services and hex-ize address
|
// TODO: render services and hex-ize address
|
||||||
write!(f, "Address {{services: {}, address: {}, port: {}}}",
|
write!(f, "Address {{services: {:?}, address: {:?}, port: {:?}}}",
|
||||||
self.services, self.address.as_slice(), self.port)
|
self.services, &self.address[..], self.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +75,8 @@ impl Clone for Address {
|
||||||
use std::intrinsics::copy_nonoverlapping;
|
use std::intrinsics::copy_nonoverlapping;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
let mut ret = mem::uninitialized();
|
let mut ret = mem::uninitialized();
|
||||||
copy_nonoverlapping(&mut ret,
|
copy_nonoverlapping(self,
|
||||||
self,
|
&mut ret,
|
||||||
mem::size_of::<Address>());
|
mem::size_of::<Address>());
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ impl Clone for Address {
|
||||||
impl PartialEq for Address {
|
impl PartialEq for Address {
|
||||||
fn eq(&self, other: &Address) -> bool {
|
fn eq(&self, other: &Address) -> bool {
|
||||||
self.services == other.services &&
|
self.services == other.services &&
|
||||||
self.address.as_slice() == other.address.as_slice() &&
|
&self.address[..] == &other.address[..] &&
|
||||||
self.port == other.port
|
self.port == other.port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ impl Eq for Address {}
|
||||||
mod test {
|
mod test {
|
||||||
use super::Address;
|
use super::Address;
|
||||||
|
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_address_test() {
|
fn deserialize_address_test() {
|
||||||
let mut addr: IoResult<Address> = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
let mut addr: io::Result<Address> = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
|
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
|
||||||
0, 1, 0x20, 0x8d]);
|
0, 1, 0x20, 0x8d]);
|
||||||
assert!(addr.is_ok());
|
assert!(addr.is_ok());
|
||||||
|
|
|
@ -22,14 +22,14 @@ use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||||
use network::serialize::{SimpleEncoder, SimpleDecoder};
|
use network::serialize::{SimpleEncoder, SimpleDecoder};
|
||||||
|
|
||||||
user_enum! {
|
user_enum! {
|
||||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||||
#[doc="The cryptocurrency to act on"]
|
#[doc="The cryptocurrency to act on"]
|
||||||
pub enum Network {
|
pub enum Network {
|
||||||
#[doc="Classic Bitcoin"]
|
#[doc="Classic Bitcoin"]
|
||||||
Bitcoin <-> "bitcoin",
|
Bitcoin <-> "bitcoin",
|
||||||
#[doc="Bitcoin's testnet"]
|
#[doc="Bitcoin's testnet"]
|
||||||
Testnet <-> "testnet"
|
Testnet <-> "testnet"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PROTOCOL_VERSION: u32 = 70001;
|
pub const PROTOCOL_VERSION: u32 = 70001;
|
||||||
|
@ -39,30 +39,30 @@ pub const USER_AGENT: &'static str = "bitcoin-rust v0.1";
|
||||||
/// Return the network magic bytes, which should be encoded little-endian
|
/// Return the network magic bytes, which should be encoded little-endian
|
||||||
/// at the start of every message
|
/// at the start of every message
|
||||||
pub fn magic(network: Network) -> u32 {
|
pub fn magic(network: Network) -> u32 {
|
||||||
match network {
|
match network {
|
||||||
Network::Bitcoin => 0xD9B4BEF9,
|
Network::Bitcoin => 0xD9B4BEF9,
|
||||||
Network::Testnet => 0x0709110B
|
Network::Testnet => 0x0709110B
|
||||||
// Note: any new entries here must be added to `deserialize` below
|
// Note: any new entries here must be added to `consensus_decode` below
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Network {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for Network {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
magic(*self).consensus_encode(s)
|
magic(*self).consensus_encode(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for Network {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for Network {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<Network, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<Network, D::Error> {
|
||||||
let magic: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
let magic: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
match magic {
|
match magic {
|
||||||
0xD9B4BEF9 => Ok(Network::Bitcoin),
|
0xD9B4BEF9 => Ok(Network::Bitcoin),
|
||||||
0x0709110B => Ok(Network::Testnet),
|
0x0709110B => Ok(Network::Testnet),
|
||||||
x => Err(d.error(format!("Unknown network (magic {:x})", x).as_slice()))
|
x => Err(d.error(format!("Unknown network (magic {:x})", x)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -31,7 +31,8 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::Hash;
|
||||||
|
use std::collections::hash_state::HashState;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
|
||||||
use util::hash::Sha256dHash;
|
use util::hash::Sha256dHash;
|
||||||
|
@ -39,14 +40,14 @@ use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||||
|
|
||||||
/// Data which can be encoded in a consensus-consistent way
|
/// Data which can be encoded in a consensus-consistent way
|
||||||
pub trait ConsensusEncodable<S: SimpleEncoder> {
|
pub trait ConsensusEncodable<S: SimpleEncoder> {
|
||||||
/// Encode an object with a well-defined format
|
/// Encode an object with a well-defined format
|
||||||
fn consensus_encode(&self, e: &mut S) -> Result<(), S::Error>;
|
fn consensus_encode(&self, e: &mut S) -> Result<(), S::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data which can be encoded in a consensus-consistent way
|
/// Data which can be encoded in a consensus-consistent way
|
||||||
pub trait ConsensusDecodable<D: SimpleDecoder> {
|
pub trait ConsensusDecodable<D: SimpleDecoder> {
|
||||||
/// Decode an object with a well-defined format
|
/// Decode an object with a well-defined format
|
||||||
fn consensus_decode(d: &mut D) -> Result<Self, D::Error>;
|
fn consensus_decode(d: &mut D) -> Result<Self, D::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A variable-length unsigned integer
|
/// A variable-length unsigned integer
|
||||||
|
@ -59,140 +60,140 @@ pub struct CheckedData(pub Vec<u8>);
|
||||||
|
|
||||||
// Primitive types
|
// Primitive types
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for u8 {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for u8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u8(*self) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u8(*self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for u16 {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for u16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u16(self.to_le()) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u16(self.to_le()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for u32 {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for u32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u32(self.to_le()) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u32(self.to_le()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for u64 {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for u64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u64(self.to_le()) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u64(self.to_le()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for i32 {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for i32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_i32(self.to_le()) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_i32(self.to_le()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for i64 {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for i64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_i64(self.to_le()) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_i64(self.to_le()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for VarInt {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for VarInt {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
let &VarInt(n) = self;
|
match self.0 {
|
||||||
match n {
|
0...0xFC => { (self.0 as u8).consensus_encode(s) }
|
||||||
0...0xFC => { (n as u8).consensus_encode(s) }
|
0xFD...0xFFFF => { try!(s.emit_u8(0xFD)); (self.0 as u16).consensus_encode(s) }
|
||||||
0xFD...0xFFFF => { try!(s.emit_u8(0xFD)); (n as u16).consensus_encode(s) }
|
0x10000...0xFFFFFFFF => { try!(s.emit_u8(0xFE)); (self.0 as u32).consensus_encode(s) }
|
||||||
0x10000...0xFFFFFFFF => { try!(s.emit_u8(0xFE)); (n as u32).consensus_encode(s) }
|
_ => { try!(s.emit_u8(0xFF)); (self.0 as u64).consensus_encode(s) }
|
||||||
_ => { try!(s.emit_u8(0xFF)); (n as u64).consensus_encode(s) }
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for u8 {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for u8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<u8, D::Error> { d.read_u8() }
|
fn consensus_decode(d: &mut D) -> Result<u8, D::Error> { d.read_u8() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for u16 {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for u16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<u16, D::Error> { d.read_u16().map(|n| u16::from_le(n)) }
|
fn consensus_decode(d: &mut D) -> Result<u16, D::Error> { d.read_u16().map(|n| u16::from_le(n)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for u32 {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for u32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<u32, D::Error> { d.read_u32().map(|n| u32::from_le(n)) }
|
fn consensus_decode(d: &mut D) -> Result<u32, D::Error> { d.read_u32().map(|n| u32::from_le(n)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for u64 {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for u64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<u64, D::Error> { d.read_u64().map(|n| u64::from_le(n)) }
|
fn consensus_decode(d: &mut D) -> Result<u64, D::Error> { d.read_u64().map(|n| u64::from_le(n)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for i32 {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for i32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<i32, D::Error> { d.read_i32().map(|n| i32::from_le(n)) }
|
fn consensus_decode(d: &mut D) -> Result<i32, D::Error> { d.read_i32().map(|n| i32::from_le(n)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for i64 {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for i64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<i64, D::Error> { d.read_i64().map(|n| i64::from_le(n)) }
|
fn consensus_decode(d: &mut D) -> Result<i64, D::Error> { d.read_i64().map(|n| i64::from_le(n)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for VarInt {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for VarInt {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<VarInt, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<VarInt, D::Error> {
|
||||||
let n = try!(d.read_u8());
|
let n = try!(d.read_u8());
|
||||||
match n {
|
match n {
|
||||||
0xFF => d.read_u64().map(|n| VarInt(u64::from_le(n))),
|
0xFF => d.read_u64().map(|n| VarInt(u64::from_le(n))),
|
||||||
0xFE => d.read_u32().map(|n| VarInt(u32::from_le(n) as u64)),
|
0xFE => d.read_u32().map(|n| VarInt(u32::from_le(n) as u64)),
|
||||||
0xFD => d.read_u16().map(|n| VarInt(u16::from_le(n) as u64)),
|
0xFD => d.read_u16().map(|n| VarInt(u16::from_le(n) as u64)),
|
||||||
n => Ok(VarInt(n as u64))
|
n => Ok(VarInt(n as u64))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Booleans
|
// Booleans
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for bool {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u8(if *self {1} else {0}) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u8(if *self {1} else {0}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for bool {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<bool, D::Error> { d.read_u8().map(|n| n != 0) }
|
fn consensus_decode(d: &mut D) -> Result<bool, D::Error> { d.read_u8().map(|n| n != 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strings
|
// Strings
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for String {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for String {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
self.as_bytes().consensus_encode(s)
|
self.as_bytes().consensus_encode(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for String {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for String {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<String, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<String, D::Error> {
|
||||||
String::from_utf8(try!(ConsensusDecodable::consensus_decode(d))).map_err(|_| d.error("String was not valid UTF8"))
|
String::from_utf8(try!(ConsensusDecodable::consensus_decode(d)))
|
||||||
}
|
.map_err(|_| d.error("String was not valid UTF8".to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Arrays
|
// Arrays
|
||||||
macro_rules! impl_array {
|
macro_rules! impl_array {
|
||||||
( $size:expr ) => (
|
( $size:expr ) => (
|
||||||
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for [T; $size] {
|
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for [T; $size] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
for i in self.iter() { try!(i.consensus_encode(s)); }
|
for i in self.iter() { try!(i.consensus_encode(s)); }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder, T:ConsensusDecodable<D> + Copy> ConsensusDecodable<D> for [T; $size] {
|
impl<D: SimpleDecoder, T:ConsensusDecodable<D> + Copy> ConsensusDecodable<D> for [T; $size] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<[T; $size], D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<[T; $size], D::Error> {
|
||||||
// Set everything to the first decode
|
// Set everything to the first decode
|
||||||
let mut ret = [try!(ConsensusDecodable::consensus_decode(d)); $size];
|
let mut ret = [try!(ConsensusDecodable::consensus_decode(d)); $size];
|
||||||
// Set the rest
|
// Set the rest
|
||||||
for i in 1..$size { ret[i] = try!(ConsensusDecodable::consensus_decode(d)); }
|
for i in 1..$size { ret[i] = try!(ConsensusDecodable::consensus_decode(d)); }
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_array!(2);
|
impl_array!(2);
|
||||||
|
@ -203,136 +204,135 @@ impl_array!(16);
|
||||||
impl_array!(32);
|
impl_array!(32);
|
||||||
|
|
||||||
impl<'a, S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for &'a [T] {
|
impl<'a, S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for &'a [T] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
try!(VarInt(self.len() as u64).consensus_encode(s));
|
try!(VarInt(self.len() as u64).consensus_encode(s));
|
||||||
for c in self.iter() { try!(c.consensus_encode(s)); }
|
for c in self.iter() { try!(c.consensus_encode(s)); }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot decode a slice
|
// Cannot decode a slice
|
||||||
|
|
||||||
// Vectors
|
// Vectors
|
||||||
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Vec<T> {
|
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Vec<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { self.as_slice().consensus_encode(s) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { (&self[..]).consensus_encode(s) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Vec<T> {
|
impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Vec<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<Vec<T>, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<Vec<T>, D::Error> {
|
||||||
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
let mut ret = Vec::with_capacity(len as usize);
|
let mut ret = Vec::with_capacity(len as usize);
|
||||||
for _ in 0..len { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
for _ in 0..len { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Box<[T]> {
|
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Box<[T]> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { (&self[..]).consensus_encode(s) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { (&self[..]).consensus_encode(s) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Box<[T]> {
|
impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Box<[T]> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<Box<[T]>, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<Box<[T]>, D::Error> {
|
||||||
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
unsafe {
|
unsafe {
|
||||||
let len = len as usize;
|
let len = len as usize;
|
||||||
let mut ret = Vec::with_capacity(len);
|
let mut ret = Vec::with_capacity(len);
|
||||||
for i in 0..len { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
for i in 0..len { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
||||||
Ok(ret.into_boxed_slice())
|
Ok(ret.into_boxed_slice())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options (encoded as vectors of length 0 or 1)
|
// Options (encoded as vectors of length 0 or 1)
|
||||||
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Option<T> {
|
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Option<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Some(ref data) => {
|
Some(ref data) => {
|
||||||
try!(1u8.consensus_encode(s));
|
try!(1u8.consensus_encode(s));
|
||||||
try!(data.consensus_encode(s));
|
try!(data.consensus_encode(s));
|
||||||
}
|
}
|
||||||
None => { try!(0u8.consensus_encode(s)); }
|
None => { try!(0u8.consensus_encode(s)); }
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder, T:ConsensusDecodable<D>> ConsensusDecodable<D> for Option<T> {
|
impl<D: SimpleDecoder, T:ConsensusDecodable<D>> ConsensusDecodable<D> for Option<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<Option<T>, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<Option<T>, D::Error> {
|
||||||
let bit: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
let bit: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
Ok(if bit != 0 {
|
Ok(if bit != 0 {
|
||||||
Some(try!(ConsensusDecodable::consensus_decode(d)))
|
Some(try!(ConsensusDecodable::consensus_decode(d)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Do a double-SHA256 on some data and return the first 4 bytes
|
/// Do a double-SHA256 on some data and return the first 4 bytes
|
||||||
fn sha2_checksum(data: &[u8]) -> [u8; 4] {
|
fn sha2_checksum(data: &[u8]) -> [u8; 4] {
|
||||||
let checksum = Sha256dHash::from_data(data);
|
let checksum = Sha256dHash::from_data(data);
|
||||||
[checksum[0], checksum[1], checksum[2], checksum[3]]
|
[checksum[0], checksum[1], checksum[2], checksum[3]]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checked data
|
// Checked data
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for CheckedData {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for CheckedData {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
let &CheckedData(ref data) = self;
|
try!((self.0.len() as u32).consensus_encode(s));
|
||||||
try!((data.len() as u32).consensus_encode(s));
|
try!(sha2_checksum(&self.0).consensus_encode(s));
|
||||||
try!(sha2_checksum(data.as_slice()).consensus_encode(s));
|
// We can't just pass to the slice encoder since it'll insert a length
|
||||||
// We can't just pass to the slice encoder since it'll insert a length
|
for ch in self.0.iter() {
|
||||||
for ch in data.iter() {
|
try!(ch.consensus_encode(s));
|
||||||
try!(ch.consensus_encode(s));
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for CheckedData {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for CheckedData {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<CheckedData, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<CheckedData, D::Error> {
|
||||||
let len: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
let len: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
let checksum: [u8; 4] = try!(ConsensusDecodable::consensus_decode(d));
|
let checksum: [u8; 4] = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
let mut ret = Vec::with_capacity(len as usize);
|
let mut ret = Vec::with_capacity(len as usize);
|
||||||
for _ in 0..len { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
for _ in 0..len { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
||||||
let expected_checksum = sha2_checksum(ret.as_slice());
|
let expected_checksum = sha2_checksum(&ret);
|
||||||
if expected_checksum != checksum {
|
if expected_checksum != checksum {
|
||||||
Err(d.error("bad checksum"))
|
Err(d.error(format!("bad checksum {:?} (expected {:?})", checksum, expected_checksum)))
|
||||||
} else {
|
} else {
|
||||||
Ok(CheckedData(ret))
|
Ok(CheckedData(ret))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tuples
|
// Tuples
|
||||||
macro_rules! tuple_encode {
|
macro_rules! tuple_encode {
|
||||||
($($x:ident),*) => (
|
($($x:ident),*) => (
|
||||||
impl <S: SimpleEncoder, $($x: ConsensusEncodable<S>),*> ConsensusEncodable<S> for ($($x),*) {
|
impl <S: SimpleEncoder, $($x: ConsensusEncodable<S>),*> ConsensusEncodable<S> for ($($x),*) {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
let &($(ref $x),*) = self;
|
let &($(ref $x),*) = self;
|
||||||
$( try!($x.consensus_encode(s)); )*
|
$( try!($x.consensus_encode(s)); )*
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder, $($x: ConsensusDecodable<D>),*> ConsensusDecodable<D> for ($($x),*) {
|
impl<D: SimpleDecoder, $($x: ConsensusDecodable<D>),*> ConsensusDecodable<D> for ($($x),*) {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn consensus_decode(d: &mut D) -> Result<($($x),*), D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<($($x),*), D::Error> {
|
||||||
Ok(($(try!({let $x = ConsensusDecodable::consensus_decode(d); $x })),*))
|
Ok(($(try!({let $x = ConsensusDecodable::consensus_decode(d); $x })),*))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tuple_encode!(T0, T1);
|
tuple_encode!(T0, T1);
|
||||||
|
@ -342,52 +342,52 @@ tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
|
||||||
|
|
||||||
// References
|
// References
|
||||||
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Box<T> {
|
impl<S: SimpleEncoder, T: ConsensusEncodable<S>> ConsensusEncodable<S> for Box<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { (**self).consensus_encode(s) }
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { (**self).consensus_encode(s) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Box<T> {
|
impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Box<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<Box<T>, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<Box<T>, D::Error> {
|
||||||
ConsensusDecodable::consensus_decode(d).map(|res| Box::new(res))
|
ConsensusDecodable::consensus_decode(d).map(|res| Box::new(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashMap
|
// HashMap
|
||||||
impl<S, K, V, H> ConsensusEncodable<S> for HashMap<K, V, H>
|
impl<S, K, V, H> ConsensusEncodable<S> for HashMap<K, V, H>
|
||||||
where S: SimpleEncoder,
|
where S: SimpleEncoder,
|
||||||
H: Hasher + Default,
|
H: HashState + Default,
|
||||||
K: ConsensusEncodable<S> + Eq + Hash,
|
K: ConsensusEncodable<S> + Eq + Hash,
|
||||||
V: ConsensusEncodable<S>
|
V: ConsensusEncodable<S>
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
try!(VarInt(self.len() as u64).consensus_encode(s));
|
try!(VarInt(self.len() as u64).consensus_encode(s));
|
||||||
for (key, value) in self.iter() {
|
for (key, value) in self.iter() {
|
||||||
try!(key.consensus_encode(s));
|
try!(key.consensus_encode(s));
|
||||||
try!(value.consensus_encode(s));
|
try!(value.consensus_encode(s));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D, K, V, H> ConsensusDecodable<D> for HashMap<K, V, H>
|
impl<D, K, V, H> ConsensusDecodable<D> for HashMap<K, V, H>
|
||||||
where D: SimpleDecoder,
|
where D: SimpleDecoder,
|
||||||
H: Hasher + Default,
|
H: HashState + Default,
|
||||||
K: ConsensusDecodable<D> + Eq + Hash,
|
K: ConsensusDecodable<D> + Eq + Hash,
|
||||||
V: ConsensusDecodable<D>
|
V: ConsensusDecodable<D>
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<HashMap<K, V, H>, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<HashMap<K, V, H>, D::Error> {
|
||||||
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
|
|
||||||
let mut ret = HashMap::with_capacity_and_hasher(len as usize, Default::default());
|
let mut ret = HashMap::with_capacity_and_hash_state(len as usize, Default::default());
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
ret.insert(try!(ConsensusDecodable::consensus_decode(d)),
|
ret.insert(try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
try!(ConsensusDecodable::consensus_decode(d)));
|
try!(ConsensusDecodable::consensus_decode(d)));
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
}
|
}
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,169 +395,169 @@ impl<D, K, V, H> ConsensusDecodable<D> for HashMap<K, V, H>
|
||||||
// Tests
|
// Tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{CheckedData, VarInt};
|
use super::{CheckedData, VarInt};
|
||||||
|
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_int_test() {
|
fn serialize_int_test() {
|
||||||
// bool
|
// bool
|
||||||
assert_eq!(serialize(&false), Ok(vec![0u8]));
|
assert_eq!(serialize(&false), Ok(vec![0u8]));
|
||||||
assert_eq!(serialize(&true), Ok(vec![1u8]));
|
assert_eq!(serialize(&true), Ok(vec![1u8]));
|
||||||
// u8
|
// u8
|
||||||
assert_eq!(serialize(&1u8), Ok(vec![1u8]));
|
assert_eq!(serialize(&1u8), Ok(vec![1u8]));
|
||||||
assert_eq!(serialize(&0u8), Ok(vec![0u8]));
|
assert_eq!(serialize(&0u8), Ok(vec![0u8]));
|
||||||
assert_eq!(serialize(&255u8), Ok(vec![255u8]));
|
assert_eq!(serialize(&255u8), Ok(vec![255u8]));
|
||||||
// u16
|
// u16
|
||||||
assert_eq!(serialize(&1u16), Ok(vec![1u8, 0]));
|
assert_eq!(serialize(&1u16), Ok(vec![1u8, 0]));
|
||||||
assert_eq!(serialize(&256u16), Ok(vec![0u8, 1]));
|
assert_eq!(serialize(&256u16), Ok(vec![0u8, 1]));
|
||||||
assert_eq!(serialize(&5000u16), Ok(vec![136u8, 19]));
|
assert_eq!(serialize(&5000u16), Ok(vec![136u8, 19]));
|
||||||
// u32
|
// u32
|
||||||
assert_eq!(serialize(&1u32), Ok(vec![1u8, 0, 0, 0]));
|
assert_eq!(serialize(&1u32), Ok(vec![1u8, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&256u32), Ok(vec![0u8, 1, 0, 0]));
|
assert_eq!(serialize(&256u32), Ok(vec![0u8, 1, 0, 0]));
|
||||||
assert_eq!(serialize(&5000u32), Ok(vec![136u8, 19, 0, 0]));
|
assert_eq!(serialize(&5000u32), Ok(vec![136u8, 19, 0, 0]));
|
||||||
assert_eq!(serialize(&500000u32), Ok(vec![32u8, 161, 7, 0]));
|
assert_eq!(serialize(&500000u32), Ok(vec![32u8, 161, 7, 0]));
|
||||||
assert_eq!(serialize(&168430090u32), Ok(vec![10u8, 10, 10, 10]));
|
assert_eq!(serialize(&168430090u32), Ok(vec![10u8, 10, 10, 10]));
|
||||||
// TODO: test negative numbers
|
// TODO: test negative numbers
|
||||||
assert_eq!(serialize(&1i32), Ok(vec![1u8, 0, 0, 0]));
|
assert_eq!(serialize(&1i32), Ok(vec![1u8, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&256i32), Ok(vec![0u8, 1, 0, 0]));
|
assert_eq!(serialize(&256i32), Ok(vec![0u8, 1, 0, 0]));
|
||||||
assert_eq!(serialize(&5000i32), Ok(vec![136u8, 19, 0, 0]));
|
assert_eq!(serialize(&5000i32), Ok(vec![136u8, 19, 0, 0]));
|
||||||
assert_eq!(serialize(&500000i32), Ok(vec![32u8, 161, 7, 0]));
|
assert_eq!(serialize(&500000i32), Ok(vec![32u8, 161, 7, 0]));
|
||||||
assert_eq!(serialize(&168430090i32), Ok(vec![10u8, 10, 10, 10]));
|
assert_eq!(serialize(&168430090i32), Ok(vec![10u8, 10, 10, 10]));
|
||||||
// u64
|
// u64
|
||||||
assert_eq!(serialize(&1u64), Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&1u64), Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&256u64), Ok(vec![0u8, 1, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&256u64), Ok(vec![0u8, 1, 0, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&5000u64), Ok(vec![136u8, 19, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&5000u64), Ok(vec![136u8, 19, 0, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&500000u64), Ok(vec![32u8, 161, 7, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&500000u64), Ok(vec![32u8, 161, 7, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&723401728380766730u64), Ok(vec![10u8, 10, 10, 10, 10, 10, 10, 10]));
|
assert_eq!(serialize(&723401728380766730u64), Ok(vec![10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||||
// TODO: test negative numbers
|
// TODO: test negative numbers
|
||||||
assert_eq!(serialize(&1i64), Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&1i64), Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&256i64), Ok(vec![0u8, 1, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&256i64), Ok(vec![0u8, 1, 0, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&5000i64), Ok(vec![136u8, 19, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&5000i64), Ok(vec![136u8, 19, 0, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&500000i64), Ok(vec![32u8, 161, 7, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&500000i64), Ok(vec![32u8, 161, 7, 0, 0, 0, 0, 0]));
|
||||||
assert_eq!(serialize(&723401728380766730i64), Ok(vec![10u8, 10, 10, 10, 10, 10, 10, 10]));
|
assert_eq!(serialize(&723401728380766730i64), Ok(vec![10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_varint_test() {
|
fn serialize_varint_test() {
|
||||||
assert_eq!(serialize(&VarInt(10)), Ok(vec![10u8]));
|
assert_eq!(serialize(&VarInt(10)), Ok(vec![10u8]));
|
||||||
assert_eq!(serialize(&VarInt(0xFC)), Ok(vec![0xFCu8]));
|
assert_eq!(serialize(&VarInt(0xFC)), Ok(vec![0xFCu8]));
|
||||||
assert_eq!(serialize(&VarInt(0xFD)), Ok(vec![0xFDu8, 0xFD, 0]));
|
assert_eq!(serialize(&VarInt(0xFD)), Ok(vec![0xFDu8, 0xFD, 0]));
|
||||||
assert_eq!(serialize(&VarInt(0xFFF)), Ok(vec![0xFDu8, 0xFF, 0xF]));
|
assert_eq!(serialize(&VarInt(0xFFF)), Ok(vec![0xFDu8, 0xFF, 0xF]));
|
||||||
assert_eq!(serialize(&VarInt(0xF0F0F0F)), Ok(vec![0xFEu8, 0xF, 0xF, 0xF, 0xF]));
|
assert_eq!(serialize(&VarInt(0xF0F0F0F)), Ok(vec![0xFEu8, 0xF, 0xF, 0xF, 0xF]));
|
||||||
assert_eq!(serialize(&VarInt(0xF0F0F0F0F0E0)), Ok(vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]));
|
assert_eq!(serialize(&VarInt(0xF0F0F0F0F0E0)), Ok(vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_checkeddata_test() {
|
fn serialize_checkeddata_test() {
|
||||||
let cd = CheckedData(vec![1u8, 2, 3, 4, 5]);
|
let cd = CheckedData(vec![1u8, 2, 3, 4, 5]);
|
||||||
assert_eq!(serialize(&cd), Ok(vec![5, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]));
|
assert_eq!(serialize(&cd), Ok(vec![5, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_vector_test() {
|
fn serialize_vector_test() {
|
||||||
assert_eq!(serialize(&vec![1u8, 2, 3]), Ok(vec![3u8, 1, 2, 3]));
|
assert_eq!(serialize(&vec![1u8, 2, 3]), Ok(vec![3u8, 1, 2, 3]));
|
||||||
assert_eq!(serialize(&[1u8, 2, 3].as_slice()), Ok(vec![3u8, 1, 2, 3]));
|
assert_eq!(serialize(&[1u8, 2, 3].as_slice()), Ok(vec![3u8, 1, 2, 3]));
|
||||||
// TODO: test vectors of more interesting objects
|
// TODO: test vectors of more interesting objects
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_strbuf_test() {
|
fn serialize_strbuf_test() {
|
||||||
assert_eq!(serialize(&"Andrew".to_string()), Ok(vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]));
|
assert_eq!(serialize(&"Andrew".to_string()), Ok(vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_box_test() {
|
fn serialize_box_test() {
|
||||||
assert_eq!(serialize(&Box::new(1u8)), Ok(vec![1u8]));
|
assert_eq!(serialize(&Box::new(1u8)), Ok(vec![1u8]));
|
||||||
assert_eq!(serialize(&Box::new(1u16)), Ok(vec![1u8, 0]));
|
assert_eq!(serialize(&Box::new(1u16)), Ok(vec![1u8, 0]));
|
||||||
assert_eq!(serialize(&Box::new(1u64)), Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&Box::new(1u64)), Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_option_test() {
|
fn serialize_option_test() {
|
||||||
let none_ser = serialize(&None::<u8>);
|
let none_ser = serialize(&None::<u8>);
|
||||||
let some_ser = serialize(&Some(0xFFu8));
|
let some_ser = serialize(&Some(0xFFu8));
|
||||||
assert_eq!(none_ser, Ok(vec![0]));
|
assert_eq!(none_ser, Ok(vec![0]));
|
||||||
assert_eq!(some_ser, Ok(vec![1, 0xFF]));
|
assert_eq!(some_ser, Ok(vec![1, 0xFF]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_int_test() {
|
fn deserialize_int_test() {
|
||||||
// bool
|
// bool
|
||||||
assert_eq!(deserialize(vec![58u8, 0]), Ok(true));
|
assert_eq!(deserialize(vec![58u8, 0]), Ok(true));
|
||||||
assert_eq!(deserialize(vec![58u8]), Ok(true));
|
assert_eq!(deserialize(vec![58u8]), Ok(true));
|
||||||
assert_eq!(deserialize(vec![1u8]), Ok(true));
|
assert_eq!(deserialize(vec![1u8]), Ok(true));
|
||||||
assert_eq!(deserialize(vec![0u8]), Ok(false));
|
assert_eq!(deserialize(vec![0u8]), Ok(false));
|
||||||
assert_eq!(deserialize(vec![0u8, 1]), Ok(false));
|
assert_eq!(deserialize(vec![0u8, 1]), Ok(false));
|
||||||
|
|
||||||
// u8
|
// u8
|
||||||
assert_eq!(deserialize(vec![58u8]), Ok(58u8));
|
assert_eq!(deserialize(vec![58u8]), Ok(58u8));
|
||||||
|
|
||||||
// u16
|
// u16
|
||||||
assert_eq!(deserialize(vec![0x01u8, 0x02]), Ok(0x0201u16));
|
assert_eq!(deserialize(vec![0x01u8, 0x02]), Ok(0x0201u16));
|
||||||
assert_eq!(deserialize(vec![0xABu8, 0xCD]), Ok(0xCDABu16));
|
assert_eq!(deserialize(vec![0xABu8, 0xCD]), Ok(0xCDABu16));
|
||||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D]), Ok(0xDA0u16));
|
assert_eq!(deserialize(vec![0xA0u8, 0x0D]), Ok(0xDA0u16));
|
||||||
let failure16: IoResult<u16> = deserialize(vec![1u8]);
|
let failure16: io::Result<u16> = deserialize(vec![1u8]);
|
||||||
assert!(failure16.is_err());
|
assert!(failure16.is_err());
|
||||||
|
|
||||||
// u32
|
// u32
|
||||||
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0]), Ok(0xCDABu32));
|
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0]), Ok(0xCDABu32));
|
||||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0xCD]), Ok(0xCDAB0DA0u32));
|
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0xCD]), Ok(0xCDAB0DA0u32));
|
||||||
let failure32: IoResult<u32> = deserialize(vec![1u8, 2, 3]);
|
let failure32: io::Result<u32> = deserialize(vec![1u8, 2, 3]);
|
||||||
assert!(failure32.is_err());
|
assert!(failure32.is_err());
|
||||||
// TODO: test negative numbers
|
// TODO: test negative numbers
|
||||||
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0]), Ok(0xCDABi32));
|
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0]), Ok(0xCDABi32));
|
||||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0x2D]), Ok(0x2DAB0DA0i32));
|
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0x2D]), Ok(0x2DAB0DA0i32));
|
||||||
let failurei32: IoResult<i32> = deserialize(vec![1u8, 2, 3]);
|
let failurei32: io::Result<i32> = deserialize(vec![1u8, 2, 3]);
|
||||||
assert!(failurei32.is_err());
|
assert!(failurei32.is_err());
|
||||||
|
|
||||||
// u64
|
// u64
|
||||||
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]), Ok(0xCDABu64));
|
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]), Ok(0xCDABu64));
|
||||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]), Ok(0x99000099CDAB0DA0u64));
|
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]), Ok(0x99000099CDAB0DA0u64));
|
||||||
let failure64: IoResult<u64> = deserialize(vec![1u8, 2, 3, 4, 5, 6, 7]);
|
let failure64: io::Result<u64> = deserialize(vec![1u8, 2, 3, 4, 5, 6, 7]);
|
||||||
assert!(failure64.is_err());
|
assert!(failure64.is_err());
|
||||||
// TODO: test negative numbers
|
// TODO: test negative numbers
|
||||||
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]), Ok(0xCDABi64));
|
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0, 0, 0, 0, 0]), Ok(0xCDABi64));
|
||||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]), Ok(-0x66ffff663254f260i64));
|
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99]), Ok(-0x66ffff663254f260i64));
|
||||||
let failurei64: IoResult<i64> = deserialize(vec![1u8, 2, 3, 4, 5, 6, 7]);
|
let failurei64: io::Result<i64> = deserialize(vec![1u8, 2, 3, 4, 5, 6, 7]);
|
||||||
assert!(failurei64.is_err());
|
assert!(failurei64.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_vec_test() {
|
fn deserialize_vec_test() {
|
||||||
assert_eq!(deserialize(vec![3u8, 2, 3, 4]), Ok(vec![2u8, 3, 4]));
|
assert_eq!(deserialize(vec![3u8, 2, 3, 4]), Ok(vec![2u8, 3, 4]));
|
||||||
assert_eq!(deserialize(vec![4u8, 2, 3, 4, 5, 6]), Ok(vec![2u8, 3, 4, 5]));
|
assert_eq!(deserialize(vec![4u8, 2, 3, 4, 5, 6]), Ok(vec![2u8, 3, 4, 5]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_strbuf_test() {
|
fn deserialize_strbuf_test() {
|
||||||
assert_eq!(deserialize(vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]), Ok(String::from_str("Andrew")));
|
assert_eq!(deserialize(vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]), Ok(String::from_str("Andrew")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_checkeddata_test() {
|
fn deserialize_checkeddata_test() {
|
||||||
let cd: IoResult<CheckedData> = deserialize(vec![5u8, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]);
|
let cd: io::Result<CheckedData> = deserialize(vec![5u8, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]);
|
||||||
assert_eq!(cd, Ok(CheckedData(vec![1u8, 2, 3, 4, 5])));
|
assert_eq!(cd, Ok(CheckedData(vec![1u8, 2, 3, 4, 5])));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_option_test() {
|
fn deserialize_option_test() {
|
||||||
let none: IoResult<Option<u8>> = deserialize(vec![0u8]);
|
let none: io::Result<Option<u8>> = deserialize(vec![0u8]);
|
||||||
let good: IoResult<Option<u8>> = deserialize(vec![1u8, 0xFF]);
|
let good: io::Result<Option<u8>> = deserialize(vec![1u8, 0xFF]);
|
||||||
let bad: IoResult<Option<u8>> = deserialize(vec![2u8]);
|
let bad: io::Result<Option<u8>> = deserialize(vec![2u8]);
|
||||||
assert!(bad.is_err());
|
assert!(bad.is_err());
|
||||||
assert_eq!(none, Ok(None));
|
assert_eq!(none, Ok(None));
|
||||||
assert_eq!(good, Ok(Some(0xFF)));
|
assert_eq!(good, Ok(Some(0xFF)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_box_test() {
|
fn deserialize_box_test() {
|
||||||
let zero: IoResult<Box<u8>> = deserialize(vec![0u8]);
|
let zero: io::Result<Box<u8>> = deserialize(vec![0u8]);
|
||||||
let one: IoResult<Box<u8>> = deserialize(vec![1u8]);
|
let one: io::Result<Box<u8>> = deserialize(vec![1u8]);
|
||||||
assert_eq!(zero, Ok(Box::new(0)));
|
assert_eq!(zero, Ok(Box::new(0)));
|
||||||
assert_eq!(one, Ok(Box::new(1)));
|
assert_eq!(one, Ok(Box::new(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,73 +25,72 @@ use network::constants::Network;
|
||||||
use network::message;
|
use network::message;
|
||||||
use network::message::NetworkMessage::Verack;
|
use network::message::NetworkMessage::Verack;
|
||||||
use network::socket::Socket;
|
use network::socket::Socket;
|
||||||
|
use util;
|
||||||
|
|
||||||
/// A message which can be sent on the Bitcoin network
|
/// A message which can be sent on the Bitcoin network
|
||||||
pub trait Listener {
|
pub trait Listener {
|
||||||
/// Return a string encoding of the peer's network address
|
/// Return a string encoding of the peer's network address
|
||||||
fn peer<'a>(&'a self) -> &'a str;
|
fn peer<'a>(&'a self) -> &'a str;
|
||||||
/// Return the port we have connected to the peer on
|
/// Return the port we have connected to the peer on
|
||||||
fn port(&self) -> u16;
|
fn port(&self) -> u16;
|
||||||
/// Return the network this `Listener` is operating on
|
/// Return the network this `Listener` is operating on
|
||||||
fn network(&self) -> Network;
|
fn network(&self) -> Network;
|
||||||
/// Main listen loop
|
/// Main listen loop
|
||||||
fn start(&self) -> io::Result<(Receiver<message::SocketResponse>, Socket)> {
|
fn start(&self) -> Result<(Receiver<message::SocketResponse>, Socket), util::Error> {
|
||||||
// Open socket
|
// Open socket
|
||||||
let mut ret_sock = Socket::new(self.network());
|
let mut ret_sock = Socket::new(self.network());
|
||||||
match ret_sock.connect(self.peer(), self.port()) {
|
if let Err(e) = ret_sock.connect(self.peer(), self.port()) {
|
||||||
Ok(_) => {},
|
return Err(util::Error::Detail("listener".to_string(), Box::new(e)));
|
||||||
Err(_) => return Err(io::Error::new(io::ErrorKind::ConnectionFailed,
|
|
||||||
"Listener connection failed", None))
|
|
||||||
}
|
|
||||||
let mut sock = ret_sock.clone();
|
|
||||||
|
|
||||||
let (recv_tx, recv_rx) = channel();
|
|
||||||
|
|
||||||
// Send version message to peer
|
|
||||||
let version_message = try!(sock.version_message(0));
|
|
||||||
try!(sock.send_message(version_message));
|
|
||||||
|
|
||||||
// Message loop
|
|
||||||
thread::spawn(move || {
|
|
||||||
let mut handshake_complete = false;
|
|
||||||
let mut sock = sock;
|
|
||||||
loop {
|
|
||||||
// Receive new message
|
|
||||||
match sock.receive_message() {
|
|
||||||
Ok(payload) => {
|
|
||||||
// React to any network messages that affect our state.
|
|
||||||
match payload {
|
|
||||||
// Make an exception for verack since there is no response required
|
|
||||||
Verack => {
|
|
||||||
// TODO: when the timeout stuff in std::io::net::tcp is sorted out we should
|
|
||||||
// actually time out if the verack doesn't come in in time
|
|
||||||
if handshake_complete {
|
|
||||||
println!("Received second verack (peer is misbehaving)");
|
|
||||||
} else {
|
|
||||||
handshake_complete = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
// We have to pass the message to the main thread for processing,
|
|
||||||
// unfortunately, because sipa says we have to handle everything
|
|
||||||
// in order.
|
|
||||||
recv_tx.send(message::SocketResponse::MessageReceived(payload));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
// On failure we send an error message to the main thread, along with
|
|
||||||
// a channel to receive an acknowledgement that we may tear down this
|
|
||||||
// thread. (If we simply exited immediately, the channel would be torn
|
|
||||||
// down and the main thread would never see the error message.)
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
recv_tx.send(message::SocketResponse::ConnectionFailed(e, tx));
|
|
||||||
rx.recv();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
let mut sock = ret_sock.clone();
|
||||||
});
|
|
||||||
Ok((recv_rx, ret_sock))
|
let (recv_tx, recv_rx) = channel();
|
||||||
}
|
|
||||||
|
// Send version message to peer
|
||||||
|
let version_message = try!(sock.version_message(0));
|
||||||
|
try!(sock.send_message(version_message));
|
||||||
|
|
||||||
|
// Message loop
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut handshake_complete = false;
|
||||||
|
let mut sock = sock;
|
||||||
|
loop {
|
||||||
|
// Receive new message
|
||||||
|
match sock.receive_message() {
|
||||||
|
Ok(payload) => {
|
||||||
|
// React to any network messages that affect our state.
|
||||||
|
match payload {
|
||||||
|
// Make an exception for verack since there is no response required
|
||||||
|
Verack => {
|
||||||
|
// TODO: when the timeout stuff in std::io::net::tcp is sorted out we should
|
||||||
|
// actually time out if the verack doesn't come in in time
|
||||||
|
if handshake_complete {
|
||||||
|
println!("Received second verack (peer is misbehaving)");
|
||||||
|
} else {
|
||||||
|
handshake_complete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
// We have to pass the message to the main thread for processing,
|
||||||
|
// unfortunately, because sipa says we have to handle everything
|
||||||
|
// in order.
|
||||||
|
recv_tx.send(message::SocketResponse::MessageReceived(payload));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// On failure we send an error message to the main thread, along with
|
||||||
|
// a channel to receive an acknowledgement that we may tear down this
|
||||||
|
// thread. (If we simply exited immediately, the channel would be torn
|
||||||
|
// down and the main thread would never see the error message.)
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
recv_tx.send(message::SocketResponse::ConnectionFailed(e, tx));
|
||||||
|
rx.recv();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok((recv_rx, ret_sock))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -32,205 +32,201 @@ use network::message_blockdata;
|
||||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||||
use network::encodable::CheckedData;
|
use network::encodable::CheckedData;
|
||||||
use network::serialize::{serialize, RawDecoder, SimpleEncoder, SimpleDecoder};
|
use network::serialize::{serialize, RawDecoder, SimpleEncoder, SimpleDecoder};
|
||||||
use util::misc::prepend_err;
|
use util::{self, propagate_err};
|
||||||
|
|
||||||
/// Serializer for command string
|
/// Serializer for command string
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct CommandString(pub String);
|
pub struct CommandString(pub String);
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for CommandString {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for CommandString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
let &CommandString(ref inner_str) = self;
|
let &CommandString(ref inner_str) = self;
|
||||||
let mut rawbytes = [0u8; 12];
|
let mut rawbytes = [0u8; 12];
|
||||||
rawbytes.clone_from_slice(inner_str.as_bytes().as_slice());
|
rawbytes.clone_from_slice(inner_str.as_bytes().as_slice());
|
||||||
rawbytes.consensus_encode(s)
|
rawbytes.consensus_encode(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for CommandString {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for CommandString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<CommandString, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<CommandString, D::Error> {
|
||||||
let rawbytes: [u8; 12] = try!(ConsensusDecodable::consensus_decode(d));
|
let rawbytes: [u8; 12] = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
let rv = iter::FromIterator::from_iter(rawbytes.iter().filter_map(|&u| if u > 0 { Some(u as char) } else { None }));
|
let rv = iter::FromIterator::from_iter(rawbytes.iter().filter_map(|&u| if u > 0 { Some(u as char) } else { None }));
|
||||||
Ok(CommandString(rv))
|
Ok(CommandString(rv))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Network message
|
/// A Network message
|
||||||
pub struct RawNetworkMessage {
|
pub struct RawNetworkMessage {
|
||||||
/// Magic bytes to identify the network these messages are meant for
|
/// Magic bytes to identify the network these messages are meant for
|
||||||
pub magic: u32,
|
pub magic: u32,
|
||||||
/// The actual message data
|
/// The actual message data
|
||||||
pub payload: NetworkMessage
|
pub payload: NetworkMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A response from the peer-connected socket
|
/// A response from the peer-connected socket
|
||||||
pub enum SocketResponse {
|
pub enum SocketResponse {
|
||||||
/// A message was received
|
/// A message was received
|
||||||
MessageReceived(NetworkMessage),
|
MessageReceived(NetworkMessage),
|
||||||
/// An error occured and the socket needs to close
|
/// An error occured and the socket needs to close
|
||||||
ConnectionFailed(io::Error, Sender<()>)
|
ConnectionFailed(util::Error, Sender<()>)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
/// A Network message payload. Proper documentation is available on the Bitcoin
|
/// A Network message payload. Proper documentation is available on the Bitcoin
|
||||||
/// wiki https://en.bitcoin.it/wiki/Protocol_specification
|
/// wiki https://en.bitcoin.it/wiki/Protocol_specification
|
||||||
pub enum NetworkMessage {
|
pub enum NetworkMessage {
|
||||||
/// `version`
|
/// `version`
|
||||||
Version(message_network::VersionMessage),
|
Version(message_network::VersionMessage),
|
||||||
/// `verack`
|
/// `verack`
|
||||||
Verack,
|
Verack,
|
||||||
/// `addr`
|
/// `addr`
|
||||||
Addr(Vec<(u32, Address)>),
|
Addr(Vec<(u32, Address)>),
|
||||||
/// `inv`
|
/// `inv`
|
||||||
Inv(Vec<message_blockdata::Inventory>),
|
Inv(Vec<message_blockdata::Inventory>),
|
||||||
/// `getdata`
|
/// `getdata`
|
||||||
GetData(Vec<message_blockdata::Inventory>),
|
GetData(Vec<message_blockdata::Inventory>),
|
||||||
/// `notfound`
|
/// `notfound`
|
||||||
NotFound(Vec<message_blockdata::Inventory>),
|
NotFound(Vec<message_blockdata::Inventory>),
|
||||||
/// `getblocks`
|
/// `getblocks`
|
||||||
GetBlocks(message_blockdata::GetBlocksMessage),
|
GetBlocks(message_blockdata::GetBlocksMessage),
|
||||||
/// `getheaders`
|
/// `getheaders`
|
||||||
GetHeaders(message_blockdata::GetHeadersMessage),
|
GetHeaders(message_blockdata::GetHeadersMessage),
|
||||||
/// tx
|
/// tx
|
||||||
Tx(transaction::Transaction),
|
Tx(transaction::Transaction),
|
||||||
/// `block`
|
/// `block`
|
||||||
Block(block::Block),
|
Block(block::Block),
|
||||||
/// `headers`
|
/// `headers`
|
||||||
Headers(Vec<block::LoneBlockHeader>),
|
Headers(Vec<block::LoneBlockHeader>),
|
||||||
// TODO: getaddr,
|
// TODO: getaddr,
|
||||||
// TODO: mempool,
|
// TODO: mempool,
|
||||||
// TODO: checkorder,
|
// TODO: checkorder,
|
||||||
// TODO: submitorder,
|
// TODO: submitorder,
|
||||||
// TODO: reply,
|
// TODO: reply,
|
||||||
/// `ping`
|
/// `ping`
|
||||||
Ping(u64),
|
Ping(u64),
|
||||||
/// `pong`
|
/// `pong`
|
||||||
Pong(u64)
|
Pong(u64)
|
||||||
// TODO: reject,
|
// TODO: reject,
|
||||||
// TODO: bloom filtering
|
// TODO: bloom filtering
|
||||||
// TODO: alert
|
// TODO: alert
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawNetworkMessage {
|
impl RawNetworkMessage {
|
||||||
fn command(&self) -> String {
|
fn command(&self) -> String {
|
||||||
match self.payload {
|
match self.payload {
|
||||||
NetworkMessage::Version(_) => "version",
|
NetworkMessage::Version(_) => "version",
|
||||||
NetworkMessage::Verack => "verack",
|
NetworkMessage::Verack => "verack",
|
||||||
NetworkMessage::Addr(_) => "addr",
|
NetworkMessage::Addr(_) => "addr",
|
||||||
NetworkMessage::Inv(_) => "inv",
|
NetworkMessage::Inv(_) => "inv",
|
||||||
NetworkMessage::GetData(_) => "getdata",
|
NetworkMessage::GetData(_) => "getdata",
|
||||||
NetworkMessage::NotFound(_) => "notfound",
|
NetworkMessage::NotFound(_) => "notfound",
|
||||||
NetworkMessage::GetBlocks(_) => "getblocks",
|
NetworkMessage::GetBlocks(_) => "getblocks",
|
||||||
NetworkMessage::GetHeaders(_) => "getheaders",
|
NetworkMessage::GetHeaders(_) => "getheaders",
|
||||||
NetworkMessage::Tx(_) => "tx",
|
NetworkMessage::Tx(_) => "tx",
|
||||||
NetworkMessage::Block(_) => "block",
|
NetworkMessage::Block(_) => "block",
|
||||||
NetworkMessage::Headers(_) => "headers",
|
NetworkMessage::Headers(_) => "headers",
|
||||||
NetworkMessage::Ping(_) => "ping",
|
NetworkMessage::Ping(_) => "ping",
|
||||||
NetworkMessage::Pong(_) => "pong",
|
NetworkMessage::Pong(_) => "pong",
|
||||||
}.to_string()
|
}.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for RawNetworkMessage {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for RawNetworkMessage {
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
try!(self.magic.consensus_encode(s));
|
try!(self.magic.consensus_encode(s));
|
||||||
try!(CommandString(self.command()).consensus_encode(s));
|
try!(CommandString(self.command()).consensus_encode(s));
|
||||||
try!(CheckedData(match self.payload {
|
try!(CheckedData(match self.payload {
|
||||||
NetworkMessage::Version(ref dat) => serialize(dat),
|
NetworkMessage::Version(ref dat) => serialize(dat),
|
||||||
NetworkMessage::Verack => Ok(vec![]),
|
NetworkMessage::Verack => Ok(vec![]),
|
||||||
NetworkMessage::Addr(ref dat) => serialize(dat),
|
NetworkMessage::Addr(ref dat) => serialize(dat),
|
||||||
NetworkMessage::Inv(ref dat) => serialize(dat),
|
NetworkMessage::Inv(ref dat) => serialize(dat),
|
||||||
NetworkMessage::GetData(ref dat) => serialize(dat),
|
NetworkMessage::GetData(ref dat) => serialize(dat),
|
||||||
NetworkMessage::NotFound(ref dat) => serialize(dat),
|
NetworkMessage::NotFound(ref dat) => serialize(dat),
|
||||||
NetworkMessage::GetBlocks(ref dat) => serialize(dat),
|
NetworkMessage::GetBlocks(ref dat) => serialize(dat),
|
||||||
NetworkMessage::GetHeaders(ref dat) => serialize(dat),
|
NetworkMessage::GetHeaders(ref dat) => serialize(dat),
|
||||||
NetworkMessage::Tx(ref dat) => serialize(dat),
|
NetworkMessage::Tx(ref dat) => serialize(dat),
|
||||||
NetworkMessage::Block(ref dat) => serialize(dat),
|
NetworkMessage::Block(ref dat) => serialize(dat),
|
||||||
NetworkMessage::Headers(ref dat) => serialize(dat),
|
NetworkMessage::Headers(ref dat) => serialize(dat),
|
||||||
NetworkMessage::Ping(ref dat) => serialize(dat),
|
NetworkMessage::Ping(ref dat) => serialize(dat),
|
||||||
NetworkMessage::Pong(ref dat) => serialize(dat),
|
NetworkMessage::Pong(ref dat) => serialize(dat),
|
||||||
}.unwrap()).consensus_encode(s));
|
}.unwrap()).consensus_encode(s));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder<Error=io::Error>> ConsensusDecodable<D> for RawNetworkMessage {
|
// TODO: restriction on D::Error is so that `propagate_err` will work;
|
||||||
fn consensus_decode(d: &mut D) -> io::Result<RawNetworkMessage> {
|
// is there a more generic way to handle this?
|
||||||
let magic = try!(ConsensusDecodable::consensus_decode(d));
|
impl<D: SimpleDecoder<Error=util::Error>> ConsensusDecodable<D> for RawNetworkMessage {
|
||||||
let CommandString(cmd): CommandString= try!(ConsensusDecodable::consensus_decode(d));
|
fn consensus_decode(d: &mut D) -> Result<RawNetworkMessage, D::Error> {
|
||||||
let CheckedData(raw_payload): CheckedData = try!(ConsensusDecodable::consensus_decode(d));
|
let magic = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
|
let CommandString(cmd): CommandString= try!(ConsensusDecodable::consensus_decode(d));
|
||||||
|
let CheckedData(raw_payload): CheckedData = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
|
|
||||||
let mut mem_d = RawDecoder::new(Cursor::new(raw_payload));
|
let mut mem_d = RawDecoder::new(Cursor::new(raw_payload));
|
||||||
let payload = match cmd.as_slice() {
|
let payload = match cmd.as_slice() {
|
||||||
"version" => NetworkMessage::Version(try!(prepend_err("version", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"version" => NetworkMessage::Version(try!(propagate_err("version".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"verack" => NetworkMessage::Verack,
|
"verack" => NetworkMessage::Verack,
|
||||||
"addr" => NetworkMessage::Addr(try!(prepend_err("addr", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"addr" => NetworkMessage::Addr(try!(propagate_err("addr".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"inv" => NetworkMessage::Inv(try!(prepend_err("inv", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"inv" => NetworkMessage::Inv(try!(propagate_err("inv".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"getdata" => NetworkMessage::GetData(try!(prepend_err("getdata", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"getdata" => NetworkMessage::GetData(try!(propagate_err("getdata".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"notfound" => NetworkMessage::NotFound(try!(prepend_err("notfound", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"notfound" => NetworkMessage::NotFound(try!(propagate_err("notfound".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"getblocks" => NetworkMessage::GetBlocks(try!(prepend_err("getblocks", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"getblocks" => NetworkMessage::GetBlocks(try!(propagate_err("getblocks".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"getheaders" => NetworkMessage::GetHeaders(try!(prepend_err("getheaders", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"getheaders" => NetworkMessage::GetHeaders(try!(propagate_err("getheaders".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"block" => NetworkMessage::Block(try!(prepend_err("block", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"block" => NetworkMessage::Block(try!(propagate_err("block".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"headers" => NetworkMessage::Headers(try!(prepend_err("headers", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"headers" => NetworkMessage::Headers(try!(propagate_err("headers".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"ping" => NetworkMessage::Ping(try!(prepend_err("ping", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"ping" => NetworkMessage::Ping(try!(propagate_err("ping".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"pong" => NetworkMessage::Ping(try!(prepend_err("pong", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"pong" => NetworkMessage::Ping(try!(propagate_err("pong".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
"tx" => NetworkMessage::Tx(try!(prepend_err("tx", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
"tx" => NetworkMessage::Tx(try!(propagate_err("tx".to_string(), ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||||
cmd => {
|
cmd => return Err(d.error(format!("unrecognized network command `{}`", cmd)))
|
||||||
return Err(io::Error {
|
};
|
||||||
kind: io::ErrorKind::OtherError,
|
Ok(RawNetworkMessage {
|
||||||
desc: "unknown message type",
|
magic: magic,
|
||||||
detail: Some(format!("`{}` not recognized", cmd))
|
payload: payload
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
|
||||||
Ok(RawNetworkMessage {
|
|
||||||
magic: magic,
|
|
||||||
payload: payload
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{RawNetworkMessage, CommandString, Verack, Ping};
|
use super::{RawNetworkMessage, NetworkMessage, CommandString};
|
||||||
|
|
||||||
use std::io::io::Result;
|
use std::io::Result;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_commandstring_test() {
|
fn serialize_commandstring_test() {
|
||||||
let cs = CommandString(String::from_str("Andrew"));
|
let cs = CommandString(String::from_str("Andrew"));
|
||||||
assert_eq!(serialize(&cs), Ok(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(serialize(&cs), Ok(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_commandstring_test() {
|
fn deserialize_commandstring_test() {
|
||||||
let cs: io::Result<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
let cs: io::Result<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
||||||
assert!(cs.is_ok());
|
assert!(cs.is_ok());
|
||||||
assert_eq!(cs.unwrap(), CommandString(String::from_str("Andrew")));
|
assert_eq!(cs.unwrap(), CommandString(String::from_str("Andrew")));
|
||||||
|
|
||||||
let short_cs: io::Result<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
|
let short_cs: io::Result<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
|
||||||
assert!(short_cs.is_err());
|
assert!(short_cs.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_verack_test() {
|
fn serialize_verack_test() {
|
||||||
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: Verack }),
|
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::Verack }),
|
||||||
Ok(vec![0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x61,
|
Ok(vec![0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x61,
|
||||||
0x63, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x63, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]));
|
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_ping_test() {
|
fn serialize_ping_test() {
|
||||||
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: Ping(100) }),
|
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::Ping(100) }),
|
||||||
Ok(vec![0xf9, 0xbe, 0xb4, 0xd9, 0x70, 0x69, 0x6e, 0x67,
|
Ok(vec![0xf9, 0xbe, 0xb4, 0xd9, 0x70, 0x69, 0x6e, 0x67,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x08, 0x00, 0x00, 0x00, 0x24, 0x67, 0xf1, 0x1d,
|
0x08, 0x00, 0x00, 0x00, 0x24, 0x67, 0xf1, 0x1d,
|
||||||
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
|
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{GetHeadersMessage, GetBlocksMessage};
|
use super::{GetHeadersMessage, GetBlocksMessage};
|
||||||
|
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
@ -141,7 +141,7 @@ mod tests {
|
||||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||||
|
|
||||||
let decode: IoResult<GetBlocksMessage> = deserialize(from_sat.clone());
|
let decode: io::Result<GetBlocksMessage> = deserialize(from_sat.clone());
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
let real_decode = decode.unwrap();
|
let real_decode = decode.unwrap();
|
||||||
assert_eq!(real_decode.version, 70002);
|
assert_eq!(real_decode.version, 70002);
|
||||||
|
@ -157,7 +157,7 @@ mod tests {
|
||||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||||
|
|
||||||
let decode: IoResult<GetHeadersMessage> = deserialize(from_sat.clone());
|
let decode: io::Result<GetHeadersMessage> = deserialize(from_sat.clone());
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
let real_decode = decode.unwrap();
|
let real_decode = decode.unwrap();
|
||||||
assert_eq!(real_decode.version, 70002);
|
assert_eq!(real_decode.version, 70002);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -23,95 +23,88 @@ use std::io;
|
||||||
use network::constants;
|
use network::constants;
|
||||||
use network::address::Address;
|
use network::address::Address;
|
||||||
use network::socket::Socket;
|
use network::socket::Socket;
|
||||||
|
use util;
|
||||||
|
|
||||||
/// Some simple messages
|
/// Some simple messages
|
||||||
|
|
||||||
/// The `version` message
|
/// The `version` message
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct VersionMessage {
|
pub struct VersionMessage {
|
||||||
/// The P2P network protocol version
|
/// The P2P network protocol version
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
/// A bitmask describing the services supported by this node
|
/// A bitmask describing the services supported by this node
|
||||||
pub services: u64,
|
pub services: u64,
|
||||||
/// The time at which the `version` message was sent
|
/// The time at which the `version` message was sent
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
/// The network address of the peer receiving the message
|
/// The network address of the peer receiving the message
|
||||||
pub receiver: Address,
|
pub receiver: Address,
|
||||||
/// The network address of the peer sending the message
|
/// The network address of the peer sending the message
|
||||||
pub sender: Address,
|
pub sender: Address,
|
||||||
/// A random nonce used to detect loops in the network
|
/// A random nonce used to detect loops in the network
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
/// A string describing the peer's software
|
/// A string describing the peer's software
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
/// The height of the maxmimum-work blockchain that the peer is aware of
|
/// The height of the maxmimum-work blockchain that the peer is aware of
|
||||||
pub start_height: i32,
|
pub start_height: i32,
|
||||||
/// Whether the receiving peer should relay messages to the sender; used
|
/// Whether the receiving peer should relay messages to the sender; used
|
||||||
/// if the sender is bandwidth-limited and would like to support bloom
|
/// if the sender is bandwidth-limited and would like to support bloom
|
||||||
/// filtering. Defaults to true.
|
/// filtering. Defaults to true.
|
||||||
pub relay: bool
|
pub relay: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VersionMessage {
|
impl VersionMessage {
|
||||||
// TODO: we have fixed services and relay to 0
|
// TODO: we have fixed services and relay to 0
|
||||||
/// Constructs a new `version` message
|
/// Constructs a new `version` message
|
||||||
pub fn new(timestamp: i64, mut socket: Socket, nonce: u64, start_height: i32) -> io::Result<VersionMessage> {
|
pub fn new(timestamp: i64, mut socket: Socket, nonce: u64, start_height: i32) -> Result<VersionMessage, util::Error> {
|
||||||
let recv_addr = socket.receiver_address();
|
let recv_addr = try!(socket.receiver_address());
|
||||||
let send_addr = socket.sender_address();
|
let send_addr = try!(socket.sender_address());
|
||||||
// If we are not connected, we might not be able to get these address.s
|
|
||||||
match recv_addr {
|
Ok(VersionMessage {
|
||||||
Err(e) => { return Err(e); }
|
version: constants::PROTOCOL_VERSION,
|
||||||
_ => {}
|
services: socket.services,
|
||||||
|
timestamp: timestamp,
|
||||||
|
receiver: recv_addr,
|
||||||
|
sender: send_addr,
|
||||||
|
nonce: nonce,
|
||||||
|
user_agent: socket.user_agent,
|
||||||
|
start_height: start_height,
|
||||||
|
relay: false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
match send_addr {
|
|
||||||
Err(e) => { return Err(e); }
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Ok(VersionMessage {
|
|
||||||
version: constants::PROTOCOL_VERSION,
|
|
||||||
services: socket.services,
|
|
||||||
timestamp: timestamp,
|
|
||||||
receiver: recv_addr.unwrap(),
|
|
||||||
sender: send_addr.unwrap(),
|
|
||||||
nonce: nonce,
|
|
||||||
user_agent: socket.user_agent,
|
|
||||||
start_height: start_height,
|
|
||||||
relay: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(VersionMessage, version, services, timestamp,
|
impl_consensus_encoding!(VersionMessage, version, services, timestamp,
|
||||||
receiver, sender, nonce,
|
receiver, sender, nonce,
|
||||||
user_agent, start_height, relay);
|
user_agent, start_height, relay);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::VersionMessage;
|
use super::VersionMessage;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn version_message_test() {
|
fn version_message_test() {
|
||||||
// This message is from my satoshi node, morning of May 27 2014
|
// This message is from my satoshi node, morning of May 27 2014
|
||||||
let from_sat = "721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001".from_hex().unwrap();
|
let from_sat = "721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001".from_hex().unwrap();
|
||||||
|
|
||||||
let decode: io::Result<VersionMessage> = deserialize(from_sat.clone());
|
let decode: io::Result<VersionMessage> = deserialize(from_sat.clone());
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
let real_decode = decode.unwrap();
|
let real_decode = decode.unwrap();
|
||||||
assert_eq!(real_decode.version, 70002);
|
assert_eq!(real_decode.version, 70002);
|
||||||
assert_eq!(real_decode.services, 1);
|
assert_eq!(real_decode.services, 1);
|
||||||
assert_eq!(real_decode.timestamp, 1401217254);
|
assert_eq!(real_decode.timestamp, 1401217254);
|
||||||
// address decodes should be covered by Address tests
|
// address decodes should be covered by Address tests
|
||||||
assert_eq!(real_decode.nonce, 16735069437859780935);
|
assert_eq!(real_decode.nonce, 16735069437859780935);
|
||||||
assert_eq!(real_decode.user_agent, String::from_str("/Satoshi:0.9.99/"));
|
assert_eq!(real_decode.user_agent, String::from_str("/Satoshi:0.9.99/"));
|
||||||
assert_eq!(real_decode.start_height, 302892);
|
assert_eq!(real_decode.start_height, 302892);
|
||||||
assert_eq!(real_decode.relay, true);
|
assert_eq!(real_decode.relay, true);
|
||||||
|
|
||||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,74 +21,73 @@
|
||||||
|
|
||||||
use collections::Vec;
|
use collections::Vec;
|
||||||
use std::io::{self, Cursor, Read, Write};
|
use std::io::{self, Cursor, Read, Write};
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt, ReadBytesExt};
|
||||||
use serialize::hex::ToHex;
|
use serialize::hex::ToHex;
|
||||||
|
|
||||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||||
use util::hash::Sha256dHash;
|
use util::hash::Sha256dHash;
|
||||||
|
use util;
|
||||||
|
|
||||||
/// Objects which are referred to by hash
|
/// Objects which are referred to by hash
|
||||||
pub trait BitcoinHash {
|
pub trait BitcoinHash {
|
||||||
/// Produces a Sha256dHash which can be used to refer to the object
|
/// Produces a Sha256dHash which can be used to refer to the object
|
||||||
fn bitcoin_hash(&self) -> Sha256dHash;
|
fn bitcoin_hash(&self) -> Sha256dHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitcoinHash for Vec<u8> {
|
impl BitcoinHash for Vec<u8> {
|
||||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
#[inline]
|
||||||
Sha256dHash::from_data(self.as_slice())
|
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||||
}
|
Sha256dHash::from_data(&self[..])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode an object into a vector
|
/// Encode an object into a vector
|
||||||
pub fn serialize<T: ConsensusEncodable<RawEncoder<Cursor<Vec<u8>>>>>(obj: &T) -> io::Result<Vec<u8>> {
|
pub fn serialize<T>(data: &T) -> Result<Vec<u8>, util::Error>
|
||||||
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
where T: ConsensusEncodable<RawEncoder<Cursor<Vec<u8>>>>,
|
||||||
try!(obj.consensus_encode(&mut encoder));
|
{
|
||||||
Ok(encoder.unwrap().unwrap())
|
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
||||||
|
try!(data.consensus_encode(&mut encoder));
|
||||||
|
Ok(encoder.into_inner().into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode an object into a hex-encoded string
|
/// Encode an object into a hex-encoded string
|
||||||
pub fn serialize_hex<T: ConsensusEncodable<RawEncoder<Cursor<Vec<u8>>>>>(obj: &T) -> io::Result<String> {
|
pub fn serialize_hex<T>(data: &T) -> Result<String, util::Error>
|
||||||
let serial = try!(serialize(obj));
|
where T: ConsensusEncodable<RawEncoder<Cursor<Vec<u8>>>>
|
||||||
Ok(serial.as_slice().to_hex())
|
{
|
||||||
|
let serial = try!(serialize(data));
|
||||||
|
Ok(serial.to_hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize an object from a vector
|
/// Deserialize an object from a vector
|
||||||
pub fn deserialize<T: ConsensusDecodable<RawDecoder<Cursor<Vec<u8>>>>>(data: Vec<u8>) -> io::Result<T> {
|
pub fn deserialize<'a, T>(data: &'a [u8]) -> Result<T, util::Error>
|
||||||
let mut decoder = RawDecoder::new(Cursor::new(data));
|
where T: ConsensusDecodable<RawDecoder<Cursor<&'a [u8]>>>
|
||||||
ConsensusDecodable::consensus_decode(&mut decoder)
|
{
|
||||||
|
let mut decoder = RawDecoder::new(Cursor::new(data));
|
||||||
|
ConsensusDecodable::consensus_decode(&mut decoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An encoder for raw binary data
|
/// An encoder for raw binary data
|
||||||
pub struct RawEncoder<W> {
|
pub struct RawEncoder<W> {
|
||||||
writer: W
|
writer: W
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An decoder for raw binary data
|
/// An decoder for raw binary data
|
||||||
pub struct RawDecoder<R> {
|
pub struct RawDecoder<R> {
|
||||||
reader: R
|
reader: R
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W:Write> RawEncoder<W> {
|
impl<W: Write> RawEncoder<W> {
|
||||||
/// Constructor
|
/// Constructor
|
||||||
pub fn new(writer: W) -> RawEncoder<W> {
|
pub fn new(writer: W) -> RawEncoder<W> { RawEncoder { writer: writer } }
|
||||||
RawEncoder { writer: writer }
|
/// Returns the underlying Writer
|
||||||
}
|
pub fn into_inner(self) -> W { self.writer }
|
||||||
|
|
||||||
/// Returns the underlying Writer
|
|
||||||
pub fn unwrap(self) -> W {
|
|
||||||
self.writer
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R:Read> RawDecoder<R> {
|
impl<R: Read> RawDecoder<R> {
|
||||||
/// Constructor
|
/// Constructor
|
||||||
pub fn new(reader: R) -> RawDecoder<R> {
|
pub fn new(reader: R) -> RawDecoder<R> { RawDecoder { reader: reader } }
|
||||||
RawDecoder { reader: reader }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the underlying Reader
|
/// Returns the underlying Reader
|
||||||
pub fn unwrap(self) -> R {
|
pub fn into_inner(self) -> R { self.reader }
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simple Encoder trait
|
/// A simple Encoder trait
|
||||||
|
@ -143,68 +142,81 @@ pub trait SimpleDecoder {
|
||||||
fn read_bool(&mut self) -> Result<bool, Self::Error>;
|
fn read_bool(&mut self) -> Result<bool, Self::Error>;
|
||||||
|
|
||||||
/// Signal a decoding error
|
/// Signal a decoding error
|
||||||
fn error(&mut self, err: &str) -> Self::Error;
|
fn error(&mut self, err: String) -> Self::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: trait reform: impl SimpleEncoder for every Encoder, ditto for Decoder
|
macro_rules! encoder_fn {
|
||||||
|
($name:ident, $val_type:ty, $writefn:ident) => {
|
||||||
|
#[inline]
|
||||||
|
fn $name(&mut self, v: $val_type) -> Result<(), util::Error> {
|
||||||
|
self.writer.$writefn::<LittleEndian>(v).map_err(util::Error::ByteOrder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! decoder_fn {
|
||||||
|
($name:ident, $val_type:ty, $readfn:ident) => {
|
||||||
|
#[inline]
|
||||||
|
fn $name(&mut self) -> Result<$val_type, util::Error> {
|
||||||
|
self.reader.$readfn::<LittleEndian>().map_err(util::Error::ByteOrder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<W: Write> SimpleEncoder for RawEncoder<W> {
|
impl<W: Write> SimpleEncoder for RawEncoder<W> {
|
||||||
type Error = io::Error;
|
type Error = util::Error;
|
||||||
|
|
||||||
#[inline]
|
encoder_fn!(emit_u64, u64, write_u64);
|
||||||
fn emit_u64(&mut self, v: u64) -> io::Result<()> { self.writer.write_le_u64(v) }
|
encoder_fn!(emit_u32, u32, write_u32);
|
||||||
#[inline]
|
encoder_fn!(emit_u16, u16, write_u16);
|
||||||
fn emit_u32(&mut self, v: u32) -> io::Result<()> { self.writer.write_le_u32(v) }
|
encoder_fn!(emit_i64, i64, write_i64);
|
||||||
#[inline]
|
encoder_fn!(emit_i32, i32, write_i32);
|
||||||
fn emit_u16(&mut self, v: u16) -> io::Result<()> { self.writer.write_le_u16(v) }
|
encoder_fn!(emit_i16, i16, write_i16);
|
||||||
#[inline]
|
|
||||||
fn emit_u8(&mut self, v: u8) -> io::Result<()> { self.writer.write_u8(v) }
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_i64(&mut self, v: i64) -> io::Result<()> { self.writer.write_le_i64(v) }
|
fn emit_i8(&mut self, v: i8) -> Result<(), util::Error> {
|
||||||
#[inline]
|
self.writer.write_i8(v).map_err(util::Error::ByteOrder)
|
||||||
fn emit_i32(&mut self, v: i32) -> io::Result<()> { self.writer.write_le_i32(v) }
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_i16(&mut self, v: i16) -> io::Result<()> { self.writer.write_le_i16(v) }
|
fn emit_u8(&mut self, v: u8) -> Result<(), util::Error> {
|
||||||
#[inline]
|
self.writer.write_u8(v).map_err(util::Error::ByteOrder)
|
||||||
fn emit_i8(&mut self, v: i8) -> io::Result<()> { self.writer.write_i8(v) }
|
}
|
||||||
|
#[inline]
|
||||||
#[inline]
|
fn emit_bool(&mut self, v: bool) -> Result<(), util::Error> {
|
||||||
fn emit_bool(&mut self, v: bool) -> io::Result<()> { self.writer.write_i8(if v {1} else {0}) }
|
self.writer.write_i8(if v {1} else {0}).map_err(util::Error::ByteOrder)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read> SimpleDecoder for RawDecoder<R> {
|
impl<R: Read> SimpleDecoder for RawDecoder<R> {
|
||||||
type Error = io::Error;
|
type Error = util::Error;
|
||||||
|
|
||||||
#[inline]
|
decoder_fn!(read_u64, u64, read_u64);
|
||||||
fn read_u64(&mut self) -> io::Result<u64> { self.reader.read_le_u64() }
|
decoder_fn!(read_u32, u32, read_u32);
|
||||||
#[inline]
|
decoder_fn!(read_u16, u16, read_u16);
|
||||||
fn read_u32(&mut self) -> io::Result<u32> { self.reader.read_le_u32() }
|
decoder_fn!(read_i64, i64, read_i64);
|
||||||
#[inline]
|
decoder_fn!(read_i32, i32, read_i32);
|
||||||
fn read_u16(&mut self) -> io::Result<u16> { self.reader.read_le_u16() }
|
decoder_fn!(read_i16, i16, read_i16);
|
||||||
#[inline]
|
|
||||||
fn read_u8(&mut self) -> io::Result<u8> { self.reader.read_u8() }
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_i64(&mut self) -> io::Result<i64> { self.reader.read_le_i64() }
|
fn read_u8(&mut self) -> Result<u8, util::Error> {
|
||||||
#[inline]
|
self.reader.read_u8().map_err(util::Error::ByteOrder)
|
||||||
fn read_i32(&mut self) -> io::Result<i32> { self.reader.read_le_i32() }
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_i16(&mut self) -> io::Result<i16> { self.reader.read_le_i16() }
|
fn read_i8(&mut self) -> Result<i8, util::Error> {
|
||||||
#[inline]
|
self.reader.read_i8().map_err(util::Error::ByteOrder)
|
||||||
fn read_i8(&mut self) -> io::Result<i8> { self.reader.read_i8() }
|
}
|
||||||
|
#[inline]
|
||||||
#[inline]
|
fn read_bool(&mut self) -> Result<bool, util::Error> {
|
||||||
fn read_bool(&mut self) -> io::Result<bool> { self.reader.read_u8().map(|res| res != 0) }
|
match self.reader.read_i8() {
|
||||||
|
Ok(bit) => Ok(bit != 0),
|
||||||
#[inline]
|
Err(e) => Err(util::Error::ByteOrder(e))
|
||||||
fn error(&mut self, err: &str) -> io::Error {
|
}
|
||||||
io::Error {
|
}
|
||||||
kind: io::ErrorKind::OtherError,
|
|
||||||
desc: "parse error",
|
#[inline]
|
||||||
detail: Some(err.to_string())
|
fn error(&mut self, err: String) -> util::Error {
|
||||||
|
util::Error::Detail(err, Box::new(util::Error::ParseFailed))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aren't really any tests here.. the main functions are serialize and
|
// Aren't really any tests here.. the main functions are serialize and
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
|
|
||||||
use time::now;
|
use time::now;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use std::io::Cursor;
|
use std::io::{self, Cursor, Write};
|
||||||
use std::io::{Error, Result, ErrorKind};
|
|
||||||
use std::net::{ip, tcp};
|
use std::net::{ip, tcp};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ use network::message::{RawNetworkMessage, NetworkMessage};
|
||||||
use network::message::NetworkMessage::Version;
|
use network::message::NetworkMessage::Version;
|
||||||
use network::message_network::VersionMessage;
|
use network::message_network::VersionMessage;
|
||||||
use network::serialize::{RawEncoder, RawDecoder};
|
use network::serialize::{RawEncoder, RawDecoder};
|
||||||
use util::misc::prepend_err;
|
use util::{self, propagate_err};
|
||||||
|
|
||||||
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
||||||
fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] {
|
fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] {
|
||||||
|
@ -44,14 +43,8 @@ fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] {
|
||||||
/// A network socket along with information about the peer
|
/// A network socket along with information about the peer
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Socket {
|
pub struct Socket {
|
||||||
/// The underlying socket, which is only used directly to (a) get
|
/// The underlying TCP socket
|
||||||
/// information about the socket, and (b) to close down the socket,
|
socket: Arc<Mutex<Option<tcp::TcpStream>>>,
|
||||||
/// quickly cancelling any read/writes and unlocking the Mutexes.
|
|
||||||
socket: Option<tcp::TcpStream>,
|
|
||||||
/// The underlying network data stream read buffer
|
|
||||||
buffered_reader: Arc<Mutex<Option<tcp::TcpStream>>>,
|
|
||||||
/// The underlying network data stream write buffer
|
|
||||||
buffered_writer: Arc<Mutex<Option<tcp::TcpStream>>>,
|
|
||||||
/// Services supported by us
|
/// Services supported by us
|
||||||
pub services: u64,
|
pub services: u64,
|
||||||
/// Our user agent
|
/// Our user agent
|
||||||
|
@ -63,154 +56,136 @@ pub struct Socket {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Socket {
|
impl Socket {
|
||||||
// TODO: we fix services to 0
|
// TODO: we fix services to 0
|
||||||
/// Construct a new socket
|
/// Construct a new socket
|
||||||
pub fn new(network: constants::Network) -> Socket {
|
pub fn new(network: constants::Network) -> Socket {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
Socket {
|
Socket {
|
||||||
socket: None,
|
socket: Arc::new(Mutex::new(None)),
|
||||||
buffered_reader: Arc::new(Mutex::new(None)),
|
services: 0,
|
||||||
buffered_writer: Arc::new(Mutex::new(None)),
|
version_nonce: rng.gen(),
|
||||||
services: 0,
|
user_agent: String::from_str(constants::USER_AGENT),
|
||||||
version_nonce: rng.gen(),
|
magic: constants::magic(network)
|
||||||
user_agent: String::from_str(constants::USER_AGENT),
|
|
||||||
magic: constants::magic(network)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connect to the peer
|
|
||||||
pub fn connect(&mut self, host: &str, port: u16) -> Result<()> {
|
|
||||||
// Boot off any lingering readers or writers
|
|
||||||
if self.socket.is_some() {
|
|
||||||
let _ = self.socket.as_mut().unwrap().close_read();
|
|
||||||
let _ = self.socket.as_mut().unwrap().close_write();
|
|
||||||
}
|
|
||||||
// These locks should just pop open now
|
|
||||||
let mut reader_lock = self.buffered_reader.lock();
|
|
||||||
let mut writer_lock = self.buffered_writer.lock();
|
|
||||||
match tcp::TcpStream::connect(host, port) {
|
|
||||||
Ok(s) => {
|
|
||||||
*reader_lock = Some(s.clone());
|
|
||||||
*writer_lock = Some(s.clone());
|
|
||||||
self.socket = Some(s);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Peer address
|
|
||||||
pub fn receiver_address(&mut self) -> Result<Address> {
|
|
||||||
match self.socket {
|
|
||||||
Some(ref mut s) => match s.peer_name() {
|
|
||||||
Ok(addr) => {
|
|
||||||
Ok(Address {
|
|
||||||
services: self.services,
|
|
||||||
address: ipaddr_to_bitcoin_addr(&addr.ip),
|
|
||||||
port: addr.port
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Err(e) => Err(e)
|
|
||||||
},
|
|
||||||
None => Err(Error::new(ErrorKind::NotConnected,
|
|
||||||
"receiver_address: not connected to peer", None))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Our own address
|
/// (Re)connect to the peer
|
||||||
pub fn sender_address(&mut self) -> Result<Address> {
|
pub fn connect(&mut self, host: &str, port: u16) -> Result<(), util::Error> {
|
||||||
match self.socket {
|
// Entirely replace the Mutex, in case it was poisoned;
|
||||||
Some(ref mut s) => match s.socket_name() {
|
// this will also drop any preexisting socket that might be open
|
||||||
Ok(addr) => {
|
match tcp::TcpStream::connect((host, port)) {
|
||||||
Ok(Address {
|
Ok(s) => {
|
||||||
services: self.services,
|
self.socket = Arc::new(Mutex::new(Some(s)));
|
||||||
address: ipaddr_to_bitcoin_addr(&addr.ip),
|
Ok(())
|
||||||
port: addr.port
|
}
|
||||||
})
|
Err(e) => {
|
||||||
|
self.socket = Arc::new(Mutex::new(None));
|
||||||
|
Err(util::Error::Io(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => Err(e)
|
|
||||||
},
|
|
||||||
None => Err(Error::new(ErrorKind::NotConnected,
|
|
||||||
"sender_address: not connected to peer", None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce a version message appropriate for this socket
|
|
||||||
pub fn version_message(&mut self, start_height: i32) -> Result<NetworkMessage> {
|
|
||||||
let timestamp = now().to_timespec().sec;
|
|
||||||
let recv_addr = self.receiver_address();
|
|
||||||
let send_addr = self.sender_address();
|
|
||||||
// If we are not connected, we might not be able to get these address.s
|
|
||||||
match recv_addr {
|
|
||||||
Err(e) => { return Err(e); }
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
match send_addr {
|
|
||||||
Err(e) => { return Err(e); }
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Version(VersionMessage {
|
fn socket(&mut self) -> Result<&mut tcp::TcpStream, util::Error> {
|
||||||
version: constants::PROTOCOL_VERSION,
|
let mut sock_lock = self.socket.lock();
|
||||||
services: constants::SERVICES,
|
match sock_lock {
|
||||||
timestamp: timestamp,
|
Err(_) => {
|
||||||
receiver: recv_addr.unwrap(),
|
let io_err = io::Error::new(io::ErrorKind::NotConnected,
|
||||||
sender: send_addr.unwrap(),
|
"socket: socket mutex was poisoned");
|
||||||
nonce: self.version_nonce,
|
Err(util::Error::Io(io_err))
|
||||||
user_agent: self.user_agent.clone(),
|
}
|
||||||
start_height: start_height,
|
Ok(guard) => {
|
||||||
relay: false
|
match *guard {
|
||||||
}))
|
Some(ref mut sock) => Ok(sock),
|
||||||
}
|
None => {
|
||||||
|
let io_err = io::Error::new(io::ErrorKind::NotConnected,
|
||||||
|
"socket: not connected to peer");
|
||||||
|
Err(util::Error::Io(io_err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a general message across the line
|
/// Peer address
|
||||||
pub fn send_message(&mut self, payload: NetworkMessage) -> Result<()> {
|
pub fn receiver_address(&mut self) -> Result<Address, util::Error> {
|
||||||
let mut writer_lock = self.buffered_writer.lock();
|
let sock = try!(self.socket());
|
||||||
match *writer_lock.deref_mut() {
|
match sock.peer_addr() {
|
||||||
None => Err(Error::new(ErrorKind::NotConnected,
|
Ok(addr) => {
|
||||||
"send_message: not connected to peer", None)),
|
Ok(Address {
|
||||||
Some(ref mut writer) => {
|
services: self.services,
|
||||||
|
address: ipaddr_to_bitcoin_addr(&addr.ip()),
|
||||||
|
port: addr.port()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Err(e) => Err(util::Error::Io(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our own address
|
||||||
|
pub fn sender_address(&mut self) -> Result<Address, util::Error> {
|
||||||
|
let sock = try!(self.socket());
|
||||||
|
match sock.local_addr() {
|
||||||
|
Ok(addr) => {
|
||||||
|
Ok(Address {
|
||||||
|
services: self.services,
|
||||||
|
address: ipaddr_to_bitcoin_addr(&addr.ip()),
|
||||||
|
port: addr.port()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Err(e) => Err(util::Error::Io(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce a version message appropriate for this socket
|
||||||
|
pub fn version_message(&mut self, start_height: i32) -> Result<NetworkMessage, util::Error> {
|
||||||
|
let recv_addr = try!(self.receiver_address());
|
||||||
|
let send_addr = try!(self.sender_address());
|
||||||
|
let timestamp = now().to_timespec().sec;
|
||||||
|
|
||||||
|
Ok(Version(VersionMessage {
|
||||||
|
version: constants::PROTOCOL_VERSION,
|
||||||
|
services: constants::SERVICES,
|
||||||
|
timestamp: timestamp,
|
||||||
|
receiver: recv_addr,
|
||||||
|
sender: send_addr,
|
||||||
|
nonce: self.version_nonce,
|
||||||
|
user_agent: self.user_agent.clone(),
|
||||||
|
start_height: start_height,
|
||||||
|
relay: false
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a general message across the line
|
||||||
|
pub fn send_message(&mut self, payload: NetworkMessage) -> Result<(), util::Error> {
|
||||||
|
let sock = try!(self.socket());
|
||||||
let message = RawNetworkMessage { magic: self.magic, payload: payload };
|
let message = RawNetworkMessage { magic: self.magic, payload: payload };
|
||||||
try!(message.consensus_encode(&mut RawEncoder::new(writer.by_ref())));
|
try!(message.consensus_encode(&mut RawEncoder::new(sock)));
|
||||||
writer.flush()
|
sock.flush().map_err(util::Error::Io)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Receive the next message from the peer, decoding the network header
|
/// Receive the next message from the peer, decoding the network header
|
||||||
/// and verifying its correctness. Returns the undecoded payload.
|
/// and verifying its correctness. Returns the undecoded payload.
|
||||||
pub fn receive_message(&mut self) -> Result<NetworkMessage> {
|
pub fn receive_message(&mut self) -> Result<NetworkMessage, util::Error> {
|
||||||
let mut reader_lock = self.buffered_reader.lock();
|
let sock = try!(self.socket());
|
||||||
match *reader_lock.deref_mut() {
|
|
||||||
None => Err(Error::new(ErrorKind::NotConnected,
|
|
||||||
"receive_message: not connected to peer", None)),
|
|
||||||
Some(ref mut buf) => {
|
|
||||||
// We need a new scope since the closure in here borrows read_err,
|
// We need a new scope since the closure in here borrows read_err,
|
||||||
// and we try to read it afterward. Letting `iter` go out fixes it.
|
// and we try to read it afterward. Letting `iter` go out fixes it.
|
||||||
let mut decoder = RawDecoder::new(buf.by_ref());
|
let mut decoder = RawDecoder::new(sock);
|
||||||
let decode: Result<RawNetworkMessage> = ConsensusDecodable::consensus_decode(&mut decoder);
|
let decode: Result<RawNetworkMessage, _> = ConsensusDecodable::consensus_decode(&mut decoder);
|
||||||
match decode {
|
match decode {
|
||||||
// Check for parse errors...
|
// Check for parse errors...
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
prepend_err("network_decode", Err(e))
|
propagate_err("receive_message".to_string(), Err(e))
|
||||||
},
|
},
|
||||||
Ok(ret) => {
|
Ok(ret) => {
|
||||||
// Then for magic (this should come before parse error, but we can't
|
// Then for magic (this should come before parse error, but we can't
|
||||||
// get to it if the deserialization failed). TODO restructure this
|
// get to it if the deserialization failed). TODO restructure this
|
||||||
if ret.magic != self.magic {
|
if ret.magic != self.magic {
|
||||||
Err(Error {
|
Err(util::Error::BadNetworkMagic(self.magic, ret.magic))
|
||||||
kind: ErrorKind::OtherError,
|
} else {
|
||||||
desc: "bad magic",
|
Ok(ret.payload)
|
||||||
detail: Some(format!("got magic {:x}, expected {:x}", ret.magic, self.magic)),
|
}
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(ret.payload)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
//! # Base58 encoder and decoder
|
//! # Base58 encoder and decoder
|
||||||
|
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
||||||
|
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ pub trait FromBase58 {
|
||||||
return Err(Error::TooShort(ret.len()));
|
return Err(Error::TooShort(ret.len()));
|
||||||
}
|
}
|
||||||
let ck_start = ret.len() - 4;
|
let ck_start = ret.len() - 4;
|
||||||
let expected = Sha256dHash::from_data(ret.slice_to(ck_start)).into_le().low_u32();
|
let expected = Sha256dHash::from_data(&ret[..ck_start]).into_le().low_u32();
|
||||||
let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]);
|
let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]);
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
return Err(Error::BadChecksum(expected, actual));
|
return Err(Error::BadChecksum(expected, actual));
|
||||||
|
@ -116,7 +116,7 @@ pub trait FromBase58 {
|
||||||
/// Directly encode a slice as base58
|
/// Directly encode a slice as base58
|
||||||
pub fn base58_encode_slice(data: &[u8]) -> String {
|
pub fn base58_encode_slice(data: &[u8]) -> String {
|
||||||
// 7/5 is just over log_58(256)
|
// 7/5 is just over log_58(256)
|
||||||
let mut scratch = Vec::from_elem(1 + data.len() * 7 / 5, 0u8);
|
let mut scratch = vec![0u8; 1 + data.len() * 7 / 5];
|
||||||
// Build in base 58
|
// Build in base 58
|
||||||
for &d256 in data.base58_layout().iter() {
|
for &d256 in data.base58_layout().iter() {
|
||||||
// Compute "X = X * 256 + next_digit" in base 58
|
// Compute "X = X * 256 + next_digit" in base 58
|
||||||
|
@ -132,9 +132,9 @@ pub fn base58_encode_slice(data: &[u8]) -> String {
|
||||||
// Unsafely translate the bytes to a utf8 string
|
// Unsafely translate the bytes to a utf8 string
|
||||||
unsafe {
|
unsafe {
|
||||||
// Copy leading zeroes directly
|
// Copy leading zeroes directly
|
||||||
let mut ret = str::from_utf8(data.iter().take_while(|&&x| x == 0)
|
let mut ret: Vec<u8> = str::from_utf8(data.iter().take_while(|&&x| x == 0)
|
||||||
.map(|_| BASE58_CHARS[0])
|
.map(|_| BASE58_CHARS[0])
|
||||||
.collect());
|
.collect()).unwrap();
|
||||||
// Copy rest of string
|
// Copy rest of string
|
||||||
ret.as_mut_vec().extend(scratch.into_iter().skip_while(|&x| x == 0)
|
ret.as_mut_vec().extend(scratch.into_iter().skip_while(|&x| x == 0)
|
||||||
.map(|x| BASE58_CHARS[x as usize]));
|
.map(|x| BASE58_CHARS[x as usize]));
|
||||||
|
@ -149,16 +149,16 @@ pub trait ToBase58 {
|
||||||
|
|
||||||
/// Obtain a string with the base58 encoding of the object
|
/// Obtain a string with the base58 encoding of the object
|
||||||
fn to_base58(&self) -> String {
|
fn to_base58(&self) -> String {
|
||||||
base58_encode_slice(self.base58_layout().as_slice())
|
base58_encode_slice(&self.base58_layout()[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a string with the base58check encoding of the object
|
/// Obtain a string with the base58check encoding of the object
|
||||||
/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.)
|
/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.)
|
||||||
fn to_base58check(&self) -> String {
|
fn to_base58check(&self) -> String {
|
||||||
let mut data = self.base58_layout();
|
let mut data = self.base58_layout();
|
||||||
let checksum = Sha256dHash::from_data(data.as_slice()).into_le().low_u32();
|
let checksum = Sha256dHash::from_data(&data).into_le().low_u32();
|
||||||
data.write_u32::<LittleEndian>(checksum);
|
data.write_u32::<LittleEndian>(checksum);
|
||||||
base58_encode_slice(data.as_slice())
|
base58_encode_slice(&data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,11 +168,6 @@ impl<'a> ToBase58 for &'a [u8] {
|
||||||
fn to_base58(&self) -> String { base58_encode_slice(*self) }
|
fn to_base58(&self) -> String { base58_encode_slice(*self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBase58 for Vec<u8> {
|
|
||||||
fn base58_layout(&self) -> Vec<u8> { self.clone() }
|
|
||||||
fn to_base58(&self) -> String { base58_encode_slice(self.as_slice()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromBase58 for Vec<u8> {
|
impl FromBase58 for Vec<u8> {
|
||||||
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||||
Ok(data)
|
Ok(data)
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
// Rust Bitcoin Library
|
|
||||||
// Written in 2014 by
|
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
|
||||||
//
|
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
|
||||||
// copyright and related and neighboring rights to this software to
|
|
||||||
// the public domain worldwide. This software is distributed without
|
|
||||||
// any warranty.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the CC0 Public Domain Dedication
|
|
||||||
// along with this software.
|
|
||||||
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
//! # Error codes
|
|
||||||
//!
|
|
||||||
//! Various utility functions
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
/// A general error code
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
|
||||||
pub enum Error {
|
|
||||||
/// An I/O error
|
|
||||||
InputOutput(io::Error),
|
|
||||||
/// An object was attempted to be added twice
|
|
||||||
DuplicateHash,
|
|
||||||
/// Some operation was attempted on a block (or blockheader) that doesn't exist
|
|
||||||
BlockNotFound,
|
|
||||||
/// An object was added but it does not link into existing history
|
|
||||||
PrevHashNotFound,
|
|
||||||
/// The `target` field of a block header did not match the expected difficulty
|
|
||||||
SpvBadTarget,
|
|
||||||
/// The header hash is not below the target
|
|
||||||
SpvBadProofOfWork
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
use std::char::from_digit;
|
use std::char::from_digit;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fmt;
|
use std::fmt::{self, Write};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
use std::hash;
|
use std::hash;
|
||||||
|
@ -65,7 +65,7 @@ impl Ripemd160Hash {
|
||||||
let mut ret = [0; 20];
|
let mut ret = [0; 20];
|
||||||
let mut rmd = Ripemd160::new();
|
let mut rmd = Ripemd160::new();
|
||||||
rmd.input(data);
|
rmd.input(data);
|
||||||
rmd.result(ret.as_mut_slice());
|
rmd.result(&mut ret);
|
||||||
Ripemd160Hash(ret)
|
Ripemd160Hash(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,10 @@ impl Sha256dHash {
|
||||||
let Sha256dHash(mut ret): Sha256dHash = Default::default();
|
let Sha256dHash(mut ret): Sha256dHash = Default::default();
|
||||||
let mut sha2 = Sha256::new();
|
let mut sha2 = Sha256::new();
|
||||||
sha2.input(data);
|
sha2.input(data);
|
||||||
sha2.result(ret.as_mut_slice());
|
sha2.result(&mut ret);
|
||||||
sha2.reset();
|
sha2.reset();
|
||||||
sha2.input(ret.as_slice());
|
sha2.input(&ret);
|
||||||
sha2.result(ret.as_mut_slice());
|
sha2.result(&mut ret);
|
||||||
Sha256dHash(ret)
|
Sha256dHash(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ impl Sha256dHash {
|
||||||
pub fn into_le(self) -> Uint256 {
|
pub fn into_le(self) -> Uint256 {
|
||||||
let Sha256dHash(data) = self;
|
let Sha256dHash(data) = self;
|
||||||
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
||||||
for x in ret.as_mut_slice().iter_mut() { *x = x.to_le(); }
|
for x in (&mut ret).iter_mut() { *x = x.to_le(); }
|
||||||
Uint256(ret)
|
Uint256(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ impl Sha256dHash {
|
||||||
let Sha256dHash(mut data) = self;
|
let Sha256dHash(mut data) = self;
|
||||||
data.reverse();
|
data.reverse();
|
||||||
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
||||||
for x in ret.iter_mut() { *x = x.to_be(); }
|
for x in (&mut ret).iter_mut() { *x = x.to_be(); }
|
||||||
Uint256(ret)
|
Uint256(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,8 +138,8 @@ impl Sha256dHash {
|
||||||
let &Sha256dHash(data) = self;
|
let &Sha256dHash(data) = self;
|
||||||
let mut ret = String::with_capacity(64);
|
let mut ret = String::with_capacity(64);
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
ret.push_char(from_digit((data[i] / 0x10) as usize, 16).unwrap());
|
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
|
||||||
ret.push_char(from_digit((data[i] & 0x0f) as usize, 16).unwrap());
|
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -149,8 +149,8 @@ impl Sha256dHash {
|
||||||
let &Sha256dHash(data) = self;
|
let &Sha256dHash(data) = self;
|
||||||
let mut ret = String::with_capacity(64);
|
let mut ret = String::with_capacity(64);
|
||||||
for i in (0..32).rev() {
|
for i in (0..32).rev() {
|
||||||
ret.push_char(from_digit((data[i] / 0x10) as usize, 16).unwrap());
|
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
|
||||||
ret.push_char(from_digit((data[i] & 0x0f) as usize, 16).unwrap());
|
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ impl serde::Deserialize for Sha256dHash {
|
||||||
if hex_str.len() != 64 {
|
if hex_str.len() != 64 {
|
||||||
return Err(serde::de::Error::syntax_error());
|
return Err(serde::de::Error::syntax_error());
|
||||||
}
|
}
|
||||||
let raw_str = try!(hex_str.as_slice().from_hex()
|
let raw_str = try!(hex_str.from_hex()
|
||||||
.map_err(|_| serde::de::Error::syntax_error()));
|
.map_err(|_| serde::de::Error::syntax_error()));
|
||||||
let mut ret = [0u8; 32];
|
let mut ret = [0u8; 32];
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
|
@ -215,12 +215,10 @@ impl fmt::LowerHex for Sha256dHash {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let &Sha256dHash(data) = self;
|
let &Sha256dHash(data) = self;
|
||||||
let mut rv = [0; 64];
|
let mut rv = [0; 64];
|
||||||
let mut hex = data.iter().rev().map(|n| *n).enumerate();
|
for ch in data.iter().rev() {
|
||||||
for (i, ch) in hex {
|
try!(write!(f, "{:02x}", ch));
|
||||||
rv[2*i] = from_digit(ch as usize / 16, 16).unwrap() as u8;
|
|
||||||
rv[2*i + 1] = from_digit(ch as usize % 16, 16).unwrap() as u8;
|
|
||||||
}
|
}
|
||||||
f.write(rv.as_slice())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +247,7 @@ impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
|
||||||
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
||||||
data[idx1].consensus_encode(&mut encoder).unwrap();
|
data[idx1].consensus_encode(&mut encoder).unwrap();
|
||||||
data[idx2].consensus_encode(&mut encoder).unwrap();
|
data[idx2].consensus_encode(&mut encoder).unwrap();
|
||||||
next.push(encoder.unwrap().unwrap().bitcoin_hash());
|
next.push(encoder.unwrap().into_inner().bitcoin_hash());
|
||||||
}
|
}
|
||||||
merkle_root(next)
|
merkle_root(next)
|
||||||
}
|
}
|
||||||
|
@ -259,7 +257,7 @@ impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
|
||||||
|
|
||||||
impl <T: BitcoinHash> MerkleRoot for Vec<T> {
|
impl <T: BitcoinHash> MerkleRoot for Vec<T> {
|
||||||
fn merkle_root(&self) -> Sha256dHash {
|
fn merkle_root(&self) -> Sha256dHash {
|
||||||
self.as_slice().merkle_root()
|
(&self[..]).merkle_root()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +301,7 @@ mod tests {
|
||||||
assert!(hash.encode(&mut encoder).is_ok());
|
assert!(hash.encode(&mut encoder).is_ok());
|
||||||
}
|
}
|
||||||
let res = writer.unwrap();
|
let res = writer.unwrap();
|
||||||
assert_eq!(res.as_slice(),
|
assert_eq!(&res.as_slice(),
|
||||||
"\"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d\"".as_bytes());
|
"\"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d\"".as_bytes());
|
||||||
assert_eq!(json::decode(from_utf8(res.as_slice()).unwrap()), Ok(hash));
|
assert_eq!(json::decode(from_utf8(res.as_slice()).unwrap()), Ok(hash));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,61 +16,47 @@
|
||||||
//!
|
//!
|
||||||
//! Various utility functions
|
//! Various utility functions
|
||||||
|
|
||||||
use std::io::{Error, Result, ErrorKind};
|
|
||||||
|
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
|
use util::Error;
|
||||||
use util::iter::Pairable;
|
use util::iter::Pairable;
|
||||||
|
|
||||||
/// Convert a hexadecimal-encoded string to its corresponding bytes
|
/// Convert a hexadecimal-encoded string to its corresponding bytes
|
||||||
pub fn hex_bytes(s: &str) -> Result<Vec<u8>> {
|
pub fn hex_bytes(s: &str) -> Result<Vec<u8>, Error> {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
let mut iter = s.chars().pair();
|
let mut iter = s.chars().pair();
|
||||||
// Do the parsing
|
// Do the parsing
|
||||||
try!(iter.fold(Ok(()), |e, (f, s)|
|
try!(iter.fold(Ok(()), |e, (f, s)|
|
||||||
if e.is_err() { return e; }
|
if e.is_err() { e }
|
||||||
else {
|
else {
|
||||||
match (f.to_digit(16), s.to_digit(16)) {
|
match (f.to_digit(16), s.to_digit(16)) {
|
||||||
(None, _) => return Err(Error {
|
(None, _) => Err(Error::Detail(
|
||||||
kind: ErrorKind::InvalidInput,
|
format!("expected hex, got {:}", f),
|
||||||
desc: "invalid hex character",
|
Box::new(Error::ParseFailed)
|
||||||
detail: Some(format!("expected hex, got {:}", f))
|
)),
|
||||||
}),
|
(_, None) => Err(Error::Detail(
|
||||||
(_, None) => return Err(Error {
|
format!("expected hex, got {:}", s),
|
||||||
kind: ErrorKind::InvalidInput,
|
Box::new(Error::ParseFailed)
|
||||||
desc: "invalid hex character",
|
)),
|
||||||
detail: Some(format!("expected hex, got {:}", s))
|
|
||||||
}),
|
|
||||||
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
|
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
// Check that there was no remainder
|
// Check that there was no remainder
|
||||||
match iter.remainder() {
|
match iter.remainder() {
|
||||||
Some(_) => Err(Error {
|
Some(_) => Err(Error::Detail(
|
||||||
kind: ErrorKind::InvalidInput,
|
format!("hexstring of odd length"),
|
||||||
desc: "hexstring of odd length",
|
Box::new(Error::ParseFailed)
|
||||||
detail: None
|
)),
|
||||||
}),
|
|
||||||
None => Ok(v)
|
None => Ok(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepend the detail of an IoResult's error with some text to get poor man's backtracing
|
|
||||||
pub fn prepend_err<T>(s: &str, res: Result<T>) -> Result<T> {
|
|
||||||
res.map_err(|err| {
|
|
||||||
Error {
|
|
||||||
kind: err.kind,
|
|
||||||
desc: err.desc,
|
|
||||||
detail: Some(format!("{}: {}", s, match err.detail { Some(s) => s, None => String::new() }))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dump an error message to the screen
|
/// Dump an error message to the screen
|
||||||
pub fn consume_err<T>(s: &str, res: Result<T>) {
|
/// TODO all uses of this should be replaced with some sort of logging infrastructure
|
||||||
|
pub fn consume_err<T>(s: &str, res: Result<T, Error>) {
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => { println!("{}: {}", s, e); }
|
Err(e) => { println!("{}: {:?}", s, e); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +72,8 @@ pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i <= top {
|
while i <= top {
|
||||||
if haystack.slice(i, i + needle.len()) == needle {
|
if &haystack[i..(i + needle.len())] == needle {
|
||||||
let v = haystack.as_mut_slice();
|
let v = &mut haystack;
|
||||||
for j in i..top {
|
for j in i..top {
|
||||||
v.swap(j + needle.len(), j);
|
v.swap(j + needle.len(), j);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +144,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hex_bytes() {
|
fn test_hex_bytes() {
|
||||||
assert_eq!(hex_bytes("abcd").unwrap().as_slice(), [171u8, 205].as_slice());
|
assert_eq!(&hex_bytes("abcd").unwrap(), &[171u8, 205]);
|
||||||
assert!(hex_bytes("abcde").is_err());
|
assert!(hex_bytes("abcde").is_err());
|
||||||
assert!(hex_bytes("aBcDeF").is_ok());
|
assert!(hex_bytes("aBcDeF").is_ok());
|
||||||
assert!(hex_bytes("aBcD4eFL").is_err());
|
assert!(hex_bytes("aBcD4eFL").is_err());
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -17,25 +17,61 @@
|
||||||
//! Functions needed by all parts of the Bitcoin library
|
//! Functions needed by all parts of the Bitcoin library
|
||||||
|
|
||||||
pub mod base58;
|
pub mod base58;
|
||||||
pub mod error;
|
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
pub mod patricia_tree;
|
pub mod patricia_tree;
|
||||||
pub mod uint;
|
pub mod uint;
|
||||||
|
|
||||||
|
use byteorder;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
/// A trait which allows numbers to act as fixed-size bit arrays
|
/// A trait which allows numbers to act as fixed-size bit arrays
|
||||||
pub trait BitArray {
|
pub trait BitArray {
|
||||||
/// Is bit set?
|
/// Is bit set?
|
||||||
fn bit(&self, idx: usize) -> bool;
|
fn bit(&self, idx: usize) -> bool;
|
||||||
|
|
||||||
/// Returns an array which is just the bits from start to end
|
/// Returns an array which is just the bits from start to end
|
||||||
fn bit_slice(&self, start: usize, end: usize) -> Self;
|
fn bit_slice(&self, start: usize, end: usize) -> Self;
|
||||||
|
|
||||||
/// Bitwise and with `n` ones
|
/// Bitwise and with `n` ones
|
||||||
fn mask(&self, n: usize) -> Self;
|
fn mask(&self, n: usize) -> Self;
|
||||||
|
|
||||||
/// Trailing zeros
|
/// Trailing zeros
|
||||||
fn trailing_zeros(&self) -> usize;
|
fn trailing_zeros(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A general error code
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// An I/O error
|
||||||
|
Io(io::Error),
|
||||||
|
/// Order from the `byteorder` crate
|
||||||
|
ByteOrder(byteorder::Error),
|
||||||
|
/// Network magic was not what we expected
|
||||||
|
BadNetworkMagic(u32, u32),
|
||||||
|
/// Network message was unrecognized
|
||||||
|
BadNetworkMessage(String),
|
||||||
|
/// An object was attempted to be added twice
|
||||||
|
DuplicateHash,
|
||||||
|
/// Some operation was attempted on a block (or blockheader) that doesn't exist
|
||||||
|
BlockNotFound,
|
||||||
|
/// Parsing error
|
||||||
|
ParseFailed,
|
||||||
|
/// An object was added but it does not link into existing history
|
||||||
|
PrevHashNotFound,
|
||||||
|
/// The `target` field of a block header did not match the expected difficulty
|
||||||
|
SpvBadTarget,
|
||||||
|
/// The header hash is not below the target
|
||||||
|
SpvBadProofOfWork,
|
||||||
|
/// Error propagated from subsystem
|
||||||
|
Detail(String, Box<Error>)
|
||||||
|
}
|
||||||
|
display_from_debug!(Error);
|
||||||
|
|
||||||
|
/// Prepend the detail of an IoResult's error with some text to get poor man's backtracing
|
||||||
|
pub fn propagate_err<T>(s: String, res: Result<T, Error>) -> Result<T, Error> {
|
||||||
|
res.map_err(|err| Error::Detail(s, Box::new(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::marker;
|
use std::marker;
|
||||||
use std::num::{Zero, One};
|
use std::num::{Zero, One};
|
||||||
use std::{cmp, ops, ptr};
|
use std::{cmp, fmt, ops, ptr};
|
||||||
|
|
||||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||||
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||||
|
@ -39,7 +39,11 @@ pub struct PatriciaTree<K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> PatriciaTree<K, V>
|
impl<K, V> PatriciaTree<K, V>
|
||||||
where K: BitArray + cmp::Eq + Zero + One + ops::BitXor<K> + ops::Shl<usize> + ops::Shr<usize>
|
where K: BitArray + cmp::Eq + Zero + One +
|
||||||
|
ops::BitXor<K, Output=K> +
|
||||||
|
ops::Add<K, Output=K> +
|
||||||
|
ops::Shr<usize, Output=K> +
|
||||||
|
ops::Shl<usize, Output=K>
|
||||||
{
|
{
|
||||||
/// Constructs a new Patricia tree
|
/// Constructs a new Patricia tree
|
||||||
pub fn new() -> PatriciaTree<K, V> {
|
pub fn new() -> PatriciaTree<K, V> {
|
||||||
|
@ -217,7 +221,10 @@ impl<K, V> PatriciaTree<K, V>
|
||||||
/// Return value is (deletable, actual return value), where `deletable` is true
|
/// Return value is (deletable, actual return value), where `deletable` is true
|
||||||
/// is true when the entire node can be deleted (i.e. it has no children)
|
/// is true when the entire node can be deleted (i.e. it has no children)
|
||||||
fn recurse<K, V>(tree: &mut PatriciaTree<K, V>, key: &K, key_len: usize) -> (bool, Option<V>)
|
fn recurse<K, V>(tree: &mut PatriciaTree<K, V>, key: &K, key_len: usize) -> (bool, Option<V>)
|
||||||
where K: BitArray + cmp::Eq + Zero + One + ops::Add<K> + ops::Shr<usize> + ops::Shl<usize>
|
where K: BitArray + cmp::Eq + Zero + One +
|
||||||
|
ops::Add<K, Output=K> +
|
||||||
|
ops::Shr<usize, Output=K> +
|
||||||
|
ops::Shl<usize, Output=K>
|
||||||
{
|
{
|
||||||
// If the search key is shorter than the node prefix, there is no
|
// If the search key is shorter than the node prefix, there is no
|
||||||
// way we can match, so fail.
|
// way we can match, so fail.
|
||||||
|
@ -275,7 +282,7 @@ impl<K, V> PatriciaTree<K, V>
|
||||||
}
|
}
|
||||||
// Otherwise, do it
|
// Otherwise, do it
|
||||||
let (delete_child, ret) = recurse(&mut **target.as_mut().unwrap(),
|
let (delete_child, ret) = recurse(&mut **target.as_mut().unwrap(),
|
||||||
&key.shr(&(tree.skip_len as usize + 1)),
|
&(*key >> (tree.skip_len as usize + 1)),
|
||||||
key_len - tree.skip_len as usize - 1);
|
key_len - tree.skip_len as usize - 1);
|
||||||
if delete_child {
|
if delete_child {
|
||||||
target.take();
|
target.take();
|
||||||
|
@ -355,22 +362,22 @@ impl<K, V> PatriciaTree<K, V>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:BitArray, V:Debug> PatriciaTree<K, V> {
|
impl<K:BitArray, V:Debug> Debug for PatriciaTree<K, V> {
|
||||||
/// Print the entire tree
|
/// Print the entire tree
|
||||||
pub fn print<'a>(&'a self) {
|
pub fn fmt<'a>(&'a self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
fn recurse<'a, K:BitArray, V:Debug>(tree: &'a PatriciaTree<K, V>, depth: usize) {
|
fn recurse<'a, K:BitArray, V:Debug>(tree: &'a PatriciaTree<K, V>, f: &mut fmt::Formatter, depth: usize) -> Result<(), fmt::Error> {
|
||||||
for i in 0..tree.skip_len as usize {
|
for i in 0..tree.skip_len as usize {
|
||||||
print!("{:}", if tree.skip_prefix.bit(i) { 1 } else { 0 });
|
try!(write!(f, "{:}", if tree.skip_prefix.bit(i) { 1 } else { 0 }));
|
||||||
}
|
}
|
||||||
println!(": {:}", tree.data);
|
try!(writeln!(f, ": {:?}", tree.data));
|
||||||
// left gets no indentation
|
// left gets no indentation
|
||||||
match tree.child_l {
|
match tree.child_l {
|
||||||
Some(ref t) => {
|
Some(ref t) => {
|
||||||
for _ in 0..(depth + tree.skip_len as usize) {
|
for _ in 0..(depth + tree.skip_len as usize) {
|
||||||
print!("-");
|
try!(write!(f, "-"));
|
||||||
}
|
}
|
||||||
print!("0");
|
try!(write!(f, "0"));
|
||||||
recurse(&**t, depth + tree.skip_len as usize + 1);
|
try!(recurse(&**t, f, depth + tree.skip_len as usize + 1));
|
||||||
}
|
}
|
||||||
None => { }
|
None => { }
|
||||||
}
|
}
|
||||||
|
@ -378,15 +385,16 @@ impl<K:BitArray, V:Debug> PatriciaTree<K, V> {
|
||||||
match tree.child_r {
|
match tree.child_r {
|
||||||
Some(ref t) => {
|
Some(ref t) => {
|
||||||
for _ in 0..(depth + tree.skip_len as usize) {
|
for _ in 0..(depth + tree.skip_len as usize) {
|
||||||
print!("_");
|
try!(write!(f, "_"));
|
||||||
}
|
}
|
||||||
print!("1");
|
try!(write!(f, "1"));
|
||||||
recurse(&**t, depth + tree.skip_len as usize + 1);
|
try!(recurse(&**t, f, depth + tree.skip_len as usize + 1));
|
||||||
}
|
}
|
||||||
None => { }
|
None => { }
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
recurse(self, 0);
|
recurse(self, f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +438,7 @@ pub struct Items<'tree, K: 'tree, V: 'tree> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable iterator
|
/// Mutable iterator
|
||||||
pub struct MutItems<'tree, K, V> {
|
pub struct MutItems<'tree, K: 'tree, V: 'tree> {
|
||||||
started: bool,
|
started: bool,
|
||||||
node: *mut PatriciaTree<K, V>,
|
node: *mut PatriciaTree<K, V>,
|
||||||
parents: Vec<*mut PatriciaTree<K, V>>,
|
parents: Vec<*mut PatriciaTree<K, V>>,
|
||||||
|
@ -490,7 +498,7 @@ impl<'a, K, V> Iterator for MutItems<'a, K, V> {
|
||||||
fn borrow_opt<'a, K, V>(opt_ptr: &'a Option<Box<PatriciaTree<K, V>>>) -> *mut PatriciaTree<K, V> {
|
fn borrow_opt<'a, K, V>(opt_ptr: &'a Option<Box<PatriciaTree<K, V>>>) -> *mut PatriciaTree<K, V> {
|
||||||
match *opt_ptr {
|
match *opt_ptr {
|
||||||
Some(ref data) => &**data as *const _ as *mut _,
|
Some(ref data) => &**data as *const _ as *mut _,
|
||||||
None => ptr::null()
|
None => ptr::null_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,7 +506,7 @@ impl<'a, K, V> Iterator for MutItems<'a, K, V> {
|
||||||
// which will be the root node.
|
// which will be the root node.
|
||||||
if !self.started {
|
if !self.started {
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.node.is_not_null() && (*self.node).data.is_some() {
|
if !self.node.is_null() && (*self.node).data.is_some() {
|
||||||
return (*self.node).data.as_mut();
|
return (*self.node).data.as_mut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -506,30 +514,30 @@ impl<'a, K, V> Iterator for MutItems<'a, K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find next data-containing node
|
// Find next data-containing node
|
||||||
while self.node.is_not_null() {
|
while !self.node.is_null() {
|
||||||
// Try to go left
|
// Try to go left
|
||||||
let child_l = unsafe { borrow_opt(&(*self.node).child_l) };
|
let child_l = unsafe { borrow_opt(&(*self.node).child_l) };
|
||||||
if child_l.is_not_null() {
|
if !child_l.is_null() {
|
||||||
self.parents.push(self.node);
|
self.parents.push(self.node);
|
||||||
self.node = child_l;
|
self.node = child_l;
|
||||||
// Try to go right, going back up the tree if necessary
|
// Try to go right, going back up the tree if necessary
|
||||||
} else {
|
} else {
|
||||||
while self.node.is_not_null() {
|
while !self.node.is_null() {
|
||||||
let child_r = unsafe { borrow_opt(&(*self.node).child_r) };
|
let child_r = unsafe { borrow_opt(&(*self.node).child_r) };
|
||||||
if child_r.is_not_null() {
|
if !child_r.is_null() {
|
||||||
self.node = child_r;
|
self.node = child_r;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.node = self.parents.pop().unwrap_or(ptr::null());
|
self.node = self.parents.pop().unwrap_or(ptr::null_mut());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Stop if we've found data.
|
// Stop if we've found data.
|
||||||
if self.node.is_not_null() && unsafe { (*self.node).data.is_some() } {
|
if !self.node.is_null() && unsafe { (*self.node).data.is_some() } {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} // end loop
|
} // end loop
|
||||||
// Return data
|
// Return data
|
||||||
if self.node.is_not_null() {
|
if !self.node.is_null() {
|
||||||
unsafe { (*self.node).data.as_mut() }
|
unsafe { (*self.node).data.as_mut() }
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -540,7 +548,7 @@ impl<'a, K, V> Iterator for MutItems<'a, K, V> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::prelude::*;
|
use std::prelude::*;
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
use std::num::Zero;
|
use std::num::Zero;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
@ -698,7 +706,7 @@ mod tests {
|
||||||
// Serialize it
|
// Serialize it
|
||||||
let serialized = serialize(&tree).unwrap();
|
let serialized = serialize(&tree).unwrap();
|
||||||
// Deserialize it
|
// Deserialize it
|
||||||
let deserialized: IoResult<PatriciaTree<Uint128, u32>> = deserialize(serialized);
|
let deserialized: io::Result<PatriciaTree<Uint128, u32>> = deserialize(serialized);
|
||||||
assert!(deserialized.is_ok());
|
assert!(deserialized.is_ok());
|
||||||
let new_tree = deserialized.unwrap();
|
let new_tree = deserialized.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -46,12 +46,12 @@ macro_rules! construct_uint {
|
||||||
for i in 1..$n_words {
|
for i in 1..$n_words {
|
||||||
if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; }
|
if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; }
|
||||||
}
|
}
|
||||||
0x40 - arr[0].leading_zeros()
|
0x40 - arr[0].leading_zeros() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multiplication by u32
|
/// Multiplication by u32
|
||||||
pub fn mul_u32(&self, other: u32) -> $name {
|
pub fn mul_u32(self, other: u32) -> $name {
|
||||||
let &$name(ref arr) = self;
|
let $name(ref arr) = self;
|
||||||
let mut carry = [0u64; $n_words];
|
let mut carry = [0u64; $n_words];
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
|
@ -93,9 +93,9 @@ macro_rules! construct_uint {
|
||||||
impl ::std::ops::Add<$name> for $name {
|
impl ::std::ops::Add<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
fn add(&self, other: &$name) -> $name {
|
fn add(self, other: $name) -> $name {
|
||||||
let &$name(ref me) = self;
|
let $name(ref me) = self;
|
||||||
let &$name(ref you) = other;
|
let $name(ref you) = other;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
let mut carry = [0u64; $n_words];
|
let mut carry = [0u64; $n_words];
|
||||||
let mut b_carry = false;
|
let mut b_carry = false;
|
||||||
|
@ -114,16 +114,16 @@ macro_rules! construct_uint {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sub(&self, other: &$name) -> $name {
|
fn sub(self, other: $name) -> $name {
|
||||||
*self + !*other + One::one()
|
self + !other + One::one()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::ops::Mul<$name> for $name {
|
impl ::std::ops::Mul<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
fn mul(&self, other: &$name) -> $name {
|
fn mul(self, other: $name) -> $name {
|
||||||
let mut me = *self;
|
let mut me = self;
|
||||||
// TODO: be more efficient about this
|
// TODO: be more efficient about this
|
||||||
for i in 0..(2 * $n_words) {
|
for i in 0..(2 * $n_words) {
|
||||||
me = me + me.mul_u32((other >> (32 * i)).low_u32()) << (32 * i);
|
me = me + me.mul_u32((other >> (32 * i)).low_u32()) << (32 * i);
|
||||||
|
@ -135,9 +135,9 @@ macro_rules! construct_uint {
|
||||||
impl ::std::ops::Div<$name> for $name {
|
impl ::std::ops::Div<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
fn div(&self, other: &$name) -> $name {
|
fn div(self, other: $name) -> $name {
|
||||||
let mut sub_copy = *self;
|
let mut sub_copy = self;
|
||||||
let mut shift_copy = *other;
|
let mut shift_copy = other;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
|
|
||||||
let my_bits = self.bits();
|
let my_bits = self.bits();
|
||||||
|
@ -157,7 +157,7 @@ macro_rules! construct_uint {
|
||||||
loop {
|
loop {
|
||||||
if sub_copy >= shift_copy {
|
if sub_copy >= shift_copy {
|
||||||
ret[shift / 64] |= 1 << (shift % 64);
|
ret[shift / 64] |= 1 << (shift % 64);
|
||||||
sub_copy = sub_copy.sub(&shift_copy);
|
sub_copy = sub_copy - shift_copy;
|
||||||
}
|
}
|
||||||
shift_copy = shift_copy >> 1;
|
shift_copy = shift_copy >> 1;
|
||||||
if shift == 0 { break; }
|
if shift == 0 { break; }
|
||||||
|
@ -177,7 +177,7 @@ macro_rules! construct_uint {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bit_slice(&self, start: usize, end: usize) -> $name {
|
fn bit_slice(&self, start: usize, end: usize) -> $name {
|
||||||
(self >> start).mask(end - start)
|
(*self >> start).mask(end - start)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -199,9 +199,9 @@ macro_rules! construct_uint {
|
||||||
fn trailing_zeros(&self) -> usize {
|
fn trailing_zeros(&self) -> usize {
|
||||||
let &$name(ref arr) = self;
|
let &$name(ref arr) = self;
|
||||||
for i in 0..($n_words - 1) {
|
for i in 0..($n_words - 1) {
|
||||||
if arr[i] > 0 { return (0x40 * i) + arr[i].trailing_zeros(); }
|
if arr[i] > 0 { return (0x40 * i) + arr[i].trailing_zeros() as usize; }
|
||||||
}
|
}
|
||||||
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros()
|
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros() as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,9 +209,9 @@ macro_rules! construct_uint {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitand(&self, other: &$name) -> $name {
|
fn bitand(self, other: $name) -> $name {
|
||||||
let &$name(ref arr1) = self;
|
let $name(ref arr1) = self;
|
||||||
let &$name(ref arr2) = other;
|
let $name(ref arr2) = other;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
ret[i] = arr1[i] & arr2[i];
|
ret[i] = arr1[i] & arr2[i];
|
||||||
|
@ -224,9 +224,9 @@ macro_rules! construct_uint {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitxor(&self, other: &$name) -> $name {
|
fn bitxor(self, other: $name) -> $name {
|
||||||
let &$name(ref arr1) = self;
|
let $name(ref arr1) = self;
|
||||||
let &$name(ref arr2) = other;
|
let $name(ref arr2) = other;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
ret[i] = arr1[i] ^ arr2[i];
|
ret[i] = arr1[i] ^ arr2[i];
|
||||||
|
@ -239,9 +239,9 @@ macro_rules! construct_uint {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitor(&self, other: &$name) -> $name {
|
fn bitor(self, other: $name) -> $name {
|
||||||
let &$name(ref arr1) = self;
|
let $name(ref arr1) = self;
|
||||||
let &$name(ref arr2) = other;
|
let $name(ref arr2) = other;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
ret[i] = arr1[i] | arr2[i];
|
ret[i] = arr1[i] | arr2[i];
|
||||||
|
@ -254,8 +254,8 @@ macro_rules! construct_uint {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn not(&self) -> $name {
|
fn not(self) -> $name {
|
||||||
let &$name(ref arr) = self;
|
let $name(ref arr) = self;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
ret[i] = !arr[i];
|
ret[i] = !arr[i];
|
||||||
|
@ -267,11 +267,11 @@ macro_rules! construct_uint {
|
||||||
impl ::std::ops::Shl<usize> for $name {
|
impl ::std::ops::Shl<usize> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
fn shl(&self, shift: &usize) -> $name {
|
fn shl(self, shift: usize) -> $name {
|
||||||
let &$name(ref original) = self;
|
let $name(ref original) = self;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
let word_shift = *shift / 64;
|
let word_shift = shift / 64;
|
||||||
let bit_shift = *shift % 64;
|
let bit_shift = shift % 64;
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
// Shift
|
// Shift
|
||||||
if bit_shift < 64 && i + word_shift < $n_words {
|
if bit_shift < 64 && i + word_shift < $n_words {
|
||||||
|
@ -290,11 +290,11 @@ macro_rules! construct_uint {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[allow(unsigned_negate)]
|
#[allow(unsigned_negate)]
|
||||||
fn shr(&self, shift: &usize) -> $name {
|
fn shr(self, shift: usize) -> $name {
|
||||||
let &$name(ref original) = self;
|
let $name(ref original) = self;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
let word_shift = *shift / 64;
|
let word_shift = shift / 64;
|
||||||
let bit_shift = *shift % 64;
|
let bit_shift = shift % 64;
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
// Shift
|
// Shift
|
||||||
if bit_shift < 64 && i - word_shift < $n_words {
|
if bit_shift < 64 && i - word_shift < $n_words {
|
||||||
|
@ -329,10 +329,12 @@ macro_rules! construct_uint {
|
||||||
|
|
||||||
impl fmt::Debug for $name {
|
impl fmt::Debug for $name {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use std::fmt::Error;
|
let &$name(ref data) = self;
|
||||||
use network::encodable::ConsensusEncodable;
|
try!(write!(f, "0x"));
|
||||||
let mut encoder = RawEncoder::new(f.by_ref());
|
for ch in data.iter().rev() {
|
||||||
self.consensus_encode(&mut encoder).map_err(|_| Error)
|
try!(write!(f, "{:02x}", ch));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +365,7 @@ impl Uint256 {
|
||||||
/// Increment by 1
|
/// Increment by 1
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn increment(&mut self) {
|
pub fn increment(&mut self) {
|
||||||
let &Uint256(ref mut arr) = self;
|
let &mut Uint256(ref mut arr) = self;
|
||||||
arr[0] += 1;
|
arr[0] += 1;
|
||||||
if arr[0] == 0 {
|
if arr[0] == 0 {
|
||||||
arr[1] += 1;
|
arr[1] += 1;
|
||||||
|
@ -386,7 +388,7 @@ impl Uint256 {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io::IoResult;
|
use std::io;
|
||||||
use std::num::from_u64;
|
use std::num::from_u64;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
@ -440,7 +442,7 @@ mod tests {
|
||||||
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
|
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
let copy = init;
|
let copy = init;
|
||||||
|
|
||||||
let add = init.add(©);
|
let add = init + copy;
|
||||||
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
|
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
|
||||||
// Bitshifts
|
// Bitshifts
|
||||||
let shl = add << 88;
|
let shl = add << 88;
|
||||||
|
@ -452,7 +454,7 @@ mod tests {
|
||||||
incr.increment();
|
incr.increment();
|
||||||
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||||
// Subtraction
|
// Subtraction
|
||||||
let sub = incr.sub(&init);
|
let sub = incr - init;
|
||||||
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||||
// Multiplication
|
// Multiplication
|
||||||
let mult = sub.mul_u32(300);
|
let mult = sub.mul_u32(300);
|
||||||
|
@ -481,7 +483,7 @@ mod tests {
|
||||||
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
|
|
||||||
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
let add = (init << 64).add(&init);
|
let add = (init << 64) + init;
|
||||||
assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
assert_eq!(add >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(add >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
|
@ -495,8 +497,8 @@ mod tests {
|
||||||
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
||||||
let serial1 = serialize(&start1).unwrap();
|
let serial1 = serialize(&start1).unwrap();
|
||||||
let serial2 = serialize(&start2).unwrap();
|
let serial2 = serialize(&start2).unwrap();
|
||||||
let end1: IoResult<Uint256> = deserialize(serial1);
|
let end1: io::Result<Uint256> = deserialize(serial1);
|
||||||
let end2: IoResult<Uint256> = deserialize(serial2);
|
let end2: io::Result<Uint256> = deserialize(serial2);
|
||||||
|
|
||||||
assert_eq!(end1, Ok(start1));
|
assert_eq!(end1, Ok(start1));
|
||||||
assert_eq!(end2, Ok(start2));
|
assert_eq!(end2, Ok(start2));
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
use secp256k1::key::PublicKey;
|
use secp256k1::key::PublicKey;
|
||||||
use crypto::digest::Digest;
|
use crypto::digest::Digest;
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
|
use std::ops;
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
|
@ -36,23 +37,16 @@ pub struct Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
/// Returns a byteslice view of the `Address` --- note that no network information
|
|
||||||
/// is contained in this.
|
|
||||||
#[inline]
|
|
||||||
pub fn as_slice<'a>(&'a self) -> &'a [u8] {
|
|
||||||
self.hash.as_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an address from a public key
|
/// Creates an address from a public key
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_key(network: Network, pk: &PublicKey) -> Address {
|
pub fn from_key(network: Network, pk: &PublicKey) -> Address {
|
||||||
let mut sha = Sha256::new();
|
let mut sha = Sha256::new();
|
||||||
let mut out = [0, ..32];
|
let mut out = [0;32];
|
||||||
sha.input(pk.as_slice());
|
sha.input(&pk[..]);
|
||||||
sha.result(out.as_mut_slice());
|
sha.result(&mut out);
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
hash: Ripemd160Hash::from_data(out)
|
hash: Ripemd160Hash::from_data(&out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +56,53 @@ impl Address {
|
||||||
let mut script = Script::new();
|
let mut script = Script::new();
|
||||||
script.push_opcode(opcodes::All::OP_DUP);
|
script.push_opcode(opcodes::All::OP_DUP);
|
||||||
script.push_opcode(opcodes::All::OP_HASH160);
|
script.push_opcode(opcodes::All::OP_HASH160);
|
||||||
script.push_slice(self.hash.as_slice());
|
script.push_slice(&self.hash[..]);
|
||||||
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
||||||
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||||
script
|
script
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ops::Index<usize> for Address {
|
||||||
|
type Output = u8;
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: usize) -> &u8 {
|
||||||
|
&self.hash[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::Range<usize>> for Address {
|
||||||
|
type Output = [u8];
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
||||||
|
&self.hash[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::RangeTo<usize>> for Address {
|
||||||
|
type Output = [u8];
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
|
||||||
|
&self.hash[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::RangeFrom<usize>> for Address {
|
||||||
|
type Output = [u8];
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
||||||
|
&self.hash[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::RangeFull> for Address {
|
||||||
|
type Output = [u8];
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
||||||
|
&self.hash[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Conversion from other types into an address
|
/// Conversion from other types into an address
|
||||||
pub trait ToAddress {
|
pub trait ToAddress {
|
||||||
/// Copies `self` into a new `Address`
|
/// Copies `self` into a new `Address`
|
||||||
|
@ -93,7 +127,7 @@ impl ToBase58 for Address {
|
||||||
Network::Testnet => 111
|
Network::Testnet => 111
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
ret.push_all(self.hash.as_slice());
|
ret.push_all(&self.hash[..]);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +144,7 @@ impl FromBase58 for Address {
|
||||||
111 => Network::Testnet,
|
111 => Network::Testnet,
|
||||||
x => { return Err(base58::Error::InvalidVersion(vec![x])); }
|
x => { return Err(base58::Error::InvalidVersion(vec![x])); }
|
||||||
},
|
},
|
||||||
hash: Ripemd160Hash::from_slice(data.slice_from(1))
|
hash: Ripemd160Hash::from_slice(&data[1..])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,10 +171,10 @@ mod tests {
|
||||||
fn test_address_58() {
|
fn test_address_58() {
|
||||||
let addr = Address {
|
let addr = Address {
|
||||||
network: Bitcoin,
|
network: Bitcoin,
|
||||||
hash: Ripemd160Hash::from_slice("162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap().as_slice())
|
hash: Ripemd160Hash::from_slice(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(addr.to_base58check().as_slice(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
||||||
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
|
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{hash, Hash, SipHasher};
|
use std::hash::{hash, Hash, Hasher, SipHasher};
|
||||||
|
|
||||||
use secp256k1::key::SecretKey;
|
use secp256k1::key::SecretKey;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ pub struct WalletTxOut {
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct AddressIndex {
|
pub struct AddressIndex {
|
||||||
tentative_index: HashMap<Script, Vec<WalletTxOut>>,
|
tentative_index: HashMap<Script, Vec<WalletTxOut>>,
|
||||||
index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>, SipHasher>,
|
index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>>,
|
||||||
network: Network,
|
network: Network,
|
||||||
k1: u64,
|
k1: u64,
|
||||||
k2: u64
|
k2: u64
|
||||||
|
@ -74,7 +74,7 @@ impl AddressIndex {
|
||||||
let (k1, k2) = wallet.siphash_key();
|
let (k1, k2) = wallet.siphash_key();
|
||||||
let mut ret = AddressIndex {
|
let mut ret = AddressIndex {
|
||||||
tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256),
|
tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256),
|
||||||
index: HashMap::with_hasher(SipHasher::new()),
|
index: HashMap::new(),
|
||||||
network: wallet.network(),
|
network: wallet.network(),
|
||||||
k1: k1,
|
k1: k1,
|
||||||
k2: k2
|
k2: k2
|
||||||
|
@ -88,7 +88,9 @@ impl AddressIndex {
|
||||||
txo: txo.clone(),
|
txo: txo.clone(),
|
||||||
kind: WalletTxOutType::Unknown
|
kind: WalletTxOutType::Unknown
|
||||||
};
|
};
|
||||||
ret.tentative_index.find_or_insert(txo.script_pubkey.clone(), vec![]).push(new);
|
let mut entry = ret.tentative_index.entry(txo.script_pubkey.clone());
|
||||||
|
let txos = entry.or_insert(vec![]);
|
||||||
|
txos.push(new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
|
@ -99,14 +101,16 @@ impl AddressIndex {
|
||||||
pub fn index_wallet_txo(&mut self, wtx: &WalletTxOut, kind: WalletTxOutType) {
|
pub fn index_wallet_txo(&mut self, wtx: &WalletTxOut, kind: WalletTxOutType) {
|
||||||
let mut new = wtx.clone();
|
let mut new = wtx.clone();
|
||||||
new.kind = kind;
|
new.kind = kind;
|
||||||
self.index.find_or_insert((wtx.txid, wtx.vout), vec![]).push(new);
|
let mut entry = self.index.entry((wtx.txid, wtx.vout));
|
||||||
|
let txos = entry.or_insert(vec![]);
|
||||||
|
txos.push(new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A filtering function used for creating a small address index.
|
/// A filtering function used for creating a small address index.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn admissible_address(&self, addr: &Address) -> bool {
|
pub fn admissible_address(&self, addr: &Address) -> bool {
|
||||||
let mut hasher = SipHasher::new_with_keys(self.k1, self.k2);
|
let mut hasher = SipHasher::new_with_keys(self.k1, self.k2);
|
||||||
addr.hash(&mut hasher);
|
(&addr[..]).hash(&mut hasher);
|
||||||
hasher.finish() & 0xFF == 0
|
hasher.finish() & 0xFF == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +127,7 @@ impl AddressIndex {
|
||||||
/// may be more than one for any given scriptpubkey.
|
/// may be more than one for any given scriptpubkey.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn find_by_script<'a>(&'a self, pubkey: &Script) -> &'a [WalletTxOut] {
|
pub fn find_by_script<'a>(&'a self, pubkey: &Script) -> &'a [WalletTxOut] {
|
||||||
self.tentative_index.find(pubkey).map(|v| v.as_slice()).unwrap_or(&[])
|
self.tentative_index.get(pubkey).map(|v| &v[..]).unwrap_or(&[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
//! at https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
//! at https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||||
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use serde;
|
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
|
||||||
use byteorder::{ByteOrder, BigEndian};
|
use crypto::cryptoutil::{read_u32_be, write_u32_be};
|
||||||
use crypto::digest::Digest;
|
use crypto::digest::Digest;
|
||||||
use crypto::hmac::Hmac;
|
use crypto::hmac::Hmac;
|
||||||
use crypto::mac::Mac;
|
use crypto::mac::Mac;
|
||||||
|
@ -92,9 +92,9 @@ pub enum ChildNumber {
|
||||||
Normal(u32),
|
Normal(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for ChildNumber {
|
impl Serialize for ChildNumber {
|
||||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||||
where S: serde::Serializer {
|
where S: Serializer {
|
||||||
match *self {
|
match *self {
|
||||||
ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s),
|
ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s),
|
||||||
ChildNumber::Normal(n) => n.serialize(s)
|
ChildNumber::Normal(n) => n.serialize(s)
|
||||||
|
@ -102,10 +102,10 @@ impl serde::Serialize for ChildNumber {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Deserialize for ChildNumber {
|
impl Deserialize for ChildNumber {
|
||||||
fn deserialize<D>(&self, d: &mut D) -> Result<ChildNumber, D::Error>
|
fn deserialize<D>(d: &mut D) -> Result<ChildNumber, D::Error>
|
||||||
where D: serde::Deserializer {
|
where D: Deserializer {
|
||||||
let n: u32 = try!(serde::Deserialize::decode(d));
|
let n: u32 = try!(Deserialize::deserialize(d));
|
||||||
if n < (1 << 31) {
|
if n < (1 << 31) {
|
||||||
Ok(ChildNumber::Normal(n))
|
Ok(ChildNumber::Normal(n))
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,7 +120,7 @@ pub enum Error {
|
||||||
/// A pk->pk derivation was attempted on a hardened key
|
/// A pk->pk derivation was attempted on a hardened key
|
||||||
CannotDeriveFromHardenedKey,
|
CannotDeriveFromHardenedKey,
|
||||||
/// A secp256k1 error occured
|
/// A secp256k1 error occured
|
||||||
EcdsaError(secp256k1::Error),
|
Ecdsa(secp256k1::Error),
|
||||||
/// A child number was provided that was out of range
|
/// A child number was provided that was out of range
|
||||||
InvalidChildNumber(ChildNumber),
|
InvalidChildNumber(ChildNumber),
|
||||||
/// Error creating a master seed --- for application use
|
/// Error creating a master seed --- for application use
|
||||||
|
@ -131,17 +131,17 @@ impl ExtendedPrivKey {
|
||||||
/// Construct a new master key from a seed value
|
/// Construct a new master key from a seed value
|
||||||
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
||||||
let mut result = [0; 64];
|
let mut result = [0; 64];
|
||||||
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed".as_slice());
|
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
|
||||||
hmac.input(seed);
|
hmac.input(seed);
|
||||||
hmac.raw_result(result.as_mut_slice());
|
hmac.raw_result(&mut result);
|
||||||
|
|
||||||
Ok(ExtendedPrivKey {
|
Ok(ExtendedPrivKey {
|
||||||
network: network,
|
network: network,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
parent_fingerprint: Default::default(),
|
parent_fingerprint: Default::default(),
|
||||||
child_number: ChildNumber::Normal(0),
|
child_number: ChildNumber::Normal(0),
|
||||||
secret_key: try!(SecretKey::from_slice(result.slice_to(32)).map_err(Error::EcdsaError)),
|
secret_key: try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)),
|
||||||
chain_code: ChainCode::from_slice(result.slice_from(32))
|
chain_code: ChainCode::from_slice(&result[32..])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,27 +158,29 @@ impl ExtendedPrivKey {
|
||||||
/// Private->Private child key derivation
|
/// Private->Private child key derivation
|
||||||
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
||||||
let mut result = [0; 64];
|
let mut result = [0; 64];
|
||||||
let mut hmac = Hmac::new(Sha512::new(), self.chain_code.as_slice());
|
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
||||||
|
let mut be_n = [0; 32];
|
||||||
match i {
|
match i {
|
||||||
ChildNumber::Normal(n) => {
|
ChildNumber::Normal(n) => {
|
||||||
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
||||||
// Non-hardened key: compute public data and use that
|
// Non-hardened key: compute public data and use that
|
||||||
secp256k1::init();
|
secp256k1::init();
|
||||||
// Note the unwrap: this is fine, we checked the SK when we created it
|
// Note the unwrap: this is fine, we checked the SK when we created it
|
||||||
hmac.input(PublicKey::from_secret_key(&self.secret_key, true).as_slice());
|
hmac.input(&PublicKey::from_secret_key(&self.secret_key, true)[..]);
|
||||||
hmac.write_u32::<BigEndian>(n);
|
write_u32_be(&mut be_n, n);
|
||||||
}
|
}
|
||||||
ChildNumber::Hardened(n) => {
|
ChildNumber::Hardened(n) => {
|
||||||
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
||||||
// Hardened key: use only secret data to prevent public derivation
|
// Hardened key: use only secret data to prevent public derivation
|
||||||
hmac.input([0]);
|
hmac.input(&[0u8]);
|
||||||
hmac.input(self.secret_key.as_slice());
|
hmac.input(&self.secret_key[..]);
|
||||||
hmac.write_u32::<BigEndian>(n + (1 << 31));
|
write_u32_be(&mut be_n, n + (1 << 31));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hmac.raw_result(result.as_mut_slice());
|
hmac.input(&be_n);
|
||||||
let mut sk = try!(SecretKey::from_slice(result.slice_to(32)).map_err(Error::EcdsaError));
|
hmac.raw_result(&mut result);
|
||||||
try!(sk.add_assign(&self.secret_key).map_err(Error::EcdsaError));
|
let mut sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
|
||||||
|
try!(sk.add_assign(&self.secret_key).map_err(Error::Ecdsa));
|
||||||
|
|
||||||
Ok(ExtendedPrivKey {
|
Ok(ExtendedPrivKey {
|
||||||
network: self.network,
|
network: self.network,
|
||||||
|
@ -186,7 +188,7 @@ impl ExtendedPrivKey {
|
||||||
parent_fingerprint: self.fingerprint(),
|
parent_fingerprint: self.fingerprint(),
|
||||||
child_number: i,
|
child_number: i,
|
||||||
secret_key: sk,
|
secret_key: sk,
|
||||||
chain_code: ChainCode::from_slice(result.slice_from(32))
|
chain_code: ChainCode::from_slice(&result[32..])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,19 +200,19 @@ impl ExtendedPrivKey {
|
||||||
let pk = ExtendedPubKey::from_private(self);
|
let pk = ExtendedPubKey::from_private(self);
|
||||||
// Do SHA256 of just the ECDSA pubkey
|
// Do SHA256 of just the ECDSA pubkey
|
||||||
let mut sha2 = Sha256::new();
|
let mut sha2 = Sha256::new();
|
||||||
sha2.input(pk.public_key.as_slice());
|
sha2.input(&pk.public_key[..]);
|
||||||
sha2.result(sha2_res.as_mut_slice());
|
sha2.result(&mut sha2_res);
|
||||||
// do RIPEMD160
|
// do RIPEMD160
|
||||||
let mut ripemd = Ripemd160::new();
|
let mut ripemd = Ripemd160::new();
|
||||||
ripemd.input(sha2_res.as_slice());
|
ripemd.input(&sha2_res);
|
||||||
ripemd.result(ripemd_res.as_mut_slice());
|
ripemd.result(&mut ripemd_res);
|
||||||
// Return
|
// Return
|
||||||
ripemd_res
|
ripemd_res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the first four bytes of the identifier
|
/// Returns the first four bytes of the identifier
|
||||||
pub fn fingerprint(&self) -> Fingerprint {
|
pub fn fingerprint(&self) -> Fingerprint {
|
||||||
Fingerprint::from_slice(self.identifier().slice_to(4))
|
Fingerprint::from_slice(&self.identifier()[0..4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,16 +241,18 @@ impl ExtendedPubKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChildNumber::Normal(n) => {
|
ChildNumber::Normal(n) => {
|
||||||
let mut hmac = Hmac::new(Sha512::new(), self.chain_code.as_slice());
|
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
||||||
hmac.input(self.public_key.as_slice());
|
hmac.input(&self.public_key[..]);
|
||||||
hmac.write_u32::<BigEndian>(n);
|
let mut be_n = [0; 32];
|
||||||
|
write_u32_be(&mut be_n, n);
|
||||||
|
hmac.input(&be_n);
|
||||||
|
|
||||||
let mut result = [0; 64];
|
let mut result = [0; 64];
|
||||||
hmac.raw_result(result.as_mut_slice());
|
hmac.raw_result(&mut result);
|
||||||
|
|
||||||
let sk = try!(SecretKey::from_slice(result.slice_to(32)).map_err(Error::EcdsaError));
|
let sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
|
||||||
let mut pk = self.public_key.clone();
|
let mut pk = self.public_key.clone();
|
||||||
try!(pk.add_exp_assign(&sk).map_err(Error::EcdsaError));
|
try!(pk.add_exp_assign(&sk).map_err(Error::Ecdsa));
|
||||||
|
|
||||||
Ok(ExtendedPubKey {
|
Ok(ExtendedPubKey {
|
||||||
network: self.network,
|
network: self.network,
|
||||||
|
@ -256,7 +260,7 @@ impl ExtendedPubKey {
|
||||||
parent_fingerprint: self.fingerprint(),
|
parent_fingerprint: self.fingerprint(),
|
||||||
child_number: i,
|
child_number: i,
|
||||||
public_key: pk,
|
public_key: pk,
|
||||||
chain_code: ChainCode::from_slice(result.slice_from(32))
|
chain_code: ChainCode::from_slice(&result[32..])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,19 +272,19 @@ impl ExtendedPubKey {
|
||||||
let mut ripemd_res = [0; 20];
|
let mut ripemd_res = [0; 20];
|
||||||
// Do SHA256 of just the ECDSA pubkey
|
// Do SHA256 of just the ECDSA pubkey
|
||||||
let mut sha2 = Sha256::new();
|
let mut sha2 = Sha256::new();
|
||||||
sha2.input(self.public_key.as_slice());
|
sha2.input(&self.public_key[..]);
|
||||||
sha2.result(sha2_res.as_mut_slice());
|
sha2.result(&mut sha2_res);
|
||||||
// do RIPEMD160
|
// do RIPEMD160
|
||||||
let mut ripemd = Ripemd160::new();
|
let mut ripemd = Ripemd160::new();
|
||||||
ripemd.input(sha2_res.as_slice());
|
ripemd.input(&sha2_res);
|
||||||
ripemd.result(ripemd_res.as_mut_slice());
|
ripemd.result(&mut ripemd_res);
|
||||||
// Return
|
// Return
|
||||||
ripemd_res
|
ripemd_res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the first four bytes of the identifier
|
/// Returns the first four bytes of the identifier
|
||||||
pub fn fingerprint(&self) -> Fingerprint {
|
pub fn fingerprint(&self) -> Fingerprint {
|
||||||
Fingerprint::from_slice(self.identifier().slice_to(4))
|
Fingerprint::from_slice(&self.identifier()[0..4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,22 +292,24 @@ impl ToBase58 for ExtendedPrivKey {
|
||||||
fn base58_layout(&self) -> Vec<u8> {
|
fn base58_layout(&self) -> Vec<u8> {
|
||||||
let mut ret = Vec::with_capacity(78);
|
let mut ret = Vec::with_capacity(78);
|
||||||
ret.push_all(match self.network {
|
ret.push_all(match self.network {
|
||||||
Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4],
|
Network::Bitcoin => &[0x04, 0x88, 0xAD, 0xE4],
|
||||||
Network::Testnet => [0x04, 0x35, 0x83, 0x94]
|
Network::Testnet => &[0x04, 0x35, 0x83, 0x94]
|
||||||
});
|
});
|
||||||
ret.push(self.depth as u8);
|
ret.push(self.depth as u8);
|
||||||
ret.push_all(self.parent_fingerprint.as_slice());
|
ret.push_all(&self.parent_fingerprint[..]);
|
||||||
|
let mut be_n = [0; 32];
|
||||||
match self.child_number {
|
match self.child_number {
|
||||||
ChildNumber::Hardened(n) => {
|
ChildNumber::Hardened(n) => {
|
||||||
ret.write_u32::<BigEndian>(n + (1 << 31));
|
write_u32_be(&mut be_n, n + (1 << 31));
|
||||||
}
|
}
|
||||||
ChildNumber::Normal(n) => {
|
ChildNumber::Normal(n) => {
|
||||||
ret.write_u32::<BigEndian>(n);
|
write_u32_be(&mut be_n, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret.push_all(self.chain_code.as_slice());
|
ret.push_all(&be_n);
|
||||||
|
ret.push_all(&self.chain_code[..]);
|
||||||
ret.push(0);
|
ret.push(0);
|
||||||
ret.push_all(self.secret_key.as_slice());
|
ret.push_all(&self.secret_key[..]);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,22 +320,22 @@ impl FromBase58 for ExtendedPrivKey {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(base58::Error::InvalidLength(data.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let cn_int = BigEndian::read_u32(&data[9..13]);
|
let cn_int = read_u32_be(&data[9..13]);
|
||||||
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
||||||
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
||||||
|
|
||||||
Ok(ExtendedPrivKey {
|
Ok(ExtendedPrivKey {
|
||||||
network: match data.slice_to(4) {
|
network: match &data[0..4] {
|
||||||
[0x04, 0x88, 0xAD, 0xE4] => Network::Bitcoin,
|
[0x04u8, 0x88, 0xAD, 0xE4] => Network::Bitcoin,
|
||||||
[0x04, 0x35, 0x83, 0x94] => Network::Testnet,
|
[0x04u8, 0x35, 0x83, 0x94] => Network::Testnet,
|
||||||
_ => { return Err(base58::Error::InvalidVersion(data.slice_to(4).to_vec())); }
|
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
|
||||||
},
|
},
|
||||||
depth: data[4],
|
depth: data[4],
|
||||||
parent_fingerprint: Fingerprint::from_slice(data.slice(5, 9)),
|
parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
|
||||||
child_number: child_number,
|
child_number: child_number,
|
||||||
chain_code: ChainCode::from_slice(data.slice(13, 45)),
|
chain_code: ChainCode::from_slice(&data[13..45]),
|
||||||
secret_key: try!(SecretKey::from_slice(
|
secret_key: try!(SecretKey::from_slice(
|
||||||
data.slice(46, 78)).map_err(|e|
|
&data[46..78]).map_err(|e|
|
||||||
base58::Error::Other(e.to_string())))
|
base58::Error::Other(e.to_string())))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -340,21 +346,23 @@ impl ToBase58 for ExtendedPubKey {
|
||||||
assert!(self.public_key.is_compressed());
|
assert!(self.public_key.is_compressed());
|
||||||
let mut ret = Vec::with_capacity(78);
|
let mut ret = Vec::with_capacity(78);
|
||||||
ret.push_all(match self.network {
|
ret.push_all(match self.network {
|
||||||
Network::Bitcoin => [0x04, 0x88, 0xB2, 0x1E],
|
Network::Bitcoin => &[0x04u8, 0x88, 0xB2, 0x1E],
|
||||||
Network::Testnet => [0x04, 0x35, 0x87, 0xCF]
|
Network::Testnet => &[0x04u8, 0x35, 0x87, 0xCF]
|
||||||
});
|
});
|
||||||
ret.push(self.depth as u8);
|
ret.push(self.depth as u8);
|
||||||
ret.push_all(self.parent_fingerprint.as_slice());
|
ret.push_all(&self.parent_fingerprint[..]);
|
||||||
|
let mut be_n = [0; 32];
|
||||||
match self.child_number {
|
match self.child_number {
|
||||||
ChildNumber::Hardened(n) => {
|
ChildNumber::Hardened(n) => {
|
||||||
ret.write_u32::<BigEndian>(n + (1 << 31));
|
write_u32_be(&mut be_n, n + (1 << 31));
|
||||||
}
|
}
|
||||||
ChildNumber::Normal(n) => {
|
ChildNumber::Normal(n) => {
|
||||||
ret.write_u32::<BigEndian>(n);
|
write_u32_be(&mut be_n, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret.push_all(self.chain_code.as_slice());
|
ret.push_all(&be_n);
|
||||||
ret.push_all(self.public_key.as_slice());
|
ret.push_all(&self.chain_code[..]);
|
||||||
|
ret.push_all(&self.public_key[..]);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,22 +373,22 @@ impl FromBase58 for ExtendedPubKey {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(base58::Error::InvalidLength(data.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let cn_int = BigEndian::read_u32(&data[9..13]);
|
let cn_int = read_u32_be(&data[9..13]);
|
||||||
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
||||||
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
||||||
|
|
||||||
Ok(ExtendedPubKey {
|
Ok(ExtendedPubKey {
|
||||||
network: match data.slice_to(4) {
|
network: match &data[0..4] {
|
||||||
[0x04, 0x88, 0xB2, 0x1E] => Network::Bitcoin,
|
[0x04, 0x88, 0xB2, 0x1E] => Network::Bitcoin,
|
||||||
[0x04, 0x35, 0x87, 0xCF] => Network::Testnet,
|
[0x04, 0x35, 0x87, 0xCF] => Network::Testnet,
|
||||||
_ => { return Err(base58::Error::InvalidVersion(data.slice_to(4).to_vec())); }
|
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
|
||||||
},
|
},
|
||||||
depth: data[4],
|
depth: data[4],
|
||||||
parent_fingerprint: Fingerprint::from_slice(data.slice(5, 9)),
|
parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
|
||||||
child_number: child_number,
|
child_number: child_number,
|
||||||
chain_code: ChainCode::from_slice(data.slice(13, 45)),
|
chain_code: ChainCode::from_slice(&data[13..45]),
|
||||||
public_key: try!(PublicKey::from_slice(
|
public_key: try!(PublicKey::from_slice(
|
||||||
data.slice(45, 78)).map_err(|e|
|
&data[45..78]).map_err(|e|
|
||||||
base58::Error::Other(e.to_string())))
|
base58::Error::Other(e.to_string())))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -421,8 +429,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check result against expected base58
|
// Check result against expected base58
|
||||||
assert_eq!(sk.to_base58check().as_slice(), expected_sk);
|
assert_eq!(&sk.to_base58check()[..], expected_sk);
|
||||||
assert_eq!(pk.to_base58check().as_slice(), expected_pk);
|
assert_eq!(&pk.to_base58check()[..], expected_pk);
|
||||||
// Check decoded base58 against result
|
// Check decoded base58 against result
|
||||||
let decoded_sk = FromBase58::from_base58check(expected_sk);
|
let decoded_sk = FromBase58::from_base58check(expected_sk);
|
||||||
let decoded_pk = FromBase58::from_base58check(expected_pk);
|
let decoded_pk = FromBase58::from_base58check(expected_pk);
|
||||||
|
@ -434,32 +442,32 @@ mod tests {
|
||||||
fn test_vector_1() {
|
fn test_vector_1() {
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
// m
|
// m
|
||||||
test_path(Bitcoin, seed.as_slice(), [],
|
test_path(Bitcoin, &seed, [],
|
||||||
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
||||||
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
||||||
|
|
||||||
// m/0h
|
// m/0h
|
||||||
test_path(Bitcoin, seed.as_slice(), [Hardened(0)],
|
test_path(Bitcoin, &seed, [Hardened(0)],
|
||||||
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
||||||
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
||||||
|
|
||||||
// m/0h/1
|
// m/0h/1
|
||||||
test_path(Bitcoin, seed.as_slice(), [Hardened(0), Normal(1)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1)],
|
||||||
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
||||||
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
||||||
|
|
||||||
// m/0h/1/2h
|
// m/0h/1/2h
|
||||||
test_path(Bitcoin, seed.as_slice(), [Hardened(0), Normal(1), Hardened(2)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2)],
|
||||||
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
||||||
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
||||||
|
|
||||||
// m/0h/1/2h/2
|
// m/0h/1/2h/2
|
||||||
test_path(Bitcoin, seed.as_slice(), [Hardened(0), Normal(1), Hardened(2), Normal(2)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2)],
|
||||||
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
||||||
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
||||||
|
|
||||||
// m/0h/1/2h/2/1000000000
|
// m/0h/1/2h/2/1000000000
|
||||||
test_path(Bitcoin, seed.as_slice(), [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)],
|
||||||
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
||||||
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
||||||
}
|
}
|
||||||
|
@ -469,32 +477,32 @@ mod tests {
|
||||||
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
|
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
|
||||||
|
|
||||||
// m
|
// m
|
||||||
test_path(Bitcoin, seed.as_slice(), [],
|
test_path(Bitcoin, &seed, [],
|
||||||
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
||||||
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
||||||
|
|
||||||
// m/0
|
// m/0
|
||||||
test_path(Bitcoin, seed.as_slice(), [Normal(0)],
|
test_path(Bitcoin, &seed, [Normal(0)],
|
||||||
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
||||||
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
||||||
|
|
||||||
// m/0/2147483647h
|
// m/0/2147483647h
|
||||||
test_path(Bitcoin, seed.as_slice(), [Normal(0), Hardened(2147483647)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647)],
|
||||||
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
||||||
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
||||||
|
|
||||||
// m/0/2147483647h/1
|
// m/0/2147483647h/1
|
||||||
test_path(Bitcoin, seed.as_slice(), [Normal(0), Hardened(2147483647), Normal(1)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1)],
|
||||||
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
||||||
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
||||||
|
|
||||||
// m/0/2147483647h/1/2147483646h
|
// m/0/2147483647h/1/2147483646h
|
||||||
test_path(Bitcoin, seed.as_slice(), [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)],
|
||||||
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
||||||
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
||||||
|
|
||||||
// m/0/2147483647h/1/2147483646h/2
|
// m/0/2147483647h/1/2147483646h/2
|
||||||
test_path(Bitcoin, seed.as_slice(), [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)],
|
||||||
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
||||||
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
||||||
}
|
}
|
||||||
|
@ -512,8 +520,8 @@ mod tests {
|
||||||
assert!(h1 != n1);
|
assert!(h1 != n1);
|
||||||
assert!(h1_str != n1_str);
|
assert!(h1_str != n1_str);
|
||||||
|
|
||||||
let h1_dec = json::decode(h1_str.as_slice()).unwrap();
|
let h1_dec = json::decode(&h1_str).unwrap();
|
||||||
let n1_dec = json::decode(n1_str.as_slice()).unwrap();
|
let n1_dec = json::decode(&n1_str).unwrap();
|
||||||
assert_eq!(h1, h1_dec);
|
assert_eq!(h1, h1_dec);
|
||||||
assert_eq!(n1, n1_dec);
|
assert_eq!(n1, n1_dec);
|
||||||
}
|
}
|
||||||
|
@ -521,7 +529,7 @@ mod tests {
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_sequential_normal_children(bh: &mut Bencher) {
|
pub fn generate_sequential_normal_children(bh: &mut Bencher) {
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap();
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
bh.iter( || {
|
bh.iter( || {
|
||||||
black_box(msk.ckd_priv(Normal(i)));
|
black_box(msk.ckd_priv(Normal(i)));
|
||||||
|
@ -532,7 +540,7 @@ mod tests {
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
|
pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap();
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
bh.iter( || {
|
bh.iter( || {
|
||||||
black_box(msk.ckd_priv(Hardened(i)));
|
black_box(msk.ckd_priv(Hardened(i)));
|
||||||
|
@ -543,7 +551,7 @@ mod tests {
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_sequential_public_children(bh: &mut Bencher) {
|
pub fn generate_sequential_public_children(bh: &mut Bencher) {
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap();
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
let mpk = ExtendedPubKey::from_private(&msk);
|
let mpk = ExtendedPubKey::from_private(&msk);
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
@ -558,7 +566,7 @@ mod tests {
|
||||||
use wallet::address::Address;
|
use wallet::address::Address;
|
||||||
|
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap();
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
let mpk = ExtendedPubKey::from_private(&msk);
|
let mpk = ExtendedPubKey::from_private(&msk);
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use serde;
|
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
|
||||||
use secp256k1::key::PublicKey;
|
use secp256k1::key::PublicKey;
|
||||||
|
|
||||||
|
@ -85,23 +85,23 @@ pub struct Wallet {
|
||||||
index: Option<AddressIndex>
|
index: Option<AddressIndex>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for Wallet {
|
impl Serialize for Wallet {
|
||||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||||
where S: serde::Serializer {
|
where S: Serializer {
|
||||||
let len = self.accounts.len();
|
let len = self.accounts.len();
|
||||||
try!(self.master.serialize(s));
|
try!(self.master.serialize(s));
|
||||||
self.accounts.serialize(s)
|
self.accounts.serialize(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Deserialize for Wallet {
|
impl Deserialize for Wallet {
|
||||||
fn deserialize<D>(&self, d: &mut D) -> Result<Wallet, D::Error>
|
fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error>
|
||||||
where D: serde::Deserializer {
|
where D: Deserializer {
|
||||||
Wallet {
|
Ok(Wallet {
|
||||||
master: try!(serde::Deserialize::deserialize(d)),
|
master: try!(Deserialize::deserialize(d)),
|
||||||
accounts: try!(serde::Deserialize::deserialize(d)),
|
accounts: try!(Deserialize::deserialize(d)),
|
||||||
index: None
|
index: None
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ impl Wallet {
|
||||||
/// Adds an account to a wallet
|
/// Adds an account to a wallet
|
||||||
pub fn account_insert(&mut self, name: String)
|
pub fn account_insert(&mut self, name: String)
|
||||||
-> Result<(), Error> {
|
-> Result<(), Error> {
|
||||||
if self.accounts.find(&name).is_some() {
|
if self.accounts.contains_key(&name) {
|
||||||
return Err(Error::DuplicateAccount);
|
return Err(Error::DuplicateAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,9 +159,8 @@ impl Wallet {
|
||||||
|
|
||||||
/// Locates an account in a wallet
|
/// Locates an account in a wallet
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn account_find<'a>(&'a self, name: &str)
|
pub fn account_get<'a>(&'a self, name: &str) -> Option<&'a Account> {
|
||||||
-> Option<&'a Account> {
|
self.accounts.get(name)
|
||||||
self.accounts.find_equiv(&name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new address
|
/// Create a new address
|
||||||
|
@ -169,8 +168,7 @@ impl Wallet {
|
||||||
account: &str,
|
account: &str,
|
||||||
chain: AccountChain)
|
chain: AccountChain)
|
||||||
-> Result<Address, Error> {
|
-> Result<Address, Error> {
|
||||||
// TODO: unnecessary allocation, waiting on *_equiv in stdlib
|
let account = self.accounts.get_mut(account);
|
||||||
let account = self.accounts.find_mut(&account.to_string());
|
|
||||||
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
||||||
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
|
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
|
||||||
|
|
||||||
|
@ -236,7 +234,7 @@ impl Wallet {
|
||||||
|
|
||||||
/// Account balance
|
/// Account balance
|
||||||
pub fn balance(&self, account: &str) -> Result<u64, Error> {
|
pub fn balance(&self, account: &str) -> Result<u64, Error> {
|
||||||
let account = self.accounts.find_equiv(&account);
|
let account = self.accounts.get(account);
|
||||||
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
||||||
self.account_balance(account)
|
self.account_balance(account)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue