Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Update ssz uint serialization/deserialization #16

Merged
merged 13 commits into from
Sep 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ bytes = ""
crypto-mac = "^0.6.2"
clap = "2.32.0"
dirs = "1.0.3"
ethereum-types = ""
ethereum-types = "0.4.0"
futures = "0.1.23"
network-libp2p = { path = "network-libp2p" }
rand = "0.3"
Expand Down
2 changes: 1 addition & 1 deletion ssz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]

[dependencies]
bytes = "0.4.9"
ethereum-types = ""
ethereum-types = "0.4.0"
185 changes: 130 additions & 55 deletions ssz/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,59 +10,76 @@ pub enum DecodeError {
}

pub trait Decodable: Sized {
fn ssz_decode(bytes: &[u8]) -> Result<Self, DecodeError>;
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError>;
}

/// Decode the nth element of some ssz list.
/// Decode the given bytes for the given type
///
/// A single ssz encoded value can be considered a list of
/// one element, so this function will work on it too.
pub fn decode_ssz_list_element<T>(ssz_bytes: &[u8], n: usize)
-> Result<T, DecodeError>
/// 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
{
T::ssz_decode(nth_value(ssz_bytes, n)?)
if index >= ssz_bytes.len() {
return Err(DecodeError::OutOfBounds)
}
T::ssz_decode(ssz_bytes, index)
}

/// Return the nth value in some ssz encoded list.
///
/// The four-byte length prefix is not included in the return.
/// Decode a vector (list) of encoded bytes.
///
/// A single ssz encoded value can be considered a list of
/// one element, so this function will work on it too.
fn nth_value(ssz_bytes: &[u8], n: usize)
-> Result<&[u8], DecodeError>
/// 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
{
let mut c: usize = 0;
for i in 0..(n + 1) {
let length = decode_length(&ssz_bytes[c..], LENGTH_BYTES)?;
let next = c + LENGTH_BYTES + length;

if i == n {
return Ok(&ssz_bytes[c + LENGTH_BYTES..next]);
} else {
if next >= ssz_bytes.len() {
return Err(DecodeError::OutOfBounds);
} else {
c = next;
}
}
}
Err(DecodeError::OutOfBounds)

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.
fn decode_length(bytes: &[u8], length_bytes: usize)
pub fn decode_length(bytes: &[u8], index: usize, length_bytes: usize)
-> Result<usize, DecodeError>
{
if bytes.len() < length_bytes {
return Err(DecodeError::TooShort);
};
let mut len: usize = 0;
for i in 0..length_bytes {
let offset = (length_bytes - i - 1) * 8;
for i in index..index+length_bytes {
let offset = (index+length_bytes - i - 1) * 8;
len = ((bytes[i] as usize) << offset) | len;
};
Ok(len)
Expand All @@ -76,24 +93,28 @@ mod tests {
#[test]
fn test_ssz_decode_length() {
let decoded = decode_length(
&vec![0, 0, 1],
&vec![0, 0, 0, 1],
0,
LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 1);

let decoded = decode_length(
&vec![0, 1, 0],
&vec![0, 0, 1, 0],
0,
LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 256);

let decoded = decode_length(
&vec![0, 1, 255],
&vec![0, 0, 1, 255],
0,
LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 511);

let decoded = decode_length(
&vec![255, 255, 255],
&vec![255, 255, 255, 255],
0,
LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 16777215);
assert_eq!(decoded.unwrap(), 4294967295);
}

#[test]
Expand All @@ -108,28 +129,82 @@ mod tests {
for i in params {
let decoded = decode_length(
&encode_length(i, LENGTH_BYTES),
0,
LENGTH_BYTES).unwrap();
assert_eq!(i, decoded);
}
}

#[test]
fn test_ssz_nth_value() {
let ssz = vec![0, 0, 1, 0];
let result = nth_value(&ssz, 0).unwrap();
assert_eq!(result, vec![0].as_slice());

let ssz = vec![0, 0, 4, 1, 2, 3, 4];
let result = nth_value(&ssz, 0).unwrap();
assert_eq!(result, vec![1, 2, 3, 4].as_slice());

let ssz = vec![0, 0, 1, 0, 0, 0, 1, 1];
let result = nth_value(&ssz, 1).unwrap();
assert_eq!(result, vec![1].as_slice());

let mut ssz = vec![0, 1, 255];
ssz.append(&mut vec![42; 511]);
let result = nth_value(&ssz, 0).unwrap();
assert_eq!(result, vec![42; 511].as_slice());
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));
}
}
50 changes: 39 additions & 11 deletions ssz/src/encode.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
use super::LENGTH_BYTES;
use super::{
LENGTH_BYTES,
MAX_LIST_SIZE,
};

#[derive(Debug)]
pub enum EncodeError {
ListTooLong,
}

pub trait Encodable {
fn ssz_append(&self, s: &mut SszStream);
Expand Down Expand Up @@ -40,17 +48,25 @@ impl SszStream {
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: &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: &Vec<E>)
where E: Encodable
{
self.buffer.extend_from_slice(&encode_length(vec.len(), LENGTH_BYTES));
for v in vec {
v.ssz_append(self);
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.
Expand Down Expand Up @@ -89,29 +105,41 @@ mod tests {
fn test_encode_length_4_bytes() {
assert_eq!(
encode_length(0, LENGTH_BYTES),
vec![0; 3]
vec![0; 4]
);
assert_eq!(
encode_length(1, LENGTH_BYTES),
vec![0, 0, 1]
vec![0, 0, 0, 1]
);
assert_eq!(
encode_length(255, LENGTH_BYTES),
vec![0, 0, 255]
vec![0, 0, 0, 255]
);
assert_eq!(
encode_length(256, LENGTH_BYTES),
vec![0, 1, 0]
vec![0, 0, 1, 0]
);
assert_eq!(
encode_length(16777215, LENGTH_BYTES), // 2^(3*8) - 1
vec![255, 255, 255]
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(16777216, LENGTH_BYTES); // 2^(3*8)
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]);
}
}
Loading