Strip out old code

All of these files have been moved to either:

- https://github.com/sigp/lighthouse-beacon
- https://github.com/sigp/lighthouse-validator
- https://github.com/sigp/lighthouse-common

For rationale, see: https://github.com/sigp/lighthouse/issues/197
This commit is contained in:
Paul Hauner
2019-02-13 14:15:53 +11:00
parent e4f6fe047d
commit 1d5ff4359a
150 changed files with 0 additions and 14694 deletions

View File

@@ -1,12 +0,0 @@
[package]
name = "bls"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
bls-aggregates = { git = "https://github.com/sigp/signature-schemes", tag = "v0.3.0" }
hashing = { path = "../hashing" }
hex = "0.3"
serde = "1.0"
ssz = { path = "../ssz" }

View File

@@ -1,83 +0,0 @@
use super::{AggregatePublicKey, Signature};
use bls_aggregates::AggregateSignature as RawAggregateSignature;
use serde::ser::{Serialize, Serializer};
use ssz::{
decode_ssz_list, hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash,
};
/// A BLS aggregate signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, PartialEq, Clone, Default, Eq)]
pub struct AggregateSignature(RawAggregateSignature);
impl AggregateSignature {
/// Instantiate a new AggregateSignature.
pub fn new() -> Self {
AggregateSignature(RawAggregateSignature::new())
}
/// Add (aggregate) a signature to the `AggregateSignature`.
pub fn add(&mut self, signature: &Signature) {
self.0.add(signature.as_raw())
}
/// Verify the `AggregateSignature` against an `AggregatePublicKey`.
///
/// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys
/// that signed the `AggregateSignature`.
pub fn verify(&self, msg: &[u8], aggregate_public_key: &AggregatePublicKey) -> bool {
self.0.verify(msg, aggregate_public_key)
}
}
impl Encodable for AggregateSignature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_vec(&self.0.as_bytes());
}
}
impl Decodable for AggregateSignature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
let (sig_bytes, i) = decode_ssz_list(bytes, i)?;
let raw_sig =
RawAggregateSignature::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?;
Ok((AggregateSignature(raw_sig), i))
}
}
impl Serialize for AggregateSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&ssz_encode(self))
}
}
impl TreeHash for AggregateSignature {
fn hash_tree_root(&self) -> Vec<u8> {
hash(&self.0.as_bytes())
}
}
#[cfg(test)]
mod tests {
use super::super::{Keypair, Signature};
use super::*;
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let keypair = Keypair::random();
let mut original = AggregateSignature::new();
original.add(&Signature::new(&[42, 42], &keypair.sk));
let bytes = ssz_encode(&original);
let (decoded, _) = AggregateSignature::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
}

View File

@@ -1,16 +0,0 @@
use super::{PublicKey, SecretKey};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Keypair {
pub sk: SecretKey,
pub pk: PublicKey,
}
impl Keypair {
/// Instantiate a Keypair using SecretKey::random().
pub fn random() -> Self {
let sk = SecretKey::random();
let pk = PublicKey::from_secret_key(&sk);
Keypair { sk, pk }
}
}

View File

@@ -1,52 +0,0 @@
extern crate bls_aggregates;
extern crate hashing;
extern crate ssz;
mod aggregate_signature;
mod keypair;
mod public_key;
mod secret_key;
mod signature;
pub use crate::aggregate_signature::AggregateSignature;
pub use crate::keypair::Keypair;
pub use crate::public_key::PublicKey;
pub use crate::secret_key::SecretKey;
pub use crate::signature::Signature;
pub use self::bls_aggregates::AggregatePublicKey;
pub const BLS_AGG_SIG_BYTE_SIZE: usize = 97;
use hashing::hash;
use ssz::ssz_encode;
use std::default::Default;
fn extend_if_needed(hash: &mut Vec<u8>) {
// NOTE: bls_aggregates crate demands 48 bytes, this may be removed as we get closer to production
hash.resize(48, Default::default())
}
/// For some signature and public key, ensure that the signature message was the public key and it
/// was signed by the secret key that corresponds to that public key.
pub fn verify_proof_of_possession(sig: &Signature, pubkey: &PublicKey) -> bool {
let mut hash = hash(&ssz_encode(pubkey));
extend_if_needed(&mut hash);
sig.verify_hashed(&hash, &pubkey)
}
pub fn create_proof_of_possession(keypair: &Keypair) -> Signature {
let mut hash = hash(&ssz_encode(&keypair.pk));
extend_if_needed(&mut hash);
Signature::new_hashed(&hash, &keypair.sk)
}
pub fn bls_verify_aggregate(
pubkey: &AggregatePublicKey,
message: &[u8],
signature: &AggregateSignature,
_domain: u64,
) -> bool {
// TODO: add domain
signature.verify(message, pubkey)
}

View File

@@ -1,101 +0,0 @@
use super::SecretKey;
use bls_aggregates::PublicKey as RawPublicKey;
use hex::encode as hex_encode;
use serde::ser::{Serialize, Serializer};
use ssz::{
decode_ssz_list, hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash,
};
use std::default;
use std::hash::{Hash, Hasher};
/// A single BLS signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, Clone, Eq)]
pub struct PublicKey(RawPublicKey);
impl PublicKey {
pub fn from_secret_key(secret_key: &SecretKey) -> Self {
PublicKey(RawPublicKey::from_secret_key(secret_key.as_raw()))
}
/// Returns the underlying signature.
pub fn as_raw(&self) -> &RawPublicKey {
&self.0
}
/// Returns the last 6 bytes of the SSZ encoding of the public key, as a hex string.
///
/// Useful for providing a short identifier to the user.
pub fn concatenated_hex_id(&self) -> String {
let bytes = ssz_encode(self);
let end_bytes = &bytes[bytes.len().saturating_sub(6)..bytes.len()];
hex_encode(end_bytes)
}
}
impl default::Default for PublicKey {
fn default() -> Self {
let secret_key = SecretKey::random();
PublicKey::from_secret_key(&secret_key)
}
}
impl Encodable for PublicKey {
fn ssz_append(&self, s: &mut SszStream) {
s.append_vec(&self.0.as_bytes());
}
}
impl Decodable for PublicKey {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
let (sig_bytes, i) = decode_ssz_list(bytes, i)?;
let raw_sig = RawPublicKey::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?;
Ok((PublicKey(raw_sig), i))
}
}
impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&ssz_encode(self))
}
}
impl TreeHash for PublicKey {
fn hash_tree_root(&self) -> Vec<u8> {
hash(&self.0.as_bytes())
}
}
impl PartialEq for PublicKey {
fn eq(&self, other: &PublicKey) -> bool {
ssz_encode(self) == ssz_encode(other)
}
}
impl Hash for PublicKey {
fn hash<H: Hasher>(&self, state: &mut H) {
ssz_encode(self).hash(state)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let sk = SecretKey::random();
let original = PublicKey::from_secret_key(&sk);
let bytes = ssz_encode(&original);
let (decoded, _) = PublicKey::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
}

View File

@@ -1,65 +0,0 @@
use bls_aggregates::{DecodeError as BlsDecodeError, SecretKey as RawSecretKey};
use ssz::{decode_ssz_list, Decodable, DecodeError, Encodable, SszStream, TreeHash};
/// A single BLS signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, PartialEq, Clone, Eq)]
pub struct SecretKey(RawSecretKey);
impl SecretKey {
pub fn random() -> Self {
SecretKey(RawSecretKey::random())
}
/// Instantiate a SecretKey from existing bytes.
///
/// Note: this is _not_ SSZ decoding.
pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, BlsDecodeError> {
Ok(SecretKey(RawSecretKey::from_bytes(bytes)?))
}
/// Returns the underlying secret key.
pub fn as_raw(&self) -> &RawSecretKey {
&self.0
}
}
impl Encodable for SecretKey {
fn ssz_append(&self, s: &mut SszStream) {
s.append_vec(&self.0.as_bytes());
}
}
impl Decodable for SecretKey {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
let (sig_bytes, i) = decode_ssz_list(bytes, i)?;
let raw_sig = RawSecretKey::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?;
Ok((SecretKey(raw_sig), i))
}
}
impl TreeHash for SecretKey {
fn hash_tree_root(&self) -> Vec<u8> {
self.0.as_bytes().clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let original =
SecretKey::from_bytes("jzjxxgjajfjrmgodszzsgqccmhnyvetcuxobhtynojtpdtbj".as_bytes())
.unwrap();
let bytes = ssz_encode(&original);
let (decoded, _) = SecretKey::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
}

View File

@@ -1,107 +0,0 @@
use super::{PublicKey, SecretKey};
use bls_aggregates::Signature as RawSignature;
use serde::ser::{Serialize, Serializer};
use ssz::{
decode_ssz_list, hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash,
};
/// A single BLS signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, PartialEq, Clone, Eq)]
pub struct Signature(RawSignature);
impl Signature {
/// Instantiate a new Signature from a message and a SecretKey.
pub fn new(msg: &[u8], sk: &SecretKey) -> Self {
Signature(RawSignature::new(msg, sk.as_raw()))
}
/// Instantiate a new Signature from a message and a SecretKey, where the message has already
/// been hashed.
pub fn new_hashed(msg_hashed: &[u8], sk: &SecretKey) -> Self {
Signature(RawSignature::new_hashed(msg_hashed, sk.as_raw()))
}
/// Verify the Signature against a PublicKey.
pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> bool {
self.0.verify(msg, pk.as_raw())
}
/// Verify the Signature against a PublicKey, where the message has already been hashed.
pub fn verify_hashed(&self, msg_hash: &[u8], pk: &PublicKey) -> bool {
self.0.verify_hashed(msg_hash, pk.as_raw())
}
/// Returns the underlying signature.
pub fn as_raw(&self) -> &RawSignature {
&self.0
}
/// Returns a new empty signature.
pub fn empty_signature() -> Self {
let empty: Vec<u8> = vec![0; 97];
Signature(RawSignature::from_bytes(&empty).unwrap())
}
}
impl Encodable for Signature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_vec(&self.0.as_bytes());
}
}
impl Decodable for Signature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
let (sig_bytes, i) = decode_ssz_list(bytes, i)?;
let raw_sig = RawSignature::from_bytes(&sig_bytes).map_err(|_| DecodeError::TooShort)?;
Ok((Signature(raw_sig), i))
}
}
impl TreeHash for Signature {
fn hash_tree_root(&self) -> Vec<u8> {
hash(&self.0.as_bytes())
}
}
impl Serialize for Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&ssz_encode(self))
}
}
#[cfg(test)]
mod tests {
use super::super::Keypair;
use super::*;
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let keypair = Keypair::random();
let original = Signature::new(&[42, 42], &keypair.sk);
let bytes = ssz_encode(&original);
let (decoded, _) = Signature::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
#[test]
pub fn test_empty_signature() {
let sig = Signature::empty_signature();
let sig_as_bytes: Vec<u8> = sig.as_raw().as_bytes();
assert_eq!(sig_as_bytes.len(), 97);
for one_byte in sig_as_bytes.iter() {
assert_eq!(*one_byte, 0);
}
}
}

View File

@@ -1,11 +0,0 @@
[package]
name = "boolean-bitfield"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
ssz = { path = "../ssz" }
bit-vec = "0.5.0"
serde = "1.0"
serde_derive = "1.0"

View File

@@ -1,3 +0,0 @@
# Boolean Bitfield
Implements a set of boolean as a tightly-packed vector of bits.

View File

@@ -1,413 +0,0 @@
extern crate bit_vec;
extern crate ssz;
use bit_vec::BitVec;
use serde::ser::{Serialize, Serializer};
use std::cmp;
use std::default;
/// A BooleanBitfield represents a set of booleans compactly stored as a vector of bits.
/// The BooleanBitfield is given a fixed size during construction. Reads outside of the current size return an out-of-bounds error. Writes outside of the current size expand the size of the set.
#[derive(Debug, Clone)]
pub struct BooleanBitfield(BitVec);
/// Error represents some reason a request against a bitfield was not satisfied
#[derive(Debug, PartialEq)]
pub enum Error {
/// OutOfBounds refers to indexing into a bitfield where no bits exist; returns the illegal index and the current size of the bitfield, respectively
OutOfBounds(usize, usize),
}
impl BooleanBitfield {
/// Create a new bitfield.
pub fn new() -> Self {
Default::default()
}
pub fn with_capacity(initial_len: usize) -> Self {
Self::from_elem(initial_len, false)
}
/// Create a new bitfield with the given length `initial_len` and all values set to `bit`.
pub fn from_elem(inital_len: usize, bit: bool) -> Self {
Self {
0: BitVec::from_elem(inital_len, bit),
}
}
/// Create a new bitfield using the supplied `bytes` as input
pub fn from_bytes(bytes: &[u8]) -> Self {
Self {
0: BitVec::from_bytes(bytes),
}
}
/// Read the value of a bit.
///
/// If the index is in bounds, then result is Ok(value) where value is `true` if the bit is 1 and `false` if the bit is 0.
/// If the index is out of bounds, we return an error to that extent.
pub fn get(&self, i: usize) -> Result<bool, Error> {
match self.0.get(i) {
Some(value) => Ok(value),
None => Err(Error::OutOfBounds(i, self.0.len())),
}
}
/// Set the value of a bit.
///
/// If the index is out of bounds, we expand the size of the underlying set to include the new index.
/// Returns the previous value if there was one.
pub fn set(&mut self, i: usize, value: bool) -> Option<bool> {
let previous = match self.get(i) {
Ok(previous) => Some(previous),
Err(Error::OutOfBounds(_, len)) => {
let new_len = i - len + 1;
self.0.grow(new_len, false);
None
}
};
self.0.set(i, value);
previous
}
/// Returns the index of the highest set bit. Some(n) if some bit is set, None otherwise.
pub fn highest_set_bit(&self) -> Option<usize> {
self.0.iter().rposition(|bit| bit)
}
/// Returns the number of bits in this bitfield.
pub fn len(&self) -> usize {
self.0.len()
}
/// Returns true if `self.len() == 0`
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the number of bytes required to represent this bitfield.
pub fn num_bytes(&self) -> usize {
self.to_bytes().len()
}
/// Returns the number of `1` bits in the bitfield
pub fn num_set_bits(&self) -> usize {
self.0.iter().filter(|&bit| bit).count()
}
/// Returns a vector of bytes representing the bitfield
/// Note that this returns the bit layout of the underlying implementation in the `bit-vec` crate.
pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
}
impl default::Default for BooleanBitfield {
/// default provides the "empty" bitfield
/// Note: the empty bitfield is set to the `0` byte.
fn default() -> Self {
Self::from_elem(8, false)
}
}
impl cmp::PartialEq for BooleanBitfield {
/// Determines equality by comparing the `ssz` encoding of the two candidates.
/// This method ensures that the presence of high-order (empty) bits in the highest byte do not exclude equality when they are in fact representing the same information.
fn eq(&self, other: &Self) -> bool {
ssz::ssz_encode(self) == ssz::ssz_encode(other)
}
}
/// Create a new bitfield that is a union of two other bitfields.
///
/// For example `union(0101, 1000) == 1101`
impl std::ops::BitAnd for BooleanBitfield {
type Output = Self;
fn bitand(self, other: Self) -> Self {
let (biggest, smallest) = if self.len() > other.len() {
(&self, &other)
} else {
(&other, &self)
};
let mut new = biggest.clone();
for i in 0..smallest.len() {
if let Ok(true) = smallest.get(i) {
new.set(i, true);
}
}
new
}
}
impl ssz::Encodable for BooleanBitfield {
// ssz_append encodes Self according to the `ssz` spec.
fn ssz_append(&self, s: &mut ssz::SszStream) {
s.append_vec(&self.to_bytes())
}
}
impl ssz::Decodable for BooleanBitfield {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), ssz::DecodeError> {
let len = ssz::decode::decode_length(bytes, index, ssz::LENGTH_BYTES)?;
if (ssz::LENGTH_BYTES + len) > bytes.len() {
return Err(ssz::DecodeError::TooShort);
}
if len == 0 {
Ok((BooleanBitfield::new(), index + ssz::LENGTH_BYTES))
} else {
let bytes = &bytes[(index + 4)..(index + len + 4)];
let count = len * 8;
let mut field = BooleanBitfield::with_capacity(count);
for (byte_index, byte) in bytes.iter().enumerate() {
for i in 0..8 {
let bit = byte & (128 >> i);
if bit != 0 {
field.set(8 * byte_index + i, true);
}
}
}
let index = index + ssz::LENGTH_BYTES + len;
Ok((field, index))
}
}
}
impl Serialize for BooleanBitfield {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&ssz::ssz_encode(self))
}
}
impl ssz::TreeHash for BooleanBitfield {
fn hash_tree_root(&self) -> Vec<u8> {
self.to_bytes().hash_tree_root()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ssz::{ssz_encode, Decodable, SszStream};
#[test]
fn test_new_bitfield() {
let mut field = BooleanBitfield::new();
let original_len = field.len();
for i in 0..100 {
if i < original_len {
assert!(!field.get(i).unwrap());
} else {
assert!(field.get(i).is_err());
}
let previous = field.set(i, true);
if i < original_len {
assert!(!previous.unwrap());
} else {
assert!(previous.is_none());
}
}
}
#[test]
fn test_empty_bitfield() {
let mut field = BooleanBitfield::from_elem(0, false);
let original_len = field.len();
assert_eq!(original_len, 0);
for i in 0..100 {
if i < original_len {
assert!(!field.get(i).unwrap());
} else {
assert!(field.get(i).is_err());
}
let previous = field.set(i, true);
if i < original_len {
assert!(!previous.unwrap());
} else {
assert!(previous.is_none());
}
}
assert_eq!(field.len(), 100);
assert_eq!(field.num_set_bits(), 100);
}
const INPUT: &[u8] = &[0b0000_0010, 0b0000_0010];
#[test]
fn test_get_from_bitfield() {
let field = BooleanBitfield::from_bytes(INPUT);
let unset = field.get(0).unwrap();
assert!(!unset);
let set = field.get(6).unwrap();
assert!(set);
let set = field.get(14).unwrap();
assert!(set);
}
#[test]
fn test_set_for_bitfield() {
let mut field = BooleanBitfield::from_bytes(INPUT);
let previous = field.set(10, true).unwrap();
assert!(!previous);
let previous = field.get(10).unwrap();
assert!(previous);
let previous = field.set(6, false).unwrap();
assert!(previous);
let previous = field.get(6).unwrap();
assert!(!previous);
}
#[test]
fn test_highest_set_bit() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.highest_set_bit().unwrap(), 14);
let field = BooleanBitfield::from_bytes(&[0b0000_0011]);
assert_eq!(field.highest_set_bit().unwrap(), 7);
let field = BooleanBitfield::new();
assert_eq!(field.highest_set_bit(), None);
}
#[test]
fn test_len() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.len(), 16);
let field = BooleanBitfield::new();
assert_eq!(field.len(), 8);
}
#[test]
fn test_num_set_bits() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.num_set_bits(), 2);
let field = BooleanBitfield::new();
assert_eq!(field.num_set_bits(), 0);
}
#[test]
fn test_to_bytes() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.to_bytes(), INPUT);
let field = BooleanBitfield::new();
assert_eq!(field.to_bytes(), vec![0]);
}
#[test]
fn test_out_of_bounds() {
let mut field = BooleanBitfield::from_bytes(INPUT);
let out_of_bounds_index = field.len();
assert!(field.set(out_of_bounds_index, true).is_none());
assert!(field.len() == out_of_bounds_index + 1);
assert!(field.get(out_of_bounds_index).unwrap());
for i in 0..100 {
if i <= out_of_bounds_index {
assert!(field.set(i, true).is_some());
} else {
assert!(field.set(i, true).is_none());
}
}
}
#[test]
fn test_grows_with_false() {
let input_all_set: &[u8] = &[0b1111_1111, 0b1111_1111];
let mut field = BooleanBitfield::from_bytes(input_all_set);
// Define `a` and `b`, where both are out of bounds and `b` is greater than `a`.
let a = field.len();
let b = a + 1;
// Ensure `a` is out-of-bounds for test integrity.
assert!(field.get(a).is_err());
// Set `b` to `true`. Also, for test integrity, ensure it was previously out-of-bounds.
assert!(field.set(b, true).is_none());
// Ensure that `a` wasn't also set to `true` during the grow.
assert_eq!(field.get(a), Ok(false));
assert_eq!(field.get(b), Ok(true));
}
#[test]
fn test_num_bytes() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.num_bytes(), 2);
let field = BooleanBitfield::from_elem(2, true);
assert_eq!(field.num_bytes(), 1);
let field = BooleanBitfield::from_elem(13, true);
assert_eq!(field.num_bytes(), 2);
}
#[test]
fn test_ssz_encode() {
let field = create_test_bitfield();
let mut stream = SszStream::new();
stream.append(&field);
assert_eq!(stream.drain(), vec![0, 0, 0, 2, 225, 192]);
let field = BooleanBitfield::from_elem(18, true);
let mut stream = SszStream::new();
stream.append(&field);
assert_eq!(stream.drain(), vec![0, 0, 0, 3, 255, 255, 192]);
}
fn create_test_bitfield() -> BooleanBitfield {
let count = 2 * 8;
let mut field = BooleanBitfield::with_capacity(count);
let indices = &[0, 1, 2, 7, 8, 9];
for &i in indices {
field.set(i, true);
}
field
}
#[test]
fn test_ssz_decode() {
let encoded = vec![0, 0, 0, 2, 225, 192];
let (field, _): (BooleanBitfield, usize) = ssz::decode_ssz(&encoded, 0).unwrap();
let expected = create_test_bitfield();
assert_eq!(field, expected);
let encoded = vec![0, 0, 0, 3, 255, 255, 3];
let (field, _): (BooleanBitfield, usize) = ssz::decode_ssz(&encoded, 0).unwrap();
let expected = BooleanBitfield::from_bytes(&[255, 255, 3]);
assert_eq!(field, expected);
}
#[test]
fn test_ssz_round_trip() {
let original = BooleanBitfield::from_bytes(&vec![18; 12][..]);
let ssz = ssz_encode(&original);
let (decoded, _) = BooleanBitfield::ssz_decode(&ssz, 0).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn test_bitand() {
let a = BooleanBitfield::from_bytes(&vec![2, 8, 1][..]);
let b = BooleanBitfield::from_bytes(&vec![4, 8, 16][..]);
let c = BooleanBitfield::from_bytes(&vec![6, 8, 17][..]);
assert_eq!(c, a & b);
}
}

View File

@@ -1,8 +0,0 @@
[package]
name = "hashing"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
tiny-keccak = "1.4.2"

View File

@@ -1,28 +0,0 @@
use tiny_keccak::Keccak;
pub fn hash(input: &[u8]) -> Vec<u8> {
let mut keccak = Keccak::new_keccak256();
keccak.update(input);
let mut result = vec![0; 32];
keccak.finalize(result.as_mut_slice());
result
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::From;
#[test]
fn test_hashing() {
let input: Vec<u8> = From::from("hello");
let output = hash(input.as_ref());
let expected = &[
0x1c, 0x8a, 0xff, 0x95, 0x06, 0x85, 0xc2, 0xed, 0x4b, 0xc3, 0x17, 0x4f, 0x34, 0x72,
0x28, 0x7b, 0x56, 0xd9, 0x51, 0x7b, 0x9c, 0x94, 0x81, 0x27, 0x31, 0x9a, 0x09, 0xa7,
0xa3, 0x6d, 0xea, 0xc8,
];
assert_eq!(expected, output.as_slice());
}
}

View File

@@ -1,7 +0,0 @@
[package]
name = "honey-badger-split"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]

View File

@@ -1,85 +0,0 @@
/// A function for splitting a list into N pieces.
///
/// We have titled it the "honey badger split" because of its robustness. It don't care.
/// Iterator for the honey_badger_split function
pub struct Split<'a, T: 'a> {
n: usize,
current_pos: usize,
list: &'a [T],
list_length: usize,
}
impl<'a, T> Iterator for Split<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
self.current_pos += 1;
if self.current_pos <= self.n {
match self.list.get(
self.list_length * (self.current_pos - 1) / self.n
..self.list_length * self.current_pos / self.n,
) {
Some(v) => Some(v),
None => unreachable!(),
}
} else {
None
}
}
}
/// Splits a slice into chunks of size n. All postive n values are applicable,
/// hence the honey_badger prefix.
///
/// Returns an iterator over the original list.
pub trait SplitExt<T> {
fn honey_badger_split(&self, n: usize) -> Split<T>;
}
impl<T> SplitExt<T> for [T] {
fn honey_badger_split(&self, n: usize) -> Split<T> {
Split {
n,
current_pos: 0,
list: &self,
list_length: self.len(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_honey_badger_split() {
/*
* These test cases are generated from the eth2.0 spec `split()`
* function at commit cbd254a.
*/
let input: Vec<usize> = vec![0, 1, 2, 3];
let output: Vec<&[usize]> = input.honey_badger_split(2).collect();
assert_eq!(output, vec![&[0, 1], &[2, 3]]);
let input: Vec<usize> = vec![0, 1, 2, 3];
let output: Vec<&[usize]> = input.honey_badger_split(6).collect();
let expected: Vec<&[usize]> = vec![&[], &[0], &[1], &[], &[2], &[3]];
assert_eq!(output, expected);
let input: Vec<usize> = vec![0, 1, 2, 3];
let output: Vec<&[usize]> = input.honey_badger_split(10).collect();
let expected: Vec<&[usize]> = vec![&[], &[], &[0], &[], &[1], &[], &[], &[2], &[], &[3]];
assert_eq!(output, expected);
let input: Vec<usize> = vec![0];
let output: Vec<&[usize]> = input.honey_badger_split(5).collect();
let expected: Vec<&[usize]> = vec![&[], &[], &[], &[], &[0]];
assert_eq!(output, expected);
let input: Vec<usize> = vec![0, 1, 2];
let output: Vec<&[usize]> = input.honey_badger_split(2).collect();
let expected: Vec<&[usize]> = vec![&[0], &[1, 2]];
assert_eq!(output, expected);
}
}

View File

@@ -1,8 +0,0 @@
[package]
name = "slot_clock"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
types = { path = "../../types" }

View File

@@ -1,12 +0,0 @@
mod system_time_slot_clock;
mod testing_slot_clock;
pub use crate::system_time_slot_clock::{Error as SystemTimeSlotClockError, SystemTimeSlotClock};
pub use crate::testing_slot_clock::{Error as TestingSlotClockError, TestingSlotClock};
pub use types::Slot;
pub trait SlotClock: Send + Sync {
type Error;
fn present_slot(&self) -> Result<Option<Slot>, Self::Error>;
}

View File

@@ -1,139 +0,0 @@
use super::SlotClock;
use std::time::{Duration, SystemTime};
use types::Slot;
pub use std::time::SystemTimeError;
#[derive(Debug, PartialEq)]
pub enum Error {
SlotDurationIsZero,
SystemTimeError(String),
}
/// Determines the present slot based upon the present system time.
#[derive(Clone)]
pub struct SystemTimeSlotClock {
genesis_seconds: u64,
slot_duration_seconds: u64,
}
impl SystemTimeSlotClock {
/// Create a new `SystemTimeSlotClock`.
///
/// Returns an Error if `slot_duration_seconds == 0`.
pub fn new(
genesis_seconds: u64,
slot_duration_seconds: u64,
) -> Result<SystemTimeSlotClock, Error> {
if slot_duration_seconds == 0 {
Err(Error::SlotDurationIsZero)
} else {
Ok(Self {
genesis_seconds,
slot_duration_seconds,
})
}
}
}
impl SlotClock for SystemTimeSlotClock {
type Error = Error;
fn present_slot(&self) -> Result<Option<Slot>, Error> {
let syslot_time = SystemTime::now();
let duration_since_epoch = syslot_time.duration_since(SystemTime::UNIX_EPOCH)?;
let duration_since_genesis =
duration_since_epoch.checked_sub(Duration::from_secs(self.genesis_seconds));
match duration_since_genesis {
None => Ok(None),
Some(d) => Ok(slot_from_duration(self.slot_duration_seconds, d)),
}
}
}
impl From<SystemTimeError> for Error {
fn from(e: SystemTimeError) -> Error {
Error::SystemTimeError(format!("{:?}", e))
}
}
fn slot_from_duration(slot_duration_seconds: u64, duration: Duration) -> Option<Slot> {
Some(Slot::new(
duration.as_secs().checked_div(slot_duration_seconds)?,
))
}
#[cfg(test)]
mod tests {
use super::*;
/*
* Note: these tests are using actual system times and could fail if they are executed on a
* very slow machine.
*/
#[test]
fn test_slot_now() {
let slot_time = 100;
let now = SystemTime::now();
let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
let genesis = since_epoch.as_secs() - slot_time * 89;
let clock = SystemTimeSlotClock {
genesis_seconds: genesis,
slot_duration_seconds: slot_time,
};
assert_eq!(clock.present_slot().unwrap(), Some(Slot::new(89)));
let clock = SystemTimeSlotClock {
genesis_seconds: since_epoch.as_secs(),
slot_duration_seconds: slot_time,
};
assert_eq!(clock.present_slot().unwrap(), Some(Slot::new(0)));
let clock = SystemTimeSlotClock {
genesis_seconds: since_epoch.as_secs() - slot_time * 42 - 5,
slot_duration_seconds: slot_time,
};
assert_eq!(clock.present_slot().unwrap(), Some(Slot::new(42)));
}
#[test]
fn test_slot_from_duration() {
let slot_time = 100;
assert_eq!(
slot_from_duration(slot_time, Duration::from_secs(0)),
Some(Slot::new(0))
);
assert_eq!(
slot_from_duration(slot_time, Duration::from_secs(10)),
Some(Slot::new(0))
);
assert_eq!(
slot_from_duration(slot_time, Duration::from_secs(100)),
Some(Slot::new(1))
);
assert_eq!(
slot_from_duration(slot_time, Duration::from_secs(101)),
Some(Slot::new(1))
);
assert_eq!(
slot_from_duration(slot_time, Duration::from_secs(1000)),
Some(Slot::new(10))
);
}
#[test]
fn test_slot_from_duration_slot_time_zero() {
let slot_time = 0;
assert_eq!(slot_from_duration(slot_time, Duration::from_secs(0)), None);
assert_eq!(slot_from_duration(slot_time, Duration::from_secs(10)), None);
assert_eq!(
slot_from_duration(slot_time, Duration::from_secs(1000)),
None
);
}
}

View File

@@ -1,48 +0,0 @@
use super::SlotClock;
use std::sync::RwLock;
use types::Slot;
#[derive(Debug, PartialEq)]
pub enum Error {}
/// Determines the present slot based upon the present system time.
pub struct TestingSlotClock {
slot: RwLock<u64>,
}
impl TestingSlotClock {
/// Create a new `TestingSlotClock`.
///
/// Returns an Error if `slot_duration_seconds == 0`.
pub fn new(slot: u64) -> TestingSlotClock {
TestingSlotClock {
slot: RwLock::new(slot),
}
}
pub fn set_slot(&self, slot: u64) {
*self.slot.write().expect("TestingSlotClock poisoned.") = slot;
}
}
impl SlotClock for TestingSlotClock {
type Error = Error;
fn present_slot(&self) -> Result<Option<Slot>, Error> {
let slot = *self.slot.read().expect("TestingSlotClock poisoned.");
Ok(Some(Slot::new(slot)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_slot_now() {
let clock = TestingSlotClock::new(10);
assert_eq!(clock.present_slot(), Ok(Some(Slot::new(10))));
clock.set_slot(123);
assert_eq!(clock.present_slot(), Ok(Some(Slot::new(123))));
}
}

View File

@@ -1,10 +0,0 @@
[package]
name = "ssz"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
bytes = "0.4.9"
ethereum-types = "0.4.0"
hashing = { path = "../hashing" }

View File

@@ -1,543 +0,0 @@
# simpleserialize (ssz) [WIP]
This is currently a ***Work In Progress*** crate.
SimpleSerialize is a serialization protocol described by Vitalik Buterin. The
method is tentatively intended for use in the Ethereum Beacon Chain as
described in the [Ethereum 2.1 Spec](https://notes.ethereum.org/s/Syj3QZSxm).
The Beacon Chain specification is the core, canonical specification which we
are following.
The current reference implementation has been described in the [Beacon Chain
Repository](https://github.com/ethereum/beacon_chain/blob/master/ssz/ssz.py).
*Please Note: This implementation is presently a placeholder until the final
spec is decided.*\
*Do not rely upon it for reference.*
## Table of Contents
* [SimpleSerialize Overview](#simpleserialize-overview)
+ [Serialize/Encode](#serializeencode)
- [int or uint: 8/16/24/32/64/256](#int-or-uint-816243264256)
- [Address](#address)
- [Hash32](#hash32)
- [Bytes](#bytes)
- [List](#list)
+ [Deserialize/Decode](#deserializedecode)
- [Int or Uint: 8/16/24/32/64/256](#int-or-uint-816243264256)
- [Address](#address-1)
- [Hash32](#hash32-1)
- [Bytes](#bytes-1)
- [List](#list-1)
* [Technical Overview](#technical-overview)
* [Building](#building)
+ [Installing Rust](#installing-rust)
* [Dependencies](#dependencies)
+ [bytes v0.4.9](#bytes-v049)
+ [ethereum-types](#ethereum-types)
* [Interface](#interface)
+ [Encodable](#encodable)
+ [Decodable](#decodable)
+ [SszStream](#sszstream)
- [new()](#new)
- [append(&mut self, value: &E) -> &mut Self](#appendmut-self-value-e---mut-self)
- [append_encoded_val(&mut self, vec: &Vec)](#append_encoded_valmut-self-vec-vec)
- [append_vec(&mut self, vec: &Vec)](#append_vecmut-self-vec-vec)
- [drain(self) -> Vec](#drainself---vec)
+ [decode_ssz(ssz_bytes: &(u8), index: usize) -> Result](#decode_sszssz_bytes-u8-index-usize---resultt-usize-decodeerror)
+ [decode_ssz_list(ssz_bytes: &(u8), index: usize) -> Result, usize), DecodeError>](#decode_ssz_listssz_bytes-u8-index-usize---resultvec-usize-decodeerror)
+ [decode_length(bytes: &(u8), index: usize, length_bytes: usize) -> Result](#decode_lengthbytes-u8-index-usize-length_bytes-usize---resultusize-decodeerror)
* [Usage](#usage)
+ [Serializing/Encoding](#serializingencoding)
- [Rust](#rust)
* [Deserializing/Decoding](#deserializingdecoding)
- [Rust](#rust-1)
---
## SimpleSerialize Overview
The ``simpleserialize`` method for serialization follows simple byte conversion,
making it effective and efficient for encoding and decoding.
The decoding requires knowledge of the data **type** and the order of the
serialization.
Syntax:
| Shorthand | Meaning |
|:-------------|:----------------------------------------------------|
| `big` | ``big endian`` |
| `to_bytes` | convert to bytes. Params: ``(size, byte order)`` |
| `from_bytes` | convert from bytes. Params: ``(bytes, byte order)`` |
| `value` | the value to serialize |
| `rawbytes` | raw encoded/serialized bytes |
| `len(value)` | get the length of the value. (number of bytes etc) |
### Serialize/Encode
#### int or uint: 8/16/24/32/64/256
Convert directly to bytes the size of the int. (e.g. ``int16 = 2 bytes``)
All integers are serialized as **big endian**.
| Check to perform | Code |
|:-----------------------|:------------------------|
| Int size is not 0 | ``int_size > 0`` |
| Size is a byte integer | ``int_size % 8 == 0`` |
| Value is less than max | ``2**int_size > value`` |
```python
buffer_size = int_size / 8
return value.to_bytes(buffer_size, 'big')
```
#### Address
The address should already come as a hash/byte format. Ensure that length is
**20**.
| Check to perform | Code |
|:-----------------------|:---------------------|
| Length is correct (20) | ``len(value) == 20`` |
```python
assert( len(value) == 20 )
return value
```
#### Hash32
The hash32 should already be a 32 byte length serialized data format. The safety
check ensures the 32 byte length is satisfied.
| Check to perform | Code |
|:-----------------------|:---------------------|
| Length is correct (32) | ``len(value) == 32`` |
```python
assert( len(value) == 32 )
return value
```
#### Bytes
For general `byte` type:
1. Get the length/number of bytes; Encode into a 4 byte integer.
2. Append the value to the length and return: ``[ length_bytes ] + [
value_bytes ]``
```python
byte_length = (len(value)).to_bytes(4, 'big')
return byte_length + value
```
#### List
For lists of values, get the length of the list and then serialize the value
of each item in the list:
1. For each item in list:
1. serialize.
2. append to string.
2. Get size of serialized string. Encode into a 4 byte integer.
```python
serialized_list_string = ''
for item in value:
serialized_list_string += serialize(item)
serialized_len = len(serialized_list_string)
return serialized_len + serialized_list_string
```
### Deserialize/Decode
The decoding requires knowledge of the type of the item to be decoded. When
performing decoding on an entire serialized string, it also requires knowledge
of what order the objects have been serialized in.
Note: Each return will provide ``deserialized_object, new_index`` keeping track
of the new index.
At each step, the following checks should be made:
| Check Type | Check |
|:-------------------------|:----------------------------------------------------------|
| Ensure sufficient length | ``length(rawbytes) > current_index + deserialize_length`` |
#### Int or Uint: 8/16/24/32/64/256
Convert directly from bytes into integer utilising the number of bytes the same
size as the integer length. (e.g. ``int16 == 2 bytes``)
All integers are interpreted as **big endian**.
```python
byte_length = int_size / 8
new_index = current_index + int_size
return int.from_bytes(rawbytes[current_index:current_index+int_size], 'big'), new_index
```
#### Address
Return the 20 bytes.
```python
new_index = current_index + 20
return rawbytes[current_index:current_index+20], new_index
```
#### Hash32
Return the 32 bytes.
```python
new_index = current_index + 32
return rawbytes[current_index:current_index+32], new_index
```
#### Bytes
Get the length of the bytes, return the bytes.
```python
bytes_length = int.from_bytes(rawbytes[current_index:current_index+4], 'big')
new_index = current_index + 4 + bytes_lenth
return rawbytes[current_index+4:current_index+4+bytes_length], new_index
```
#### List
Deserailize each object in the list.
1. Get the length of the serialized list.
2. Loop through deseralizing each item in the list until you reach the
entire length of the list.
| Check type | code |
|:------------------------------------|:--------------------------------------|
| rawbytes has enough left for length | ``len(rawbytes) > current_index + 4`` |
```python
total_length = int.from_bytes(rawbytes[current_index:current_index+4], 'big')
new_index = current_index + 4 + total_length
item_index = current_index + 4
deserialized_list = []
while item_index < new_index:
object, item_index = deserialize(rawbytes, item_index, item_type)
deserialized_list.append(object)
return deserialized_list, new_index
```
## Technical Overview
The SimpleSerialize is a simple method for serializing objects for use in the
Ethereum beacon chain proposed by Vitalik Buterin. There are currently two
implementations denoting the functionality, the [Reference
Implementation](https://github.com/ethereum/beacon_chain/blob/master/ssz/ssz.py)
and the [Module](https://github.com/ethereum/research/tree/master/py_ssz) in
Ethereum research. It is being developed as a crate for the [**Rust programming
language**](https://www.rust-lang.org).
The crate will provide the functionality to serialize several types in
accordance with the spec and provide a serialized stream of bytes.
## Building
ssz currently builds on **rust v1.27.1**
### Installing Rust
The [**Rustup**](https://rustup.rs/) tool provides functionality to easily
manage rust on your local instance. It is a recommended method for installing
rust.
Installing on Linux or OSX:
```bash
curl https://sh.rustup.rs -sSf | sh
```
Installing on Windows:
* 32 Bit: [ https://win.rustup.rs/i686 ](https://win.rustup.rs/i686)
* 64 Bit: [ https://win.rustup.rs/x86_64 ](https://win.rustup.rs/x86_64)
## Dependencies
All dependencies are listed in the ``Cargo.toml`` file.
To build and install all related dependencies:
```bash
cargo build
```
### bytes v0.4.9
The `bytes` crate provides effective Byte Buffer implementations and
interfaces.
Documentation: [ https://docs.rs/bytes/0.4.9/bytes/ ](https://docs.rs/bytes/0.4.9/bytes/)
### ethereum-types
The `ethereum-types` provide primitives for types that are commonly used in the
ethereum protocol. This crate is provided by [Parity](https://www.parity.io/).
Github: [ https://github.com/paritytech/primitives ](https://github.com/paritytech/primitives)
---
## Interface
### Encodable
A type is **Encodable** if it has a valid ``ssz_append`` function. This is
used to ensure that the object/type can be serialized.
```rust
pub trait Encodable {
fn ssz_append(&self, s: &mut SszStream);
}
```
### Decodable
A type is **Decodable** if it has a valid ``ssz_decode`` function. This is
used to ensure the object is deserializable.
```rust
pub trait Decodable: Sized {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError>;
}
```
### SszStream
The main implementation is the `SszStream` struct. The struct contains a
buffer of bytes, a Vector of `uint8`.
#### new()
Create a new, empty instance of the SszStream.
**Example**
```rust
let mut ssz = SszStream::new()
```
#### append<E>(&mut self, value: &E) -> &mut Self
Appends a value that can be encoded into the stream.
| Parameter | Description |
|:---------:|:-----------------------------------------|
| ``value`` | Encodable value to append to the stream. |
**Example**
```rust
ssz.append(&x)
```
#### append_encoded_val(&mut self, vec: &Vec<u8>)
Appends some ssz encoded bytes to the stream.
| Parameter | Description |
|:---------:|:----------------------------------|
| ``vec`` | A vector of serialized ssz bytes. |
**Example**
```rust
let mut a = [0, 1];
ssz.append_encoded_val(&a.to_vec());
```
#### append_vec<E>(&mut self, vec: &Vec<E>)
Appends some vector (list) of encodable values to the stream.
| Parameter | Description |
|:---------:|:----------------------------------------------|
| ``vec`` | Vector of Encodable objects to be serialized. |
**Example**
```rust
ssz.append_vec(attestations);
```
#### drain(self) -> Vec<u8>
Consumes the ssz stream and returns the buffer of bytes.
**Example**
```rust
ssz.drain()
```
### decode_ssz<T>(ssz_bytes: &[u8], index: usize) -> Result<(T, usize), DecodeError>
Decodes a single ssz serialized value of type `T`. Note: `T` must be decodable.
| Parameter | Description |
|:-------------:|:------------------------------------|
| ``ssz_bytes`` | Serialized list of bytes. |
| ``index`` | Starting index to deserialize from. |
**Returns**
| Return Value | Description |
|:-------------------:|:----------------------------------------------|
| ``Tuple(T, usize)`` | Returns the tuple of the type and next index. |
| ``DecodeError`` | Error if the decoding could not be performed. |
**Example**
```rust
let res: Result<(u16, usize), DecodeError> = decode_ssz(&encoded_ssz, 0);
```
### decode_ssz_list<T>(ssz_bytes: &[u8], index: usize) -> Result<(Vec<T>, usize), DecodeError>
Decodes a list of serialized values into a vector.
| Parameter | Description |
|:-------------:|:------------------------------------|
| ``ssz_bytes`` | Serialized list of bytes. |
| ``index`` | Starting index to deserialize from. |
**Returns**
| Return Value | Description |
|:------------------------:|:----------------------------------------------|
| ``Tuple(Vec<T>, usize)`` | Returns the tuple of the type and next index. |
| ``DecodeError`` | Error if the decoding could not be performed. |
**Example**
```rust
let decoded: Result<(Vec<usize>, usize), DecodeError> = decode_ssz_list( &encoded_ssz, 0);
```
### decode_length(bytes: &[u8], index: usize, length_bytes: usize) -> Result<usize, DecodeError>
Deserializes the "length" value in the serialized bytes from the index. The
length of bytes is given (usually 4 stated in the reference implementation) and
is often the value appended to the list infront of the actual serialized
object.
| Parameter | Description |
|:----------------:|:-------------------------------------------|
| ``bytes`` | Serialized list of bytes. |
| ``index`` | Starting index to deserialize from. |
| ``length_bytes`` | Number of bytes to deserialize into usize. |
**Returns**
| Return Value | Description |
|:---------------:|:-----------------------------------------------------------|
| ``usize`` | The length of the serialized object following this length. |
| ``DecodeError`` | Error if the decoding could not be performed. |
**Example**
```rust
let length_of_serialized: Result<usize, DecodeError> = decode_length(&encoded, 0, 4);
```
---
## Usage
### Serializing/Encoding
#### Rust
Create the `simpleserialize` stream that will produce the serialized objects.
```rust
let mut ssz = SszStream::new();
```
Encode the values that you need by using the ``append(..)`` method on the `SszStream`.
The **append** function is how the value gets serialized.
```rust
let x: u64 = 1 << 32;
ssz.append(&x);
```
To get the serialized byte vector use ``drain()`` on the `SszStream`.
```rust
ssz.drain()
```
**Example**
```rust
// 1 << 32 = 4294967296;
// As bytes it should equal: [0,0,0,1,0,0,0]
let x: u64 = 1 << 32;
// Create the new ssz stream
let mut ssz = SszStream::new();
// Serialize x
ssz.append(&x);
// Check that it is correct.
assert_eq!(ssz.drain(), vec![0,0,0,1,0,0,0]);
```
## Deserializing/Decoding
#### Rust
From the `simpleserialize` bytes, we are converting to the object.
```rust
let ssz = vec![0, 0, 8, 255, 255, 255, 255, 255, 255, 255, 255];
// Returns the result and the next index to decode.
let (result, index): (u64, usize) = decode_ssz(&ssz, 3).unwrap();
// Check for correctness
// 2**64-1 = 18446744073709551615
assert_eq!(result, 18446744073709551615);
// Index = 3 (initial index) + 8 (8 byte int) = 11
assert_eq!(index, 11);
```
Decoding a list of items:
```rust
// Encoded/Serialized list with junk numbers at the front
let serialized_list = vec![ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0,
0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15];
// Returns the result (Vector of usize) and the index of the next
let decoded: (Vec<usize>, usize) = decode_ssz_list(&serialized_list, 10).unwrap();
// Check for correctness
assert_eq!(decoded.0, vec![15,15,15,15]);
assert_eq!(decoded.1, 46);
```

View File

@@ -1,193 +0,0 @@
use super::LENGTH_BYTES;
#[derive(Debug, PartialEq)]
pub enum DecodeError {
TooShort,
TooLong,
Invalid,
}
pub trait Decodable: Sized {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError>;
}
/// Decode the given bytes for the given type
///
/// The single ssz encoded value will be decoded as the given type at the
/// given index.
pub fn decode_ssz<T>(ssz_bytes: &[u8], index: usize) -> Result<(T, usize), DecodeError>
where
T: Decodable,
{
if index >= ssz_bytes.len() {
return Err(DecodeError::TooShort);
}
T::ssz_decode(ssz_bytes, index)
}
/// Decode a vector (list) of encoded bytes.
///
/// Each element in the list will be decoded and placed into the vector.
pub fn decode_ssz_list<T>(ssz_bytes: &[u8], index: usize) -> Result<(Vec<T>, usize), DecodeError>
where
T: Decodable,
{
if index + LENGTH_BYTES > ssz_bytes.len() {
return Err(DecodeError::TooShort);
};
// get the length
let serialized_length = match decode_length(ssz_bytes, index, LENGTH_BYTES) {
Err(v) => return Err(v),
Ok(v) => v,
};
let final_len: usize = index + LENGTH_BYTES + serialized_length;
if final_len > ssz_bytes.len() {
return Err(DecodeError::TooShort);
};
let mut tmp_index = index + LENGTH_BYTES;
let mut res_vec: Vec<T> = Vec::new();
while tmp_index < final_len {
match T::ssz_decode(ssz_bytes, tmp_index) {
Err(v) => return Err(v),
Ok(v) => {
tmp_index = v.1;
res_vec.push(v.0);
}
};
}
Ok((res_vec, final_len))
}
/// Given some number of bytes, interpret the first four
/// bytes as a 32-bit big-endian integer and return the
/// result.
pub fn decode_length(
bytes: &[u8],
index: usize,
length_bytes: usize,
) -> Result<usize, DecodeError> {
if bytes.len() < index + length_bytes {
return Err(DecodeError::TooShort);
};
let mut len: usize = 0;
for (i, byte) in bytes
.iter()
.enumerate()
.take(index + length_bytes)
.skip(index)
{
let offset = (index + length_bytes - i - 1) * 8;
len |= (*byte as usize) << offset;
}
Ok(len)
}
#[cfg(test)]
mod tests {
use super::super::encode::encode_length;
use super::*;
#[test]
fn test_ssz_decode_length() {
let decoded = decode_length(&vec![0, 0, 0, 1], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 1);
let decoded = decode_length(&vec![0, 0, 1, 0], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 256);
let decoded = decode_length(&vec![0, 0, 1, 255], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 511);
let decoded = decode_length(&vec![255, 255, 255, 255], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 4294967295);
}
#[test]
fn test_encode_decode_length() {
let params: Vec<usize> = vec![
0,
1,
2,
3,
7,
8,
16,
2 ^ 8,
2 ^ 8 + 1,
2 ^ 16,
2 ^ 16 + 1,
2 ^ 24,
2 ^ 24 + 1,
2 ^ 32,
];
for i in params {
let decoded = decode_length(&encode_length(i, LENGTH_BYTES), 0, LENGTH_BYTES).unwrap();
assert_eq!(i, decoded);
}
}
#[test]
fn test_decode_ssz_list() {
// u16
let v: Vec<u16> = vec![10, 10, 10, 10];
let decoded: (Vec<u16>, usize) =
decode_ssz_list(&vec![0, 0, 0, 8, 0, 10, 0, 10, 0, 10, 0, 10], 0).unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, 12);
// u32
let v: Vec<u32> = vec![10, 10, 10, 10];
let decoded: (Vec<u32>, usize) = decode_ssz_list(
&vec![
0, 0, 0, 16, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10,
],
0,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, 20);
// u64
let v: Vec<u64> = vec![10, 10, 10, 10];
let decoded: (Vec<u64>, usize) = decode_ssz_list(
&vec![
0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0, 10,
],
0,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, 36);
// Check that it can accept index
let v: Vec<usize> = vec![15, 15, 15, 15];
let decoded: (Vec<usize>, usize) = decode_ssz_list(
&vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0,
0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15,
],
10,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, 46);
// Check that length > bytes throws error
let decoded: Result<(Vec<usize>, usize), DecodeError> =
decode_ssz_list(&vec![0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 15], 0);
assert_eq!(decoded, Err(DecodeError::TooShort));
// Check that incorrect index throws error
let decoded: Result<(Vec<usize>, usize), DecodeError> =
decode_ssz_list(&vec![0, 0, 0, 0, 0, 0, 0, 15], 16);
assert_eq!(decoded, Err(DecodeError::TooShort));
}
}

View File

@@ -1,124 +0,0 @@
use super::LENGTH_BYTES;
pub trait Encodable {
fn ssz_append(&self, s: &mut SszStream);
}
/// Provides a buffer for appending ssz-encodable values.
///
/// Use the `append()` fn to add a value to a list, then use
/// the `drain()` method to consume the struct and return the
/// ssz encoded bytes.
#[derive(Default)]
pub struct SszStream {
buffer: Vec<u8>,
}
impl SszStream {
/// Create a new, empty stream for writing ssz values.
pub fn new() -> Self {
SszStream { buffer: Vec::new() }
}
/// Append some ssz encodable value to the stream.
pub fn append<E>(&mut self, value: &E) -> &mut Self
where
E: Encodable,
{
value.ssz_append(self);
self
}
/// Append some ssz encoded bytes to the stream.
///
/// The length of the supplied bytes will be concatenated
/// to the stream before the supplied bytes.
pub fn append_encoded_val(&mut self, vec: &[u8]) {
self.buffer
.extend_from_slice(&encode_length(vec.len(), LENGTH_BYTES));
self.buffer.extend_from_slice(&vec);
}
/// Append some ssz encoded bytes to the stream without calculating length
///
/// The raw bytes will be concatenated to the stream.
pub fn append_encoded_raw(&mut self, vec: &[u8]) {
self.buffer.extend_from_slice(&vec);
}
/// Append some vector (list) of encodable values to the stream.
///
/// The length of the list will be concatenated to the stream, then
/// each item in the vector will be encoded and concatenated.
pub fn append_vec<E>(&mut self, vec: &[E])
where
E: Encodable,
{
let mut list_stream = SszStream::new();
for item in vec {
item.ssz_append(&mut list_stream);
}
self.append_encoded_val(&list_stream.drain());
}
/// Consume the stream and return the underlying bytes.
pub fn drain(self) -> Vec<u8> {
self.buffer
}
}
/// Encode some length into a ssz size prefix.
///
/// The ssz size prefix is 4 bytes, which is treated as a continuious
/// 32bit big-endian integer.
pub fn encode_length(len: usize, length_bytes: usize) -> Vec<u8> {
assert!(length_bytes > 0); // For sanity
assert!((len as usize) < 2usize.pow(length_bytes as u32 * 8));
let mut header: Vec<u8> = vec![0; length_bytes];
for (i, header_byte) in header.iter_mut().enumerate() {
let offset = (length_bytes - i - 1) * 8;
*header_byte = ((len >> offset) & 0xff) as u8;
}
header
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn test_encode_length_0_bytes_panic() {
encode_length(0, 0);
}
#[test]
fn test_encode_length_4_bytes() {
assert_eq!(encode_length(0, LENGTH_BYTES), vec![0; 4]);
assert_eq!(encode_length(1, LENGTH_BYTES), vec![0, 0, 0, 1]);
assert_eq!(encode_length(255, LENGTH_BYTES), vec![0, 0, 0, 255]);
assert_eq!(encode_length(256, LENGTH_BYTES), vec![0, 0, 1, 0]);
assert_eq!(
encode_length(4294967295, LENGTH_BYTES), // 2^(3*8) - 1
vec![255, 255, 255, 255]
);
}
#[test]
#[should_panic]
fn test_encode_length_4_bytes_panic() {
encode_length(4294967296, LENGTH_BYTES); // 2^(3*8)
}
#[test]
fn test_encode_list() {
let test_vec: Vec<u16> = vec![256; 12];
let mut stream = SszStream::new();
stream.append_vec(&test_vec);
let ssz = stream.drain();
assert_eq!(ssz.len(), 4 + (12 * 2));
assert_eq!(ssz[0..4], *vec![0, 0, 0, 24]);
assert_eq!(ssz[4..6], *vec![1, 0]);
}
}

View File

@@ -1,218 +0,0 @@
use super::decode::decode_ssz_list;
use super::ethereum_types::{Address, H256};
use super::{Decodable, DecodeError};
macro_rules! impl_decodable_for_uint {
($type: ident, $bit_size: expr) => {
impl Decodable for $type {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
assert!((0 < $bit_size) & ($bit_size <= 64) & ($bit_size % 8 == 0));
let max_bytes = $bit_size / 8;
if bytes.len() >= (index + max_bytes) {
let end_bytes = index + max_bytes;
let mut result: $type = 0;
for (i, byte) in bytes.iter().enumerate().take(end_bytes).skip(index) {
let offset = (end_bytes - i - 1) * 8;
result |= ($type::from(*byte)) << offset;
}
Ok((result, end_bytes))
} else {
Err(DecodeError::TooShort)
}
}
}
};
}
impl_decodable_for_uint!(u16, 16);
impl_decodable_for_uint!(u32, 32);
impl_decodable_for_uint!(u64, 64);
impl_decodable_for_uint!(usize, 64);
impl Decodable for u8 {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if index >= bytes.len() {
Err(DecodeError::TooShort)
} else {
Ok((bytes[index], index + 1))
}
}
}
impl Decodable for H256 {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() < 32 || bytes.len() - 32 < index {
Err(DecodeError::TooShort)
} else {
Ok((H256::from(&bytes[index..(index + 32)]), index + 32))
}
}
}
impl Decodable for Address {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() < 20 || bytes.len() - 20 < index {
Err(DecodeError::TooShort)
} else {
Ok((Address::from(&bytes[index..(index + 20)]), index + 20))
}
}
}
impl<T> Decodable for Vec<T>
where
T: Decodable,
{
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
decode_ssz_list(bytes, index)
}
}
#[cfg(test)]
mod tests {
use super::super::{decode_ssz, DecodeError};
use super::*;
#[test]
fn test_ssz_decode_h256() {
/*
* Input is exact length
*/
let input = vec![42_u8; 32];
let (decoded, i) = H256::ssz_decode(&input, 0).unwrap();
assert_eq!(decoded.to_vec(), input);
assert_eq!(i, 32);
/*
* Input is too long
*/
let mut input = vec![42_u8; 32];
input.push(12);
let (decoded, i) = H256::ssz_decode(&input, 0).unwrap();
assert_eq!(decoded.to_vec()[..], input[0..32]);
assert_eq!(i, 32);
/*
* Input is too short
*/
let input = vec![42_u8; 31];
let res = H256::ssz_decode(&input, 0);
assert_eq!(res, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_u16() {
let ssz = vec![0, 0];
let (result, index): (u16, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(result, 0);
assert_eq!(index, 2);
let ssz = vec![0, 16];
let (result, index): (u16, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(result, 16);
assert_eq!(index, 2);
let ssz = vec![1, 0];
let (result, index): (u16, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(result, 256);
assert_eq!(index, 2);
let ssz = vec![255, 255];
let (result, index): (u16, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 2);
assert_eq!(result, 65535);
let ssz = vec![1];
let result: Result<(u16, usize), DecodeError> = decode_ssz(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_u32() {
let ssz = vec![0, 0, 0, 0];
let (result, index): (u32, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(result, 0);
assert_eq!(index, 4);
let ssz = vec![0, 0, 1, 0];
let (result, index): (u32, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 4);
assert_eq!(result, 256);
let ssz = vec![255, 255, 255, 0, 0, 1, 0];
let (result, index): (u32, usize) = decode_ssz(&ssz, 3).unwrap();
assert_eq!(index, 7);
assert_eq!(result, 256);
let ssz = vec![0, 200, 1, 0];
let (result, index): (u32, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 4);
assert_eq!(result, 13107456);
let ssz = vec![255, 255, 255, 255];
let (result, index): (u32, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 4);
assert_eq!(result, 4294967295);
let ssz = vec![0, 0, 1];
let result: Result<(u32, usize), DecodeError> = decode_ssz(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_u64() {
let ssz = vec![0, 0, 0, 0, 0, 0, 0, 0];
let (result, index): (u64, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 0);
let ssz = vec![255, 255, 255, 255, 255, 255, 255, 255];
let (result, index): (u64, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 18446744073709551615);
let ssz = vec![0, 0, 8, 255, 0, 0, 0, 0, 0, 0, 0];
let (result, index): (u64, usize) = decode_ssz(&ssz, 3).unwrap();
assert_eq!(index, 11);
assert_eq!(result, 18374686479671623680);
let ssz = vec![0, 0, 0, 0, 0, 0, 0];
let result: Result<(u64, usize), DecodeError> = decode_ssz(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_usize() {
let ssz = vec![0, 0, 0, 0, 0, 0, 0, 0];
let (result, index): (usize, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 0);
let ssz = vec![0, 0, 8, 255, 255, 255, 255, 255, 255, 255, 255];
let (result, index): (usize, usize) = decode_ssz(&ssz, 3).unwrap();
assert_eq!(index, 11);
assert_eq!(result, 18446744073709551615);
let ssz = vec![255, 255, 255, 255, 255, 255, 255, 255, 255];
let (result, index): (usize, usize) = decode_ssz(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 18446744073709551615);
let ssz = vec![0, 0, 0, 0, 0, 0, 1];
let result: Result<(usize, usize), DecodeError> = decode_ssz(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_decode_ssz_bounds() {
let err: Result<(u16, usize), DecodeError> = decode_ssz(&vec![1], 2);
assert_eq!(err, Err(DecodeError::TooShort));
let err: Result<(u16, usize), DecodeError> = decode_ssz(&vec![0, 0, 0, 0], 3);
assert_eq!(err, Err(DecodeError::TooShort));
let result: u16 = decode_ssz(&vec![0, 0, 0, 0, 1], 3).unwrap().0;
assert_eq!(result, 1);
}
}

View File

@@ -1,209 +0,0 @@
extern crate bytes;
use self::bytes::{BufMut, BytesMut};
use super::ethereum_types::{Address, H256};
use super::{Encodable, SszStream};
/*
* Note: there is a "to_bytes" function for integers
* in Rust nightly. When it is in stable, we should
* use it instead.
*/
macro_rules! impl_encodable_for_uint {
($type: ident, $bit_size: expr) => {
impl Encodable for $type {
#[allow(clippy::cast_lossless)]
fn ssz_append(&self, s: &mut SszStream) {
// Ensure bit size is valid
assert!(
(0 < $bit_size)
&& ($bit_size % 8 == 0)
&& (2_u128.pow($bit_size) > *self as u128)
);
// Serialize to bytes
let mut buf = BytesMut::with_capacity($bit_size / 8);
// Match bit size with encoding
match $bit_size {
8 => buf.put_u8(*self as u8),
16 => buf.put_u16_be(*self as u16),
32 => buf.put_u32_be(*self as u32),
64 => buf.put_u64_be(*self as u64),
_ => {}
}
// Append bytes to the SszStream
s.append_encoded_raw(&buf.to_vec());
}
}
};
}
impl_encodable_for_uint!(u8, 8);
impl_encodable_for_uint!(u16, 16);
impl_encodable_for_uint!(u32, 32);
impl_encodable_for_uint!(u64, 64);
impl_encodable_for_uint!(usize, 64);
impl Encodable for H256 {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.to_vec());
}
}
impl Encodable for Address {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.to_vec());
}
}
impl<T> Encodable for Vec<T>
where
T: Encodable,
{
fn ssz_append(&self, s: &mut SszStream) {
s.append_vec(&self);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ssz_encode_h256() {
let h = H256::zero();
let mut ssz = SszStream::new();
ssz.append(&h);
assert_eq!(ssz.drain(), vec![0; 32]);
}
#[test]
fn test_ssz_encode_address() {
let h = Address::zero();
let mut ssz = SszStream::new();
ssz.append(&h);
assert_eq!(ssz.drain(), vec![0; 20]);
}
#[test]
fn test_ssz_encode_u8() {
let x: u8 = 0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0]);
let x: u8 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1]);
let x: u8 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![100]);
let x: u8 = 255;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255]);
}
#[test]
fn test_ssz_encode_u16() {
let x: u16 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 1]);
let x: u16 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 100]);
let x: u16 = 1 << 8;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1, 0]);
let x: u16 = 65535;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255]);
}
#[test]
fn test_ssz_encode_u32() {
let x: u32 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 1]);
let x: u32 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 100]);
let x: u32 = 1 << 16;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 1, 0, 0]);
let x: u32 = 1 << 24;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1, 0, 0, 0]);
let x: u32 = !0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255, 255, 255]);
}
#[test]
fn test_ssz_encode_u64() {
let x: u64 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 0, 0, 0, 1]);
let x: u64 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 0, 0, 0, 100]);
let x: u64 = 1 << 32;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 1, 0, 0, 0, 0]);
let x: u64 = !0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255, 255, 255, 255, 255, 255, 255]);
}
#[test]
fn test_ssz_encode_usize() {
let x: usize = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 0, 0, 0, 1]);
let x: usize = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 0, 0, 0, 100]);
let x: usize = 1 << 32;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 1, 0, 0, 0, 0]);
let x: usize = !0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255, 255, 255, 255, 255, 255, 255]);
}
}

View File

@@ -1,79 +0,0 @@
use super::ethereum_types::{Address, H256};
use super::{merkle_hash, ssz_encode, TreeHash};
use hashing::hash;
impl TreeHash for u8 {
fn hash_tree_root(&self) -> Vec<u8> {
ssz_encode(self)
}
}
impl TreeHash for u16 {
fn hash_tree_root(&self) -> Vec<u8> {
ssz_encode(self)
}
}
impl TreeHash for u32 {
fn hash_tree_root(&self) -> Vec<u8> {
ssz_encode(self)
}
}
impl TreeHash for u64 {
fn hash_tree_root(&self) -> Vec<u8> {
ssz_encode(self)
}
}
impl TreeHash for usize {
fn hash_tree_root(&self) -> Vec<u8> {
ssz_encode(self)
}
}
impl TreeHash for Address {
fn hash_tree_root(&self) -> Vec<u8> {
ssz_encode(self)
}
}
impl TreeHash for H256 {
fn hash_tree_root(&self) -> Vec<u8> {
ssz_encode(self)
}
}
impl TreeHash for [u8] {
fn hash_tree_root(&self) -> Vec<u8> {
if self.len() > 32 {
return hash(&self);
}
self.to_vec()
}
}
impl<T> TreeHash for Vec<T>
where
T: TreeHash,
{
/// Returns the merkle_hash of a list of hash_tree_root values created
/// from the given list.
/// Note: A byte vector, Vec<u8>, must be converted to a slice (as_slice())
/// to be handled properly (i.e. hashed) as byte array.
fn hash_tree_root(&self) -> Vec<u8> {
let mut tree_hashes = self.iter().map(|x| x.hash_tree_root()).collect();
merkle_hash(&mut tree_hashes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_impl_tree_hash_vec() {
let result = vec![1u32, 2, 3, 4, 5, 6, 7].hash_tree_root();
assert_eq!(result.len(), 32);
}
}

View File

@@ -1,38 +0,0 @@
/*
* This is a WIP of implementing an alternative
* serialization strategy. It attempts to follow Vitalik's
* "simpleserialize" format here:
* https://github.com/ethereum/beacon_chain/blob/master/beacon_chain/utils/simpleserialize.py
*
* This implementation is not final and would almost certainly
* have issues.
*/
extern crate bytes;
extern crate ethereum_types;
pub mod decode;
pub mod encode;
pub mod tree_hash;
mod impl_decode;
mod impl_encode;
mod impl_tree_hash;
pub use crate::decode::{decode_ssz, decode_ssz_list, Decodable, DecodeError};
pub use crate::encode::{Encodable, SszStream};
pub use crate::tree_hash::{merkle_hash, TreeHash};
pub use hashing::hash;
pub const LENGTH_BYTES: usize = 4;
pub const MAX_LIST_SIZE: usize = 1 << (4 * 8);
/// Convenience function to SSZ encode an object supporting ssz::Encode.
pub fn ssz_encode<T>(val: &T) -> Vec<u8>
where
T: Encodable,
{
let mut ssz_stream = SszStream::new();
ssz_stream.append(val);
ssz_stream.drain()
}

View File

@@ -1,83 +0,0 @@
use hashing::hash;
const SSZ_CHUNK_SIZE: usize = 128;
const HASHSIZE: usize = 32;
pub trait TreeHash {
fn hash_tree_root(&self) -> Vec<u8>;
}
/// Returns a 32 byte hash of 'list' - a vector of byte vectors.
/// Note that this will consume 'list'.
pub fn merkle_hash(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
// flatten list
let (mut chunk_size, mut chunkz) = list_to_blob(list);
// get data_len as bytes. It will hashed will the merkle root
let datalen = list.len().to_le_bytes();
// Tree-hash
while chunkz.len() > HASHSIZE {
let mut new_chunkz: Vec<u8> = Vec::new();
for two_chunks in chunkz.chunks(chunk_size * 2) {
if two_chunks.len() == chunk_size {
// Odd number of chunks
let mut c = two_chunks.to_vec();
c.append(&mut vec![0; SSZ_CHUNK_SIZE]);
new_chunkz.append(&mut hash(&c));
} else {
// Hash two chuncks together
new_chunkz.append(&mut hash(two_chunks));
}
}
chunk_size = HASHSIZE;
chunkz = new_chunkz;
}
chunkz.append(&mut datalen.to_vec());
hash(&chunkz)
}
fn list_to_blob(list: &mut Vec<Vec<u8>>) -> (usize, Vec<u8>) {
let chunk_size = if list.is_empty() {
SSZ_CHUNK_SIZE
} else if list[0].len() < SSZ_CHUNK_SIZE {
let items_per_chunk = SSZ_CHUNK_SIZE / list[0].len();
items_per_chunk * list[0].len()
} else {
list[0].len()
};
let mut data = Vec::new();
if list.is_empty() {
// handle and empty list
data.append(&mut vec![0; SSZ_CHUNK_SIZE]);
} else {
// just create a blob here; we'll divide into
// chunked slices when we merklize
data.reserve(list[0].len() * list.len());
for item in list.iter_mut() {
data.append(item);
}
}
(chunk_size, data)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_merkle_hash() {
let data1 = vec![1; 100];
let data2 = vec![2; 100];
let data3 = vec![3; 100];
let mut list = vec![data1, data2, data3];
let result = merkle_hash(&mut list);
//note: should test againt a known test hash value
assert_eq!(HASHSIZE, result.len());
}
}

View File

@@ -1,11 +0,0 @@
[package]
name = "vec_shuffle"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
hashing = { path = "../hashing" }
[dev-dependencies]
yaml-rust = "0.4.2"

View File

@@ -1,81 +0,0 @@
/// A library for performing deterministic, pseudo-random shuffling on a vector.
///
/// This library is designed to confirm to the Ethereum 2.0 specification.
extern crate hashing;
mod rng;
use self::rng::ShuffleRng;
#[derive(Debug)]
pub enum ShuffleErr {
ExceedsListLength,
}
/// Performs a deterministic, in-place shuffle of a vector.
///
/// The final order of the shuffle is determined by successive hashes
/// of the supplied `seed`.
///
/// This is a Fisher-Yates-Durtstenfeld shuffle.
pub fn shuffle<T>(seed: &[u8], mut list: Vec<T>) -> Result<Vec<T>, ShuffleErr> {
let mut rng = ShuffleRng::new(seed);
if list.len() > rng.rand_max as usize {
return Err(ShuffleErr::ExceedsListLength);
}
if list.is_empty() {
return Ok(list);
}
for i in 0..(list.len() - 1) {
let n = list.len() - i;
let j = rng.rand_range(n as u32) as usize + i;
list.swap(i, j);
}
Ok(list)
}
#[cfg(test)]
mod tests {
extern crate yaml_rust;
use self::yaml_rust::yaml;
use std::{fs::File, io::prelude::*, path::PathBuf};
use super::{hashing::hash, *};
#[test]
fn test_shuffling() {
let mut file = {
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
file_path_buf.push("src/specs/shuffle_test_vectors.yaml");
File::open(file_path_buf).unwrap()
};
let mut yaml_str = String::new();
file.read_to_string(&mut yaml_str).unwrap();
let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
let doc = &docs[0];
let test_cases = doc["test_cases"].as_vec().unwrap();
for test_case in test_cases {
let input = test_case["input"].clone().into_vec().unwrap();
let output = test_case["output"].clone().into_vec().unwrap();
let seed_bytes = test_case["seed"].as_str().unwrap().as_bytes();
let seed = if seed_bytes.len() > 0 {
hash(seed_bytes)
} else {
vec![]
};
assert_eq!(shuffle(&seed, input).unwrap(), output);
}
}
}

View File

@@ -1,90 +0,0 @@
use super::hashing::hash;
const SEED_SIZE_BYTES: usize = 32;
const RAND_BYTES: usize = 3; // 24 / 8
const RAND_MAX: u32 = 16_777_215; // 2 ** (rand_bytes * 8) - 1
/// A pseudo-random number generator which given a seed
/// uses successive blake2s hashing to generate "entropy".
pub struct ShuffleRng {
seed: Vec<u8>,
idx: usize,
pub rand_max: u32,
}
impl ShuffleRng {
/// Create a new instance given some "seed" bytes.
pub fn new(initial_seed: &[u8]) -> Self {
Self {
seed: hash(initial_seed),
idx: 0,
rand_max: RAND_MAX,
}
}
/// "Regenerates" the seed by hashing it.
fn rehash_seed(&mut self) {
self.seed = hash(&self.seed);
self.idx = 0;
}
/// Extracts 3 bytes from the `seed`. Rehashes seed if required.
fn rand(&mut self) -> u32 {
self.idx += RAND_BYTES;
if self.idx >= SEED_SIZE_BYTES {
self.rehash_seed();
self.rand()
} else {
int_from_byte_slice(&self.seed, self.idx - RAND_BYTES)
}
}
/// Generate a random u32 below the specified maximum `n`.
///
/// Provides a filtered result from a higher-level rng, by discarding
/// results which may bias the output. Because of this, execution time is
/// not linear and may potentially be infinite.
pub fn rand_range(&mut self, n: u32) -> u32 {
assert!(n < RAND_MAX, "RAND_MAX exceed");
let mut x = self.rand();
while x >= self.rand_max - (self.rand_max % n) {
x = self.rand();
}
x % n
}
}
/// Reads the next three bytes of `source`, starting from `offset` and
/// interprets those bytes as a 24 bit big-endian integer.
/// Returns that integer.
fn int_from_byte_slice(source: &[u8], offset: usize) -> u32 {
(u32::from(source[offset + 2]))
| (u32::from(source[offset + 1]) << 8)
| (u32::from(source[offset]) << 16)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shuffling_int_from_slice() {
let mut x = int_from_byte_slice(&[0, 0, 1], 0);
assert_eq!((x as u32), 1);
x = int_from_byte_slice(&[0, 1, 1], 0);
assert_eq!(x, 257);
x = int_from_byte_slice(&[1, 1, 1], 0);
assert_eq!(x, 65793);
x = int_from_byte_slice(&[255, 1, 1], 0);
assert_eq!(x, 16711937);
x = int_from_byte_slice(&[255, 255, 255], 0);
assert_eq!(x, 16777215);
x = int_from_byte_slice(&[0x8f, 0xbb, 0xc7], 0);
assert_eq!(x, 9419719);
}
}

View File

@@ -1,131 +0,0 @@
title: Shuffling Algorithm Tests
summary: Test vectors for shuffling a list based upon a seed.
test_suite: Shuffling
test_cases:
- input: []
output: []
seed: ''
- input: [0]
output: [0]
seed: ''
- input: [255]
output: [255]
seed: ''
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 1, 1, 5, 6, 6, 6, 2, 4, 4]
seed: ''
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [4, 9, 6, 8, 13, 3, 2, 11, 5, 1, 12, 7, 10]
seed: ''
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 1, 1, 5, 6, 6, 6, 2, 4, 65]
seed: ''
- input: []
output: []
seed: 4kn4driuctg8
- input: [0]
output: [0]
seed: 4kn4driuctg8
- input: [255]
output: [255]
seed: 4kn4driuctg8
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 4, 4, 2, 1, 1, 6, 5, 6, 6]
seed: 4kn4driuctg8
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [7, 6, 3, 12, 11, 1, 8, 13, 10, 5, 9, 4, 2]
seed: 4kn4driuctg8
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 4, 65, 2, 1, 1, 6, 5, 6, 6]
seed: 4kn4driuctg8
- input: []
output: []
seed: ytre1p
- input: [0]
output: [0]
seed: ytre1p
- input: [255]
output: [255]
seed: ytre1p
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 1, 1, 5, 6, 2, 6, 2, 4, 4]
seed: ytre1p
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [6, 2, 3, 4, 8, 5, 12, 9, 7, 11, 10, 1, 13]
seed: ytre1p
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 1, 1, 5, 6, 2, 6, 2, 4, 65]
seed: ytre1p
- input: []
output: []
seed: mytobcffnkvj
- input: [0]
output: [0]
seed: mytobcffnkvj
- input: [255]
output: [255]
seed: mytobcffnkvj
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 4, 1, 1, 6, 4, 6, 5, 6, 2]
seed: mytobcffnkvj
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [11, 5, 9, 7, 2, 4, 12, 10, 8, 1, 6, 3, 13]
seed: mytobcffnkvj
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 65, 1, 1, 6, 4, 6, 5, 6, 2]
seed: mytobcffnkvj
- input: []
output: []
seed: myzu3g7evxp5nkvj
- input: [0]
output: [0]
seed: myzu3g7evxp5nkvj
- input: [255]
output: [255]
seed: myzu3g7evxp5nkvj
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 1, 4, 2, 6, 5, 6, 4, 1]
seed: myzu3g7evxp5nkvj
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [2, 1, 11, 3, 9, 7, 8, 13, 4, 10, 5, 6, 12]
seed: myzu3g7evxp5nkvj
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 1, 4, 2, 6, 5, 6, 65, 1]
seed: myzu3g7evxp5nkvj
- input: []
output: []
seed: xdpli1jsx5xb
- input: [0]
output: [0]
seed: xdpli1jsx5xb
- input: [255]
output: [255]
seed: xdpli1jsx5xb
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 1, 2, 4, 6, 6, 5, 6, 1, 4]
seed: xdpli1jsx5xb
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [5, 8, 12, 9, 11, 4, 7, 13, 1, 3, 2, 10, 6]
seed: xdpli1jsx5xb
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 1, 2, 65, 6, 6, 5, 6, 1, 4]
seed: xdpli1jsx5xb
- input: []
output: []
seed: oab3mbb3xe8qsx5xb
- input: [0]
output: [0]
seed: oab3mbb3xe8qsx5xb
- input: [255]
output: [255]
seed: oab3mbb3xe8qsx5xb
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 1, 1, 6, 2, 4, 4, 6, 5]
seed: oab3mbb3xe8qsx5xb
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [1, 8, 5, 13, 2, 10, 7, 11, 12, 6, 3, 4, 9]
seed: oab3mbb3xe8qsx5xb
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 1, 1, 6, 2, 4, 65, 6, 5]
seed: oab3mbb3xe8qsx5xb