Start works on SOS-SSZ
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "ssz2"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bytes = "0.4.9"
|
||||
ethereum-types = "0.5"
|
||||
hashing = { path = "../hashing" }
|
||||
int_to_bytes = { path = "../int_to_bytes" }
|
||||
hex = "0.3"
|
||||
yaml-rust = "0.4"
|
||||
@@ -0,0 +1,543 @@
|
||||
# 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 |
|
||||
|:-------------|:----------------------------------------------------|
|
||||
| `little` | ``little 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 **little 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, 'little')
|
||||
```
|
||||
|
||||
#### 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, 'little')
|
||||
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 **little endian**.
|
||||
|
||||
```python
|
||||
byte_length = int_size / 8
|
||||
new_index = current_index + int_size
|
||||
return int.from_bytes(rawbytes[current_index:current_index+int_size], 'little'), 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], 'little')
|
||||
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], 'little')
|
||||
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);
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
target
|
||||
corpus
|
||||
artifacts
|
||||
@@ -0,0 +1,105 @@
|
||||
|
||||
[package]
|
||||
name = "ssz-fuzz"
|
||||
version = "0.0.1"
|
||||
authors = ["Automatically generated"]
|
||||
publish = false
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.5"
|
||||
|
||||
[dependencies.ssz]
|
||||
path = ".."
|
||||
[dependencies.libfuzzer-sys]
|
||||
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_bool_decode"
|
||||
path = "fuzz_targets/fuzz_target_bool_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_bool_encode"
|
||||
path = "fuzz_targets/fuzz_target_bool_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u8_decode"
|
||||
path = "fuzz_targets/fuzz_target_u8_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u8_encode"
|
||||
path = "fuzz_targets/fuzz_target_u8_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u16_decode"
|
||||
path = "fuzz_targets/fuzz_target_u16_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u16_encode"
|
||||
path = "fuzz_targets/fuzz_target_u16_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u32_decode"
|
||||
path = "fuzz_targets/fuzz_target_u32_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u32_encode"
|
||||
path = "fuzz_targets/fuzz_target_u32_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u64_decode"
|
||||
path = "fuzz_targets/fuzz_target_u64_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_u64_encode"
|
||||
path = "fuzz_targets/fuzz_target_u64_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_usize_decode"
|
||||
path = "fuzz_targets/fuzz_target_usize_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_usize_encode"
|
||||
path = "fuzz_targets/fuzz_target_usize_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_hash256_decode"
|
||||
path = "fuzz_targets/fuzz_target_hash256_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_hash256_encode"
|
||||
path = "fuzz_targets/fuzz_target_hash256_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_address_decode"
|
||||
path = "fuzz_targets/fuzz_target_address_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_address_encode"
|
||||
path = "fuzz_targets/fuzz_target_address_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_vec_address_decode"
|
||||
path = "fuzz_targets/fuzz_target_vec_address_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_vec_bool_decode"
|
||||
path = "fuzz_targets/fuzz_target_vec_bool_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_vec_decode"
|
||||
path = "fuzz_targets/fuzz_target_vec_decode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_vec_encode"
|
||||
path = "fuzz_targets/fuzz_target_vec_encode.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_vec_u64_decode"
|
||||
path = "fuzz_targets/fuzz_target_vec_u64_decode.rs"
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ethereum_types;
|
||||
extern crate ssz;
|
||||
|
||||
use ethereum_types::Address;
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let result: Result<Address, DecodeError> = decode(data);
|
||||
if data.len() == 20 {
|
||||
// Should have valid result
|
||||
let address = result.unwrap();
|
||||
assert_eq!(address, Address::from_slice(&data[..20]));
|
||||
} else {
|
||||
// Length of less than 32 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ethereum_types;
|
||||
extern crate ssz;
|
||||
|
||||
use ethereum_types::Address;
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
if data.len() >= 20 {
|
||||
let hash = Address::from_slice(&data[..20]);
|
||||
ssz.append(&hash);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(data[..20], ssz[..20]);
|
||||
assert_eq!(ssz.len(), 20);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let result: Result<bool, DecodeError> = decode(data);
|
||||
if data.len() == 1 {
|
||||
if data[0] == 1 {
|
||||
let val_bool = result.unwrap();
|
||||
assert!(val_bool);
|
||||
} else if data[0] == 0 {
|
||||
let val_bool = result.unwrap();
|
||||
assert!(!val_bool);
|
||||
} else {
|
||||
assert_eq!(result, Err(DecodeError::Invalid));
|
||||
}
|
||||
} else {
|
||||
// Length of 0 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
let mut val_bool = 0;
|
||||
if data.len() >= 1 {
|
||||
val_bool = data[0] % u8::pow(2, 6);
|
||||
}
|
||||
|
||||
ssz.append(&val_bool);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(val_bool, ssz[0]);
|
||||
assert_eq!(ssz.len(), 1);
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ethereum_types;
|
||||
extern crate ssz;
|
||||
|
||||
use ethereum_types::H256;
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let result: Result<H256, DecodeError> = decode(data);
|
||||
if data.len() == 32 {
|
||||
// Should have valid result
|
||||
let hash = result.unwrap();
|
||||
assert_eq!(hash, H256::from_slice(&data[..32]));
|
||||
} else {
|
||||
// Length of less than 32 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ethereum_types;
|
||||
extern crate ssz;
|
||||
|
||||
use ethereum_types::H256;
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
if data.len() >= 32 {
|
||||
let hash = H256::from_slice(&data[..32]);
|
||||
ssz.append(&hash);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(data[..32], ssz[..32]);
|
||||
assert_eq!(ssz.len(), 32);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let result: Result<u16, DecodeError> = decode(data);
|
||||
if data.len() == 2 {
|
||||
// Valid result
|
||||
let number_u16 = result.unwrap();
|
||||
let val = u16::from_le_bytes([data[0], data[1]]);
|
||||
assert_eq!(number_u16, val);
|
||||
} else {
|
||||
// Length of 0 or 1 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
let mut number_u16 = 0;
|
||||
if data.len() >= 2 {
|
||||
number_u16 = u16::from_be_bytes([data[0], data[1]]);
|
||||
}
|
||||
|
||||
ssz.append(&number_u16);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(ssz.len(), 2);
|
||||
assert_eq!(number_u16, u16::from_le_bytes([ssz[0], ssz[1]]));
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let result: Result<u32, DecodeError> = decode(data);
|
||||
if data.len() == 4 {
|
||||
// Valid result
|
||||
let number_u32 = result.unwrap();
|
||||
let val = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
|
||||
assert_eq!(number_u32, val);
|
||||
} else {
|
||||
// Length not 4 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
let mut number_u32 = 0;
|
||||
if data.len() >= 4 {
|
||||
number_u32 = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
|
||||
}
|
||||
|
||||
ssz.append(&number_u32);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(ssz.len(), 4);
|
||||
assert_eq!(number_u32, u32::from_le_bytes([ssz[0], ssz[1], ssz[2], ssz[3]]));
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let result: Result<u64, DecodeError> = decode(data);
|
||||
if data.len() == 8 {
|
||||
// Valid result
|
||||
let number_u64 = result.unwrap();
|
||||
let val = u64::from_le_bytes([
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
data[4],
|
||||
data[5],
|
||||
data[6],
|
||||
data[7],
|
||||
]);
|
||||
assert_eq!(number_u64, val);
|
||||
} else {
|
||||
// Length not 8 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
let mut number_u64 = 0;
|
||||
if data.len() >= 8 {
|
||||
number_u64 = u64::from_le_bytes([
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
data[4],
|
||||
data[5],
|
||||
data[6],
|
||||
data[7],
|
||||
]);
|
||||
}
|
||||
|
||||
ssz.append(&number_u64);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(ssz.len(), 8);
|
||||
assert_eq!(number_u64, u64::from_le_bytes([
|
||||
ssz[0],
|
||||
ssz[1],
|
||||
ssz[2],
|
||||
ssz[3],
|
||||
ssz[4],
|
||||
ssz[5],
|
||||
ssz[6],
|
||||
ssz[7],
|
||||
]));
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let result: Result<u8, DecodeError> = decode(data);
|
||||
if data.len() == 1 {
|
||||
// Should have valid result
|
||||
let number_u8 = result.unwrap();
|
||||
assert_eq!(number_u8, data[0]);
|
||||
} else {
|
||||
// Length not 1 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
let mut number_u8 = 0;
|
||||
if data.len() >= 1 {
|
||||
number_u8 = data[0];
|
||||
}
|
||||
|
||||
ssz.append(&number_u8);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(number_u8, ssz[0]);
|
||||
assert_eq!(ssz.len(), 1);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{DecodeError, decode};
|
||||
|
||||
// Fuzz decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
// Note: we assume architecture is 64 bit -> usize == 64 bits
|
||||
let result: Result<usize, DecodeError> = decode(data);
|
||||
if data.len() == 8 {
|
||||
// Valid result
|
||||
let number_usize = result.unwrap();
|
||||
let val = u64::from_le_bytes([
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
data[4],
|
||||
data[5],
|
||||
data[6],
|
||||
data[7],
|
||||
]);
|
||||
assert_eq!(number_usize, val as usize);
|
||||
} else {
|
||||
// Length less then 8 should return error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode (via ssz_append)
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut ssz = SszStream::new();
|
||||
let mut number_usize = 0;
|
||||
if data.len() >= 8 {
|
||||
number_usize = u64::from_le_bytes([
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
data[4],
|
||||
data[5],
|
||||
data[6],
|
||||
data[7],
|
||||
]) as usize;
|
||||
}
|
||||
|
||||
ssz.append(&number_usize);
|
||||
let ssz = ssz.drain();
|
||||
|
||||
assert_eq!(ssz.len(), 8);
|
||||
assert_eq!(number_usize, u64::from_le_bytes([
|
||||
ssz[0],
|
||||
ssz[1],
|
||||
ssz[2],
|
||||
ssz[3],
|
||||
ssz[4],
|
||||
ssz[5],
|
||||
ssz[6],
|
||||
ssz[7],
|
||||
]) as usize);
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ethereum_types;
|
||||
extern crate ssz;
|
||||
|
||||
use ethereum_types::{Address};
|
||||
use ssz::{decode, DecodeError};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let _result: Result<Vec<Address>, DecodeError> = decode(data);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{decode, DecodeError};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let _result: Result<Vec<bool>, DecodeError> = decode(data);
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ethereum_types;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{decode, DecodeError, Decodable};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let _result: Result<Vec<u8>, DecodeError> = decode(data);
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ethereum_types;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::SszStream;
|
||||
|
||||
// Fuzz ssz_encode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
|
||||
let mut ssz = SszStream::new();
|
||||
let data_vec = data.to_vec();
|
||||
ssz.append(&data_vec);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
#![no_main]
|
||||
#[macro_use] extern crate libfuzzer_sys;
|
||||
extern crate ssz;
|
||||
|
||||
use ssz::{decode, DecodeError};
|
||||
|
||||
// Fuzz ssz_decode()
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let _result: Result<Vec<u64>, DecodeError> = decode(data);
|
||||
});
|
||||
@@ -0,0 +1,215 @@
|
||||
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/container/list will be decoded as the given type,
|
||||
/// by recursively calling `ssz_decode`.
|
||||
/// Check on totality for underflowing the length of bytes and overflow checks done per container
|
||||
pub fn decode<T>(ssz_bytes: &[u8]) -> Result<(T), DecodeError>
|
||||
where
|
||||
T: Decodable,
|
||||
{
|
||||
let (decoded, i): (T, usize) = match T::ssz_decode(ssz_bytes, 0) {
|
||||
Err(e) => return Err(e),
|
||||
Ok(v) => v,
|
||||
};
|
||||
|
||||
if i < ssz_bytes.len() {
|
||||
return Err(DecodeError::TooLong);
|
||||
}
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
/// 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 little-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 = (i - index) * 8;
|
||||
len |= (*byte as usize) << offset;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::encode::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_ssz_decode_length() {
|
||||
let decoded = decode_length(&vec![1, 0, 0, 0], 0, LENGTH_BYTES);
|
||||
assert_eq!(decoded.unwrap(), 1);
|
||||
|
||||
let decoded = decode_length(&vec![0, 1, 0, 0], 0, LENGTH_BYTES);
|
||||
assert_eq!(decoded.unwrap(), 256);
|
||||
|
||||
let decoded = decode_length(&vec![255, 1, 0, 0], 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_encode_decode_ssz_list() {
|
||||
let test_vec: Vec<u16> = vec![256; 12];
|
||||
let mut stream = SszStream::new();
|
||||
stream.append_vec(&test_vec);
|
||||
let ssz = stream.drain();
|
||||
|
||||
// u16
|
||||
let decoded: (Vec<u16>, usize) = decode_ssz_list(&ssz, 0).unwrap();
|
||||
|
||||
assert_eq!(decoded.0, test_vec);
|
||||
assert_eq!(decoded.1, LENGTH_BYTES + (12 * 2));
|
||||
}
|
||||
|
||||
#[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![8, 0, 0, 0, 10, 0, 10, 0, 10, 0, 10, 0], 0).unwrap();
|
||||
|
||||
assert_eq!(decoded.0, v);
|
||||
assert_eq!(decoded.1, LENGTH_BYTES + (4 * 2));
|
||||
|
||||
// u32
|
||||
let v: Vec<u32> = vec![10, 10, 10, 10];
|
||||
let decoded: (Vec<u32>, usize) = decode_ssz_list(
|
||||
&vec![
|
||||
16, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 00,
|
||||
],
|
||||
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![
|
||||
32, 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, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
0,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(decoded.0, v);
|
||||
assert_eq!(decoded.1, LENGTH_BYTES + (8 * 4));
|
||||
|
||||
// Check that it can accept index
|
||||
let v: Vec<usize> = vec![15, 15, 15, 15];
|
||||
let offset = 10;
|
||||
let decoded: (Vec<usize>, usize) = decode_ssz_list(
|
||||
&vec![
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 32, 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, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
offset,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(decoded.0, v);
|
||||
assert_eq!(decoded.1, offset + LENGTH_BYTES + (8 * 4));
|
||||
|
||||
// Check that length > bytes throws error
|
||||
let decoded: Result<(Vec<usize>, usize), DecodeError> =
|
||||
decode_ssz_list(&vec![32, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0], 0);
|
||||
assert_eq!(decoded, Err(DecodeError::TooShort));
|
||||
|
||||
// Check that incorrect index throws error
|
||||
let decoded: Result<(Vec<usize>, usize), DecodeError> =
|
||||
decode_ssz_list(&vec![15, 0, 0, 0, 0, 0, 0, 0], 16);
|
||||
assert_eq!(decoded, Err(DecodeError::TooShort));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
use super::*;
|
||||
|
||||
mod impls;
|
||||
|
||||
pub trait Encodable {
|
||||
fn as_ssz_bytes(&self) -> Vec<u8>;
|
||||
|
||||
fn is_ssz_fixed_len() -> bool;
|
||||
|
||||
/// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes.
|
||||
///
|
||||
/// By default, this is set to `BYTES_PER_LENGTH_OFFSET` which is suitable for variable length
|
||||
/// objects, but not fixed-length objects. Fixed-length objects _must_ return a value which
|
||||
/// represents their length.
|
||||
fn ssz_fixed_len() -> usize {
|
||||
BYTES_PER_LENGTH_OFFSET
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a buffer for appending SSZ values.
|
||||
#[derive(Default)]
|
||||
pub struct SszStream {
|
||||
fixed: Vec<u8>,
|
||||
variable: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SszStream {
|
||||
/// Create a new, empty stream for writing SSZ values.
|
||||
pub fn new() -> Self {
|
||||
SszStream {
|
||||
fixed: vec![],
|
||||
variable: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Append some item to the stream.
|
||||
pub fn append<T: Encodable>(&mut self, item: &T) {
|
||||
let mut bytes = item.as_ssz_bytes();
|
||||
|
||||
if T::is_ssz_fixed_len() {
|
||||
self.fixed.append(&mut bytes);
|
||||
} else {
|
||||
self.fixed.append(&mut encode_length(bytes.len()));
|
||||
self.variable.append(&mut bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Append the variable-length bytes to the fixed-length bytes and return the result.
|
||||
pub fn drain(mut self) -> Vec<u8> {
|
||||
self.fixed.append(&mut self.variable);
|
||||
|
||||
self.fixed
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode `len` as a little-endian byte vec of `BYTES_PER_LENGTH_OFFSET` length.
|
||||
///
|
||||
/// If `len` is larger than `2 ^ BYTES_PER_LENGTH_OFFSET`, a `debug_assert` is raised.
|
||||
pub fn encode_length(len: usize) -> Vec<u8> {
|
||||
debug_assert!(len <= MAX_LENGTH_VALUE);
|
||||
|
||||
len.to_le_bytes()[0..BYTES_PER_LENGTH_OFFSET].to_vec()
|
||||
}
|
||||
|
||||
/*
|
||||
#[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![1, 0, 0, 0]);
|
||||
assert_eq!(encode_length(255, LENGTH_BYTES), vec![255, 0, 0, 0]);
|
||||
assert_eq!(encode_length(256, LENGTH_BYTES), vec![0, 1, 0, 0]);
|
||||
assert_eq!(
|
||||
encode_length(4294967295, LENGTH_BYTES), // 2^(3*8) - 1
|
||||
vec![255, 255, 255, 255]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_lower_length() {
|
||||
assert_eq!(encode_length(0, LENGTH_BYTES - 2), vec![0; 2]);
|
||||
assert_eq!(encode_length(1, LENGTH_BYTES - 2), vec![1, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_higher_length() {
|
||||
assert_eq!(encode_length(0, LENGTH_BYTES + 2), vec![0; 6]);
|
||||
assert_eq!(encode_length(1, LENGTH_BYTES + 2), vec![1, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[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(), LENGTH_BYTES + (12 * 2));
|
||||
assert_eq!(ssz[0..4], *vec![24, 0, 0, 0]);
|
||||
assert_eq!(ssz[4..6], *vec![0, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_mixed_prefixed() {
|
||||
let test_vec: Vec<u16> = vec![100, 200];
|
||||
let test_value: u8 = 5;
|
||||
|
||||
let mut stream = SszStream::new();
|
||||
stream.append_vec(&test_vec);
|
||||
stream.append(&test_value);
|
||||
let ssz = stream.drain();
|
||||
|
||||
assert_eq!(ssz.len(), LENGTH_BYTES + (2 * 2) + 1);
|
||||
assert_eq!(ssz[0..4], *vec![4, 0, 0, 0]);
|
||||
assert_eq!(ssz[4..6], *vec![100, 0]);
|
||||
assert_eq!(ssz[6..8], *vec![200, 0]);
|
||||
assert_eq!(ssz[8], 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_mixed_postfixed() {
|
||||
let test_value: u8 = 5;
|
||||
let test_vec: Vec<u16> = vec![100, 200];
|
||||
|
||||
let mut stream = SszStream::new();
|
||||
stream.append(&test_value);
|
||||
stream.append_vec(&test_vec);
|
||||
let ssz = stream.drain();
|
||||
|
||||
assert_eq!(ssz.len(), 1 + LENGTH_BYTES + (2 * 2));
|
||||
assert_eq!(ssz[0], 5);
|
||||
assert_eq!(ssz[1..5], *vec![4, 0, 0, 0]);
|
||||
assert_eq!(ssz[5..7], *vec![100, 0]);
|
||||
assert_eq!(ssz[7..9], *vec![200, 0]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,327 @@
|
||||
use super::{Encodable, SszStream};
|
||||
use ethereum_types::H256;
|
||||
|
||||
macro_rules! impl_encodable_for_uint {
|
||||
($type: ident, $bit_size: expr) => {
|
||||
impl Encodable for $type {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
$bit_size / 8
|
||||
}
|
||||
|
||||
fn as_ssz_bytes(&self) -> Vec<u8> {
|
||||
self.to_le_bytes().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<T: Encodable> Encodable for Vec<T> {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn as_ssz_bytes(&self) -> Vec<u8> {
|
||||
let mut stream = SszStream::new();
|
||||
|
||||
for item in self {
|
||||
stream.append(item)
|
||||
}
|
||||
|
||||
stream.drain()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl Encodable for bool {
|
||||
fn ssz_fixed_len() -> Option<usize> {
|
||||
Some(8)
|
||||
}
|
||||
|
||||
fn as_ssz_bytes(&self) -> Vec<u8> {
|
||||
(*self as u8).to_le_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for H256 {
|
||||
fn ssz_fixed_len() -> Option<usize> {
|
||||
Some(32)
|
||||
}
|
||||
|
||||
fn as_ssz_bytes(&self) -> Vec<u8> {
|
||||
self.as_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_encodable_for_u8_array {
|
||||
($len: expr) => {
|
||||
impl Encodable for [u8; $len] {
|
||||
fn ssz_fixed_len() -> Option<usize> {
|
||||
Some($len)
|
||||
}
|
||||
|
||||
fn as_ssz_bytes(&self) -> Vec<u8> {
|
||||
self.to_vec()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_encodable_for_u8_array!(4);
|
||||
|
||||
macro_rules! impl_encodable_for_u8_array {
|
||||
($len: expr) => {
|
||||
impl Encodable for [u8; $len] {
|
||||
|
||||
fn ssz_append(&self, s: &mut SszStream) {
|
||||
let bytes: Vec<u8> = self.iter().cloned().collect();
|
||||
s.append_encoded_raw(&bytes);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_encodable_for_u8_array!(4);
|
||||
|
||||
impl Encodable for bool {
|
||||
fn ssz_append(&self, s: &mut SszStream) {
|
||||
let byte = if *self { 0b0000_0001 } else { 0b0000_0000 };
|
||||
s.append_encoded_raw(&[byte]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for H256 {
|
||||
fn ssz_append(&self, s: &mut SszStream) {
|
||||
s.append_encoded_raw(self.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Address {
|
||||
fn ssz_append(&self, s: &mut SszStream) {
|
||||
s.append_encoded_raw(self.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
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::*;
|
||||
use crate::ssz_encode;
|
||||
|
||||
#[test]
|
||||
fn test_vec_of_u8() {
|
||||
let vec: Vec<u8> = vec![];
|
||||
assert_eq!(vec.as_ssz_bytes(), vec![]);
|
||||
|
||||
let vec: Vec<u8> = vec![1];
|
||||
assert_eq!(vec.as_ssz_bytes(), vec![1]);
|
||||
|
||||
let vec: Vec<u8> = vec![0, 1, 2, 3];
|
||||
assert_eq!(vec.as_ssz_bytes(), vec![0, 1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_of_vec_of_u8() {
|
||||
let vec: Vec<Vec<u8>> = vec![vec![]];
|
||||
assert_eq!(vec.as_ssz_bytes(), vec![0, 0, 0, 0]);
|
||||
|
||||
let vec: Vec<Vec<u8>> = vec![vec![0, 1, 2], vec![11, 22, 33]];
|
||||
assert_eq!(
|
||||
vec.as_ssz_bytes(),
|
||||
vec![3, 0, 0, 0, 3, 0, 0, 0, 0, 1, 2, 11, 22, 33]
|
||||
);
|
||||
}
|
||||
|
||||
#[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![1, 0]);
|
||||
|
||||
let x: u16 = 100;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![100, 0]);
|
||||
|
||||
let x: u16 = 1 << 8;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![0, 1]);
|
||||
|
||||
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![1, 0, 0, 0]);
|
||||
|
||||
let x: u32 = 100;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![100, 0, 0, 0]);
|
||||
|
||||
let x: u32 = 1 << 16;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![0, 0, 1, 0]);
|
||||
|
||||
let x: u32 = 1 << 24;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![0, 0, 0, 1]);
|
||||
|
||||
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![1, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let x: u64 = 100;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![100, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let x: u64 = 1 << 32;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 1, 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![1, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let x: usize = 100;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![100, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let x: usize = 1 << 32;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 1, 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]);
|
||||
}
|
||||
|
||||
/*
|
||||
#[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_mixed() {
|
||||
let mut stream = SszStream::new();
|
||||
|
||||
let h = H256::zero();
|
||||
let a: u8 = 100;
|
||||
let b: u16 = 65535;
|
||||
let c: u32 = 1 << 24;
|
||||
|
||||
stream.append(&h);
|
||||
stream.append(&a);
|
||||
stream.append(&b);
|
||||
stream.append(&c);
|
||||
|
||||
let ssz = stream.drain();
|
||||
assert_eq!(ssz[0..32], *vec![0; 32]);
|
||||
assert_eq!(ssz[32], 100);
|
||||
assert_eq!(ssz[33..55], *vec![255, 255]);
|
||||
assert_eq!(ssz[55..59], *vec![0, 0, 0, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_encode_bool() {
|
||||
let x: bool = false;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![0b0000_0000]);
|
||||
|
||||
let x: bool = true;
|
||||
let mut ssz = SszStream::new();
|
||||
ssz.append(&x);
|
||||
assert_eq!(ssz.drain(), vec![0b0000_0001]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_encode_u8_array() {
|
||||
let x: [u8; 4] = [0, 1, 7, 8];
|
||||
let ssz = ssz_encode(&x);
|
||||
assert_eq!(ssz, vec![0, 1, 7, 8]);
|
||||
|
||||
let x: [u8; 4] = [255, 255, 255, 255];
|
||||
let ssz = ssz_encode(&x);
|
||||
assert_eq!(ssz, vec![255, 255, 255, 255]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
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 = (i - index) * 8;
|
||||
result |= ($type::from(*byte)) << offset;
|
||||
}
|
||||
Ok((result, end_bytes))
|
||||
} else {
|
||||
Err(DecodeError::TooShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_decodable_for_u8_array {
|
||||
($len: expr) => {
|
||||
impl Decodable for [u8; $len] {
|
||||
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
|
||||
if index + $len > bytes.len() {
|
||||
Err(DecodeError::TooShort)
|
||||
} else {
|
||||
let mut array: [u8; $len] = [0; $len];
|
||||
array.copy_from_slice(&bytes[index..index + $len]);
|
||||
|
||||
Ok((array, index + $len))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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_array!(4);
|
||||
|
||||
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 bool {
|
||||
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
|
||||
if index >= bytes.len() {
|
||||
Err(DecodeError::TooShort)
|
||||
} else {
|
||||
let result = match bytes[index] {
|
||||
0b0000_0000 => false,
|
||||
0b0000_0001 => true,
|
||||
_ => return Err(DecodeError::Invalid),
|
||||
};
|
||||
Ok((result, 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_slice(&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_slice(&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, 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.as_bytes(), &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.as_bytes(), &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) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(result, 0);
|
||||
assert_eq!(index, 2);
|
||||
|
||||
let ssz = vec![16, 0];
|
||||
let (result, index): (u16, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(result, 16);
|
||||
assert_eq!(index, 2);
|
||||
|
||||
let ssz = vec![0, 1];
|
||||
let (result, index): (u16, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(result, 256);
|
||||
assert_eq!(index, 2);
|
||||
|
||||
let ssz = vec![255, 255];
|
||||
let (result, index): (u16, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(index, 2);
|
||||
assert_eq!(result, 65535);
|
||||
|
||||
let ssz = vec![1];
|
||||
let result: Result<(u16, usize), DecodeError> = <_>::ssz_decode(&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) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(result, 0);
|
||||
assert_eq!(index, 4);
|
||||
|
||||
let ssz = vec![0, 1, 0, 0];
|
||||
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(index, 4);
|
||||
assert_eq!(result, 256);
|
||||
|
||||
let ssz = vec![255, 255, 255, 0, 1, 0, 0];
|
||||
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 3).unwrap();
|
||||
assert_eq!(index, 7);
|
||||
assert_eq!(result, 256);
|
||||
|
||||
let ssz = vec![0, 1, 200, 0];
|
||||
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(index, 4);
|
||||
assert_eq!(result, 13107456);
|
||||
|
||||
let ssz = vec![255, 255, 255, 255];
|
||||
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(index, 4);
|
||||
assert_eq!(result, 4294967295);
|
||||
|
||||
let ssz = vec![1, 0, 0];
|
||||
let result: Result<(u32, usize), DecodeError> = <_>::ssz_decode(&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) = <_>::ssz_decode(&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) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(index, 8);
|
||||
assert_eq!(result, 18446744073709551615);
|
||||
|
||||
let ssz = vec![0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 255];
|
||||
let (result, index): (u64, usize) = <_>::ssz_decode(&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> = <_>::ssz_decode(&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) = <_>::ssz_decode(&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) = <_>::ssz_decode(&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) = <_>::ssz_decode(&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> = <_>::ssz_decode(&ssz, 0);
|
||||
assert_eq!(result, Err(DecodeError::TooShort));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_ssz_bounds() {
|
||||
let err: Result<(u16, usize), DecodeError> = <_>::ssz_decode(&vec![1], 2);
|
||||
assert_eq!(err, Err(DecodeError::TooShort));
|
||||
|
||||
let err: Result<(u16, usize), DecodeError> = <_>::ssz_decode(&vec![0, 0, 0, 0], 3);
|
||||
assert_eq!(err, Err(DecodeError::TooShort));
|
||||
|
||||
let result: u16 = <_>::ssz_decode(&vec![0, 0, 0, 1, 0], 3).unwrap().0;
|
||||
assert_eq!(result, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_ssz_bool() {
|
||||
let ssz = vec![0b0000_0000, 0b0000_0001];
|
||||
let (result, index): (bool, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
|
||||
assert_eq!(index, 1);
|
||||
assert_eq!(result, false);
|
||||
|
||||
let (result, index): (bool, usize) = <_>::ssz_decode(&ssz, 1).unwrap();
|
||||
assert_eq!(index, 2);
|
||||
assert_eq!(result, true);
|
||||
|
||||
let ssz = vec![0b0100_0000];
|
||||
let result: Result<(bool, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
|
||||
assert_eq!(result, Err(DecodeError::Invalid));
|
||||
|
||||
let ssz = vec![];
|
||||
let result: Result<(bool, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
|
||||
assert_eq!(result, Err(DecodeError::TooShort));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_decode_ssz_list_underflow() {
|
||||
// SSZ encoded (u16::[1, 1, 1], u16::2)
|
||||
let mut encoded = vec![6, 0, 0, 0, 1, 0, 1, 0, 1, 0, 2, 0];
|
||||
let (decoded_array, i): (Vec<u16>, usize) = <_>::ssz_decode(&encoded, 0).unwrap();
|
||||
let (decoded_u16, i): (u16, usize) = <_>::ssz_decode(&encoded, i).unwrap();
|
||||
assert_eq!(decoded_array, vec![1, 1, 1]);
|
||||
assert_eq!(decoded_u16, 2);
|
||||
assert_eq!(i, 12);
|
||||
|
||||
// Underflow
|
||||
encoded[0] = 4; // change length to 4 from 6
|
||||
let (decoded_array, i): (Vec<u16>, usize) = <_>::ssz_decode(&encoded, 0).unwrap();
|
||||
let (decoded_u16, _): (u16, usize) = <_>::ssz_decode(&encoded, i).unwrap();
|
||||
assert_eq!(decoded_array, vec![1, 1]);
|
||||
assert_eq!(decoded_u16, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_too_long() {
|
||||
let encoded = vec![6, 0, 0, 0, 1, 0, 1, 0, 1, 0, 2];
|
||||
let decoded_array: Result<Vec<u16>, DecodeError> = decode(&encoded);
|
||||
assert_eq!(decoded_array, Err(DecodeError::TooLong));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_u8_array() {
|
||||
let ssz = vec![0, 1, 2, 3];
|
||||
let result: [u8; 4] = decode(&ssz).unwrap();
|
||||
assert_eq!(result.len(), 4);
|
||||
assert_eq!(result, [0, 1, 2, 3]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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;
|
||||
*/
|
||||
mod encode;
|
||||
|
||||
pub use encode::{Encodable, SszStream};
|
||||
|
||||
pub const BYTES_PER_LENGTH_OFFSET: usize = 4;
|
||||
pub const MAX_LENGTH_VALUE: usize = 1 << (BYTES_PER_LENGTH_OFFSET * 8) - 1;
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
mod impl_decode;
|
||||
mod impl_encode;
|
||||
|
||||
pub use crate::decode::{decode, decode_ssz_list, Decodable, DecodeError};
|
||||
pub use crate::encode::{Encodable, SszStream};
|
||||
|
||||
pub use hashing::hash;
|
||||
|
||||
pub const LENGTH_BYTES: usize = 4;
|
||||
pub const MAX_LIST_SIZE: usize = 1 << (4 * 8);
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate hex;
|
||||
extern crate yaml_rust;
|
||||
|
||||
use self::yaml_rust::yaml;
|
||||
use super::*;
|
||||
use std::{fs::File, io::prelude::*, path::PathBuf};
|
||||
|
||||
#[test]
|
||||
pub fn test_vector_uint_bounds() {
|
||||
let mut file = {
|
||||
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
file_path_buf.push("src/test_vectors/uint_bounds.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];
|
||||
|
||||
// Load test cases
|
||||
let test_cases = doc["test_cases"].clone();
|
||||
|
||||
for test_case in test_cases {
|
||||
// Only the valid cases are checked as parse::<uX>() will fail for all invalid cases
|
||||
if test_case["valid"].as_bool().unwrap() {
|
||||
// Convert test vector 'ssz' encoded yaml to Vec<u8>
|
||||
let ssz = test_case["ssz"].as_str().unwrap().trim_start_matches("0x");
|
||||
let test_vector_bytes = hex::decode(ssz).unwrap();
|
||||
|
||||
// Convert test vector 'value' to ssz encoded bytes
|
||||
let mut bytes: Vec<u8>;
|
||||
match test_case["type"].as_str().unwrap() {
|
||||
"uint8" => {
|
||||
let value: u8 = test_case["value"].as_str().unwrap().parse::<u8>().unwrap();
|
||||
bytes = ssz_encode::<u8>(&value); // check encoding
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u8>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
"uint16" => {
|
||||
let value: u16 =
|
||||
test_case["value"].as_str().unwrap().parse::<u16>().unwrap();
|
||||
bytes = ssz_encode::<u16>(&value);
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u16>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
"uint32" => {
|
||||
let value: u32 =
|
||||
test_case["value"].as_str().unwrap().parse::<u32>().unwrap();
|
||||
bytes = ssz_encode::<u32>(&value);
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u32>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
"uint64" => {
|
||||
let value: u64 =
|
||||
test_case["value"].as_str().unwrap().parse::<u64>().unwrap();
|
||||
bytes = ssz_encode::<u64>(&value);
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u64>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
assert_eq!(test_vector_bytes, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_vector_uint_random() {
|
||||
let mut file = {
|
||||
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
file_path_buf.push("src/test_vectors/uint_random.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];
|
||||
|
||||
// Load test cases
|
||||
let test_cases = doc["test_cases"].clone();
|
||||
|
||||
for test_case in test_cases {
|
||||
// Only the valid cases are checked as parse::<uX>() will fail for all invalid cases
|
||||
if test_case["valid"].as_bool().unwrap() {
|
||||
// Convert test vector 'ssz' encoded yaml to Vec<u8>
|
||||
let ssz = test_case["ssz"].as_str().unwrap().trim_start_matches("0x");
|
||||
let test_vector_bytes = hex::decode(ssz).unwrap();
|
||||
|
||||
// Convert test vector 'value' to ssz encoded bytes
|
||||
let mut bytes: Vec<u8>;
|
||||
match test_case["type"].as_str().unwrap() {
|
||||
"uint8" => {
|
||||
let value: u8 = test_case["value"].as_str().unwrap().parse::<u8>().unwrap();
|
||||
bytes = ssz_encode::<u8>(&value); // check encoding
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u8>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
"uint16" => {
|
||||
let value: u16 =
|
||||
test_case["value"].as_str().unwrap().parse::<u16>().unwrap();
|
||||
bytes = ssz_encode::<u16>(&value);
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u16>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
"uint32" => {
|
||||
let value: u32 =
|
||||
test_case["value"].as_str().unwrap().parse::<u32>().unwrap();
|
||||
bytes = ssz_encode::<u32>(&value);
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u32>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
"uint64" => {
|
||||
let value: u64 =
|
||||
test_case["value"].as_str().unwrap().parse::<u64>().unwrap();
|
||||
bytes = ssz_encode::<u64>(&value);
|
||||
|
||||
// Check decoding
|
||||
let decoded = decode::<u64>(&test_vector_bytes).unwrap();
|
||||
assert_eq!(decoded, value);
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
assert_eq!(test_vector_bytes, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_vector_uint_wrong_length() {
|
||||
let mut file = {
|
||||
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
file_path_buf.push("src/test_vectors/uint_wrong_length.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];
|
||||
|
||||
// Load test cases
|
||||
let test_cases = doc["test_cases"].clone();
|
||||
|
||||
for test_case in test_cases {
|
||||
// Convert test vector 'ssz' encoded yaml to Vec<u8>
|
||||
let ssz = test_case["ssz"].as_str().unwrap().trim_start_matches("0x");
|
||||
let test_vector_bytes = hex::decode(ssz).unwrap();
|
||||
|
||||
// Attempt to decode invalid ssz bytes
|
||||
match test_case["type"].as_str().unwrap() {
|
||||
"uint8" => {
|
||||
let decoded = decode::<u8>(&test_vector_bytes);
|
||||
assert!(decoded.is_err());
|
||||
}
|
||||
"uint16" => {
|
||||
let decoded = decode::<u16>(&test_vector_bytes);
|
||||
assert!(decoded.is_err());
|
||||
}
|
||||
"uint32" => {
|
||||
let decoded = decode::<u32>(&test_vector_bytes);
|
||||
assert!(decoded.is_err());
|
||||
}
|
||||
"uint64" => {
|
||||
let decoded = decode::<u64>(&test_vector_bytes);
|
||||
assert!(decoded.is_err());
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user