From b51f3b4c6bbd19c625f846e5960b18950576e3d9 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 15 Aug 2016 14:50:20 +0200 Subject: [PATCH] basic serialization --- .gitignore | 2 + Cargo.toml | 8 ++++ src/block_header.rs | 55 ++++++++++++++++++++++++++ src/compact_integer.rs | 77 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 16 ++++++++ src/stream.rs | 87 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 245 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/block_header.rs create mode 100644 src/compact_integer.rs create mode 100644 src/lib.rs create mode 100644 src/stream.rs diff --git a/.gitignore b/.gitignore index cb14a420..cc5b41f0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock + +*.swp diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..efbdae18 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pbtc" +version = "0.1.0" +authors = ["debris "] + +[dependencies] +byteorder = "0.5" +rustc-serialize = "0.3" diff --git a/src/block_header.rs b/src/block_header.rs new file mode 100644 index 00000000..42b6b882 --- /dev/null +++ b/src/block_header.rs @@ -0,0 +1,55 @@ + +use stream::{Serializable, Stream}; + +pub struct BlockHeader { + version: u32, + previous_header_hash: [u8; 32], + merkle_root_hash: [u8; 32], + time: u32, + nbits: u32, + nonce: u32, +} + +impl Serializable for BlockHeader { + fn serialize(&self, stream: &mut Stream) { + stream + .append(&self.version) + .append_bytes(&self.previous_header_hash) + .append_bytes(&self.merkle_root_hash) + .append(&self.time) + .append(&self.nbits) + .append(&self.nonce); + } +} + +#[cfg(test)] +mod tests { + use stream::Stream; + use super::BlockHeader; + + #[test] + fn test_block_header_stream() { + let block_header = BlockHeader { + version: 1, + previous_header_hash: [2; 32], + merkle_root_hash: [3; 32], + time: 4, + nbits: 5, + nonce: 6, + }; + + let mut stream = Stream::default(); + stream.append(&block_header); + + let expected = vec![ + 1, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 0, 0, 0, + 5, 0, 0, 0, + 6, 0, 0, 0, + ]; + + assert_eq!(stream.out(), expected); + } +} diff --git a/src/compact_integer.rs b/src/compact_integer.rs new file mode 100644 index 00000000..5625f21a --- /dev/null +++ b/src/compact_integer.rs @@ -0,0 +1,77 @@ +//! A type of variable-length integer commonly used in the Bitcoin P2P protocol and Bitcoin serialized data structures. +//! https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers + +use stream::{Serializable, Stream}; + +/// A type of variable-length integer commonly used in the Bitcoin P2P protocol and Bitcoin serialized data structures. +#[derive(Default, Debug, Clone, Copy, PartialEq)] +pub struct CompactInteger(u64); + +impl From for u64 { + fn from(i: CompactInteger) -> Self { + i.0 + } +} + +impl From for CompactInteger { + fn from(i: u64) -> Self { + CompactInteger(i) + } +} + +impl Serializable for CompactInteger { + fn serialize(&self, stream: &mut Stream) { + match self.0 { + 0...0xfc => { + stream.append(&(self.0 as u8)); + }, + 0xfd...0xffff => { + stream + .append(&0xfdu8) + .append(&(self.0 as u16)); + }, + 0x10000...0xffff_ffff => { + stream + .append(&0xfeu8) + .append(&(self.0 as u32)); + }, + _ => { + stream + .append(&0xffu8) + .append(&self.0); + } + } + } +} + +#[cfg(test)] +mod tests { + use stream::Stream; + use super::CompactInteger; + + #[test] + fn test_compact_integer_stream() { + let mut stream = Stream::default(); + + stream + .append(&CompactInteger::from(0)) + .append(&CompactInteger::from(0xfc)) + .append(&CompactInteger::from(0xfd)) + .append(&CompactInteger::from(0xffff)) + .append(&CompactInteger::from(0x10000)) + .append(&CompactInteger::from(0xffff_ffff)) + .append(&CompactInteger::from(0x1_0000_0000)); + + let expected = vec![ + 0, + 0xfc, + 0xfd, 0xfd, 0x00, + 0xfd, 0xff, 0xff, + 0xfe, 0x00, 0x00, 0x01, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + ]; + + assert_eq!(stream.out(), expected); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..4a173932 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,16 @@ +//! Ethcore's bitcoin library. + +extern crate byteorder; +extern crate rustc_serialize; + +pub mod block_header; +pub mod compact_integer; +pub mod stream; + + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + } +} diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 00000000..12029c57 --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,87 @@ +//! Stream used for serialization. +use std::io::Write; +use byteorder::{LittleEndian, WriteBytesExt}; + +pub trait Serializable { + /// Serialize the struct and appends it to the end of stream. + fn serialize(&self, s: &mut Stream); +} + +/// Stream used for serialization. +#[derive(Default)] +pub struct Stream { + buffer: Vec, +} + +impl Stream { + /// Serializes the struct and appends it to the end of stream. + pub fn append(&mut self, t: &Serializable) -> &mut Self { + t.serialize(self); + self + } + + /// Appends raw bytes to the end of the stream. + pub fn append_bytes(&mut self, bytes: &[u8]) -> &mut Self { + // discard error for now, since we write to sinple vector + self.buffer.write(bytes).unwrap(); + self + } + + /// Full stream. + pub fn out(self) -> Vec { + self.buffer + } +} + +impl Serializable for u8 { + #[inline] + fn serialize(&self, s: &mut Stream) { + s.buffer.write_u8(*self).unwrap(); + } +} + +impl Serializable for u16 { + #[inline] + fn serialize(&self, s: &mut Stream) { + s.buffer.write_u16::(*self).unwrap(); + } +} + +impl Serializable for u32 { + #[inline] + fn serialize(&self, s: &mut Stream) { + s.buffer.write_u32::(*self).unwrap(); + } +} + +impl Serializable for u64 { + #[inline] + fn serialize(&self, s: &mut Stream) { + s.buffer.write_u64::(*self).unwrap(); + } +} + +#[cfg(test)] +mod tests { + use super::Stream; + + #[test] + fn test_stream_append() { + let mut stream = Stream::default(); + + stream + .append(&1u8) + .append(&2u16) + .append(&3u32) + .append(&4u64); + + let expected = vec![ + 1u8, + 2, 0, + 3, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, + ]; + + assert_eq!(expected, stream.out()); + } +}