Revamp Serializable interface to be similar to Encoder/Encodable
This is a massive simplification, fixes a couple endianness bugs (though not all of them I don't think), should give a speedup, gets rid of the `serialize_iter` crap.
This commit is contained in:
parent
020295f8c9
commit
a2ce000b2b
|
@ -20,13 +20,13 @@
|
|||
//! these blocks and the blockchain.
|
||||
//!
|
||||
|
||||
use std::io::IoResult;
|
||||
use std::num::{Zero, from_u64};
|
||||
|
||||
use util::error::{BitcoinResult, SpvBadTarget, SpvBadProofOfWork};
|
||||
use util::hash::Sha256dHash;
|
||||
use util::uint::Uint256;
|
||||
use network::serialize::{Serializable, SerializeIter, VarInt};
|
||||
use network::encodable::{ConsensusEncodable, VarInt};
|
||||
use network::serialize::BitcoinHash;
|
||||
use blockdata::transaction::Transaction;
|
||||
|
||||
/// A block header, which contains all the block's information except
|
||||
|
@ -101,7 +101,7 @@ impl BlockHeader {
|
|||
if target != required_target {
|
||||
return Err(SpvBadTarget);
|
||||
}
|
||||
let ref hash = self.bitcoin_hash().as_uint256();
|
||||
let ref hash = self.bitcoin_hash().into_uint256();
|
||||
if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) }
|
||||
}
|
||||
|
||||
|
@ -117,10 +117,23 @@ impl BlockHeader {
|
|||
}
|
||||
}
|
||||
|
||||
impl_serializable!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce)
|
||||
impl BitcoinHash for BlockHeader {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
use network::serialize::serialize;
|
||||
Sha256dHash::from_data(serialize(self).unwrap().as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl BitcoinHash for Block {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
self.header.bitcoin_hash()
|
||||
}
|
||||
}
|
||||
|
||||
impl_consensus_encoding!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce)
|
||||
impl_json!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce)
|
||||
impl_serializable!(Block, header, txdata)
|
||||
impl_serializable!(LoneBlockHeader, header, tx_count)
|
||||
impl_consensus_encoding!(Block, header, txdata)
|
||||
impl_consensus_encoding!(LoneBlockHeader, header, tx_count)
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -128,7 +141,7 @@ mod tests {
|
|||
use serialize::hex::FromHex;
|
||||
|
||||
use blockdata::block::Block;
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn block_test() {
|
||||
|
@ -138,8 +151,8 @@ mod tests {
|
|||
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
||||
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
||||
|
||||
let decode: IoResult<Block> = Serializable::deserialize(some_block.iter().map(|n| *n));
|
||||
let bad_decode: IoResult<Block> = Serializable::deserialize(cutoff_block.iter().map(|n| *n));
|
||||
let decode: IoResult<Block> = deserialize(some_block.clone());
|
||||
let bad_decode: IoResult<Block> = deserialize(cutoff_block);
|
||||
|
||||
assert!(decode.is_ok());
|
||||
assert!(bad_decode.is_err());
|
||||
|
@ -153,8 +166,7 @@ mod tests {
|
|||
assert_eq!(real_decode.header.nonce, 2067413810);
|
||||
// [test] TODO: check the transaction data
|
||||
|
||||
let reserialize = real_decode.serialize();
|
||||
assert_eq!(reserialize.as_slice(), some_block.as_slice());
|
||||
assert_eq!(serialize(&real_decode), Ok(some_block));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
//! to make sure we are holding the only references.
|
||||
//!
|
||||
|
||||
use std::io::{IoResult, IoError, OtherIoError};
|
||||
use std::num::Zero;
|
||||
use std::kinds::marker;
|
||||
|
||||
|
@ -31,15 +30,15 @@ use blockdata::transaction::Transaction;
|
|||
use blockdata::constants::{DIFFCHANGE_INTERVAL, DIFFCHANGE_TIMESPAN,
|
||||
TARGET_BLOCK_SPACING, max_target, genesis_block};
|
||||
use network::constants::{Network, BitcoinTestnet};
|
||||
use network::serialize::{Serializable, SerializeIter};
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::serialize::{BitcoinHash, SimpleDecoder, SimpleEncoder};
|
||||
use util::BitArray;
|
||||
use util::error::{BitcoinResult, BlockNotFound, DuplicateHash, PrevHashNotFound};
|
||||
use util::uint::Uint256;
|
||||
use util::hash::Sha256dHash;
|
||||
use util::misc::prepend_err;
|
||||
use util::patricia_tree::PatriciaTree;
|
||||
|
||||
type BlockTree = PatriciaTree<Box<BlockchainNode>, Uint256>;
|
||||
type BlockTree = PatriciaTree<Uint256, Box<BlockchainNode>>;
|
||||
type NodePtr = *const BlockchainNode;
|
||||
|
||||
/// A link in the blockchain
|
||||
|
@ -80,32 +79,35 @@ impl BlockchainNode {
|
|||
}
|
||||
}
|
||||
|
||||
impl Serializable for BlockchainNode {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut ret = vec![];
|
||||
ret.extend(self.block.serialize().move_iter());
|
||||
ret.extend(self.total_work.serialize().move_iter());
|
||||
ret.extend(self.required_difficulty.serialize().move_iter());
|
||||
ret.extend(self.height.serialize().move_iter());
|
||||
ret.extend(self.has_txdata.serialize().move_iter());
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for BlockchainNode {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
try!(self.block.consensus_encode(s));
|
||||
try!(self.total_work.consensus_encode(s));
|
||||
try!(self.required_difficulty.consensus_encode(s));
|
||||
try!(self.height.consensus_encode(s));
|
||||
try!(self.has_txdata.consensus_encode(s));
|
||||
// Don't serialize the prev or next pointers
|
||||
ret
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<BlockchainNode> {
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for BlockchainNode {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<BlockchainNode, E> {
|
||||
Ok(BlockchainNode {
|
||||
block: try!(prepend_err("block", Serializable::deserialize(iter.by_ref()))),
|
||||
total_work: try!(prepend_err("total_work", Serializable::deserialize(iter.by_ref()))),
|
||||
required_difficulty: try!(prepend_err("req_difficulty", Serializable::deserialize(iter.by_ref()))),
|
||||
height: try!(prepend_err("height", Serializable::deserialize(iter.by_ref()))),
|
||||
has_txdata: try!(prepend_err("has_txdata", Serializable::deserialize(iter.by_ref()))),
|
||||
block: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
total_work: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
required_difficulty: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
height: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
has_txdata: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
prev: RawPtr::null(),
|
||||
next: RawPtr::null()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Override Serialize::hash to return the blockheader hash, since the
|
||||
// hash of the node itself is pretty much meaningless.
|
||||
impl BitcoinHash for BlockchainNode {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
self.block.header.bitcoin_hash()
|
||||
}
|
||||
|
@ -120,55 +122,39 @@ pub struct Blockchain {
|
|||
genesis_hash: Sha256dHash
|
||||
}
|
||||
|
||||
impl Serializable for Blockchain {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut ret = vec![];
|
||||
ret.extend(self.network.serialize().move_iter());
|
||||
ret.extend(self.tree.serialize().move_iter());
|
||||
ret.extend(self.best_hash.serialize().move_iter());
|
||||
ret.extend(self.genesis_hash.serialize().move_iter());
|
||||
ret
|
||||
}
|
||||
|
||||
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||
SerializeIter {
|
||||
data_iter: None,
|
||||
sub_iter_iter: box vec![ &self.network as &Serializable,
|
||||
&self.tree as &Serializable,
|
||||
&self.best_hash as &Serializable,
|
||||
&self.genesis_hash as &Serializable ].move_iter(),
|
||||
sub_iter: None,
|
||||
sub_started: false
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for Blockchain {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
try!(self.network.consensus_encode(s));
|
||||
try!(self.tree.consensus_encode(s));
|
||||
try!(self.best_hash.consensus_encode(s));
|
||||
try!(self.genesis_hash.consensus_encode(s));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Blockchain> {
|
||||
let network: Network = try!(prepend_err("network", Serializable::deserialize(iter.by_ref())));
|
||||
let mut tree: BlockTree = try!(prepend_err("tree", Serializable::deserialize(iter.by_ref())));
|
||||
let best_hash: Sha256dHash = try!(prepend_err("best_hash", Serializable::deserialize(iter.by_ref())));
|
||||
let genesis_hash: Sha256dHash = try!(prepend_err("genesis_hash", Serializable::deserialize(iter.by_ref())));
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for Blockchain {
|
||||
fn consensus_decode(d: &mut D) -> Result<Blockchain, E> {
|
||||
let network: Network = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let mut tree: BlockTree = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let best_hash: Sha256dHash = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let genesis_hash: Sha256dHash = try!(ConsensusDecodable::consensus_decode(d));
|
||||
|
||||
// Lookup best tip
|
||||
let best = match tree.lookup(&best_hash.as_uint256(), 256) {
|
||||
let best = match tree.lookup(&best_hash.into_uint256(), 256) {
|
||||
Some(node) => &**node as NodePtr,
|
||||
None => { return Err(IoError {
|
||||
kind: OtherIoError,
|
||||
desc: "best tip reference not found in tree",
|
||||
detail: Some(format!("best tip {:x} not found", best_hash))
|
||||
});
|
||||
None => {
|
||||
return Err(d.error(format!("best tip {:x} not in tree", best_hash).as_slice()));
|
||||
}
|
||||
};
|
||||
// Lookup genesis
|
||||
if tree.lookup(&genesis_hash.as_uint256(), 256).is_none() {
|
||||
return Err(IoError {
|
||||
kind: OtherIoError,
|
||||
desc: "genesis block not found in tree",
|
||||
detail: Some(format!("genesis {:x} not found", genesis_hash))
|
||||
});
|
||||
if tree.lookup(&genesis_hash.into_uint256(), 256).is_none() {
|
||||
return Err(d.error(format!("genesis {:x} not in tree", genesis_hash).as_slice()));
|
||||
}
|
||||
// Reconnect all prev pointers
|
||||
let raw_tree = &tree as *const _;
|
||||
for node in tree.mut_iter() {
|
||||
let hash = node.block.header.prev_blockhash.as_uint256();
|
||||
let hash = node.block.header.prev_blockhash.into_uint256();
|
||||
let prevptr =
|
||||
match unsafe { (*raw_tree).lookup(&hash, 256) } {
|
||||
Some(node) => &**node as NodePtr,
|
||||
|
@ -187,11 +173,8 @@ impl Serializable for Blockchain {
|
|||
|
||||
// Check that "genesis" is the genesis
|
||||
if (*scan).bitcoin_hash() != genesis_hash {
|
||||
return Err(IoError {
|
||||
kind: OtherIoError,
|
||||
desc: "best tip did not link back to genesis",
|
||||
detail: Some(format!("no path from tip {:x} to genesis {:x}", best_hash, genesis_hash))
|
||||
});
|
||||
return Err(d.error(format!("no path from tip {:x} to genesis {:x}",
|
||||
best_hash, genesis_hash).as_slice()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -372,7 +355,7 @@ impl Blockchain {
|
|||
network: network,
|
||||
tree: {
|
||||
let mut pat = PatriciaTree::new();
|
||||
pat.insert(&genhash.as_uint256(), 256, new_node);
|
||||
pat.insert(&genhash.into_uint256(), 256, new_node);
|
||||
pat
|
||||
},
|
||||
best_hash: genhash,
|
||||
|
@ -417,17 +400,17 @@ impl Blockchain {
|
|||
|
||||
/// Looks up a block in the chain and returns the BlockchainNode containing it
|
||||
pub fn get_block<'a>(&'a self, hash: Sha256dHash) -> Option<&'a BlockchainNode> {
|
||||
self.tree.lookup(&hash.as_uint256(), 256).map(|node| &**node)
|
||||
self.tree.lookup(&hash.into_uint256(), 256).map(|node| &**node)
|
||||
}
|
||||
|
||||
/// Locates a block in the chain and overwrites its txdata
|
||||
pub fn add_txdata(&mut self, block: Block) -> BitcoinResult<()> {
|
||||
self.replace_txdata(&block.header.bitcoin_hash().as_uint256(), block.txdata, true)
|
||||
self.replace_txdata(&block.header.bitcoin_hash().into_uint256(), block.txdata, true)
|
||||
}
|
||||
|
||||
/// Locates a block in the chain and removes its txdata
|
||||
pub fn remove_txdata(&mut self, hash: Sha256dHash) -> BitcoinResult<()> {
|
||||
self.replace_txdata(&hash.as_uint256(), vec![], false)
|
||||
self.replace_txdata(&hash.into_uint256(), vec![], false)
|
||||
}
|
||||
|
||||
/// Adds a block header to the chain
|
||||
|
@ -447,13 +430,13 @@ impl Blockchain {
|
|||
if hash == chain.best_hash {
|
||||
Some(chain.best_tip)
|
||||
} else {
|
||||
chain.tree.lookup(&hash.as_uint256(), 256).map(|boxptr| &**boxptr as NodePtr)
|
||||
chain.tree.lookup(&hash.into_uint256(), 256).map(|boxptr| &**boxptr as NodePtr)
|
||||
}
|
||||
}
|
||||
// Check for multiple inserts (bitcoind from c9a09183 to 3c85d2ec doesn't
|
||||
// handle locator hashes properly and may return blocks multiple times,
|
||||
// and this may also happen in case of a reorg.
|
||||
if self.tree.lookup(&block.header.bitcoin_hash().as_uint256(), 256).is_some() {
|
||||
if self.tree.lookup(&block.header.bitcoin_hash().into_uint256(), 256).is_some() {
|
||||
return Err(DuplicateHash);
|
||||
}
|
||||
// Construct node, if possible
|
||||
|
@ -532,7 +515,7 @@ impl Blockchain {
|
|||
|
||||
// Insert the new block
|
||||
let raw_ptr = &*new_block as NodePtr;
|
||||
self.tree.insert(&new_block.block.header.bitcoin_hash().as_uint256(), 256, new_block);
|
||||
self.tree.insert(&new_block.block.header.bitcoin_hash().into_uint256(), 256, new_block);
|
||||
// Replace the best tip if necessary
|
||||
if unsafe { (*raw_ptr).total_work > (*self.best_tip).total_work } {
|
||||
self.set_best_tip(raw_ptr);
|
||||
|
@ -582,7 +565,7 @@ impl Blockchain {
|
|||
|
||||
/// An iterator over all blocks in the chain starting from `start_hash`
|
||||
pub fn iter<'a>(&'a self, start_hash: Sha256dHash) -> BlockIter<'a> {
|
||||
let start = match self.tree.lookup(&start_hash.as_uint256(), 256) {
|
||||
let start = match self.tree.lookup(&start_hash.into_uint256(), 256) {
|
||||
Some(boxptr) => &**boxptr as NodePtr,
|
||||
None => RawPtr::null()
|
||||
};
|
||||
|
@ -594,7 +577,7 @@ impl Blockchain {
|
|||
|
||||
/// An iterator over all blocks in reverse order to the genesis, starting with `start_hash`
|
||||
pub fn rev_iter<'a>(&'a self, start_hash: Sha256dHash) -> RevBlockIter<'a> {
|
||||
let start = match self.tree.lookup(&start_hash.as_uint256(), 256) {
|
||||
let start = match self.tree.lookup(&start_hash.into_uint256(), 256) {
|
||||
Some(boxptr) => &**boxptr as NodePtr,
|
||||
None => RawPtr::null()
|
||||
};
|
||||
|
@ -606,7 +589,7 @@ impl Blockchain {
|
|||
|
||||
/// An iterator over all blocks -not- in the best chain, in reverse order, starting from `start_hash`
|
||||
pub fn rev_stale_iter<'a>(&'a self, start_hash: Sha256dHash) -> RevStaleBlockIter<'a> {
|
||||
let start = match self.tree.lookup(&start_hash.as_uint256(), 256) {
|
||||
let start = match self.tree.lookup(&start_hash.into_uint256(), 256) {
|
||||
Some(boxptr) => {
|
||||
// If we are already on the main chain, we have a dead iterator
|
||||
if boxptr.is_on_main_chain(self) {
|
||||
|
@ -626,28 +609,26 @@ impl Blockchain {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::prelude::*;
|
||||
use std::io::IoResult;
|
||||
|
||||
use blockdata::blockchain::Blockchain;
|
||||
use blockdata::constants::genesis_block;
|
||||
use network::constants::Bitcoin;
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::{BitcoinHash, deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn blockchain_serialize_test() {
|
||||
let empty_chain = Blockchain::new(Bitcoin);
|
||||
assert_eq!(empty_chain.best_tip().header.bitcoin_hash().serialize(),
|
||||
genesis_block(Bitcoin).header.bitcoin_hash().serialize());
|
||||
assert_eq!(empty_chain.best_tip().header.bitcoin_hash(),
|
||||
genesis_block(Bitcoin).header.bitcoin_hash());
|
||||
|
||||
let serial = empty_chain.serialize();
|
||||
assert_eq!(serial, empty_chain.serialize_iter().collect());
|
||||
let serial = serialize(&empty_chain);
|
||||
let deserial: IoResult<Blockchain> = deserialize(serial.unwrap());
|
||||
|
||||
let deserial: IoResult<Blockchain> = Serializable::deserialize(serial.iter().map(|n| *n));
|
||||
assert!(deserial.is_ok());
|
||||
let read_chain = deserial.unwrap();
|
||||
assert_eq!(read_chain.best_tip().header.bitcoin_hash().serialize(),
|
||||
genesis_block(Bitcoin).header.bitcoin_hash().serialize());
|
||||
assert_eq!(read_chain.best_tip().header.bitcoin_hash(),
|
||||
genesis_block(Bitcoin).header.bitcoin_hash());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,11 +113,12 @@ pub fn genesis_block(network: Network) -> Block {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use network::serialize::Serializable;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use network::constants::{Bitcoin, BitcoinTestnet};
|
||||
use network::serialize::{BitcoinHash, serialize};
|
||||
use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
|
||||
use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};
|
||||
use util::misc::hex_bytes;
|
||||
use util::hash::zero_hash;
|
||||
|
||||
#[test]
|
||||
|
@ -128,13 +129,13 @@ mod test {
|
|||
assert_eq!(gen.input.len(), 1);
|
||||
assert_eq!(gen.input[0].prev_hash.as_slice(), zero_hash().as_slice());
|
||||
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
|
||||
assert_eq!(gen.input[0].script_sig.serialize().as_slice(),
|
||||
hex_bytes("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73").unwrap().as_slice());
|
||||
assert_eq!(serialize(&gen.input[0].script_sig),
|
||||
Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));
|
||||
|
||||
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
|
||||
assert_eq!(gen.output.len(), 1);
|
||||
assert_eq!(gen.output[0].script_pubkey.serialize().as_slice(),
|
||||
hex_bytes("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap().as_slice());
|
||||
assert_eq!(serialize(&gen.output[0].script_pubkey),
|
||||
Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
|
||||
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
||||
assert_eq!(gen.lock_time, 0);
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
//!
|
||||
|
||||
use std::char::from_digit;
|
||||
use std::io::IoResult;
|
||||
use serialize::json;
|
||||
|
||||
use network::serialize::Serializable;
|
||||
use blockdata::opcodes;
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||
use util::thinvec::ThinVec;
|
||||
|
||||
#[deriving(PartialEq, Show, Clone)]
|
||||
|
@ -136,15 +136,18 @@ impl json::ToJson for Script {
|
|||
}
|
||||
|
||||
// Network serialization
|
||||
impl Serializable for Script {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for Script {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
let &Script(ref data) = self;
|
||||
data.serialize()
|
||||
data.consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Script> {
|
||||
let raw = Serializable::deserialize(iter);
|
||||
raw.map(|ok| Script(ok))
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for Script {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Script, E> {
|
||||
Ok(Script(try!(ConsensusDecodable::consensus_decode(d))))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +155,7 @@ impl Serializable for Script {
|
|||
mod test {
|
||||
use std::io::IoResult;
|
||||
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use blockdata::script::Script;
|
||||
use blockdata::opcodes;
|
||||
use util::misc::hex_bytes;
|
||||
|
@ -189,9 +192,9 @@ mod test {
|
|||
#[test]
|
||||
fn script_serialize() {
|
||||
let hex_script = hex_bytes("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52").unwrap();
|
||||
let script: IoResult<Script> = Serializable::deserialize(hex_script.iter().map(|n| *n));
|
||||
let script: IoResult<Script> = deserialize(hex_script.clone());
|
||||
assert!(script.is_ok());
|
||||
assert_eq!(script.unwrap().serialize().as_slice(), hex_script.as_slice());
|
||||
assert_eq!(serialize(&script.unwrap()), Ok(hex_script));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,12 +23,10 @@
|
|||
//! This module provides the structures and functions needed to support transactions.
|
||||
//!
|
||||
|
||||
use std::io::IoResult;
|
||||
use util::hash::Sha256dHash;
|
||||
use network::serialize::{Serializable, SerializeIter};
|
||||
use blockdata::script::Script;
|
||||
#[cfg(test)]
|
||||
use util::misc::hex_bytes;
|
||||
use network::encodable::ConsensusEncodable;
|
||||
use network::serialize::BitcoinHash;
|
||||
|
||||
/// A transaction input, which defines old coins to be consumed
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
|
@ -70,23 +68,41 @@ pub struct Transaction {
|
|||
pub output: Vec<TxOut>
|
||||
}
|
||||
|
||||
impl_serializable!(TxIn, prev_hash, prev_index, script_sig, sequence)
|
||||
impl BitcoinHash for Transaction {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
use network::serialize::serialize;
|
||||
Sha256dHash::from_data(serialize(self).unwrap().as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl_consensus_encoding!(TxIn, prev_hash, prev_index, script_sig, sequence)
|
||||
impl_json!(TxIn, prev_hash, prev_index, script_sig, sequence)
|
||||
impl_serializable!(TxOut, value, script_pubkey)
|
||||
impl_consensus_encoding!(TxOut, value, script_pubkey)
|
||||
impl_json!(TxOut, value, script_pubkey)
|
||||
impl_serializable!(Transaction, version, input, output, lock_time)
|
||||
impl_consensus_encoding!(Transaction, version, input, output, lock_time)
|
||||
impl_json!(Transaction, version, input, output, lock_time)
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Transaction, TxIn};
|
||||
|
||||
use std::io::IoResult;
|
||||
|
||||
use network::serialize::BitcoinHash;
|
||||
use network::serialize::deserialize;
|
||||
use util::misc::hex_bytes;
|
||||
|
||||
#[test]
|
||||
fn test_txin() {
|
||||
let txin: IoResult<TxIn> = Serializable::deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap().iter().map(|n| *n));
|
||||
let txin: IoResult<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
|
||||
assert!(txin.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction() {
|
||||
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||
let tx: IoResult<Transaction> = Serializable::deserialize(hex_tx.iter().map(|n| *n));
|
||||
let tx: IoResult<Transaction> = deserialize(hex_tx);
|
||||
assert!(tx.is_ok());
|
||||
let realtx = tx.unwrap();
|
||||
// All these tests aren't really needed because if they fail, the hash check at the end
|
||||
|
@ -104,6 +120,5 @@ fn test_transaction() {
|
|||
assert_eq!(realtx.bitcoin_hash().le_hex_string(),
|
||||
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,13 @@
|
|||
//!
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::IoResult;
|
||||
use std::mem;
|
||||
|
||||
use blockdata::transaction::{Transaction, TxOut};
|
||||
use blockdata::constants::genesis_block;
|
||||
use blockdata::block::Block;
|
||||
use network::constants::Network;
|
||||
use network::serialize::{Serializable, SerializeIter};
|
||||
use network::serialize::BitcoinHash;
|
||||
use util::hash::{DumbHasher, Sha256dHash};
|
||||
use util::uint::Uint128;
|
||||
use util::thinvec::ThinVec;
|
||||
|
@ -45,7 +44,7 @@ pub struct UtxoSet {
|
|||
n_utxos: u64
|
||||
}
|
||||
|
||||
impl_serializable!(UtxoSet, last_hash, n_utxos, spent_txos, spent_idx, table)
|
||||
impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, spent_txos, spent_idx, table)
|
||||
|
||||
impl UtxoSet {
|
||||
/// Constructs a new UTXO set
|
||||
|
@ -67,14 +66,17 @@ impl UtxoSet {
|
|||
fn add_utxos(&mut self, tx: &Transaction) -> Option<UtxoNode> {
|
||||
let txid = tx.bitcoin_hash();
|
||||
// Locate node if it's already there
|
||||
let new_node = unsafe {
|
||||
let mut new_node = ThinVec::with_capacity(tx.output.len() as u32);
|
||||
for (vout, txo) in tx.output.iter().enumerate() {
|
||||
// Unsafe since we are not uninitializing the old data in the vector
|
||||
unsafe { new_node.init(vout as uint, Some(txo.clone())); }
|
||||
new_node.init(vout as uint, Some(txo.clone()));
|
||||
}
|
||||
new_node
|
||||
};
|
||||
// Get the old value, if any (this is suprisingly possible, c.f. BIP30
|
||||
// and the other comments in this file referring to it)
|
||||
let ret = self.table.swap(txid.as_uint128(), new_node);
|
||||
let ret = self.table.swap(txid.into_uint128(), new_node);
|
||||
if ret.is_none() {
|
||||
self.n_utxos += tx.output.len() as u64;
|
||||
}
|
||||
|
@ -86,7 +88,7 @@ impl UtxoSet {
|
|||
// This whole function has awkward scoping thx to lexical borrow scoping :(
|
||||
let (ret, should_delete) = {
|
||||
// Locate the UTXO, failing if not found
|
||||
let node = match self.table.find_mut(&txid.as_uint128()) {
|
||||
let node = match self.table.find_mut(&txid.into_uint128()) {
|
||||
Some(node) => node,
|
||||
None => return None
|
||||
};
|
||||
|
@ -104,7 +106,7 @@ impl UtxoSet {
|
|||
|
||||
// Delete the whole node if it is no longer being used
|
||||
if should_delete {
|
||||
self.table.remove(&txid.as_uint128());
|
||||
self.table.remove(&txid.into_uint128());
|
||||
}
|
||||
|
||||
self.n_utxos -= if ret.is_some() { 1 } else { 0 };
|
||||
|
@ -114,7 +116,7 @@ impl UtxoSet {
|
|||
/// Get a reference to a UTXO in the set
|
||||
pub fn get_utxo<'a>(&'a mut self, txid: Sha256dHash, vout: u32) -> Option<&'a TxOut> {
|
||||
// Locate the UTXO, failing if not found
|
||||
let node = match self.table.find_mut(&txid.as_uint128()) {
|
||||
let node = match self.table.find_mut(&txid.into_uint128()) {
|
||||
Some(node) => node,
|
||||
None => return None
|
||||
};
|
||||
|
@ -227,7 +229,7 @@ impl UtxoSet {
|
|||
for ((txid, n), txo) in extract_vec.move_iter() {
|
||||
// Remove the tx's utxo list and patch the txo into place
|
||||
let new_node =
|
||||
match self.table.pop(&txid.as_uint128()) {
|
||||
match self.table.pop(&txid.into_uint128()) {
|
||||
Some(mut thinvec) => {
|
||||
let old_len = thinvec.len() as u32;
|
||||
if old_len < n + 1 {
|
||||
|
@ -242,16 +244,18 @@ impl UtxoSet {
|
|||
thinvec
|
||||
}
|
||||
None => {
|
||||
unsafe {
|
||||
let mut thinvec = ThinVec::with_capacity(n + 1);
|
||||
for i in range(0, n + 1) {
|
||||
unsafe { thinvec.init(i as uint, None); }
|
||||
for i in range(0, n) {
|
||||
thinvec.init(i as uint, None);
|
||||
}
|
||||
unsafe { *thinvec.get_mut(n as uint) = Some(txo); }
|
||||
thinvec.init(n as uint, Some(txo));
|
||||
thinvec
|
||||
}
|
||||
}
|
||||
};
|
||||
// Ram it back into the tree
|
||||
self.table.insert(txid.as_uint128(), new_node);
|
||||
self.table.insert(txid.into_uint128(), new_node);
|
||||
}
|
||||
}
|
||||
skipped_genesis = true;
|
||||
|
@ -284,13 +288,13 @@ mod tests {
|
|||
use blockdata::block::Block;
|
||||
use blockdata::utxoset::UtxoSet;
|
||||
use network::constants::Bitcoin;
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::{BitcoinHash, deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn utxoset_serialize_test() {
|
||||
let mut empty_set = UtxoSet::new(Bitcoin, 100);
|
||||
|
||||
let new_block: Block = Serializable::deserialize("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap().iter().map(|n| *n)).unwrap();
|
||||
let new_block: Block = deserialize("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap()).unwrap();
|
||||
|
||||
// Make sure we can't add the block directly, since we are missing the inputs
|
||||
assert!(!empty_set.update(&new_block));
|
||||
|
@ -324,10 +328,9 @@ mod tests {
|
|||
}
|
||||
|
||||
// Serialize/deserialize the resulting UTXO set
|
||||
let serial = empty_set.serialize();
|
||||
assert_eq!(serial, empty_set.serialize_iter().collect());
|
||||
let serial = serialize(&empty_set).unwrap();
|
||||
|
||||
let deserial: IoResult<UtxoSet> = Serializable::deserialize(serial.iter().map(|n| *n));
|
||||
let deserial: IoResult<UtxoSet> = deserialize(serial.clone());
|
||||
assert!(deserial.is_ok());
|
||||
|
||||
// Check that all outputs are there
|
||||
|
@ -347,7 +350,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
let deserial_again: IoResult<UtxoSet> = Serializable::deserialize(serial.iter().map(|n| *n));
|
||||
let deserial_again: IoResult<UtxoSet> = deserialize(serial);
|
||||
let mut read_again = deserial_again.unwrap();
|
||||
assert!(read_again.rewind(&new_block));
|
||||
assert_eq!(read_again.n_utxos(), 0);
|
||||
|
|
|
@ -14,30 +14,23 @@
|
|||
|
||||
#![macro_escape]
|
||||
|
||||
macro_rules! impl_serializable(
|
||||
macro_rules! impl_consensus_encoding(
|
||||
($thing:ident, $($field:ident),+) => (
|
||||
impl Serializable for $thing {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut ret = vec![];
|
||||
$( ret.extend(self.$field.serialize().move_iter()); )+
|
||||
ret
|
||||
}
|
||||
|
||||
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||
SerializeIter {
|
||||
data_iter: None,
|
||||
sub_iter_iter: box vec![ $( &self.$field as &Serializable, )+ ].move_iter(),
|
||||
sub_iter: None,
|
||||
sub_started: false
|
||||
impl<S: ::network::serialize::SimpleEncoder<E>, E> ::network::encodable::ConsensusEncodable<S, E> for $thing {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
$( try!(self.$field.consensus_encode(s)); )+
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<$thing> {
|
||||
use util::misc::prepend_err;
|
||||
let ret = Ok($thing {
|
||||
$( $field: try!(prepend_err(stringify!($field), Serializable::deserialize(iter.by_ref()))), )+
|
||||
});
|
||||
ret
|
||||
impl<D: ::network::serialize::SimpleDecoder<E>, E> ::network::encodable::ConsensusDecodable<D, E> for $thing {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<$thing, E> {
|
||||
use network::encodable::ConsensusDecodable;
|
||||
Ok($thing {
|
||||
$( $field: try!(ConsensusDecodable::consensus_decode(d)), )+
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -50,7 +43,6 @@ macro_rules! impl_json(
|
|||
use std::collections::TreeMap;
|
||||
use serialize::json::{ToJson, Object};
|
||||
let mut ret = TreeMap::new();
|
||||
ret.insert("hash".to_string(), self.bitcoin_hash().to_json());
|
||||
$( ret.insert(stringify!($field).to_string(), self.$field.to_json()); )+
|
||||
Object(ret)
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
//!
|
||||
|
||||
use std::fmt;
|
||||
use std::io::{IoResult, standard_error, InvalidInput};
|
||||
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::{SimpleEncoder, SimpleDecoder};
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
|
||||
/// A message which can be sent on the Bitcoin network
|
||||
pub struct Address {
|
||||
|
@ -33,31 +33,31 @@ pub struct Address {
|
|||
pub port: u16
|
||||
}
|
||||
|
||||
impl Serializable for Address {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut rv = vec!();
|
||||
rv.extend(self.services.serialize().move_iter());
|
||||
rv.extend(self.address.iter().map(|n| *n));
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for Address {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
try!(self.services.consensus_encode(s));
|
||||
try!(self.address.consensus_encode(s));
|
||||
// Explicitly code the port since it needs to be big-endian
|
||||
rv.extend([(self.port / 256) as u8, (self.port % 256) as u8].iter().map(|n| *n));
|
||||
rv
|
||||
try!(((self.port / 0x100) as u8).consensus_encode(s));
|
||||
try!(((self.port % 0x100) as u8).consensus_encode(s));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Address> {
|
||||
let ret = Address {
|
||||
services: try!(Serializable::deserialize(iter.by_ref())),
|
||||
address: try!(Serializable::deserialize(iter.by_ref())),
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for Address {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Address, E> {
|
||||
Ok(Address {
|
||||
services: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
address: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
// Explicitly code the port since it needs to be big-endian
|
||||
port: {
|
||||
let b1 = iter.next();
|
||||
let b2 = iter.next();
|
||||
if b1.is_none() || b2.is_none() {
|
||||
return Err(standard_error(InvalidInput));
|
||||
let b1: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let b2: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
(b1 as u16 * 0x100) + (b2 as u16)
|
||||
}
|
||||
(b1.unwrap() as u16) * 0x100 + (b2.unwrap() as u16)
|
||||
}
|
||||
};
|
||||
Ok(ret)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,32 +69,39 @@ impl fmt::Show for Address {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Address;
|
||||
|
||||
use std::io::IoResult;
|
||||
|
||||
use network::serialize::{deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn serialize_address_test() {
|
||||
assert!(Address {
|
||||
assert_eq!(serialize(&Address {
|
||||
services: 1,
|
||||
address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1],
|
||||
port: 8333
|
||||
}.serialize() == Vec::from_slice([1u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1,
|
||||
0x20, 0x8d]));
|
||||
}),
|
||||
Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1, 0x20, 0x8d]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_address_test() {
|
||||
let mut addr: IoResult<Address> = Serializable::deserialize([1u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1,
|
||||
0x20, 0x8d].iter().map(|n| *n));
|
||||
let mut addr: IoResult<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, 1, 0x20, 0x8d]);
|
||||
assert!(addr.is_ok())
|
||||
let full = addr.unwrap();
|
||||
assert!(full.services == 1);
|
||||
assert!(full.address == [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
|
||||
assert!(full.port == 8333);
|
||||
|
||||
addr = Serializable::deserialize([1u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1].iter().map(|n| *n));
|
||||
addr = 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, 1]);
|
||||
assert!(addr.is_err());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
//! protocol, such as protocol versioning and magic header bytes.
|
||||
//!
|
||||
|
||||
use std::io::{IoResult, InvalidInput, standard_error};
|
||||
|
||||
use network::serialize::Serializable;
|
||||
use util::misc::prepend_err;
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::serialize::{SimpleEncoder, SimpleDecoder};
|
||||
|
||||
/// The cryptocurrency to operate on
|
||||
#[deriving(Encodable, Decodable, PartialEq, Eq, Clone, Show, Hash)]
|
||||
|
@ -46,18 +44,21 @@ pub fn magic(network: Network) -> u32 {
|
|||
}
|
||||
}
|
||||
|
||||
// This affects the representation of the `Network` in text files
|
||||
impl Serializable for Network {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
magic(*self).serialize()
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for Network {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
magic(*self).consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Network> {
|
||||
let magic: u32 = try!(prepend_err("magic", Serializable::deserialize(iter)));
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for Network {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Network, E> {
|
||||
let magic: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
match magic {
|
||||
0xD9B4BEF9 => Ok(Bitcoin),
|
||||
0x0709110B => Ok(BitcoinTestnet),
|
||||
_ => Err(standard_error(InvalidInput))
|
||||
x => Err(d.error(format!("Unknown network (magic {:x})", x).as_slice()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,558 @@
|
|||
// 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/>.
|
||||
//
|
||||
|
||||
//! # Consensus-encodable types
|
||||
//!
|
||||
//! This is basically a replacement of the `Encodable` trait which does
|
||||
//! normalization for endianness, etc., to ensure that the encoding
|
||||
//! matches for endianness, etc., to ensure that the encoding matches
|
||||
//! the network consensus encoding.
|
||||
//!
|
||||
//! Essentially, anything that must go on the -disk- or -network- must
|
||||
//! be encoded using the `ConsensusEncodable` trait, since this data
|
||||
//! must be the same for all systems. Any data going to the -user-, e.g.
|
||||
//! over JSONRPC, should use the ordinary `Encodable` trait. (This
|
||||
//! should also be the same across systems, of course, but has some
|
||||
//! critical differences from the network format, e.g. scripts come
|
||||
//! with an opcode decode, hashes are big-endian, numbers are typically
|
||||
//! big-endian decimals, etc.)
|
||||
//!
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::default::Default;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::u32;
|
||||
|
||||
use util::thinvec::ThinVec;
|
||||
use util::hash::Sha256dHash;
|
||||
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||
|
||||
/// Data which can be encoded in a consensus-consistent way
|
||||
pub trait ConsensusEncodable<S:SimpleEncoder<E>, E> {
|
||||
/// Encode an object with a well-defined format
|
||||
fn consensus_encode(&self, e: &mut S) -> Result<(), E>;
|
||||
}
|
||||
|
||||
/// Data which can be encoded in a consensus-consistent way
|
||||
pub trait ConsensusDecodable<D:SimpleDecoder<E>, E> {
|
||||
/// Decode an object with a well-defined format
|
||||
fn consensus_decode(d: &mut D) -> Result<Self, E>;
|
||||
}
|
||||
|
||||
/// A variable-length unsigned integer
|
||||
#[deriving(PartialEq, Show)]
|
||||
pub struct VarInt(pub u64);
|
||||
|
||||
/// Data which must be preceded by a 4-byte checksum
|
||||
#[deriving(PartialEq, Clone, Show)]
|
||||
pub struct CheckedData(pub Vec<u8>);
|
||||
|
||||
// Primitive types
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for u8 {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { s.emit_u8(*self) }
|
||||
}
|
||||
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for u16 {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { s.emit_u16(self.to_le()) }
|
||||
}
|
||||
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for u32 {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { s.emit_u32(self.to_le()) }
|
||||
}
|
||||
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for u64 {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { s.emit_u64(self.to_le()) }
|
||||
}
|
||||
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for i32 {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { s.emit_i32(self.to_le()) }
|
||||
}
|
||||
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for i64 {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { s.emit_i64(self.to_le()) }
|
||||
}
|
||||
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for VarInt {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
let &VarInt(n) = self;
|
||||
match n {
|
||||
0..0xFC => { (n as u8).consensus_encode(s) }
|
||||
0xFD..0xFFFF => { try!(s.emit_u8(0xFD)); (n as u16).consensus_encode(s) }
|
||||
0x10000..0xFFFFFFFF => { try!(s.emit_u8(0xFE)); (n as u32).consensus_encode(s) }
|
||||
_ => { try!(s.emit_u8(0xFF)); (n as u64).consensus_encode(s) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for u8 {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<u8, E> { d.read_u8() }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for u16 {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<u16, E> { d.read_u16().map(|n| Int::from_le(n)) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for u32 {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<u32, E> { d.read_u32().map(|n| Int::from_le(n)) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for u64 {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<u64, E> { d.read_u64().map(|n| Int::from_le(n)) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for i32 {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<i32, E> { d.read_i32().map(|n| Int::from_le(n)) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for i64 {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<i64, E> { d.read_i64().map(|n| Int::from_le(n)) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for VarInt {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<VarInt, E> {
|
||||
let n = try!(d.read_u8());
|
||||
match n {
|
||||
0xFF => d.read_u64().map(|n| VarInt(Int::from_le(n))),
|
||||
0xFE => d.read_u32().map(|n| VarInt(Int::from_le(n) as u64)),
|
||||
0xFD => d.read_u16().map(|n| VarInt(Int::from_le(n) as u64)),
|
||||
n => Ok(VarInt(n as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Booleans
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for bool {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { s.emit_u8(if *self {1} else {0}) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for bool {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<bool, E> { d.read_u8().map(|n| n != 0) }
|
||||
}
|
||||
|
||||
// Strings
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for String {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
self.as_bytes().consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for String {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<String, E> {
|
||||
String::from_utf8(try!(ConsensusDecodable::consensus_decode(d))).map_err(|_| d.error("String was not valid UTF8"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Arrays
|
||||
macro_rules! impl_array(
|
||||
( $size:expr ) => (
|
||||
impl<S:SimpleEncoder<E>, E, T:ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for [T, ..$size] {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
for i in self.iter() { try!(i.consensus_encode(s)); }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E, T:ConsensusDecodable<D, E>+Copy> ConsensusDecodable<D, E> for [T, ..$size] {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<[T, ..$size], E> {
|
||||
// Set everything to the first decode
|
||||
let mut ret = [try!(ConsensusDecodable::consensus_decode(d)), ..$size];
|
||||
// Set the rest
|
||||
for i in range(1, $size) { ret[i] = try!(ConsensusDecodable::consensus_decode(d)); }
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
impl_array!(2)
|
||||
impl_array!(4)
|
||||
impl_array!(8)
|
||||
impl_array!(12)
|
||||
impl_array!(16)
|
||||
impl_array!(32)
|
||||
|
||||
impl<'a, S:SimpleEncoder<E>, E, T:ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for &'a [T] {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
try!(VarInt(self.len() as u64).consensus_encode(s));
|
||||
for c in self.iter() { try!(c.consensus_encode(s)); }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot decode a slice
|
||||
|
||||
// Vectors
|
||||
impl<S:SimpleEncoder<E>, E, T:ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for Vec<T> {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { self.as_slice().consensus_encode(s) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E, T:ConsensusDecodable<D, E>> ConsensusDecodable<D, E> for Vec<T> {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Vec<T>, E> {
|
||||
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let mut ret = Vec::with_capacity(len as uint);
|
||||
for _ in range(0, len) { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S:SimpleEncoder<E>, E, T:ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for ThinVec<T> {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { self.as_slice().consensus_encode(s) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E, T:ConsensusDecodable<D, E>> ConsensusDecodable<D, E> for ThinVec<T> {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<ThinVec<T>, E> {
|
||||
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
||||
if len > u32::MAX as u64 {
|
||||
return Err(d.error("ThinVec length out of range!"));
|
||||
}
|
||||
unsafe {
|
||||
let mut ret = ThinVec::with_capacity(len as u32);
|
||||
// Huge danger: if this fails, the remaining uninitialized part of the ThinVec
|
||||
// will be freed. This is ok, but only because the memory is u8, which has no
|
||||
// destructor...and assuming there are no trap representations...very fragile.
|
||||
for i in range(0, len as uint) { ret.init(i, try!(ConsensusDecodable::consensus_decode(d))); }
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Options (encoded as vectors of length 0 or 1)
|
||||
impl<S:SimpleEncoder<E>, E, T:ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for Option<T> {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
match *self {
|
||||
Some(ref data) => {
|
||||
try!(1u8.consensus_encode(s));
|
||||
try!(data.consensus_encode(s));
|
||||
}
|
||||
None => { try!(0u8.consensus_encode(s)); }
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E, T:ConsensusDecodable<D, E>> ConsensusDecodable<D, E> for Option<T> {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Option<T>, E> {
|
||||
let bit: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
Ok(if bit != 0 {
|
||||
Some(try!(ConsensusDecodable::consensus_decode(d)))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Do a double-SHA256 on some data and return the first 4 bytes
|
||||
fn sha2_checksum(data: &[u8]) -> [u8, ..4] {
|
||||
let checksum = Sha256dHash::from_data(data);
|
||||
[checksum[0u], checksum[1u], checksum[2u], checksum[3u]]
|
||||
}
|
||||
|
||||
// Checked data
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for CheckedData {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
let &CheckedData(ref data) = self;
|
||||
try!((data.len() as u32).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
|
||||
for ch in data.iter() {
|
||||
try!(ch.consensus_encode(s));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for CheckedData {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<CheckedData, E> {
|
||||
let len: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let checksum: [u8, ..4] = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let mut ret = Vec::with_capacity(len as uint);
|
||||
for _ in range(0, len) { ret.push(try!(ConsensusDecodable::consensus_decode(d))); }
|
||||
let expected_checksum = sha2_checksum(ret.as_slice());
|
||||
if expected_checksum != checksum {
|
||||
Err(d.error("bad checksum"))
|
||||
} else {
|
||||
Ok(CheckedData(ret))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tuples
|
||||
impl<S:SimpleEncoder<E>, E, T: ConsensusEncodable<S, E>, U: ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for (T, U) {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
let &(ref s1, ref s2) = self;
|
||||
try!(s1.consensus_encode(s));
|
||||
try!(s2.consensus_encode(s));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E, T: ConsensusDecodable<D, E>, U: ConsensusDecodable<D, E>> ConsensusDecodable<D, E> for (T, U) {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<(T, U), E> {
|
||||
Ok((try!(ConsensusDecodable::consensus_decode(d)),
|
||||
try!(ConsensusDecodable::consensus_decode(d))))
|
||||
}
|
||||
}
|
||||
|
||||
// References
|
||||
impl<S:SimpleEncoder<E>, E, T: ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for Box<T> {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> { (**self).consensus_encode(s) }
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E, T: ConsensusDecodable<D, E>> ConsensusDecodable<D, E> for Box<T> {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Box<T>, E> {
|
||||
ConsensusDecodable::consensus_decode(d).map(|res| box res)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// HashMap
|
||||
impl<S:SimpleEncoder<E>, E,
|
||||
K:ConsensusEncodable<S,E>+Eq+Hash<u64>,
|
||||
V:ConsensusEncodable<S,E>,
|
||||
H:Hasher<u64>+Default> ConsensusEncodable<S, E> for HashMap<K, V, H> {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
try!(VarInt(self.len() as u64).consensus_encode(s));
|
||||
for (key, value) in self.iter() {
|
||||
try!(key.consensus_encode(s));
|
||||
try!(value.consensus_encode(s));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D:SimpleDecoder<E>, E,
|
||||
K:ConsensusDecodable<D,E>+Eq+Hash<u64>,
|
||||
V:ConsensusDecodable<D,E>,
|
||||
H:Hasher<u64>+Default> ConsensusDecodable<D, E> for HashMap<K, V, H> {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<HashMap<K, V, H>, E> {
|
||||
let VarInt(len): VarInt = try!(ConsensusDecodable::consensus_decode(d));
|
||||
|
||||
let mut ret = HashMap::with_capacity_and_hasher(len as uint, Default::default());
|
||||
for _ in range(0, len) {
|
||||
ret.insert(try!(ConsensusDecodable::consensus_decode(d)),
|
||||
try!(ConsensusDecodable::consensus_decode(d)));
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{CheckedData, VarInt};
|
||||
|
||||
use std::io::IoResult;
|
||||
|
||||
use network::serialize::{deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn serialize_int_test() {
|
||||
// bool
|
||||
assert_eq!(serialize(&false), Ok(vec![0u8]));
|
||||
assert_eq!(serialize(&true), Ok(vec![1u8]));
|
||||
// u8
|
||||
assert_eq!(serialize(&1u8), Ok(vec![1u8]));
|
||||
assert_eq!(serialize(&0u8), Ok(vec![0u8]));
|
||||
assert_eq!(serialize(&255u8), Ok(vec![255u8]));
|
||||
// u16
|
||||
assert_eq!(serialize(&1u16), Ok(vec![1u8, 0]));
|
||||
assert_eq!(serialize(&256u16), Ok(vec![0u8, 1]));
|
||||
assert_eq!(serialize(&5000u16), Ok(vec![136u8, 19]));
|
||||
// u32
|
||||
assert_eq!(serialize(&1u32), Ok(vec![1u8, 0, 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(&500000u32), Ok(vec![32u8, 161, 7, 0]));
|
||||
assert_eq!(serialize(&168430090u32), Ok(vec![10u8, 10, 10, 10]));
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(serialize(&1i32), Ok(vec![1u8, 0, 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(&500000i32), Ok(vec![32u8, 161, 7, 0]));
|
||||
assert_eq!(serialize(&168430090i32), Ok(vec![10u8, 10, 10, 10]));
|
||||
// u64
|
||||
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(&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(&723401728380766730u64), Ok(vec![10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||
// TODO: test negative numbers
|
||||
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(&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(&723401728380766730i64), Ok(vec![10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_varint_test() {
|
||||
assert_eq!(serialize(&VarInt(10)), Ok(vec![10u8]));
|
||||
assert_eq!(serialize(&VarInt(0xFC)), Ok(vec![0xFCu8]));
|
||||
assert_eq!(serialize(&VarInt(0xFD)), Ok(vec![0xFDu8, 0xFD, 0]));
|
||||
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(0xF0F0F0F0F0E0)), Ok(vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_checkeddata_test() {
|
||||
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]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_vector_test() {
|
||||
assert_eq!(serialize(&vec![1u8, 2, 3]), Ok(vec![3u8, 1, 2, 3]));
|
||||
assert_eq!(serialize(&&[1u8, 2, 3]), Ok(vec![3u8, 1, 2, 3]));
|
||||
// TODO: test vectors of more interesting objects
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_strbuf_test() {
|
||||
assert_eq!(serialize(&"Andrew".to_string()), Ok(vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_box_test() {
|
||||
assert_eq!(serialize(&box 1u8), Ok(vec![1u8]));
|
||||
assert_eq!(serialize(&box 1u16), Ok(vec![1u8, 0]));
|
||||
assert_eq!(serialize(&box 1u64), Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_option_test() {
|
||||
let none: Option<u8> = None;
|
||||
let none_ser = serialize(&none);
|
||||
let some_ser = serialize(&Some(0xFFu8));
|
||||
assert_eq!(none_ser, Ok(vec![0]));
|
||||
assert_eq!(some_ser, Ok(vec![1, 0xFF]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_int_test() {
|
||||
// bool
|
||||
assert_eq!(deserialize(vec![58u8, 0]), Ok(true));
|
||||
assert_eq!(deserialize(vec![58u8]), Ok(true));
|
||||
assert_eq!(deserialize(vec![1u8]), Ok(true));
|
||||
assert_eq!(deserialize(vec![0u8]), Ok(false));
|
||||
assert_eq!(deserialize(vec![0u8, 1]), Ok(false));
|
||||
|
||||
// u8
|
||||
assert_eq!(deserialize(vec![58u8]), Ok(58u8));
|
||||
|
||||
// u16
|
||||
assert_eq!(deserialize(vec![0x01u8, 0x02]), Ok(0x0201u16));
|
||||
assert_eq!(deserialize(vec![0xABu8, 0xCD]), Ok(0xCDABu16));
|
||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D]), Ok(0xDA0u16));
|
||||
let failure16: IoResult<u16> = deserialize(vec![1u8]);
|
||||
assert!(failure16.is_err());
|
||||
|
||||
// u32
|
||||
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0]), Ok(0xCDABu32));
|
||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0xCD]), Ok(0xCDAB0DA0u32));
|
||||
let failure32: IoResult<u32> = deserialize(vec![1u8, 2, 3]);
|
||||
assert!(failure32.is_err());
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(deserialize(vec![0xABu8, 0xCD, 0, 0]), Ok(0xCDABi32));
|
||||
assert_eq!(deserialize(vec![0xA0u8, 0x0D, 0xAB, 0x2D]), Ok(0x2DAB0DA0i32));
|
||||
let failurei32: IoResult<i32> = deserialize(vec![1u8, 2, 3]);
|
||||
assert!(failurei32.is_err());
|
||||
|
||||
// u64
|
||||
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));
|
||||
let failure64: IoResult<u64> = deserialize(vec![1u8, 2, 3, 4, 5, 6, 7]);
|
||||
assert!(failure64.is_err());
|
||||
// TODO: test negative numbers
|
||||
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(0x99000099CDAB0DA0i64));
|
||||
let failurei64: IoResult<i64> = deserialize(vec![1u8, 2, 3, 4, 5, 6, 7]);
|
||||
assert!(failurei64.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_vec_test() {
|
||||
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]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_strbuf_test() {
|
||||
assert_eq!(deserialize(vec![6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]), Ok(String::from_str("Andrew")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_checkeddata_test() {
|
||||
let cd: IoResult<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])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_option_test() {
|
||||
let none: IoResult<Option<u8>> = deserialize(vec![0u8]);
|
||||
let good: IoResult<Option<u8>> = deserialize(vec![1u8, 0xFF]);
|
||||
let bad: IoResult<Option<u8>> = deserialize(vec![2u8]);
|
||||
assert!(bad.is_err());
|
||||
assert_eq!(none, Ok(None));
|
||||
assert_eq!(good, Ok(Some(0xFF)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_box_test() {
|
||||
let zero: IoResult<Box<u8>> = deserialize(vec![0u8]);
|
||||
let one: IoResult<Box<u8>> = deserialize(vec![1u8]);
|
||||
assert_eq!(zero, Ok(box 0));
|
||||
assert_eq!(one, Ok(box 1));
|
||||
}
|
||||
}
|
||||
|
|
@ -20,37 +20,38 @@
|
|||
//!
|
||||
|
||||
use collections::Vec;
|
||||
use std::io::{IoError, IoResult, InvalidInput, OtherIoError, standard_error};
|
||||
use std::io::{IoError, IoResult, OtherIoError};
|
||||
use std::io::MemReader;
|
||||
|
||||
use blockdata::block;
|
||||
use network::address::Address;
|
||||
use network::message_network;
|
||||
use network::message_blockdata;
|
||||
use network::serialize::{Serializable, CheckedData};
|
||||
use util::iter::FixedTakeable;
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::encodable::CheckedData;
|
||||
use network::serialize::{serialize, RawDecoder, SimpleEncoder, SimpleDecoder};
|
||||
use util::misc::prepend_err;
|
||||
|
||||
/// Serializer for command string
|
||||
#[deriving(PartialEq, Clone, Show)]
|
||||
pub struct CommandString(pub String);
|
||||
|
||||
impl Serializable for CommandString {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for CommandString {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
let &CommandString(ref inner_str) = self;
|
||||
let mut rawbytes = [0u8, ..12];
|
||||
rawbytes.copy_from(inner_str.as_bytes().as_slice());
|
||||
Vec::from_slice(rawbytes.as_slice())
|
||||
rawbytes.consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<CommandString> {
|
||||
let mut fixiter = iter.fixed_take(12);
|
||||
let rv: String = FromIterator::from_iter(fixiter.by_ref().filter_map(|u| if u > 0 { Some(u as char) } else { None }));
|
||||
// Once we've read the string, run out the iterator
|
||||
for _ in fixiter {}
|
||||
match fixiter.is_err() {
|
||||
false => Ok(CommandString(rv)),
|
||||
true => Err(standard_error(InvalidInput))
|
||||
}
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for CommandString {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<CommandString, E> {
|
||||
let rawbytes: [u8, ..12] = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let rv: String = FromIterator::from_iter(rawbytes.iter().filter_map(|&u| if u > 0 { Some(u as char) } else { None }));
|
||||
Ok(CommandString(rv))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,46 +121,48 @@ impl RawNetworkMessage {
|
|||
}
|
||||
}
|
||||
|
||||
impl Serializable for RawNetworkMessage {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut ret = vec![];
|
||||
ret.extend(self.magic.serialize().move_iter());
|
||||
ret.extend(CommandString(self.command()).serialize().move_iter());
|
||||
let payload_data = match self.payload {
|
||||
Version(ref dat) => dat.serialize(),
|
||||
Verack => vec![],
|
||||
Addr(ref dat) => dat.serialize(),
|
||||
Inv(ref dat) => dat.serialize(),
|
||||
GetData(ref dat) => dat.serialize(),
|
||||
NotFound(ref dat) => dat.serialize(),
|
||||
GetBlocks(ref dat) => dat.serialize(),
|
||||
GetHeaders(ref dat) => dat.serialize(),
|
||||
Block(ref dat) => dat.serialize(),
|
||||
Headers(ref dat) => dat.serialize(),
|
||||
Ping(ref dat) => dat.serialize(),
|
||||
Pong(ref dat) => dat.serialize()
|
||||
};
|
||||
ret.extend(CheckedData(payload_data).serialize().move_iter());
|
||||
ret
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for RawNetworkMessage {
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
try!(self.magic.consensus_encode(s));
|
||||
try!(CommandString(self.command()).consensus_encode(s));
|
||||
match self.payload {
|
||||
Version(ref dat) => serialize(dat),
|
||||
Verack => Ok(vec![]),
|
||||
Addr(ref dat) => serialize(dat),
|
||||
Inv(ref dat) => serialize(dat),
|
||||
GetData(ref dat) => serialize(dat),
|
||||
NotFound(ref dat) => serialize(dat),
|
||||
GetBlocks(ref dat) => serialize(dat),
|
||||
GetHeaders(ref dat) => serialize(dat),
|
||||
Block(ref dat) => serialize(dat),
|
||||
Headers(ref dat) => serialize(dat),
|
||||
Ping(ref dat) => serialize(dat),
|
||||
Pong(ref dat) => serialize(dat),
|
||||
}.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<RawNetworkMessage> {
|
||||
let magic = try!(prepend_err("magic", Serializable::deserialize(iter.by_ref())));
|
||||
let CommandString(cmd): CommandString = try!(prepend_err("command", Serializable::deserialize(iter.by_ref())));
|
||||
let CheckedData(raw_payload): CheckedData = try!(prepend_err("payload", Serializable::deserialize(iter.by_ref())));
|
||||
impl<D:SimpleDecoder<IoError>> ConsensusDecodable<D, IoError> for RawNetworkMessage {
|
||||
fn consensus_decode(d: &mut D) -> IoResult<RawNetworkMessage> {
|
||||
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(MemReader::new(raw_payload));
|
||||
let payload = match cmd.as_slice() {
|
||||
"version" => Version(try!(prepend_err("version", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"version" => Version(try!(prepend_err("version", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"verack" => Verack,
|
||||
"addr" => Addr(try!(prepend_err("addr", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"inv" => Inv(try!(prepend_err("inv", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"getdata" => GetData(try!(prepend_err("getdata", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"notfound" => NotFound(try!(prepend_err("notfound", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"getblocks" => GetBlocks(try!(prepend_err("getblocks", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"getheaders" => GetHeaders(try!(prepend_err("getheaders", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"block" => Block(try!(prepend_err("block", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"headers" => Headers(try!(prepend_err("headers", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"ping" => Ping(try!(prepend_err("ping", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"pong" => Ping(try!(prepend_err("pong", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||
"addr" => Addr(try!(prepend_err("addr", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"inv" => Inv(try!(prepend_err("inv", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"getdata" => GetData(try!(prepend_err("getdata", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"notfound" => NotFound(try!(prepend_err("notfound", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"getblocks" => GetBlocks(try!(prepend_err("getblocks", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"getheaders" => GetHeaders(try!(prepend_err("getheaders", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"block" => Block(try!(prepend_err("block", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"headers" => Headers(try!(prepend_err("headers", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"ping" => Ping(try!(prepend_err("ping", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
"pong" => Ping(try!(prepend_err("pong", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
||||
cmd => {
|
||||
return Err(IoError {
|
||||
kind: OtherIoError,
|
||||
|
@ -177,24 +180,25 @@ impl Serializable for RawNetworkMessage {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::CommandString;
|
||||
|
||||
use std::io::IoResult;
|
||||
|
||||
use network::message::CommandString;
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn serialize_commandstring_test() {
|
||||
let cs = CommandString(String::from_str("Andrew"));
|
||||
assert_eq!(cs.serialize(), 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]
|
||||
fn deserialize_commandstring_test() {
|
||||
let cs: IoResult<CommandString> = Serializable::deserialize([0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0].iter().map(|n| *n));
|
||||
let cs: IoResult<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
||||
assert!(cs.is_ok());
|
||||
assert_eq!(cs.unwrap(), CommandString(String::from_str("Andrew")));
|
||||
|
||||
let short_cs: IoResult<CommandString> = Serializable::deserialize([0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0].iter().map(|n| *n));
|
||||
let short_cs: IoResult<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
|
||||
assert!(short_cs.is_err());
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,9 @@
|
|||
//! Bitcoin data (blocks and transactions) around.
|
||||
//!
|
||||
|
||||
use std::io::{IoResult, IoError, InvalidInput};
|
||||
#[cfg(test)]
|
||||
use serialize::hex::FromHex;
|
||||
#[cfg(test)]
|
||||
use util::hash::zero_hash;
|
||||
|
||||
use network::constants;
|
||||
use network::serialize::{Serializable, SerializeIter};
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||
use util::hash::Sha256dHash;
|
||||
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
|
@ -87,7 +82,7 @@ impl GetBlocksMessage {
|
|||
}
|
||||
}
|
||||
|
||||
impl_serializable!(GetBlocksMessage, version, locator_hashes, stop_hash)
|
||||
impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash)
|
||||
|
||||
impl GetHeadersMessage {
|
||||
/// Construct a new `getheaders` message
|
||||
|
@ -100,45 +95,53 @@ impl GetHeadersMessage {
|
|||
}
|
||||
}
|
||||
|
||||
impl_serializable!(GetHeadersMessage, version, locator_hashes, stop_hash)
|
||||
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash)
|
||||
|
||||
impl Serializable for Inventory {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let int_type: u32 = match self.inv_type {
|
||||
InvError => 0,
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for Inventory {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
try!(match self.inv_type {
|
||||
InvError => 0u32,
|
||||
InvTransaction => 1,
|
||||
InvBlock => 2
|
||||
};
|
||||
let mut rv = vec!();
|
||||
rv.extend(int_type.serialize().move_iter());
|
||||
rv.extend(self.hash.serialize().move_iter());
|
||||
rv
|
||||
}.consensus_encode(s));
|
||||
self.hash.consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Inventory> {
|
||||
let int_type: u32 = try!(Serializable::deserialize(iter.by_ref()));
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for Inventory {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Inventory, E> {
|
||||
let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
Ok(Inventory {
|
||||
inv_type: match int_type {
|
||||
0 => InvError,
|
||||
1 => InvTransaction,
|
||||
2 => InvBlock,
|
||||
_ => { return Err(IoError {
|
||||
kind: InvalidInput,
|
||||
desc: "bad inventory type field",
|
||||
detail: None
|
||||
})}
|
||||
// TODO do not fail here
|
||||
_ => { fail!("bad inventory type field") }
|
||||
},
|
||||
hash: try!(Serializable::deserialize(iter.by_ref()))
|
||||
hash: try!(ConsensusDecodable::consensus_decode(d))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{GetHeadersMessage, GetBlocksMessage};
|
||||
|
||||
use std::io::IoResult;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use util::hash::zero_hash;
|
||||
|
||||
#[test]
|
||||
fn getblocks_message_test() {
|
||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||
|
||||
let decode: IoResult<GetBlocksMessage> = Serializable::deserialize(from_sat.iter().map(|n| *n));
|
||||
let decode: IoResult<GetBlocksMessage> = deserialize(from_sat.clone());
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.version, 70002);
|
||||
|
@ -146,8 +149,7 @@ fn getblocks_message_test() {
|
|||
assert_eq!(real_decode.locator_hashes[0].as_slice(), genhash.as_slice());
|
||||
assert_eq!(real_decode.stop_hash.as_slice(), zero_hash().as_slice());
|
||||
|
||||
let reserialize = real_decode.serialize();
|
||||
assert_eq!(reserialize.as_slice(), from_sat.as_slice());
|
||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -155,7 +157,7 @@ fn getheaders_message_test() {
|
|||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||
|
||||
let decode: IoResult<GetHeadersMessage> = Serializable::deserialize(from_sat.iter().map(|n| *n));
|
||||
let decode: IoResult<GetHeadersMessage> = deserialize(from_sat.clone());
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.version, 70002);
|
||||
|
@ -163,7 +165,7 @@ fn getheaders_message_test() {
|
|||
assert_eq!(real_decode.locator_hashes[0].as_slice(), genhash.as_slice());
|
||||
assert_eq!(real_decode.stop_hash.as_slice(), zero_hash().as_slice());
|
||||
|
||||
let reserialize = real_decode.serialize();
|
||||
assert_eq!(reserialize.as_slice(), from_sat.as_slice());
|
||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,9 @@
|
|||
//!
|
||||
|
||||
use std::io::IoResult;
|
||||
#[cfg(test)]
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use network::constants;
|
||||
use network::address::Address;
|
||||
use network::serialize::Serializable;
|
||||
use network::socket::Socket;
|
||||
|
||||
/// Some simple messages
|
||||
|
@ -83,44 +80,25 @@ impl VersionMessage {
|
|||
}
|
||||
}
|
||||
|
||||
impl Serializable for VersionMessage {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut rv = vec!();
|
||||
rv.extend(self.version.serialize().move_iter());
|
||||
rv.extend(self.services.serialize().move_iter());
|
||||
rv.extend(self.timestamp.serialize().move_iter());
|
||||
rv.extend(self.receiver.serialize().move_iter());
|
||||
rv.extend(self.sender.serialize().move_iter());
|
||||
rv.extend(self.nonce.serialize().move_iter());
|
||||
rv.extend(self.user_agent.serialize().move_iter());
|
||||
rv.extend(self.start_height.serialize().move_iter());
|
||||
if self.version >= 70001 {
|
||||
rv.extend(self.relay.serialize().move_iter());
|
||||
}
|
||||
rv
|
||||
}
|
||||
impl_consensus_encoding!(VersionMessage, version, services, timestamp,
|
||||
receiver, sender, nonce,
|
||||
user_agent, start_height, relay)
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<VersionMessage> {
|
||||
Ok(VersionMessage {
|
||||
version: try!(Serializable::deserialize(iter.by_ref())),
|
||||
services: try!(Serializable::deserialize(iter.by_ref())),
|
||||
timestamp: try!(Serializable::deserialize(iter.by_ref())),
|
||||
receiver: try!(Serializable::deserialize(iter.by_ref())),
|
||||
sender: try!(Serializable::deserialize(iter.by_ref())),
|
||||
nonce: try!(Serializable::deserialize(iter.by_ref())),
|
||||
user_agent: try!(Serializable::deserialize(iter.by_ref())),
|
||||
start_height: try!(Serializable::deserialize(iter.by_ref())),
|
||||
relay: try!(Serializable::deserialize(iter.by_ref()))
|
||||
})
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::VersionMessage;
|
||||
|
||||
use std::io::IoResult;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use network::serialize::{deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn version_message_test() {
|
||||
// This message is from my satoshi node, morning of May 27 2014
|
||||
let from_sat = "721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001".from_hex().unwrap();
|
||||
|
||||
let decode: IoResult<VersionMessage> = Serializable::deserialize(from_sat.iter().map(|n| *n));
|
||||
let decode: IoResult<VersionMessage> = deserialize(from_sat.clone());
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.version, 70002);
|
||||
|
@ -132,8 +110,8 @@ fn version_message_test() {
|
|||
assert_eq!(real_decode.start_height, 302892);
|
||||
assert_eq!(real_decode.relay, true);
|
||||
|
||||
let reserialize = real_decode.serialize();
|
||||
assert_eq!(reserialize.as_slice(), from_sat.as_slice());
|
||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
//!
|
||||
|
||||
pub mod constants;
|
||||
pub mod encodable;
|
||||
pub mod socket;
|
||||
pub mod serialize;
|
||||
|
||||
|
|
|
@ -20,742 +20,160 @@
|
|||
//!
|
||||
|
||||
use collections::Vec;
|
||||
use collections::bitv::{Bitv, from_bytes};
|
||||
use std::default::Default;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{IoError, IoResult, EndOfFile, InvalidInput, OtherIoError, standard_error};
|
||||
use std::io::{BufferedReader, BufferedWriter, File, Truncate, Write};
|
||||
use std::io::fs::rename;
|
||||
use std::mem::transmute;
|
||||
use std::u32;
|
||||
use serialize::{Encoder, Encodable};
|
||||
use std::io::{IoError, IoResult, OtherIoError, MemReader, MemWriter};
|
||||
|
||||
use util::iter::{FixedTake, FixedTakeable, NullIterator};
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use util::hash::Sha256dHash;
|
||||
use util::thinvec::ThinVec;
|
||||
|
||||
/// An iterator which returns serialized data one byte at a time
|
||||
pub struct SerializeIter<'a> {
|
||||
/// Iterator over actual data
|
||||
pub data_iter: Option<Box<Iterator<u8>>>,
|
||||
/// Objects which are serialized through their own `SerializeIter`s
|
||||
pub sub_iter_iter: Box<Iterator<&'a Serializable>>,
|
||||
/// Current subiterator
|
||||
pub sub_iter: Option<Box<SerializeIter<'a>>>,
|
||||
/// Whether we have started using sub_iter_iter
|
||||
pub sub_started: bool
|
||||
/// Objects which are referred to by hash
|
||||
pub trait BitcoinHash {
|
||||
/// Produces a Sha256dHash which can be used to refer to the object
|
||||
fn bitcoin_hash(&self) -> Sha256dHash;
|
||||
}
|
||||
|
||||
impl<'a> Iterator<u8> for SerializeIter<'a> {
|
||||
fn next(&mut self) -> Option<u8> {
|
||||
let mut ret = None;
|
||||
// Try to use the data iterator
|
||||
if self.data_iter.is_some() {
|
||||
// Unwrap the current data iterator to use it
|
||||
let mut it = self.data_iter.take_unwrap();
|
||||
ret = it.next();
|
||||
// Delete the current data iterator if it's exhausted, by putting
|
||||
// it back only when it's -not- exhausted
|
||||
if ret.is_some() { self.data_iter = Some(it); }
|
||||
}
|
||||
// Failing that, start using the subobject iterator
|
||||
if ret.is_none() && !self.sub_started {
|
||||
// Unwrap the current data iterator to use it
|
||||
self.sub_started = true;
|
||||
self.sub_iter = self.sub_iter_iter.next().map(|obj| box obj.serialize_iter());
|
||||
}
|
||||
// If it doesn't work, find one that does
|
||||
while ret.is_none() && self.sub_iter.is_some() {
|
||||
let mut iter = self.sub_iter.take_unwrap();
|
||||
ret = iter.next();
|
||||
self.sub_iter = if ret.is_none() {
|
||||
self.sub_iter_iter.next().map(|obj| box obj.serialize_iter())
|
||||
} else {
|
||||
Some(iter)
|
||||
}
|
||||
}
|
||||
// Eventually we got Some(u8) --- or None and we're exhausted
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[deriving(PartialEq, Clone, Show)]
|
||||
/// Data which must be preceded by a 4-byte checksum
|
||||
pub struct CheckedData(pub Vec<u8>);
|
||||
|
||||
/// An object which can be (de)serialized. If the object can be sent on the
|
||||
/// Bitcoin network, the serialization must be the standard p2p network
|
||||
/// serialization.
|
||||
pub trait Serializable {
|
||||
/// Turn an object into a bytestring that can be put on the wire
|
||||
fn serialize(&self) -> Vec<u8>;
|
||||
/// Serialize an object, returning an iterator rather than complete vector
|
||||
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||
SerializeIter {
|
||||
data_iter: Some(box self.serialize().move_iter() as Box<Iterator<u8>>),
|
||||
sub_iter_iter: box NullIterator::<&Serializable>::new(),
|
||||
sub_iter: None,
|
||||
sub_started: false
|
||||
}
|
||||
}
|
||||
/// Read an object off the wire
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Self>;
|
||||
/// Obtain a hash of the object
|
||||
impl BitcoinHash for Vec<u8> {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
Sha256dHash::from_data(self.serialize().as_slice())
|
||||
}
|
||||
/// Dump the object to a file
|
||||
fn serialize_file(&self, p: &Path) -> IoResult<()> {
|
||||
let tmp_path = p.with_extension("0");
|
||||
{
|
||||
let file = File::open_mode(&tmp_path, Truncate, Write);
|
||||
let mut writer = BufferedWriter::new(file);
|
||||
for ch in self.serialize_iter() {
|
||||
try!(writer.write_u8(ch));
|
||||
}
|
||||
try!(writer.flush());
|
||||
}
|
||||
rename(&tmp_path, p)
|
||||
}
|
||||
/// Read the object from a file
|
||||
fn deserialize_file(p: &Path) -> IoResult<Self> {
|
||||
let file = try!(File::open(p));
|
||||
let mut reader = BufferedReader::new(file);
|
||||
let mut error: IoResult<u8> = Ok(0);
|
||||
// This is kinda a hacky way to catch file read errors
|
||||
let ret = Serializable::deserialize(reader.bytes().filter_map(|res| {
|
||||
if res.is_err() {
|
||||
error = res;
|
||||
None
|
||||
} else {
|
||||
res.ok()
|
||||
}
|
||||
}));
|
||||
// Return file error if there was one, else parse error
|
||||
match error {
|
||||
Ok(_) => ret,
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A variable-length unsigned integer
|
||||
#[deriving(PartialEq, Show)]
|
||||
pub enum VarInt {
|
||||
/// 8-bit int
|
||||
VarU8(u8),
|
||||
/// 16-bit int
|
||||
VarU16(u16),
|
||||
/// 32-bit int
|
||||
VarU32(u32),
|
||||
/// 64-bit int
|
||||
VarU64(u64)
|
||||
}
|
||||
|
||||
impl<S:Encoder<E>, E> Encodable<S, E> for VarInt {
|
||||
fn encode(&self, s: &mut S) -> Result<(), E> {
|
||||
s.emit_u64(varint_to_u64(*self))
|
||||
Sha256dHash::from_data(self.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
/// Convert a Rust uint to a Bitcoin network Varint
|
||||
pub fn u64_to_varint(n: u64) -> VarInt {
|
||||
match n {
|
||||
n if n < 0xFD => VarU8(n as u8),
|
||||
n if n <= 0xFFFF => VarU16(n as u16),
|
||||
n if n <= 0xFFFFFFFF => VarU32(n as u32),
|
||||
n => VarU64(n)
|
||||
/// Encode an object into a vector
|
||||
pub fn serialize<T: ConsensusEncodable<RawEncoder<MemWriter>, IoError>>(obj: &T) -> IoResult<Vec<u8>> {
|
||||
let mut encoder = RawEncoder::new(MemWriter::new());
|
||||
try!(obj.consensus_encode(&mut encoder));
|
||||
Ok(encoder.unwrap().unwrap())
|
||||
}
|
||||
|
||||
/// Deserialize an object from a vector
|
||||
pub fn deserialize<T: ConsensusDecodable<RawDecoder<MemReader>, IoError>>(data: Vec<u8>) -> IoResult<T> {
|
||||
let mut decoder = RawDecoder::new(MemReader::new(data));
|
||||
ConsensusDecodable::consensus_decode(&mut decoder)
|
||||
}
|
||||
|
||||
/// An encoder for raw binary data
|
||||
pub struct RawEncoder<W> {
|
||||
writer: W
|
||||
}
|
||||
|
||||
/// An decoder for raw binary data
|
||||
pub struct RawDecoder<R> {
|
||||
reader: R
|
||||
}
|
||||
|
||||
impl<W:Writer> RawEncoder<W> {
|
||||
/// Constructor
|
||||
pub fn new(writer: W) -> RawEncoder<W> {
|
||||
RawEncoder { writer: writer }
|
||||
}
|
||||
|
||||
/// Returns the underlying Writer
|
||||
pub fn unwrap(self) -> W {
|
||||
self.writer
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a Bitcoin network Varint to a Rust uint
|
||||
pub fn varint_to_u64(n: VarInt) -> u64 {
|
||||
match n {
|
||||
VarU8(m) => m as u64,
|
||||
VarU16(m) => m as u64,
|
||||
VarU32(m) => m as u64,
|
||||
VarU64(m) => m,
|
||||
impl<R:Reader> RawDecoder<R> {
|
||||
/// Constructor
|
||||
pub fn new(reader: R) -> RawDecoder<R> {
|
||||
RawDecoder { reader: reader }
|
||||
}
|
||||
|
||||
/// Returns the underlying Reader
|
||||
pub fn unwrap(self) -> R {
|
||||
self.reader
|
||||
}
|
||||
}
|
||||
|
||||
fn read_uint_le<I: Iterator<u8>>(mut iter: FixedTake<I>) -> Option<u64> {
|
||||
let (rv, _) = iter.fold((0u64, 1u64), |(old, mult), next| (old + next as u64 * mult, mult * 0x100));
|
||||
match iter.is_err() {
|
||||
false => Some(rv),
|
||||
true => None
|
||||
}
|
||||
/// A simple Encoder trait
|
||||
pub trait SimpleEncoder<E> {
|
||||
fn emit_u64(&mut self, v: u64) -> Result<(), E>;
|
||||
fn emit_u32(&mut self, v: u32) -> Result<(), E>;
|
||||
fn emit_u16(&mut self, v: u16) -> Result<(), E>;
|
||||
fn emit_u8(&mut self, v: u8) -> Result<(), E>;
|
||||
|
||||
fn emit_i64(&mut self, v: i64) -> Result<(), E>;
|
||||
fn emit_i32(&mut self, v: i32) -> Result<(), E>;
|
||||
fn emit_i16(&mut self, v: i16) -> Result<(), E>;
|
||||
fn emit_i8(&mut self, v: i8) -> Result<(), E>;
|
||||
|
||||
fn emit_bool(&mut self, v: bool) -> Result<(), E>;
|
||||
}
|
||||
|
||||
/// Do a double-SHA256 on some data and return the first 4 bytes
|
||||
fn sha2_checksum(data: &[u8]) -> u32 {
|
||||
let checksum = Sha256dHash::from_data(data);
|
||||
read_uint_le(checksum.as_slice().iter().map(|n| *n).fixed_take(4)).unwrap() as u32
|
||||
/// A simple Decoder trait
|
||||
pub trait SimpleDecoder<E> {
|
||||
fn read_u64(&mut self) -> Result<u64, E>;
|
||||
fn read_u32(&mut self) -> Result<u32, E>;
|
||||
fn read_u16(&mut self) -> Result<u16, E>;
|
||||
fn read_u8(&mut self) -> Result<u8, E>;
|
||||
|
||||
fn read_i64(&mut self) -> Result<i64, E>;
|
||||
fn read_i32(&mut self) -> Result<i32, E>;
|
||||
fn read_i16(&mut self) -> Result<i16, E>;
|
||||
fn read_i8(&mut self) -> Result<i8, E>;
|
||||
|
||||
fn read_bool(&mut self) -> Result<bool, E>;
|
||||
|
||||
fn error(&mut self, err: &str) -> E;
|
||||
}
|
||||
|
||||
/// Primitives
|
||||
impl Serializable for bool {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
if *self { Vec::from_slice(&[1u8]) } else { Vec::from_slice(&[0u8]) }
|
||||
// TODO: trait reform: impl SimpleEncoder for every Encoder, ditto for Decoder
|
||||
|
||||
impl<W:Writer> SimpleEncoder<IoError> for RawEncoder<W> {
|
||||
#[inline]
|
||||
fn emit_u64(&mut self, v: u64) -> IoResult<()> { self.writer.write_le_u64(v) }
|
||||
#[inline]
|
||||
fn emit_u32(&mut self, v: u32) -> IoResult<()> { self.writer.write_le_u32(v) }
|
||||
#[inline]
|
||||
fn emit_u16(&mut self, v: u16) -> IoResult<()> { self.writer.write_le_u16(v) }
|
||||
#[inline]
|
||||
fn emit_u8(&mut self, v: u8) -> IoResult<()> { self.writer.write_u8(v) }
|
||||
|
||||
#[inline]
|
||||
fn emit_i64(&mut self, v: i64) -> IoResult<()> { self.writer.write_le_i64(v) }
|
||||
#[inline]
|
||||
fn emit_i32(&mut self, v: i32) -> IoResult<()> { self.writer.write_le_i32(v) }
|
||||
#[inline]
|
||||
fn emit_i16(&mut self, v: i16) -> IoResult<()> { self.writer.write_le_i16(v) }
|
||||
#[inline]
|
||||
fn emit_i8(&mut self, v: i8) -> IoResult<()> { self.writer.write_i8(v) }
|
||||
|
||||
#[inline]
|
||||
fn emit_bool(&mut self, v: bool) -> IoResult<()> { self.writer.write_i8(if v {1} else {0}) }
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<bool> {
|
||||
match iter.next() {
|
||||
Some(u) => Ok(u != 0),
|
||||
None => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<R:Reader> SimpleDecoder<IoError> for RawDecoder<R> {
|
||||
#[inline]
|
||||
fn read_u64(&mut self) -> IoResult<u64> { self.reader.read_le_u64() }
|
||||
#[inline]
|
||||
fn read_u32(&mut self) -> IoResult<u32> { self.reader.read_le_u32() }
|
||||
#[inline]
|
||||
fn read_u16(&mut self) -> IoResult<u16> { self.reader.read_le_u16() }
|
||||
#[inline]
|
||||
fn read_u8(&mut self) -> IoResult<u8> { self.reader.read_u8() }
|
||||
|
||||
impl Serializable for u8 {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
Vec::from_slice(&[*self])
|
||||
}
|
||||
#[inline]
|
||||
fn read_i64(&mut self) -> IoResult<i64> { self.reader.read_le_i64() }
|
||||
#[inline]
|
||||
fn read_i32(&mut self) -> IoResult<i32> { self.reader.read_le_i32() }
|
||||
#[inline]
|
||||
fn read_i16(&mut self) -> IoResult<i16> { self.reader.read_le_i16() }
|
||||
#[inline]
|
||||
fn read_i8(&mut self) -> IoResult<i8> { self.reader.read_i8() }
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<u8> {
|
||||
match iter.next() {
|
||||
Some(u) => Ok(u as u8),
|
||||
None => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn read_bool(&mut self) -> IoResult<bool> { self.reader.read_u8().map(|res| res != 0) }
|
||||
|
||||
impl Serializable for u16 {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
unsafe { Vec::from_slice(transmute::<_, [u8, ..2]>(self.to_le())) }
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<u16> {
|
||||
match read_uint_le(iter.fixed_take(2)) {
|
||||
Some(u) => Ok(u as u16),
|
||||
None => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for u32 {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
unsafe { Vec::from_slice(transmute::<_, [u8, ..4]>(self.to_le())) }
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<u32> {
|
||||
match read_uint_le(iter.fixed_take(4)) {
|
||||
Some(u) => Ok(u as u32),
|
||||
None => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for i32 {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
unsafe { Vec::from_slice(transmute::<_, [u8, ..4]>(self.to_le())) }
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<i32> {
|
||||
match read_uint_le(iter.fixed_take(4)) {
|
||||
Some(u) => Ok(u as i32),
|
||||
None => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for u64 {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
unsafe { Vec::from_slice(transmute::<_, [u8, ..8]>(self.to_le())) }
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<u64> {
|
||||
match read_uint_le(iter.fixed_take(8)) {
|
||||
Some(u) => Ok(u as u64),
|
||||
None => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for i64 {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
unsafe { Vec::from_slice(transmute::<_, [u8, ..8]>(self.to_le())) }
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<i64> {
|
||||
match read_uint_le(iter.fixed_take(8)) {
|
||||
Some(u) => Ok(u as i64),
|
||||
None => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for VarInt {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
match *self {
|
||||
VarU8(n) => Vec::from_slice(&[n]),
|
||||
VarU16(n) => { let mut rv = n.serialize(); rv.insert(0, 0xFD); rv },
|
||||
VarU32(n) => { let mut rv = n.serialize(); rv.insert(0, 0xFE); rv },
|
||||
VarU64(n) => { let mut rv = n.serialize(); rv.insert(0, 0xFF); rv },
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<VarInt> {
|
||||
match iter.next() {
|
||||
Some(n) if n < 0xFD => Ok(VarU8(n)),
|
||||
Some(n) if n == 0xFD => Ok(VarU16(try!(Serializable::deserialize(iter)))),
|
||||
Some(n) if n == 0xFE => Ok(VarU32(try!(Serializable::deserialize(iter)))),
|
||||
Some(n) if n == 0xFF => Ok(VarU64(try!(Serializable::deserialize(iter)))),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! serialize_fixvec(
|
||||
($($size:expr),+) => (
|
||||
$(
|
||||
impl Serializable for [u8, ..$size] {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
Vec::from_slice(self.as_slice())
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<[u8, ..$size]> {
|
||||
let mut v = [0u8, ..$size];
|
||||
let mut fixiter = iter.fixed_take($size);
|
||||
let mut n = 0;
|
||||
for ch in fixiter {
|
||||
v[n] = ch;
|
||||
n += 1;
|
||||
}
|
||||
match fixiter.is_err() {
|
||||
false => Ok(v),
|
||||
true => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
||||
#[test]
|
||||
fn test_fixvec() {
|
||||
$(
|
||||
let vec = [5u8, ..$size];
|
||||
let short_vec = [5u8, ..($size - 1)];
|
||||
assert_eq!(vec.as_slice(), vec.serialize().as_slice());
|
||||
|
||||
let decode: IoResult<[u8, ..$size]> = Serializable::deserialize(vec.iter().map(|n| *n));
|
||||
let short_decode: IoResult<[u8, ..$size]> = Serializable::deserialize(short_vec.iter().map(|n| *n));
|
||||
|
||||
assert!(decode.is_ok());
|
||||
assert!(short_decode.is_err());
|
||||
assert_eq!(decode.unwrap().as_slice(), vec.as_slice());
|
||||
)+
|
||||
}
|
||||
);
|
||||
)
|
||||
// we need to do this in one call so that we can do a test for
|
||||
// every value; we can't define a new test fn for each invocation
|
||||
// because there are no gensyms.
|
||||
serialize_fixvec!(4, 8, 12, 16, 32)
|
||||
|
||||
impl Serializable for CheckedData {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let &CheckedData(ref data) = self;
|
||||
let mut ret = (data.len() as u32).serialize();
|
||||
ret.extend(sha2_checksum(data.as_slice()).serialize().move_iter());
|
||||
ret.extend(data.iter().map(|n| *n));
|
||||
ret
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<CheckedData> {
|
||||
let length: u32 = try!(Serializable::deserialize(iter.by_ref()));
|
||||
let checksum: u32 = try!(Serializable::deserialize(iter.by_ref()));
|
||||
|
||||
let mut fixiter = iter.fixed_take(length as uint);
|
||||
let v: Vec<u8> = FromIterator::from_iter(fixiter.by_ref());
|
||||
if fixiter.is_err() {
|
||||
return Err(IoError {
|
||||
kind: EndOfFile,
|
||||
desc: "overrun",
|
||||
detail: Some(format!("checksummed data length given as {:}, but read fewer bytes", length))
|
||||
});
|
||||
}
|
||||
|
||||
let expected_checksum = sha2_checksum(v.as_slice());
|
||||
if checksum == expected_checksum {
|
||||
Ok(CheckedData(v))
|
||||
} else {
|
||||
Err(IoError {
|
||||
#[inline]
|
||||
fn error(&mut self, err: &str) -> IoError {
|
||||
IoError {
|
||||
kind: OtherIoError,
|
||||
desc: "bad checksum",
|
||||
detail: Some(format!("checksum {:4x} did not match expected {:4x}", checksum, expected_checksum)),
|
||||
})
|
||||
desc: "parse error",
|
||||
detail: Some(err.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for String {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut rv = u64_to_varint(self.len() as u64).serialize();
|
||||
rv.push_all(self.as_bytes());
|
||||
rv
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<String> {
|
||||
let length: VarInt = try!(Serializable::deserialize(iter.by_ref()));
|
||||
let mut fixiter = iter.fixed_take(varint_to_u64(length) as uint);
|
||||
let rv: String = FromIterator::from_iter(fixiter.by_ref().map(|u| u as char));
|
||||
match fixiter.is_err() {
|
||||
false => Ok(rv),
|
||||
true => Err(standard_error(EndOfFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serializable> Serializable for Vec<T> {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let n_elems = u64_to_varint(self.len() as u64);
|
||||
let mut rv = n_elems.serialize();
|
||||
for elem in self.iter() {
|
||||
rv.extend(elem.serialize().move_iter());
|
||||
}
|
||||
rv
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Vec<T>> {
|
||||
let mut n_elems = varint_to_u64(try!(Serializable::deserialize(iter.by_ref())));
|
||||
let mut v: Vec<T> = vec![];
|
||||
while n_elems > 0 {
|
||||
v.push(try!(Serializable::deserialize(iter.by_ref())));
|
||||
n_elems -= 1;
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl <K: Serializable+Eq+Hash<u64>, T: Serializable, H: Hasher<u64>+Default> Serializable for HashMap<K, T, H> {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let n_elems = u64_to_varint(self.len() as u64);
|
||||
let mut rv = n_elems.serialize();
|
||||
for (key, value) in self.iter() {
|
||||
rv.extend(key.serialize().move_iter());
|
||||
rv.extend(value.serialize().move_iter());
|
||||
}
|
||||
rv
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<HashMap<K, T, H>> {
|
||||
let mut n_elems = varint_to_u64(try!(Serializable::deserialize(iter.by_ref())));
|
||||
let mut ret = HashMap::with_capacity_and_hasher(n_elems as uint, Default::default());
|
||||
while n_elems > 0 {
|
||||
let key: K = try!(Serializable::deserialize(iter.by_ref()));
|
||||
let value: T = try!(Serializable::deserialize(iter.by_ref()));
|
||||
ret.insert(key, value);
|
||||
n_elems -= 1;
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serializable> Serializable for ThinVec<T> {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let n_elems = u64_to_varint(self.len() as u64);
|
||||
let mut rv = n_elems.serialize();
|
||||
for elem in self.iter() {
|
||||
rv.extend(elem.serialize().move_iter());
|
||||
}
|
||||
rv
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<ThinVec<T>> {
|
||||
let n_elems = varint_to_u64(try!(Serializable::deserialize(iter.by_ref())));
|
||||
if n_elems >= u32::MAX as u64 {
|
||||
return Err(IoError {
|
||||
kind: InvalidInput,
|
||||
desc: "vector length too large",
|
||||
detail: Some(format!("tried to read ThinVec with len {} > 4bn", n_elems))
|
||||
});
|
||||
}
|
||||
|
||||
let mut v: ThinVec<T> = ThinVec::with_capacity(n_elems as u32);
|
||||
for i in range(0, n_elems) {
|
||||
unsafe {
|
||||
v.init(i as uint, try!(Serializable::deserialize(iter.by_ref())));
|
||||
};
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Serializable, U:Serializable> Serializable for (T, U) {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let &(ref self1, ref self2) = self;
|
||||
let mut ret = vec![];
|
||||
ret.extend(self1.serialize().move_iter());
|
||||
ret.extend(self2.serialize().move_iter());
|
||||
ret
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<(T, U)> {
|
||||
// FIXME: assign then return is a workaround for https://github.com/rust-lang/rust/issues/15763
|
||||
let ret = Ok((try!(Serializable::deserialize(iter.by_ref())),
|
||||
try!(Serializable::deserialize(iter.by_ref()))));
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Serializable+'static> Serializable for Option<T> {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
match self {
|
||||
&Some(ref dat) => {
|
||||
let mut ret = vec![1];
|
||||
ret.extend(dat.serialize().move_iter());
|
||||
ret
|
||||
},
|
||||
&None => vec![0]
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||
match self {
|
||||
&Some(ref dat) => SerializeIter {
|
||||
data_iter: Some(box Some(1u8).move_iter() as Box<Iterator<u8>>),
|
||||
sub_iter_iter: box vec![ dat as &Serializable ].move_iter(),
|
||||
sub_iter: None,
|
||||
sub_started: false
|
||||
},
|
||||
&None => SerializeIter {
|
||||
data_iter: Some(box Some(0u8).move_iter() as Box<Iterator<u8>>),
|
||||
sub_iter_iter: box NullIterator::<&Serializable>::new(),
|
||||
sub_iter: None,
|
||||
sub_started: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Option<T>> {
|
||||
match iter.next() {
|
||||
Some(0) => Ok(None),
|
||||
Some(1) => Ok(Some(try!(Serializable::deserialize(iter)))),
|
||||
_ => Err(standard_error(InvalidInput))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T:Serializable> Serializable for Box<T> {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
(**self).serialize()
|
||||
}
|
||||
|
||||
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||
(**self).serialize_iter()
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Box<T>> {
|
||||
let ret: T = try!(Serializable::deserialize(iter));
|
||||
Ok(box ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for Bitv {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let n_elems = u64_to_varint(self.len() as u64);
|
||||
let mut rv = n_elems.serialize();
|
||||
for elem in self.to_bytes().iter() {
|
||||
rv.extend(elem.serialize().move_iter());
|
||||
}
|
||||
rv
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Bitv> {
|
||||
let n_elems = varint_to_u64(try!(Serializable::deserialize(iter.by_ref())));
|
||||
let mut v: Vec<u8> = vec![];
|
||||
for _ in range(0, (n_elems + 7) / 8) {
|
||||
v.push(try!(Serializable::deserialize(iter.by_ref())));
|
||||
}
|
||||
let mut ret = from_bytes(v.as_slice());
|
||||
ret.truncate(n_elems as uint); // from_bytes will round up to 8
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_iter_test() {
|
||||
assert_eq!(true.serialize(), true.serialize_iter().collect());
|
||||
assert_eq!(1u8.serialize(), 1u8.serialize_iter().collect());
|
||||
assert_eq!(300u32.serialize(), 300u32.serialize_iter().collect());
|
||||
assert_eq!(20u64.serialize(), 20u64.serialize_iter().collect());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_int_test() {
|
||||
// bool
|
||||
assert_eq!(false.serialize(), Vec::from_slice([0u8]));
|
||||
assert_eq!(true.serialize(), Vec::from_slice([1u8]));
|
||||
// u8
|
||||
assert_eq!(1u8.serialize(), Vec::from_slice([1u8]));
|
||||
assert_eq!(0u8.serialize(), Vec::from_slice([0u8]));
|
||||
assert_eq!(255u8.serialize(), Vec::from_slice([255u8]));
|
||||
// u16
|
||||
assert_eq!(1u16.serialize(), Vec::from_slice([1u8, 0]));
|
||||
assert_eq!(256u16.serialize(), Vec::from_slice([0u8, 1]));
|
||||
assert_eq!(5000u16.serialize(), Vec::from_slice([136u8, 19]));
|
||||
// u32
|
||||
assert_eq!(1u32.serialize(), Vec::from_slice([1u8, 0, 0, 0]));
|
||||
assert_eq!(256u32.serialize(), Vec::from_slice([0u8, 1, 0, 0]));
|
||||
assert_eq!(5000u32.serialize(), Vec::from_slice([136u8, 19, 0, 0]));
|
||||
assert_eq!(500000u32.serialize(), Vec::from_slice([32u8, 161, 7, 0]));
|
||||
assert_eq!(168430090u32.serialize(), Vec::from_slice([10u8, 10, 10, 10]));
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(1i32.serialize(), Vec::from_slice([1u8, 0, 0, 0]));
|
||||
assert_eq!(256i32.serialize(), Vec::from_slice([0u8, 1, 0, 0]));
|
||||
assert_eq!(5000i32.serialize(), Vec::from_slice([136u8, 19, 0, 0]));
|
||||
assert_eq!(500000i32.serialize(), Vec::from_slice([32u8, 161, 7, 0]));
|
||||
assert_eq!(168430090i32.serialize(), Vec::from_slice([10u8, 10, 10, 10]));
|
||||
// u64
|
||||
assert_eq!(1u64.serialize(), Vec::from_slice([1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(256u64.serialize(), Vec::from_slice([0u8, 1, 0, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(5000u64.serialize(), Vec::from_slice([136u8, 19, 0, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(500000u64.serialize(), Vec::from_slice([32u8, 161, 7, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(723401728380766730u64.serialize(), Vec::from_slice([10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(1i64.serialize(), Vec::from_slice([1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(256i64.serialize(), Vec::from_slice([0u8, 1, 0, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(5000i64.serialize(), Vec::from_slice([136u8, 19, 0, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(500000i64.serialize(), Vec::from_slice([32u8, 161, 7, 0, 0, 0, 0, 0]));
|
||||
assert_eq!(723401728380766730i64.serialize(), Vec::from_slice([10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_varint_test() {
|
||||
assert_eq!(VarU8(10).serialize(), Vec::from_slice([10u8]));
|
||||
assert_eq!(VarU8(0xFC).serialize(), Vec::from_slice([0xFCu8]));
|
||||
assert_eq!(VarU16(0xFD).serialize(), Vec::from_slice([0xFDu8, 0xFD, 0]));
|
||||
assert_eq!(VarU16(0xFFF).serialize(), Vec::from_slice([0xFDu8, 0xFF, 0xF]));
|
||||
assert_eq!(VarU32(0xF0F0F0F).serialize(), Vec::from_slice([0xFEu8, 0xF, 0xF, 0xF, 0xF]));
|
||||
assert_eq!(VarU64(0xF0F0F0F0F0E0).serialize(), Vec::from_slice([0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_vector_test() {
|
||||
assert_eq!(Vec::from_slice([1u8, 2, 3]).serialize(), Vec::from_slice([3u8, 1, 2, 3]));
|
||||
// TODO: test vectors of more interesting objects
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_strbuf_test() {
|
||||
assert_eq!(String::from_str("Andrew").serialize(), Vec::from_slice([6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_checkeddata_test() {
|
||||
let cd = CheckedData(vec![1u8, 2, 3, 4, 5]);
|
||||
assert_eq!(cd.serialize(), vec![5, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_box_test() {
|
||||
assert_eq!((box 1u8).serialize(), vec![1u8]);
|
||||
assert_eq!((box 1u16).serialize(), vec![1u8, 0]);
|
||||
assert_eq!((box 1u64).serialize(), vec![1u8, 0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_option_test() {
|
||||
let none: Option<u8> = None;
|
||||
let none_ser = none.serialize();
|
||||
let some_ser = Some(0xFFu8).serialize();
|
||||
assert_eq!(none_ser, vec![0]);
|
||||
assert_eq!(some_ser, vec![1, 0xFF]);
|
||||
|
||||
assert_eq!(none.serialize(), none.serialize_iter().collect());
|
||||
assert_eq!(Some(true).serialize(), Some(true).serialize_iter().collect());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_bitv_test() {
|
||||
let bv = Bitv::with_capacity(10, true);
|
||||
assert_eq!(bv.serialize(), vec![10, 0xFF, 0xC0]);
|
||||
assert_eq!(bv.serialize(), bv.serialize_iter().collect());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_int_test() {
|
||||
// bool
|
||||
assert_eq!(Serializable::deserialize([58u8, 0].iter().map(|n| *n)), Ok(true));
|
||||
assert_eq!(Serializable::deserialize([58u8].iter().map(|n| *n)), Ok(true));
|
||||
assert_eq!(Serializable::deserialize([1u8].iter().map(|n| *n)), Ok(true));
|
||||
assert_eq!(Serializable::deserialize([0u8].iter().map(|n| *n)), Ok(false));
|
||||
assert_eq!(Serializable::deserialize([0u8, 1].iter().map(|n| *n)), Ok(false));
|
||||
|
||||
// u8
|
||||
assert_eq!(Serializable::deserialize([58u8].iter().map(|n| *n)), Ok(58u8));
|
||||
|
||||
// u16
|
||||
assert_eq!(Serializable::deserialize([0x01u8, 0x02].iter().map(|n| *n)), Ok(0x0201u16));
|
||||
assert_eq!(Serializable::deserialize([0xABu8, 0xCD].iter().map(|n| *n)), Ok(0xCDABu16));
|
||||
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D].iter().map(|n| *n)), Ok(0xDA0u16));
|
||||
let failure16: IoResult<u16> = Serializable::deserialize([1u8].iter().map(|n| *n));
|
||||
assert!(failure16.is_err());
|
||||
|
||||
// u32
|
||||
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0].iter().map(|n| *n)), Ok(0xCDABu32));
|
||||
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0xCD].iter().map(|n| *n)), Ok(0xCDAB0DA0u32));
|
||||
let failure32: IoResult<u32> = Serializable::deserialize([1u8, 2, 3].iter().map(|n| *n));
|
||||
assert!(failure32.is_err());
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0].iter().map(|n| *n)), Ok(0xCDABi32));
|
||||
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0x2D].iter().map(|n| *n)), Ok(0x2DAB0DA0i32));
|
||||
let failurei32: IoResult<i32> = Serializable::deserialize([1u8, 2, 3].iter().map(|n| *n));
|
||||
assert!(failurei32.is_err());
|
||||
|
||||
// u64
|
||||
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0, 0, 0, 0, 0].iter().map(|n| *n)), Ok(0xCDABu64));
|
||||
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99].iter().map(|n| *n)), Ok(0x99000099CDAB0DA0u64));
|
||||
let failure64: IoResult<u64> = Serializable::deserialize([1u8, 2, 3, 4, 5, 6, 7].iter().map(|n| *n));
|
||||
assert!(failure64.is_err());
|
||||
// TODO: test negative numbers
|
||||
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0, 0, 0, 0, 0].iter().map(|n| *n)), Ok(0xCDABi64));
|
||||
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99].iter().map(|n| *n)), Ok(0x99000099CDAB0DA0i64));
|
||||
let failurei64: IoResult<i64> = Serializable::deserialize([1u8, 2, 3, 4, 5, 6, 7].iter().map(|n| *n));
|
||||
assert!(failurei64.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_vec_test() {
|
||||
assert_eq!(Serializable::deserialize([3u8, 2, 3, 4].iter().map(|n| *n)), Ok(vec![2u8, 3, 4]));
|
||||
assert_eq!(Serializable::deserialize([4u8, 2, 3, 4, 5, 6].iter().map(|n| *n)), Ok(vec![2u8, 3, 4, 5]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_strbuf_test() {
|
||||
assert_eq!(Serializable::deserialize([6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77].iter().map(|n| *n)), Ok(String::from_str("Andrew")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_checkeddata_test() {
|
||||
let cd: IoResult<CheckedData> = Serializable::deserialize([5u8, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5].iter().map(|n| *n));
|
||||
assert!(cd.is_ok());
|
||||
assert_eq!(cd.unwrap(), CheckedData(Vec::from_slice([1u8, 2, 3, 4, 5])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_option_test() {
|
||||
let none: IoResult<Option<u8>> = Serializable::deserialize([0u8].iter().map(|n| *n));
|
||||
let good: IoResult<Option<u8>> = Serializable::deserialize([1u8, 0xFF].iter().map(|n| *n));
|
||||
let bad: IoResult<Option<u8>> = Serializable::deserialize([2u8].iter().map(|n| *n));
|
||||
assert!(bad.is_err());
|
||||
assert_eq!(none, Ok(None));
|
||||
assert_eq!(good, Ok(Some(0xFF)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_box_test() {
|
||||
let zero: IoResult<Box<u8>> = Serializable::deserialize([0u8].iter().map(|n| *n));
|
||||
let one: IoResult<Box<u8>> = Serializable::deserialize([1u8].iter().map(|n| *n));
|
||||
assert_eq!(zero, Ok(box 0));
|
||||
assert_eq!(one, Ok(box 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_bitv_test() {
|
||||
let bv: IoResult<Bitv> = Serializable::deserialize([10u8, 0xFF, 0xC0].iter().map(|n| *n));
|
||||
assert!(bv.is_ok());
|
||||
assert_eq!(bv.unwrap(), Bitv::with_capacity(10, true));
|
||||
}
|
||||
// Aren't really any tests here.. the main functions are serialize and
|
||||
// deserialize, which get the crap tested out of them it every other
|
||||
// module.
|
||||
|
||||
|
|
|
@ -27,9 +27,10 @@ use std::sync::{Arc, Mutex};
|
|||
|
||||
use network::constants;
|
||||
use network::address::Address;
|
||||
use network::encodable::{ConsensusEncodable, ConsensusDecodable};
|
||||
use network::message::{RawNetworkMessage, NetworkMessage, Version};
|
||||
use network::serialize::Serializable;
|
||||
use network::message_network::VersionMessage;
|
||||
use network::serialize::{RawEncoder, RawDecoder};
|
||||
use util::misc::prepend_err;
|
||||
|
||||
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
||||
|
@ -185,7 +186,7 @@ impl Socket {
|
|||
None => Err(standard_error(NotConnected)),
|
||||
Some(ref mut writer) => {
|
||||
let message = RawNetworkMessage { magic: self.magic, payload: payload };
|
||||
try!(writer.write(message.serialize().as_slice()));
|
||||
try!(message.consensus_encode(&mut RawEncoder::new(writer.by_ref())));
|
||||
writer.flush()
|
||||
}
|
||||
}
|
||||
|
@ -198,33 +199,17 @@ impl Socket {
|
|||
match *reader_lock.deref_mut() {
|
||||
None => Err(standard_error(NotConnected)),
|
||||
Some(ref mut buf) => {
|
||||
let mut read_err = None;
|
||||
// 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.
|
||||
let ret: IoResult<RawNetworkMessage> = {
|
||||
// Set up iterator so we will catch network errors properly
|
||||
let iter = buf.bytes().filter_map(|res|
|
||||
match res {
|
||||
Ok(ch) => Some(ch),
|
||||
Err(e) => { read_err = Some(e); None }
|
||||
});
|
||||
// Receive message
|
||||
Serializable::deserialize(iter)
|
||||
};
|
||||
// Return
|
||||
match read_err {
|
||||
// Network errors get priority since they are probably more meaningful
|
||||
Some(e) => {
|
||||
prepend_err("network", Err(e))
|
||||
},
|
||||
_ => {
|
||||
match ret {
|
||||
// Next come parse errors
|
||||
let mut decoder = RawDecoder::new(buf.by_ref());
|
||||
let decode: IoResult<RawNetworkMessage> = ConsensusDecodable::consensus_decode(&mut decoder);
|
||||
match decode {
|
||||
// Check for parse errors...
|
||||
Err(e) => {
|
||||
prepend_err("decode", Err(e))
|
||||
prepend_err("network_decode", Err(e))
|
||||
},
|
||||
Ok(ret) => {
|
||||
// Finally 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
|
||||
if ret.magic != self.magic {
|
||||
Err(IoError {
|
||||
|
@ -241,7 +226,5 @@ impl Socket {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ use core::char::from_digit;
|
|||
use core::cmp::min;
|
||||
use std::default::Default;
|
||||
use std::fmt;
|
||||
use std::io::{IoResult, IoError, InvalidInput};
|
||||
use std::io::MemWriter;
|
||||
use std::mem::transmute;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use serialize::json::{mod, ToJson};
|
||||
|
@ -28,8 +28,8 @@ use serialize::json::{mod, ToJson};
|
|||
use crypto::digest::Digest;
|
||||
use crypto::sha2;
|
||||
|
||||
use network::serialize::Serializable;
|
||||
use util::iter::FixedTakeable;
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::serialize::{RawEncoder, BitcoinHash, SimpleDecoder, SimpleEncoder};
|
||||
use util::uint::Uint128;
|
||||
use util::uint::Uint256;
|
||||
|
||||
|
@ -108,15 +108,16 @@ impl Sha256dHash {
|
|||
from_bytes(self.as_slice())
|
||||
}
|
||||
|
||||
/// Converts a hash to a Uint256, interpreting it as a little endian encoding.
|
||||
pub fn as_uint256(&self) -> Uint256 {
|
||||
let &Sha256dHash(data) = self;
|
||||
/// Converts a hash to a Uint256, interpreting it as a little endian number.
|
||||
pub fn into_uint256(self) -> Uint256 {
|
||||
let Sha256dHash(data) = self;
|
||||
unsafe { Uint256(transmute(data)) }
|
||||
}
|
||||
|
||||
/// Converts a hash to a Uint128, interpreting it as a little endian encoding.
|
||||
pub fn as_uint128(&self) -> Uint128 {
|
||||
let &Sha256dHash(data) = self;
|
||||
/// Converts a hash to a Uint128, interpreting it as a little endian number.
|
||||
pub fn into_uint128(self) -> Uint128 {
|
||||
let Sha256dHash(data) = self;
|
||||
// TODO: this function won't work correctly on big-endian machines
|
||||
unsafe { Uint128(transmute([data[16], data[17], data[18], data[19], data[20],
|
||||
data[21], data[22], data[23], data[24], data[25],
|
||||
data[26], data[27], data[28], data[29], data[30],
|
||||
|
@ -156,9 +157,17 @@ impl PartialEq for Sha256dHash {
|
|||
|
||||
impl Eq for Sha256dHash {}
|
||||
|
||||
impl Index<uint, u8> for Sha256dHash {
|
||||
#[inline]
|
||||
fn index<'a>(&'a self, idx: &uint) -> &'a u8 {
|
||||
let &Sha256dHash(ref data) = self;
|
||||
&data[*idx]
|
||||
}
|
||||
}
|
||||
|
||||
// Note that this outputs hashes as big endian hex numbers, so this should be
|
||||
// used only for user-facing stuff. Internal and network serialization is
|
||||
// little-endian and should be done using the consensus `network::serialize`
|
||||
// little-endian and should be done using the consensus `encodable::ConsensusEncodable`
|
||||
// interface.
|
||||
impl ToJson for Sha256dHash {
|
||||
fn to_json(&self) -> json::Json {
|
||||
|
@ -166,26 +175,17 @@ impl ToJson for Sha256dHash {
|
|||
}
|
||||
}
|
||||
|
||||
impl Serializable for Sha256dHash {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let &Sha256dHash(ref data) = self;
|
||||
data.iter().map(|n| *n).collect()
|
||||
impl<S:SimpleEncoder<E>, E> ConsensusEncodable<S, E> for Sha256dHash {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
self.into_uint256().consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Sha256dHash> {
|
||||
let Sha256dHash(mut ret) = zero_hash();
|
||||
let mut fixediter = iter.enumerate().fixed_take(32);
|
||||
for (n, data) in fixediter {
|
||||
ret[n] = data;
|
||||
}
|
||||
match fixediter.is_err() {
|
||||
false => Ok(Sha256dHash(ret)),
|
||||
true => Err(IoError {
|
||||
kind: InvalidInput,
|
||||
desc: "unexpected end of input",
|
||||
detail: Some(format!("Need 32 bytes, was {:} short.", fixediter.remaining()))
|
||||
})
|
||||
}
|
||||
impl<D:SimpleDecoder<E>, E> ConsensusDecodable<D, E> for Sha256dHash {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Sha256dHash, E> {
|
||||
Ok(Sha256dHash(try!(ConsensusDecodable::consensus_decode(d))))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ pub trait MerkleRoot {
|
|||
fn merkle_root(&self) -> Sha256dHash;
|
||||
}
|
||||
|
||||
impl<'a, T: Serializable> MerkleRoot for &'a [T] {
|
||||
impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
|
||||
fn merkle_root(&self) -> Sha256dHash {
|
||||
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
|
||||
// Base case
|
||||
|
@ -230,9 +230,10 @@ impl<'a, T: Serializable> MerkleRoot for &'a [T] {
|
|||
for idx in range(0, (data.len() + 1) / 2) {
|
||||
let idx1 = 2 * idx;
|
||||
let idx2 = min(idx1 + 1, data.len() - 1);
|
||||
let to_hash = data[idx1].bitcoin_hash().serialize()
|
||||
.append(data[idx2].bitcoin_hash().serialize().as_slice());
|
||||
next.push(to_hash.bitcoin_hash());
|
||||
let mut encoder = RawEncoder::new(MemWriter::new());
|
||||
data[idx1].consensus_encode(&mut encoder).unwrap();
|
||||
data[idx2].consensus_encode(&mut encoder).unwrap();
|
||||
next.push(encoder.unwrap().unwrap().bitcoin_hash());
|
||||
}
|
||||
merkle_root(next)
|
||||
}
|
||||
|
@ -240,7 +241,7 @@ impl<'a, T: Serializable> MerkleRoot for &'a [T] {
|
|||
}
|
||||
}
|
||||
|
||||
impl <T: Serializable> MerkleRoot for Vec<T> {
|
||||
impl <T: BitcoinHash> MerkleRoot for Vec<T> {
|
||||
fn merkle_root(&self) -> Sha256dHash {
|
||||
self.as_slice().merkle_root()
|
||||
}
|
||||
|
|
|
@ -17,77 +17,6 @@
|
|||
//! Iterator adaptors needed by Bitcoin but not provided by the Rust
|
||||
//! standard library.
|
||||
|
||||
/// An iterator that just returns None
|
||||
pub struct NullIterator<T>;
|
||||
impl<T> Iterator<T> for NullIterator<T> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<T> { None }
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (uint, Option<uint>) { (0, Some(0)) }
|
||||
}
|
||||
|
||||
impl<T> NullIterator<T> {
|
||||
/// Creates a new NullIterator
|
||||
pub fn new() -> NullIterator<T> { NullIterator }
|
||||
}
|
||||
|
||||
/// An Iterator which will give n elements of the contained iterator
|
||||
/// before returning None. If the contained iterator returns None too
|
||||
/// early,
|
||||
pub struct FixedTake<I> {
|
||||
iter: I,
|
||||
n_elems: uint,
|
||||
is_err: bool
|
||||
}
|
||||
|
||||
impl<T, I: Iterator<T>> Iterator<T> for FixedTake<I> {
|
||||
fn next(&mut self) -> Option<T> {
|
||||
if self.n_elems == 0 {
|
||||
None
|
||||
} else {
|
||||
self.n_elems -= 1;
|
||||
match self.iter.next() {
|
||||
Some(e) => Some(e),
|
||||
None => {
|
||||
self.is_err = true;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> FixedTake<I> {
|
||||
/// Constructs a FixedTake iterator from an underlying iterator
|
||||
pub fn new(iter: I, n_elems: uint) -> FixedTake<I> {
|
||||
FixedTake { iter: iter, n_elems: n_elems, is_err: false }
|
||||
}
|
||||
|
||||
/// Indicates whether the underlying iterator has ended early
|
||||
pub fn is_err(&self) -> bool {
|
||||
self.is_err
|
||||
}
|
||||
|
||||
/// Number of remaining elements
|
||||
pub fn remaining(&self) -> uint {
|
||||
self.n_elems
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that returns at most `n_elems` elements, entering an error
|
||||
/// state if the underlying iterator yields fewer than `n_elems` elements.
|
||||
pub trait FixedTakeable<I> {
|
||||
/// Returns an iterator similar to Take but which detects if the original
|
||||
/// iterator runs out early
|
||||
fn fixed_take(self, n_elems: uint) -> FixedTake<I>;
|
||||
}
|
||||
|
||||
impl<T, I: Iterator<T>> FixedTakeable<I> for I {
|
||||
fn fixed_take(self, n_elems: uint) -> FixedTake<I> {
|
||||
FixedTake::new(self, n_elems)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that returns pairs of elements
|
||||
pub struct Pair<A, I> {
|
||||
iter: I,
|
||||
|
|
|
@ -39,4 +39,3 @@ pub trait BitArray {
|
|||
fn trailing_zeros(&self) -> uint;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,28 +21,26 @@
|
|||
//!
|
||||
|
||||
use core::fmt::Show;
|
||||
use core::iter::ByRef;
|
||||
use core::cmp;
|
||||
use std::kinds::marker;
|
||||
use std::num::{Zero, One};
|
||||
use std::io::{IoResult, InvalidInput, standard_error};
|
||||
|
||||
use network::serialize::{Serializable, SerializeIter};
|
||||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||
use util::BitArray;
|
||||
use util::misc::prepend_err;
|
||||
|
||||
/// Patricia troo
|
||||
pub struct PatriciaTree<T, K> {
|
||||
data: Option<T>,
|
||||
child_l: Option<Box<PatriciaTree<T, K>>>,
|
||||
child_r: Option<Box<PatriciaTree<T, K>>>,
|
||||
pub struct PatriciaTree<K, V> {
|
||||
data: Option<V>,
|
||||
child_l: Option<Box<PatriciaTree<K, V>>>,
|
||||
child_r: Option<Box<PatriciaTree<K, V>>>,
|
||||
skip_prefix: K,
|
||||
skip_len: u8
|
||||
}
|
||||
|
||||
impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree<T, K> {
|
||||
impl<K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>, V> PatriciaTree<K, V> {
|
||||
/// Constructs a new Patricia tree
|
||||
pub fn new() -> PatriciaTree<T, K> {
|
||||
pub fn new() -> PatriciaTree<K, V> {
|
||||
PatriciaTree {
|
||||
data: None,
|
||||
child_l: None,
|
||||
|
@ -53,7 +51,7 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
}
|
||||
|
||||
/// Lookup a value by exactly matching `key` and return a referenc
|
||||
pub fn lookup_mut<'a>(&'a mut self, key: &K, key_len: uint) -> Option<&'a mut T> {
|
||||
pub fn lookup_mut<'a>(&'a mut self, key: &K, key_len: uint) -> Option<&'a mut V> {
|
||||
// Caution: `lookup_mut` never modifies its self parameter (in fact its
|
||||
// internal recursion uses a non-mutable self, so we are OK to just
|
||||
// transmute our self pointer into a mutable self before passing it in.
|
||||
|
@ -62,7 +60,7 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
}
|
||||
|
||||
/// Lookup a value by exactly matching `key` and return a mutable reference
|
||||
pub fn lookup<'a>(&'a self, key: &K, key_len: uint) -> Option<&'a T> {
|
||||
pub fn lookup<'a>(&'a self, key: &K, key_len: uint) -> Option<&'a V> {
|
||||
let mut node = self;
|
||||
let mut key_idx = 0;
|
||||
|
||||
|
@ -98,18 +96,18 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
/// Inserts a value with key `key`, returning true on success. If a value is already
|
||||
/// stored against `key`, do nothing and return false.
|
||||
#[inline]
|
||||
pub fn insert(&mut self, key: &K, key_len: uint, value: T) -> bool {
|
||||
pub fn insert(&mut self, key: &K, key_len: uint, value: V) -> bool {
|
||||
self.real_insert(key, key_len, value, false)
|
||||
}
|
||||
|
||||
/// Inserts a value with key `key`, returning true on success. If a value is already
|
||||
/// stored against `key`, overwrite it and return false.
|
||||
#[inline]
|
||||
pub fn insert_or_update(&mut self, key: &K, key_len: uint, value: T) -> bool {
|
||||
pub fn insert_or_update(&mut self, key: &K, key_len: uint, value: V) -> bool {
|
||||
self.real_insert(key, key_len, value, true)
|
||||
}
|
||||
|
||||
fn real_insert(&mut self, key: &K, key_len: uint, value: T, overwrite: bool) -> bool {
|
||||
fn real_insert(&mut self, key: &K, key_len: uint, value: V, overwrite: bool) -> bool {
|
||||
let mut node = self;
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
|
@ -213,10 +211,10 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
|
||||
/// Deletes a value with key `key`, returning it on success. If no value with
|
||||
/// the given key is found, return None
|
||||
pub fn delete(&mut self, key: &K, key_len: uint) -> Option<T> {
|
||||
pub fn delete(&mut self, key: &K, key_len: uint) -> Option<V> {
|
||||
/// 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)
|
||||
fn recurse<T, K:BitArray+Eq+Zero+One+Add<K,K>+Shr<uint,K>+Shl<uint,K>>(tree: &mut PatriciaTree<T, K>, key: &K, key_len: uint) -> (bool, Option<T>) {
|
||||
fn recurse<K:BitArray+Eq+Zero+One+Add<K,K>+Shr<uint,K>+Shl<uint,K>, V>(tree: &mut PatriciaTree<K, V>, key: &K, key_len: uint) -> (bool, Option<V>) {
|
||||
// If the search key is shorter than the node prefix, there is no
|
||||
// way we can match, so fail.
|
||||
if key_len < tree.skip_len as uint {
|
||||
|
@ -322,7 +320,7 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
|
||||
/// Count all the nodes
|
||||
pub fn node_count(&self) -> uint {
|
||||
fn recurse<T, K>(node: &Option<Box<PatriciaTree<T, K>>>) -> uint {
|
||||
fn recurse<K, V>(node: &Option<Box<PatriciaTree<K, V>>>) -> uint {
|
||||
match node {
|
||||
&Some(ref node) => { 1 + recurse(&node.child_l) + recurse(&node.child_r) }
|
||||
&None => 0
|
||||
|
@ -332,7 +330,7 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
}
|
||||
|
||||
/// Returns an iterator over all elements in the tree
|
||||
pub fn iter<'a>(&'a self) -> Items<'a, T, K> {
|
||||
pub fn iter<'a>(&'a self) -> Items<'a, K, V> {
|
||||
Items {
|
||||
node: Some(self),
|
||||
parents: vec![],
|
||||
|
@ -341,7 +339,7 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
}
|
||||
|
||||
/// Returns a mutable iterator over all elements in the tree
|
||||
pub fn mut_iter<'a>(&'a mut self) -> MutItems<'a, T, K> {
|
||||
pub fn mut_iter<'a>(&'a mut self) -> MutItems<'a, K, V> {
|
||||
MutItems {
|
||||
node: self as *mut _,
|
||||
parents: vec![],
|
||||
|
@ -351,10 +349,10 @@ impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree
|
|||
}
|
||||
}
|
||||
|
||||
impl<T:Show, K:BitArray> PatriciaTree<T, K> {
|
||||
impl<K:BitArray, V:Show> PatriciaTree<K, V> {
|
||||
/// Print the entire tree
|
||||
pub fn print<'a>(&'a self) {
|
||||
fn recurse<'a, T:Show, K:BitArray>(tree: &'a PatriciaTree<T, K>, depth: uint) {
|
||||
fn recurse<'a, K:BitArray, V:Show>(tree: &'a PatriciaTree<K, V>, depth: uint) {
|
||||
for i in range(0, tree.skip_len as uint) {
|
||||
print!("{:}", if tree.skip_prefix.bit(i) { 1u } else { 0 });
|
||||
}
|
||||
|
@ -386,74 +384,48 @@ impl<T:Show, K:BitArray> PatriciaTree<T, K> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T:Serializable+'static, K:BitArray+Serializable+'static> Serializable for PatriciaTree<T, K> {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
// Depth-first serialization
|
||||
let mut ret = vec![];
|
||||
// Serialize self, then children
|
||||
ret.extend(self.skip_prefix.serialize().move_iter());
|
||||
ret.extend(self.skip_len.serialize().move_iter());
|
||||
ret.extend(self.data.serialize().move_iter());
|
||||
ret.extend(self.child_l.serialize().move_iter());
|
||||
ret.extend(self.child_r.serialize().move_iter());
|
||||
ret
|
||||
}
|
||||
|
||||
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||
SerializeIter {
|
||||
data_iter: None,
|
||||
sub_iter_iter: box vec![ &self.skip_prefix as &Serializable,
|
||||
&self.skip_len as &Serializable,
|
||||
&self.data as &Serializable,
|
||||
&self.child_l as &Serializable,
|
||||
&self.child_r as &Serializable ].move_iter(),
|
||||
sub_iter: None,
|
||||
sub_started: false
|
||||
impl<S:SimpleEncoder<E>, E, K:ConsensusEncodable<S, E>, V:ConsensusEncodable<S, E>> ConsensusEncodable<S, E> for PatriciaTree<K, V> {
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
// Depth-first serialization: serialize self, then children
|
||||
try!(self.skip_prefix.consensus_encode(s));
|
||||
try!(self.skip_len.consensus_encode(s));
|
||||
try!(self.data.consensus_encode(s));
|
||||
try!(self.child_l.consensus_encode(s));
|
||||
try!(self.child_r.consensus_encode(s));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<PatriciaTree<T, K>> {
|
||||
// This goofy deserialization routine is to prevent an infinite
|
||||
// regress of ByRef<ByRef<...<ByRef<I>>...>>, see #15188
|
||||
fn recurse<T:Serializable+'static, K:Serializable, I: Iterator<u8>>(iter: &mut ByRef<I>) -> IoResult<PatriciaTree<T, K>> {
|
||||
impl<D:SimpleDecoder<E>, E, K:ConsensusDecodable<D, E>, V:ConsensusDecodable<D, E>> ConsensusDecodable<D, E> for PatriciaTree<K, V> {
|
||||
fn consensus_decode(d: &mut D) -> Result<PatriciaTree<K, V>, E> {
|
||||
Ok(PatriciaTree {
|
||||
skip_prefix: try!(prepend_err("skip_prefix", Serializable::deserialize(iter.by_ref()))),
|
||||
skip_len: try!(prepend_err("skip_len", Serializable::deserialize(iter.by_ref()))),
|
||||
data: try!(prepend_err("data", Serializable::deserialize(iter.by_ref()))),
|
||||
child_l: match iter.next() {
|
||||
Some(1) => Some(box try!(prepend_err("child_l", recurse(iter)))),
|
||||
Some(0) => None,
|
||||
_ => { return Err(standard_error(InvalidInput)) }
|
||||
},
|
||||
child_r: match iter.next() {
|
||||
Some(1) => Some(box try!(prepend_err("child_r", recurse(iter)))),
|
||||
Some(0) => None,
|
||||
_ => { return Err(standard_error(InvalidInput)) }
|
||||
}
|
||||
skip_prefix: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
skip_len: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
data: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
child_l: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
child_r: try!(ConsensusDecodable::consensus_decode(d))
|
||||
})
|
||||
}
|
||||
recurse(&mut iter.by_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator
|
||||
pub struct Items<'tree, T, K> {
|
||||
pub struct Items<'tree, K, V> {
|
||||
started: bool,
|
||||
node: Option<&'tree PatriciaTree<T, K>>,
|
||||
parents: Vec<&'tree PatriciaTree<T, K>>
|
||||
node: Option<&'tree PatriciaTree<K, V>>,
|
||||
parents: Vec<&'tree PatriciaTree<K, V>>
|
||||
}
|
||||
|
||||
/// Mutable iterator
|
||||
pub struct MutItems<'tree, T, K> {
|
||||
pub struct MutItems<'tree, K, V> {
|
||||
started: bool,
|
||||
node: *mut PatriciaTree<T, K>,
|
||||
parents: Vec<*mut PatriciaTree<T, K>>,
|
||||
node: *mut PatriciaTree<K, V>,
|
||||
parents: Vec<*mut PatriciaTree<K, V>>,
|
||||
marker: marker::ContravariantLifetime<'tree>
|
||||
}
|
||||
|
||||
impl<'a, T, K> Iterator<&'a T> for Items<'a, T, K> {
|
||||
fn next(&mut self) -> Option<&'a T> {
|
||||
fn borrow_opt<'a, T, K>(opt_ptr: &'a Option<Box<PatriciaTree<T, K>>>) -> Option<&'a PatriciaTree<T, K>> {
|
||||
impl<'a, K, V> Iterator<&'a V> for Items<'a, K, V> {
|
||||
fn next(&mut self) -> Option<&'a V> {
|
||||
fn borrow_opt<'a, K, V>(opt_ptr: &'a Option<Box<PatriciaTree<K, V>>>) -> Option<&'a PatriciaTree<K, V>> {
|
||||
opt_ptr.as_ref().map(|b| &**b)
|
||||
}
|
||||
|
||||
|
@ -495,9 +467,9 @@ impl<'a, T, K> Iterator<&'a T> for Items<'a, T, K> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T, K> Iterator<&'a mut T> for MutItems<'a, T, K> {
|
||||
fn next(&mut self) -> Option<&'a mut T> {
|
||||
fn borrow_opt<'a, T, K>(opt_ptr: &'a Option<Box<PatriciaTree<T, K>>>) -> *mut PatriciaTree<T, K> {
|
||||
impl<'a, K, V> Iterator<&'a mut V> for MutItems<'a, K, V> {
|
||||
fn next(&mut self) -> Option<&'a mut V> {
|
||||
fn borrow_opt<'a, K, V>(opt_ptr: &'a Option<Box<PatriciaTree<K, V>>>) -> *mut PatriciaTree<K, V> {
|
||||
match *opt_ptr {
|
||||
Some(ref data) => &**data as *const _ as *mut _,
|
||||
None => RawPtr::null()
|
||||
|
@ -553,11 +525,11 @@ mod tests {
|
|||
use std::io::IoResult;
|
||||
use std::num::Zero;
|
||||
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use util::hash::Sha256dHash;
|
||||
use util::uint::Uint128;
|
||||
use util::uint::Uint256;
|
||||
use util::patricia_tree::PatriciaTree;
|
||||
use network::serialize::Serializable;
|
||||
|
||||
#[test]
|
||||
fn patricia_single_insert_lookup_delete_test() {
|
||||
|
@ -579,7 +551,7 @@ mod tests {
|
|||
let mut tree = PatriciaTree::new();
|
||||
let mut hashes = vec![];
|
||||
for i in range(0u32, 5000) {
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_uint128();
|
||||
tree.insert(&hash, 250, i);
|
||||
hashes.push(hash);
|
||||
}
|
||||
|
@ -620,7 +592,7 @@ mod tests {
|
|||
let mut hashes = vec![];
|
||||
// Start by inserting a bunch of chunder
|
||||
for i in range(1u32, 500) {
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_uint128();
|
||||
tree.insert(&hash, 128, i * 1000);
|
||||
hashes.push(hash);
|
||||
}
|
||||
|
@ -652,7 +624,7 @@ mod tests {
|
|||
let mut data = Vec::from_elem(n_elems, None);
|
||||
// Start by inserting a bunch of stuff
|
||||
for i in range(0, n_elems) {
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_uint128();
|
||||
tree.insert(&hash, 128, i);
|
||||
*data.get_mut(i) = Some(());
|
||||
}
|
||||
|
@ -674,7 +646,7 @@ mod tests {
|
|||
let mut data = Vec::from_elem(n_elems, None);
|
||||
// Start by inserting a bunch of stuff
|
||||
for i in range(0, n_elems) {
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_uint128();
|
||||
tree.insert(&hash, 128, i);
|
||||
*data.get_mut(i) = Some(());
|
||||
}
|
||||
|
@ -700,18 +672,15 @@ mod tests {
|
|||
let mut tree = PatriciaTree::new();
|
||||
let mut hashes = vec![];
|
||||
for i in range(0u32, 5000) {
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_uint128();
|
||||
tree.insert(&hash, 250, i);
|
||||
hashes.push(hash);
|
||||
}
|
||||
|
||||
// Serialize it
|
||||
let serialized = tree.serialize();
|
||||
// Check iterator
|
||||
let serialized_1 = tree.serialize_iter().collect();
|
||||
assert_eq!(serialized, serialized_1);
|
||||
let serialized = serialize(&tree).unwrap();
|
||||
// Deserialize it
|
||||
let deserialized: IoResult<PatriciaTree<u32, Uint128>> = Serializable::deserialize(serialized.iter().map(|n| *n));
|
||||
let deserialized: IoResult<PatriciaTree<Uint128, u32>> = deserialize(serialized);
|
||||
assert!(deserialized.is_ok());
|
||||
let new_tree = deserialized.unwrap();
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ impl<T> ThinVec<T> {
|
|||
|
||||
/// Constructor with predetermined capacity
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: u32) -> ThinVec<T> {
|
||||
pub unsafe fn with_capacity(capacity: u32) -> ThinVec<T> {
|
||||
if mem::size_of::<T>() == 0 {
|
||||
ThinVec { ptr: RawPtr::null(), cap: capacity }
|
||||
} else if capacity == 0 {
|
||||
|
@ -46,7 +46,7 @@ impl<T> ThinVec<T> {
|
|||
} else {
|
||||
let size = (capacity as uint).checked_mul(&mem::size_of::<T>())
|
||||
.expect("ThinVec::reserve: capacity overflow");
|
||||
let ptr = unsafe { allocate(size, mem::min_align_of::<T>()) };
|
||||
let ptr = allocate(size, mem::min_align_of::<T>());
|
||||
ThinVec { ptr: ptr as *mut T, cap: capacity }
|
||||
}
|
||||
}
|
||||
|
@ -159,17 +159,17 @@ impl<T> Vector<T> for ThinVec<T> {
|
|||
|
||||
impl<T:Clone> Clone for ThinVec<T> {
|
||||
fn clone(&self) -> ThinVec<T> {
|
||||
unsafe {
|
||||
let mut ret = ThinVec::with_capacity(self.len() as u32);
|
||||
// Copied from vec.rs, which claims this will be optimized to a memcpy
|
||||
// if T is Copy
|
||||
for i in range(0, self.len()) {
|
||||
unsafe {
|
||||
ptr::write(ret.as_mut_slice().unsafe_mut_ref(i),
|
||||
self.as_slice().unsafe_ref(i).clone());
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: clone_from
|
||||
}
|
||||
|
@ -179,10 +179,11 @@ impl<T> FromIterator<T> for ThinVec<T> {
|
|||
fn from_iter<I: Iterator<T>>(iter: I) -> ThinVec<T> {
|
||||
let (lower, _) = iter.size_hint();
|
||||
assert!(lower < u32::MAX as uint);
|
||||
unsafe {
|
||||
let mut vector = ThinVec::with_capacity(lower as u32);
|
||||
for (n, elem) in iter.enumerate() {
|
||||
if n < lower {
|
||||
unsafe { vector.init(n, elem) };
|
||||
vector.init(n, elem);
|
||||
} else {
|
||||
vector.push(elem);
|
||||
}
|
||||
|
@ -190,6 +191,7 @@ impl<T> FromIterator<T> for ThinVec<T> {
|
|||
vector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Extendable<T> for ThinVec<T> {
|
||||
#[inline]
|
||||
|
@ -252,14 +254,14 @@ mod tests {
|
|||
#[test]
|
||||
fn simple_destructor_thinvec_test() {
|
||||
let cap = 2;
|
||||
unsafe {
|
||||
let mut thinvec = ThinVec::with_capacity(cap);
|
||||
|
||||
for i in range(0, cap) {
|
||||
unsafe { thinvec.init(i as uint, Some(box i)); }
|
||||
thinvec.init(i as uint, Some(box i));
|
||||
}
|
||||
|
||||
for i in range(0, cap) {
|
||||
unsafe {
|
||||
assert_eq!(thinvec.get_mut(i as uint).take(), Some(box i));
|
||||
assert_eq!(thinvec.get_mut(i as uint).take(), None);
|
||||
}
|
||||
|
|
|
@ -19,11 +19,9 @@
|
|||
//!
|
||||
|
||||
use std::fmt;
|
||||
use std::io::IoResult;
|
||||
use std::num::{Zero, One};
|
||||
use std::mem::transmute;
|
||||
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::RawEncoder;
|
||||
use util::BitArray;
|
||||
|
||||
macro_rules! construct_uint(
|
||||
|
@ -308,8 +306,8 @@ macro_rules! construct_uint(
|
|||
let &$name(ref me) = self;
|
||||
let &$name(ref you) = other;
|
||||
for i in range(0, $n_words) {
|
||||
if me[3 - i] < you[3 - i] { return Less; }
|
||||
if me[3 - i] > you[3 - i] { return Greater; }
|
||||
if me[$n_words - 1 - i] < you[$n_words - 1 - i] { return Less; }
|
||||
if me[$n_words - 1 - i] > you[$n_words - 1 - i] { return Greater; }
|
||||
}
|
||||
return Equal;
|
||||
}
|
||||
|
@ -323,19 +321,28 @@ macro_rules! construct_uint(
|
|||
|
||||
impl fmt::Show for $name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.serialize().as_slice())
|
||||
use std::fmt::WriteError;
|
||||
use network::encodable::ConsensusEncodable;
|
||||
let mut encoder = RawEncoder::new(f.by_ref());
|
||||
self.consensus_encode(&mut encoder).map_err(|_| WriteError)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for $name {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let vec = unsafe { transmute::<$name, [u8, ..($n_words*8)]>(*self) };
|
||||
vec.serialize()
|
||||
impl<S: ::network::serialize::SimpleEncoder<E>, E> ::network::encodable::ConsensusEncodable<S, E> for $name {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), E> {
|
||||
use network::encodable::ConsensusEncodable;
|
||||
let &$name(ref data) = self;
|
||||
for word in data.iter() { try!(word.consensus_encode(s)); }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<$name> {
|
||||
let ret: [u8, ..($n_words*8)] = try!(Serializable::deserialize(iter.by_ref()));
|
||||
Ok(unsafe { transmute(ret) })
|
||||
impl<D: ::network::serialize::SimpleDecoder<E>, E> ::network::encodable::ConsensusDecodable<D, E> for $name {
|
||||
fn consensus_decode(d: &mut D) -> Result<$name, E> {
|
||||
use network::encodable::ConsensusDecodable;
|
||||
let ret: [u64, ..$n_words] = try!(ConsensusDecodable::consensus_decode(d));
|
||||
Ok($name(ret))
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -367,7 +374,7 @@ mod tests {
|
|||
use std::io::IoResult;
|
||||
use std::num::from_u64;
|
||||
|
||||
use network::serialize::Serializable;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use util::uint::Uint256;
|
||||
use util::BitArray;
|
||||
|
||||
|
@ -471,10 +478,10 @@ mod tests {
|
|||
pub fn uint256_serialize_test() {
|
||||
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
||||
let serial1 = start1.serialize();
|
||||
let serial2 = start2.serialize();
|
||||
let end1: IoResult<Uint256> = Serializable::deserialize(serial1.iter().map(|n| *n));
|
||||
let end2: IoResult<Uint256> = Serializable::deserialize(serial2.iter().map(|n| *n));
|
||||
let serial1 = serialize(&start1).unwrap();
|
||||
let serial2 = serialize(&start2).unwrap();
|
||||
let end1: IoResult<Uint256> = deserialize(serial1);
|
||||
let end2: IoResult<Uint256> = deserialize(serial2);
|
||||
|
||||
assert_eq!(end1, Ok(start1));
|
||||
assert_eq!(end2, Ok(start2));
|
||||
|
|
Loading…
Reference in New Issue