mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-03 21:04:28 +00:00
Heavily restructure repo
Separate most modules into crates
This commit is contained in:
11
beacon_chain/types/Cargo.toml
Normal file
11
beacon_chain/types/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "types"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
|
||||
[dependencies]
|
||||
bls = { path = "../utils/bls" }
|
||||
boolean-bitfield = { path = "../utils/boolean-bitfield" }
|
||||
ethereum-types = "0.4.0"
|
||||
rand = "0.3"
|
||||
ssz = { path = "../utils/ssz" }
|
||||
30
beacon_chain/types/src/active_state.rs
Normal file
30
beacon_chain/types/src/active_state.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use super::Hash256;
|
||||
use super::attestation_record::AttestationRecord;
|
||||
|
||||
pub struct ActiveState {
|
||||
pub pending_attestations: Vec<AttestationRecord>,
|
||||
pub recent_block_hashes: Vec<Hash256>,
|
||||
}
|
||||
|
||||
impl ActiveState {
|
||||
/// Returns a new instance where all fields are empty vectors.
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
pending_attestations: vec![],
|
||||
recent_block_hashes: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_act_state_zero() {
|
||||
let a = ActiveState::zero();
|
||||
assert_eq!(a.pending_attestations.len(), 0);
|
||||
assert_eq!(a.recent_block_hashes.len(), 0);
|
||||
}
|
||||
}
|
||||
137
beacon_chain/types/src/attestation_record.rs
Normal file
137
beacon_chain/types/src/attestation_record.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use super::{ Hash256, Bitfield };
|
||||
use super::bls::{
|
||||
AggregateSignature,
|
||||
BLS_AGG_SIG_BYTE_SIZE,
|
||||
};
|
||||
use super::ssz::{
|
||||
Encodable,
|
||||
Decodable,
|
||||
DecodeError,
|
||||
decode_ssz_list,
|
||||
SszStream,
|
||||
};
|
||||
|
||||
pub const MIN_SSZ_ATTESTION_RECORD_LENGTH: usize = {
|
||||
8 + // slot
|
||||
2 + // shard_id
|
||||
4 + // oblique_parent_hashes (empty list)
|
||||
32 + // shard_block_hash
|
||||
5 + // attester_bitfield (assuming 1 byte of bitfield)
|
||||
8 + // justified_slot
|
||||
32 + // justified_block_hash
|
||||
4 + BLS_AGG_SIG_BYTE_SIZE // aggregate sig (two 256 bit points)
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct AttestationRecord {
|
||||
pub slot: u64,
|
||||
pub shard_id: u16,
|
||||
pub oblique_parent_hashes: Vec<Hash256>,
|
||||
pub shard_block_hash: Hash256,
|
||||
pub attester_bitfield: Bitfield,
|
||||
pub justified_slot: u64,
|
||||
pub justified_block_hash: Hash256,
|
||||
pub aggregate_sig: AggregateSignature,
|
||||
}
|
||||
|
||||
impl Encodable for AttestationRecord {
|
||||
fn ssz_append(&self, s: &mut SszStream) {
|
||||
s.append(&self.slot);
|
||||
s.append(&self.shard_id);
|
||||
s.append_vec(&self.oblique_parent_hashes);
|
||||
s.append(&self.shard_block_hash);
|
||||
s.append_vec(&self.attester_bitfield.to_be_vec());
|
||||
s.append(&self.justified_slot);
|
||||
s.append(&self.justified_block_hash);
|
||||
s.append_vec(&self.aggregate_sig.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for AttestationRecord {
|
||||
fn ssz_decode(bytes: &[u8], i: usize)
|
||||
-> Result<(Self, usize), DecodeError>
|
||||
{
|
||||
let (slot, i) = u64::ssz_decode(bytes, i)?;
|
||||
let (shard_id, i) = u16::ssz_decode(bytes, i)?;
|
||||
let (oblique_parent_hashes, i) = decode_ssz_list(bytes, i)?;
|
||||
let (shard_block_hash, i) = Hash256::ssz_decode(bytes, i)?;
|
||||
let (attester_bitfield, i) = Bitfield::ssz_decode(bytes, i)?;
|
||||
let (justified_slot, i) = u64::ssz_decode(bytes, i)?;
|
||||
let (justified_block_hash, i) = Hash256::ssz_decode(bytes, i)?;
|
||||
// Do aggregate sig decoding properly.
|
||||
let (agg_sig_bytes, i) = decode_ssz_list(bytes, i)?;
|
||||
let aggregate_sig = AggregateSignature::from_bytes(&agg_sig_bytes)
|
||||
.map_err(|_| DecodeError::TooShort)?; // also could be TooLong
|
||||
|
||||
let attestation_record = Self {
|
||||
slot,
|
||||
shard_id,
|
||||
oblique_parent_hashes,
|
||||
shard_block_hash,
|
||||
attester_bitfield,
|
||||
justified_slot,
|
||||
justified_block_hash,
|
||||
aggregate_sig,
|
||||
};
|
||||
Ok((attestation_record, i))
|
||||
}
|
||||
}
|
||||
|
||||
impl AttestationRecord {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
slot: 0,
|
||||
shard_id: 0,
|
||||
oblique_parent_hashes: vec![],
|
||||
shard_block_hash: Hash256::zero(),
|
||||
attester_bitfield: Bitfield::new(),
|
||||
justified_slot: 0,
|
||||
justified_block_hash: Hash256::zero(),
|
||||
aggregate_sig: AggregateSignature::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::ssz::SszStream;
|
||||
|
||||
#[test]
|
||||
pub fn test_attestation_record_min_ssz_length() {
|
||||
let ar = AttestationRecord::zero();
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(&ar);
|
||||
let ssz = ssz_stream.drain();
|
||||
|
||||
assert_eq!(ssz.len(), MIN_SSZ_ATTESTION_RECORD_LENGTH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_attestation_record_min_ssz_encode_decode() {
|
||||
let original = AttestationRecord {
|
||||
slot: 7,
|
||||
shard_id: 9,
|
||||
oblique_parent_hashes: vec![Hash256::from(&vec![14; 32][..])],
|
||||
shard_block_hash: Hash256::from(&vec![15; 32][..]),
|
||||
attester_bitfield: Bitfield::from(&vec![17; 42][..]),
|
||||
justified_slot: 19,
|
||||
justified_block_hash: Hash256::from(&vec![15; 32][..]),
|
||||
aggregate_sig: AggregateSignature::new(),
|
||||
};
|
||||
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(&original);
|
||||
|
||||
let (decoded, _) = AttestationRecord::
|
||||
ssz_decode(&ssz_stream.drain(), 0).unwrap();
|
||||
assert_eq!(original.slot, decoded.slot);
|
||||
assert_eq!(original.shard_id, decoded.shard_id);
|
||||
assert_eq!(original.oblique_parent_hashes, decoded.oblique_parent_hashes);
|
||||
assert_eq!(original.shard_block_hash, decoded.shard_block_hash);
|
||||
assert_eq!(original.attester_bitfield, decoded.attester_bitfield);
|
||||
assert_eq!(original.justified_slot, decoded.justified_slot);
|
||||
assert_eq!(original.justified_block_hash, decoded.justified_block_hash);
|
||||
}
|
||||
}
|
||||
80
beacon_chain/types/src/block.rs
Normal file
80
beacon_chain/types/src/block.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use super::Hash256;
|
||||
use super::attestation_record::AttestationRecord;
|
||||
use super::ssz::{ Encodable, SszStream };
|
||||
|
||||
pub const MIN_SSZ_BLOCK_LENGTH: usize = {
|
||||
32 + // parent_hash
|
||||
8 + // slot_number
|
||||
32 + // randao_reveal
|
||||
4 + // attestations (assuming zero)
|
||||
32 + // pow_chain_ref
|
||||
32 + // active_state_root
|
||||
32 // crystallized_state_root
|
||||
};
|
||||
pub const MAX_SSZ_BLOCK_LENGTH: usize = MIN_SSZ_BLOCK_LENGTH + (1 << 24);
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Block {
|
||||
pub parent_hash: Hash256,
|
||||
pub slot_number: u64,
|
||||
pub randao_reveal: Hash256,
|
||||
pub attestations: Vec<AttestationRecord>,
|
||||
pub pow_chain_ref: Hash256,
|
||||
pub active_state_root: Hash256,
|
||||
pub crystallized_state_root: Hash256,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
parent_hash: Hash256::zero(),
|
||||
slot_number: 0,
|
||||
randao_reveal: Hash256::zero(),
|
||||
attestations: vec![],
|
||||
pow_chain_ref: Hash256::zero(),
|
||||
active_state_root: Hash256::zero(),
|
||||
crystallized_state_root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Block {
|
||||
fn ssz_append(&self, s: &mut SszStream) {
|
||||
s.append(&self.parent_hash);
|
||||
s.append(&self.slot_number);
|
||||
s.append(&self.randao_reveal);
|
||||
s.append_vec(&self.attestations);
|
||||
s.append(&self.pow_chain_ref);
|
||||
s.append(&self.active_state_root);
|
||||
s.append(&self.crystallized_state_root);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_block_zero() {
|
||||
let b = Block::zero();
|
||||
assert!(b.parent_hash.is_zero());
|
||||
assert_eq!(b.slot_number, 0);
|
||||
assert!(b.randao_reveal.is_zero());
|
||||
assert_eq!(b.attestations.len(), 0);
|
||||
assert!(b.pow_chain_ref.is_zero());
|
||||
assert!(b.active_state_root.is_zero());
|
||||
assert!(b.crystallized_state_root.is_zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_block_min_ssz_length() {
|
||||
let b = Block::zero();
|
||||
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(&b);
|
||||
let ssz = ssz_stream.drain();
|
||||
|
||||
assert_eq!(ssz.len(), MIN_SSZ_BLOCK_LENGTH);
|
||||
}
|
||||
}
|
||||
32
beacon_chain/types/src/chain_config.rs
Normal file
32
beacon_chain/types/src/chain_config.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
pub struct ChainConfig {
|
||||
pub cycle_length: u8,
|
||||
pub shard_count: u16,
|
||||
pub min_committee_size: u64,
|
||||
pub genesis_time: u64,
|
||||
}
|
||||
|
||||
/*
|
||||
* Presently this is just some arbitrary time in Sept 2018.
|
||||
*/
|
||||
const GENESIS_TIME: u64 = 1_537_488_655;
|
||||
|
||||
impl ChainConfig {
|
||||
pub fn standard() -> Self {
|
||||
Self {
|
||||
cycle_length: 64,
|
||||
shard_count: 1024,
|
||||
min_committee_size: 128,
|
||||
genesis_time: GENESIS_TIME, // arbitrary
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn super_fast_tests() -> Self {
|
||||
Self {
|
||||
cycle_length: 2,
|
||||
shard_count: 2,
|
||||
min_committee_size: 2,
|
||||
genesis_time: GENESIS_TIME, // arbitrary
|
||||
}
|
||||
}
|
||||
}
|
||||
62
beacon_chain/types/src/common/delegation/block_hash.rs
Normal file
62
beacon_chain/types/src/common/delegation/block_hash.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use super::utils::errors::ParameterError;
|
||||
use super::utils::types::Hash256;
|
||||
|
||||
/*
|
||||
* Work-in-progress function: not ready for review.
|
||||
*/
|
||||
|
||||
pub fn get_block_hash(
|
||||
active_state_recent_block_hashes: &[Hash256],
|
||||
current_block_slot: u64,
|
||||
slot: u64,
|
||||
cycle_length: u64, // convert from standard u8
|
||||
) -> Result<Hash256, ParameterError> {
|
||||
// active_state must have at 2*cycle_length hashes
|
||||
assert_error!(
|
||||
active_state_recent_block_hashes.len() as u64 == cycle_length * 2,
|
||||
ParameterError::InvalidInput(String::from(
|
||||
"active state has incorrect number of block hashes"
|
||||
))
|
||||
);
|
||||
|
||||
let state_start_slot = (current_block_slot)
|
||||
.checked_sub(cycle_length * 2)
|
||||
.unwrap_or(0);
|
||||
|
||||
assert_error!(
|
||||
(state_start_slot <= slot) && (slot < current_block_slot),
|
||||
ParameterError::InvalidInput(String::from("incorrect slot number"))
|
||||
);
|
||||
|
||||
let index = 2 * cycle_length + slot - current_block_slot; // should always be positive
|
||||
Ok(active_state_recent_block_hashes[index as usize])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_block_hash() {
|
||||
let block_slot: u64 = 10;
|
||||
let slot: u64 = 3;
|
||||
let cycle_length: u64 = 8;
|
||||
|
||||
let mut block_hashes: Vec<Hash256> = Vec::new();
|
||||
for _i in 0..2 * cycle_length {
|
||||
block_hashes.push(Hash256::random());
|
||||
}
|
||||
|
||||
let result = get_block_hash(
|
||||
&block_hashes,
|
||||
block_slot,
|
||||
slot,
|
||||
cycle_length)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
block_hashes[(2 * cycle_length + slot - block_slot) as usize]
|
||||
);
|
||||
}
|
||||
}
|
||||
3
beacon_chain/types/src/common/delegation/mod.rs
Normal file
3
beacon_chain/types/src/common/delegation/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod block_hash;
|
||||
|
||||
use super::utils;
|
||||
7
beacon_chain/types/src/common/maps.rs
Normal file
7
beacon_chain/types/src/common/maps.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Maps a (slot, shard_id) to attestation_indices.
|
||||
pub type AttesterMap = HashMap<(u64, u16), Vec<usize>>;
|
||||
|
||||
/// Maps a slot to a block proposer.
|
||||
pub type ProposerMap = HashMap<u64, usize>;
|
||||
9
beacon_chain/types/src/common/mod.rs
Normal file
9
beacon_chain/types/src/common/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
mod delegation;
|
||||
mod shuffling;
|
||||
|
||||
pub mod maps;
|
||||
pub mod attestation_parent_hashes;
|
||||
|
||||
use super::utils;
|
||||
use super::utils::types::Hash256;
|
||||
pub use self::shuffling::shuffle;
|
||||
2
beacon_chain/types/src/common/shuffling/README.md
Normal file
2
beacon_chain/types/src/common/shuffling/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
This module includes the fundamental shuffling function. It does not do the
|
||||
full validator delegation amongst slots.
|
||||
52
beacon_chain/types/src/common/shuffling/mod.rs
Normal file
52
beacon_chain/types/src/common/shuffling/mod.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
extern crate blake2_rfc;
|
||||
|
||||
mod rng;
|
||||
|
||||
use self::rng::ShuffleRng;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ShuffleErr {
|
||||
ExceedsListLength,
|
||||
}
|
||||
|
||||
/// Performs a deterministic, in-place shuffle of a vector of bytes.
|
||||
/// The final order of the shuffle is determined by successive hashes
|
||||
/// of the supplied `seed`.
|
||||
pub fn shuffle(
|
||||
seed: &[u8],
|
||||
mut list: Vec<usize>)
|
||||
-> Result<Vec<usize>, ShuffleErr>
|
||||
{
|
||||
let mut rng = ShuffleRng::new(seed);
|
||||
if list.len() > rng.rand_max as usize {
|
||||
return Err(ShuffleErr::ExceedsListLength);
|
||||
}
|
||||
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 {
|
||||
use super::*;
|
||||
use super::blake2_rfc::blake2s::{ blake2s, Blake2sResult };
|
||||
|
||||
fn hash(seed: &[u8]) -> Blake2sResult {
|
||||
blake2s(32, &[], seed)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shuffling() {
|
||||
let seed = hash(b"4kn4driuctg8");
|
||||
let list: Vec<usize> = (0..12).collect();
|
||||
let s = shuffle(seed.as_bytes(), list).unwrap();
|
||||
assert_eq!(
|
||||
s,
|
||||
vec![7, 4, 8, 6, 5, 3, 0, 11, 1, 2, 10, 9],
|
||||
)
|
||||
}
|
||||
}
|
||||
129
beacon_chain/types/src/common/shuffling/rng.rs
Normal file
129
beacon_chain/types/src/common/shuffling/rng.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use super::blake2_rfc::blake2s::{ Blake2s, Blake2sResult };
|
||||
|
||||
const SEED_SIZE_BYTES: usize = 32;
|
||||
const RAND_BYTES: usize = 3; // 24 / 8
|
||||
const RAND_MAX: u32 = 16_777_216; // 2**24
|
||||
|
||||
/// A pseudo-random number generator which given a seed
|
||||
/// uses successive blake2s hashing to generate "entropy".
|
||||
pub struct ShuffleRng {
|
||||
seed: Blake2sResult,
|
||||
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.as_bytes());
|
||||
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.as_bytes(),
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
/// Peform a blake2s hash on the given bytes.
|
||||
fn hash(bytes: &[u8]) -> Blake2sResult {
|
||||
let mut hasher = Blake2s::new(SEED_SIZE_BYTES);
|
||||
hasher.update(bytes);
|
||||
hasher.finalize()
|
||||
}
|
||||
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shuffling_hash_fn() {
|
||||
let digest = hash(hash(b"4kn4driuctg8").as_bytes()); // double-hash is intentional
|
||||
let digest_bytes = digest.as_bytes();
|
||||
let expected = [
|
||||
0xff, 0xff, 0xff, 0x8f, 0xbb, 0xc7, 0xab, 0x64, 0x43, 0x9a,
|
||||
0xe5, 0x12, 0x44, 0xd8, 0x70, 0xcf, 0xe5, 0x79, 0xf6, 0x55,
|
||||
0x6b, 0xbd, 0x81, 0x43, 0xc5, 0xcd, 0x70, 0x2b, 0xbe, 0xe3,
|
||||
0x87, 0xc7,
|
||||
];
|
||||
assert_eq!(digest_bytes.len(), expected.len());
|
||||
assert_eq!(digest_bytes, expected)
|
||||
}
|
||||
}
|
||||
29
beacon_chain/types/src/crosslink_record.rs
Normal file
29
beacon_chain/types/src/crosslink_record.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use super::Hash256;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CrosslinkRecord {
|
||||
pub dynasty: u64,
|
||||
pub hash: Hash256,
|
||||
}
|
||||
|
||||
impl CrosslinkRecord {
|
||||
/// Generates a new instance where `dynasty` and `hash` are both zero.
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
dynasty: 0,
|
||||
hash: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_crosslink_record_zero() {
|
||||
let c = CrosslinkRecord::zero();
|
||||
assert_eq!(c.dynasty, 0);
|
||||
assert!(c.hash.is_zero());
|
||||
}
|
||||
}
|
||||
65
beacon_chain/types/src/crystallized_state.rs
Normal file
65
beacon_chain/types/src/crystallized_state.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use super::validator_record::ValidatorRecord;
|
||||
use super::crosslink_record::CrosslinkRecord;
|
||||
use super::shard_and_committee::ShardAndCommittee;
|
||||
use super::ethereum_types::U256;
|
||||
use super::Hash256;
|
||||
|
||||
|
||||
pub struct CrystallizedState {
|
||||
pub validators: Vec<ValidatorRecord>,
|
||||
pub epoch_number: u64,
|
||||
pub indicies_for_heights: Vec<ShardAndCommittee>,
|
||||
pub last_justified_slot: u64,
|
||||
pub justified_streak: u16,
|
||||
pub last_finalized_slot: u64,
|
||||
pub current_dynasty: u64,
|
||||
pub crosslinking_shard_start: u16,
|
||||
pub crosslink_records: Vec<CrosslinkRecord>,
|
||||
pub total_deposits: U256,
|
||||
pub dynasty_seed: Hash256,
|
||||
pub dynasty_seed_last_reset: u64,
|
||||
}
|
||||
|
||||
impl CrystallizedState {
|
||||
/// Returns a new instance where all fields are either zero or an
|
||||
/// empty vector.
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
validators: vec![],
|
||||
epoch_number: 0,
|
||||
indicies_for_heights: vec![],
|
||||
last_justified_slot: 0,
|
||||
justified_streak: 0,
|
||||
last_finalized_slot: 0,
|
||||
current_dynasty: 0,
|
||||
crosslinking_shard_start: 0,
|
||||
crosslink_records: vec![],
|
||||
total_deposits: U256::zero(),
|
||||
dynasty_seed: Hash256::zero(),
|
||||
dynasty_seed_last_reset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cry_state_zero() {
|
||||
let c = CrystallizedState::zero();
|
||||
assert_eq!(c.validators.len(), 0);
|
||||
assert_eq!(c.epoch_number, 0);
|
||||
assert_eq!(c.indicies_for_heights.len(), 0);
|
||||
assert_eq!(c.last_justified_slot, 0);
|
||||
assert_eq!(c.justified_streak, 0);
|
||||
assert_eq!(c.last_finalized_slot, 0);
|
||||
assert_eq!(c.current_dynasty, 0);
|
||||
assert_eq!(c.crosslinking_shard_start, 0);
|
||||
assert_eq!(c.crosslink_records.len(), 0);
|
||||
assert!(c.total_deposits.is_zero());
|
||||
assert!(c.dynasty_seed.is_zero());
|
||||
assert_eq!(c.dynasty_seed_last_reset, 0);
|
||||
}
|
||||
|
||||
}
|
||||
49
beacon_chain/types/src/lib.rs
Normal file
49
beacon_chain/types/src/lib.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
extern crate ethereum_types;
|
||||
extern crate bls;
|
||||
extern crate boolean_bitfield;
|
||||
extern crate ssz;
|
||||
|
||||
pub mod active_state;
|
||||
pub mod attestation_record;
|
||||
pub mod crystallized_state;
|
||||
pub mod chain_config;
|
||||
pub mod block;
|
||||
pub mod crosslink_record;
|
||||
pub mod shard_and_committee;
|
||||
pub mod validator_record;
|
||||
|
||||
use self::ethereum_types::{
|
||||
H256,
|
||||
H160,
|
||||
U256
|
||||
};
|
||||
use self::boolean_bitfield::BooleanBitfield;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use active_state::ActiveState;
|
||||
pub use attestation_record::AttestationRecord;
|
||||
pub use crystallized_state::CrystallizedState;
|
||||
pub use chain_config::ChainConfig;
|
||||
pub use block::Block;
|
||||
pub use crosslink_record::CrosslinkRecord;
|
||||
pub use shard_and_committee::ShardAndCommittee;
|
||||
pub use validator_record::ValidatorRecord;
|
||||
|
||||
pub type Hash256 = H256;
|
||||
pub type Address = H160;
|
||||
pub type EthBalance = U256;
|
||||
pub type Bitfield = BooleanBitfield;
|
||||
|
||||
/// Maps a (slot, shard_id) to attestation_indices.
|
||||
pub type AttesterMap = HashMap<(u64, u16), Vec<usize>>;
|
||||
|
||||
/// Maps a slot to a block proposer.
|
||||
pub type ProposerMap = HashMap<u64, usize>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
}
|
||||
20
beacon_chain/types/src/mod.rs
Normal file
20
beacon_chain/types/src/mod.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
extern crate rlp;
|
||||
extern crate ethereum_types;
|
||||
extern crate blake2_rfc as blake2;
|
||||
extern crate bytes;
|
||||
extern crate ssz;
|
||||
|
||||
mod common;
|
||||
|
||||
pub mod active_state;
|
||||
pub mod attestation_record;
|
||||
pub mod crystallized_state;
|
||||
pub mod chain_config;
|
||||
pub mod block;
|
||||
pub mod crosslink_record;
|
||||
pub mod shard_and_committee;
|
||||
pub mod validator_record;
|
||||
|
||||
use super::bls;
|
||||
use super::db;
|
||||
use super::utils;
|
||||
28
beacon_chain/types/src/shard_and_committee.rs
Normal file
28
beacon_chain/types/src/shard_and_committee.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ShardAndCommittee {
|
||||
pub shard_id: u16,
|
||||
pub committee: Vec<usize>
|
||||
}
|
||||
|
||||
impl ShardAndCommittee {
|
||||
/// Returns a new instance where the `shard_id` is zero and the
|
||||
/// committee is an empty vector.
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
shard_id: 0,
|
||||
committee: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_shard_and_committee_zero() {
|
||||
let s = ShardAndCommittee::zero();
|
||||
assert_eq!(s.shard_id, 0);
|
||||
assert_eq!(s.committee.len(), 0);
|
||||
}
|
||||
}
|
||||
67
beacon_chain/types/src/validator_record.rs
Normal file
67
beacon_chain/types/src/validator_record.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use super::{
|
||||
Hash256,
|
||||
Address,
|
||||
EthBalance,
|
||||
};
|
||||
use super::bls::{
|
||||
PublicKey,
|
||||
Keypair
|
||||
};
|
||||
|
||||
pub struct ValidatorRecord {
|
||||
pub pubkey: PublicKey,
|
||||
pub withdrawal_shard: u16,
|
||||
pub withdrawal_address: Address,
|
||||
pub randao_commitment: Hash256,
|
||||
pub balance: EthBalance,
|
||||
pub start_dynasty: u64,
|
||||
pub end_dynasty: u64,
|
||||
}
|
||||
|
||||
impl ValidatorRecord {
|
||||
/// Generates a new instance where the keypair is generated using
|
||||
/// `rand::thread_rng` entropy and all other fields are set to zero.
|
||||
///
|
||||
/// Returns the new instance and new keypair.
|
||||
pub fn zero_with_thread_rand_keypair() -> (Self, Keypair) {
|
||||
let keypair = Keypair::random();
|
||||
let s = Self {
|
||||
pubkey: keypair.pk.clone(),
|
||||
withdrawal_shard: 0,
|
||||
withdrawal_address: Address::zero(),
|
||||
randao_commitment: Hash256::zero(),
|
||||
balance: EthBalance::zero(),
|
||||
start_dynasty: 0,
|
||||
end_dynasty: 0,
|
||||
};
|
||||
(s, keypair)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ValidatorRecord {
|
||||
fn clone(&self) -> ValidatorRecord {
|
||||
ValidatorRecord {
|
||||
pubkey: self.pubkey.clone(),
|
||||
..*self
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_validator_record_zero_rand_keypair() {
|
||||
let (v, _kp) = ValidatorRecord::zero_with_thread_rand_keypair();
|
||||
// TODO: check keys
|
||||
assert_eq!(v.withdrawal_shard, 0);
|
||||
assert!(v.withdrawal_address.is_zero());
|
||||
assert!(v.randao_commitment.is_zero());
|
||||
assert!(v.balance.is_zero());
|
||||
assert_eq!(v.start_dynasty, 0);
|
||||
assert_eq!(v.end_dynasty, 0);
|
||||
}
|
||||
}
|
||||
7
beacon_chain/utils/bls/Cargo.toml
Normal file
7
beacon_chain/utils/bls/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "bls"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
|
||||
[dependencies]
|
||||
bls-aggregates = { git = "https://github.com/sigp/signature-schemes" }
|
||||
10
beacon_chain/utils/bls/src/lib.rs
Normal file
10
beacon_chain/utils/bls/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
extern crate bls_aggregates;
|
||||
|
||||
pub use self::bls_aggregates::AggregateSignature;
|
||||
pub use self::bls_aggregates::AggregatePublicKey;
|
||||
pub use self::bls_aggregates::Signature;
|
||||
pub use self::bls_aggregates::Keypair;
|
||||
pub use self::bls_aggregates::PublicKey;
|
||||
pub use self::bls_aggregates::SecretKey;
|
||||
|
||||
pub const BLS_AGG_SIG_BYTE_SIZE: usize = 97;
|
||||
7
beacon_chain/utils/boolean-bitfield/Cargo.toml
Normal file
7
beacon_chain/utils/boolean-bitfield/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "boolean-bitfield"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
|
||||
[dependencies]
|
||||
ssz = { path = "../ssz" }
|
||||
7
beacon_chain/utils/boolean-bitfield/README.md
Normal file
7
beacon_chain/utils/boolean-bitfield/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Boolean Bitfield
|
||||
|
||||
A work-in-progress implementation of an unbounded boolean bitfield.
|
||||
|
||||
Based upon a `Vec<u8>`
|
||||
|
||||
Documentation TBC...
|
||||
334
beacon_chain/utils/boolean-bitfield/src/lib.rs
Normal file
334
beacon_chain/utils/boolean-bitfield/src/lib.rs
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Implemenation of a bitfield as a vec. Only
|
||||
* supports bytes (Vec<u8>) as the underlying
|
||||
* storage.
|
||||
*
|
||||
* A future implementation should be more efficient,
|
||||
* this is just to get the job done for now.
|
||||
*/
|
||||
extern crate ssz;
|
||||
|
||||
use std::cmp::max;
|
||||
|
||||
#[derive(Eq, Clone, Default, Debug)]
|
||||
pub struct BooleanBitfield{
|
||||
len: usize,
|
||||
vec: Vec<u8>
|
||||
}
|
||||
|
||||
impl BooleanBitfield {
|
||||
/// Create a new bitfield with a length of zero.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
len: 0,
|
||||
vec: vec![0]
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new bitfield of a certain capacity
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let mut vec = Vec::with_capacity(capacity / 8 + 1);
|
||||
vec.push(0);
|
||||
Self {
|
||||
len: 0,
|
||||
vec
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the value of a bit.
|
||||
///
|
||||
/// Will return `true` if the bit has been set to `true`
|
||||
/// without then being set to `False`.
|
||||
pub fn get_bit(&self, i: usize) -> bool {
|
||||
let bit = |i: usize| i % 8;
|
||||
let byte = |i: usize| i / 8;
|
||||
|
||||
if byte(i) >= self.vec.len() {
|
||||
false
|
||||
} else {
|
||||
self.vec[byte(i)] & (1 << (bit(i) as u8)) != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the value of a bit.
|
||||
///
|
||||
/// If this bit is larger than the length of the underlying byte
|
||||
/// array it will be extended.
|
||||
pub fn set_bit(&mut self, i: usize, to: bool) {
|
||||
let bit = |i: usize| i % 8;
|
||||
let byte = |i: usize| i / 8;
|
||||
|
||||
self.len = max(self.len, i + 1);
|
||||
|
||||
if byte(i) >= self.vec.len() {
|
||||
self.vec.resize(byte(i) + 1, 0);
|
||||
}
|
||||
if to {
|
||||
self.vec[byte(i)] =
|
||||
self.vec[byte(i)] | (1 << (bit(i) as u8))
|
||||
} else {
|
||||
self.vec[byte(i)] =
|
||||
self.vec[byte(i)] & !(1 << (bit(i) as u8))
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the "length" of this bitfield. Length is defined as
|
||||
/// the highest bit that has been set.
|
||||
///
|
||||
/// Note: this is distinct from the length of the underlying
|
||||
/// vector.
|
||||
pub fn len(&self) -> usize { self.len }
|
||||
|
||||
/// True if no bits have ever been set. A bit that is set and then
|
||||
/// unset will still count to the length of the bitfield.
|
||||
///
|
||||
/// Note: this is distinct from the length of the underlying
|
||||
/// vector.
|
||||
pub fn is_empty(&self) -> bool { self.len == 0 }
|
||||
|
||||
/// The number of bytes required to represent the bitfield.
|
||||
pub fn num_bytes(&self) -> usize { self.vec.len() }
|
||||
|
||||
/// Iterate through the underlying vector and count the number of
|
||||
/// true bits.
|
||||
pub fn num_true_bits(&self) -> u64 {
|
||||
let mut count: u64 = 0;
|
||||
for byte in &self.vec {
|
||||
for bit in 0..8 {
|
||||
if byte & (1 << (bit as u8)) != 0 {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/// Iterate through the underlying vector and find the highest
|
||||
/// set bit. Useful for instantiating a new instance from
|
||||
/// some set of bytes.
|
||||
pub fn compute_length(bytes: &[u8]) -> usize {
|
||||
for byte in (0..bytes.len()).rev() {
|
||||
for bit in (0..8).rev() {
|
||||
if bytes[byte] & (1 << (bit as u8)) != 0 {
|
||||
return (byte * 8) + bit + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Get the byte at a position, assuming big-endian encoding.
|
||||
pub fn get_byte(&self, n: usize) -> Option<&u8> {
|
||||
self.vec.get(n)
|
||||
}
|
||||
|
||||
/// Clone and return the underlying byte array (`Vec<u8>`).
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.vec.clone()
|
||||
}
|
||||
|
||||
/// Clone and return the underlying byte array (`Vec<u8>`) in big-endinan format.
|
||||
pub fn to_be_vec(&self) -> Vec<u8> {
|
||||
let mut o = self.vec.clone();
|
||||
o.reverse();
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for BooleanBitfield {
|
||||
fn from(input: &[u8]) -> Self {
|
||||
let mut vec = input.to_vec();
|
||||
vec.reverse();
|
||||
BooleanBitfield {
|
||||
vec,
|
||||
len: BooleanBitfield::compute_length(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BooleanBitfield {
|
||||
fn eq(&self, other: &BooleanBitfield) -> bool {
|
||||
(self.vec == other.vec) &
|
||||
(self.len == other.len)
|
||||
}
|
||||
}
|
||||
|
||||
impl ssz::Encodable for BooleanBitfield {
|
||||
fn ssz_append(&self, s: &mut ssz::SszStream) {
|
||||
s.append_vec(&self.to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
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 b = BooleanBitfield::
|
||||
from(&bytes[(index + 4)..(index + len + 4)]);
|
||||
let index = index + ssz::LENGTH_BYTES + len;
|
||||
Ok((b, index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ssz::Decodable;
|
||||
|
||||
#[test]
|
||||
fn test_new_from_slice() {
|
||||
let s = [0];
|
||||
let b = BooleanBitfield::from(&s[..]);
|
||||
assert_eq!(b.len, 0);
|
||||
|
||||
let s = [255];
|
||||
let b = BooleanBitfield::from(&s[..]);
|
||||
assert_eq!(b.len, 8);
|
||||
|
||||
let s = [0, 1];
|
||||
let b = BooleanBitfield::from(&s[..]);
|
||||
assert_eq!(b.len, 9);
|
||||
|
||||
let s = [31];
|
||||
let b = BooleanBitfield::from(&s[..]);
|
||||
assert_eq!(b.len, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_encoding() {
|
||||
let mut b = BooleanBitfield::new();
|
||||
b.set_bit(8, true);
|
||||
|
||||
let mut stream = ssz::SszStream::new();
|
||||
stream.append(&b);
|
||||
|
||||
assert_eq!(stream.drain(), vec![0, 0, 0, 2, 0, 1]);
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn test_ssz_decoding() {
|
||||
/*
|
||||
* Correct input
|
||||
*/
|
||||
let input = vec![0, 0, 0, 2, 0, 1];
|
||||
let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
|
||||
assert_eq!(i, 6);
|
||||
assert_eq!(b.num_true_bits(), 1);
|
||||
assert_eq!(b.get_bit(8), true);
|
||||
|
||||
/*
|
||||
* Input too long
|
||||
*/
|
||||
let mut input = vec![0, 0, 0, 2, 0, 1];
|
||||
input.push(42);
|
||||
let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
|
||||
assert_eq!(i, 6);
|
||||
assert_eq!(b.num_true_bits(), 1);
|
||||
assert_eq!(b.get_bit(8), true);
|
||||
|
||||
/*
|
||||
* Input too short
|
||||
*/
|
||||
let input = vec![0, 0, 0, 2, 1];
|
||||
let res = BooleanBitfield::ssz_decode(&input, 0);
|
||||
assert_eq!(res, Err(ssz::DecodeError::TooShort));
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_new_bitfield_len() {
|
||||
let b = BooleanBitfield::new();
|
||||
assert_eq!(b.len(), 0);
|
||||
assert_eq!(b.to_be_vec(), vec![0]);
|
||||
|
||||
let b = BooleanBitfield::with_capacity(100);
|
||||
assert_eq!(b.len(), 0);
|
||||
assert_eq!(b.to_be_vec(), vec![0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bitfield_set() {
|
||||
let mut b = BooleanBitfield::new();
|
||||
b.set_bit(0, false);
|
||||
assert_eq!(b.to_be_vec(), [0]);
|
||||
|
||||
b = BooleanBitfield::new();
|
||||
b.set_bit(7, true);
|
||||
assert_eq!(b.to_be_vec(), [128]);
|
||||
b.set_bit(7, false);
|
||||
assert_eq!(b.to_be_vec(), [0]);
|
||||
assert_eq!(b.len(), 8);
|
||||
|
||||
b = BooleanBitfield::new();
|
||||
b.set_bit(7, true);
|
||||
b.set_bit(0, true);
|
||||
assert_eq!(b.to_be_vec(), [129]);
|
||||
b.set_bit(7, false);
|
||||
assert_eq!(b.to_be_vec(), [1]);
|
||||
assert_eq!(b.len(), 8);
|
||||
|
||||
b = BooleanBitfield::new();
|
||||
b.set_bit(8, true);
|
||||
assert_eq!(b.to_be_vec(), [1, 0]);
|
||||
assert_eq!(b.len(), 9);
|
||||
b.set_bit(8, false);
|
||||
assert_eq!(b.to_be_vec(), [0, 0]);
|
||||
assert_eq!(b.len(), 9);
|
||||
|
||||
b = BooleanBitfield::new();
|
||||
b.set_bit(15, true);
|
||||
assert_eq!(b.to_be_vec(), [128, 0]);
|
||||
b.set_bit(15, false);
|
||||
assert_eq!(b.to_be_vec(), [0, 0]);
|
||||
assert_eq!(b.len(), 16);
|
||||
|
||||
b = BooleanBitfield::new();
|
||||
b.set_bit(8, true);
|
||||
b.set_bit(15, true);
|
||||
assert_eq!(b.to_be_vec(), [129, 0]);
|
||||
b.set_bit(15, false);
|
||||
assert_eq!(b.to_be_vec(), [1, 0]);
|
||||
assert_eq!(b.len(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bitfield_get() {
|
||||
let test_nums = vec![0, 8, 15, 42, 1337];
|
||||
for i in test_nums {
|
||||
let mut b = BooleanBitfield::new();
|
||||
assert_eq!(b.get_bit(i), false);
|
||||
b.set_bit(i, true);
|
||||
assert_eq!(b.get_bit(i), true);
|
||||
b.set_bit(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bitfield_num_true_bits() {
|
||||
let mut b = BooleanBitfield::new();
|
||||
assert_eq!(b.num_true_bits(), 0);
|
||||
b.set_bit(15, true);
|
||||
assert_eq!(b.num_true_bits(), 1);
|
||||
b.set_bit(15, false);
|
||||
assert_eq!(b.num_true_bits(), 0);
|
||||
b.set_bit(0, true);
|
||||
b.set_bit(7, true);
|
||||
b.set_bit(8, true);
|
||||
b.set_bit(1337, true);
|
||||
assert_eq!(b.num_true_bits(), 4);
|
||||
}
|
||||
}
|
||||
7
beacon_chain/utils/hashing/Cargo.toml
Normal file
7
beacon_chain/utils/hashing/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "hashing"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
|
||||
[dependencies]
|
||||
blake2-rfc = "0.2.18"
|
||||
8
beacon_chain/utils/hashing/src/lib.rs
Normal file
8
beacon_chain/utils/hashing/src/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
extern crate blake2_rfc;
|
||||
|
||||
use self::blake2_rfc::blake2b::blake2b;
|
||||
|
||||
pub fn canonical_hash(input: &[u8]) -> Vec<u8> {
|
||||
let result = blake2b(64, &[], input);
|
||||
result.as_bytes()[0..32].to_vec()
|
||||
}
|
||||
8
beacon_chain/utils/ssz/Cargo.toml
Normal file
8
beacon_chain/utils/ssz/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "ssz"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
|
||||
[dependencies]
|
||||
bytes = "0.4.9"
|
||||
ethereum-types = "0.4.0"
|
||||
543
beacon_chain/utils/ssz/README.md
Normal file
543
beacon_chain/utils/ssz/README.md
Normal file
@@ -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 |
|
||||
|:-------------|:----------------------------------------------------|
|
||||
| `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);
|
||||
```
|
||||
209
beacon_chain/utils/ssz/src/decode.rs
Normal file
209
beacon_chain/utils/ssz/src/decode.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use super::{
|
||||
LENGTH_BYTES,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DecodeError {
|
||||
TooShort,
|
||||
TooLong,
|
||||
}
|
||||
|
||||
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() < length_bytes {
|
||||
return Err(DecodeError::TooShort);
|
||||
};
|
||||
let mut len: usize = 0;
|
||||
for i in index..index+length_bytes {
|
||||
let offset = (index+length_bytes - i - 1) * 8;
|
||||
len = ((bytes[i] as usize) << offset) | len;
|
||||
};
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::encode::encode_length;
|
||||
|
||||
#[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));
|
||||
}
|
||||
}
|
||||
145
beacon_chain/utils/ssz/src/encode.rs
Normal file
145
beacon_chain/utils/ssz/src/encode.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
use super::{
|
||||
LENGTH_BYTES,
|
||||
MAX_LIST_SIZE,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EncodeError {
|
||||
ListTooLong,
|
||||
}
|
||||
|
||||
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.
|
||||
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: &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: &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
|
||||
{
|
||||
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 in 0..length_bytes {
|
||||
let offset = (length_bytes - i - 1) * 8;
|
||||
header[i] = ((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]);
|
||||
}
|
||||
}
|
||||
231
beacon_chain/utils/ssz/src/impl_decode.rs
Normal file
231
beacon_chain/utils/ssz/src/impl_decode.rs
Normal file
@@ -0,0 +1,231 @@
|
||||
use super::ethereum_types::H256;
|
||||
use super::{
|
||||
DecodeError,
|
||||
Decodable,
|
||||
};
|
||||
|
||||
|
||||
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 in index..end_bytes {
|
||||
let offset = ((index + max_bytes) - i - 1) * 8;
|
||||
result = ((bytes[i] as $type) << offset) | result;
|
||||
};
|
||||
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 {
|
||||
return Err(DecodeError::TooShort)
|
||||
}
|
||||
else if bytes.len() - 32 < index {
|
||||
return Err(DecodeError::TooShort)
|
||||
}
|
||||
else {
|
||||
return Ok((H256::from(&bytes[index..(index + 32)]),
|
||||
index + 32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::{
|
||||
DecodeError,
|
||||
decode_ssz,
|
||||
};
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
188
beacon_chain/utils/ssz/src/impl_encode.rs
Normal file
188
beacon_chain/utils/ssz/src/impl_encode.rs
Normal file
@@ -0,0 +1,188 @@
|
||||
extern crate bytes;
|
||||
|
||||
use super::{
|
||||
Encodable,
|
||||
SszStream
|
||||
};
|
||||
use super::ethereum_types::H256;
|
||||
use self::bytes::{ BytesMut, BufMut };
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
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(&mut 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[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_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]);
|
||||
}
|
||||
}
|
||||
31
beacon_chain/utils/ssz/src/lib.rs
Normal file
31
beacon_chain/utils/ssz/src/lib.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
mod impl_encode;
|
||||
mod impl_decode;
|
||||
|
||||
pub use decode::{
|
||||
Decodable,
|
||||
DecodeError,
|
||||
decode_ssz,
|
||||
decode_ssz_list,
|
||||
};
|
||||
pub use encode::{
|
||||
Encodable,
|
||||
SszStream,
|
||||
};
|
||||
|
||||
pub const LENGTH_BYTES: usize = 4;
|
||||
pub const MAX_LIST_SIZE : usize = 1 << (4 * 8);
|
||||
10
beacon_chain/utils/ssz_helpers/Cargo.toml
Normal file
10
beacon_chain/utils/ssz_helpers/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "ssz_helpers"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
|
||||
[dependencies]
|
||||
bls = { path = "../bls" }
|
||||
hashing = { path = "../hashing" }
|
||||
types = { path = "../../types" }
|
||||
ssz = { path = "../ssz" }
|
||||
139
beacon_chain/utils/ssz_helpers/src/attestation_ssz_splitter.rs
Normal file
139
beacon_chain/utils/ssz_helpers/src/attestation_ssz_splitter.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
use super::types::attestation_record::MIN_SSZ_ATTESTION_RECORD_LENGTH as MIN_LENGTH;
|
||||
use super::ssz::LENGTH_BYTES;
|
||||
use super::ssz::decode::decode_length;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationSplitError {
|
||||
TooShort,
|
||||
}
|
||||
|
||||
/// Given some ssz slice, find the bounds of each serialized AttestationRecord and return a vec of
|
||||
/// slices point to each.
|
||||
pub fn split_all_attestations<'a>(full_ssz: &'a [u8], index: usize)
|
||||
-> Result<Vec<&'a [u8]>, AttestationSplitError>
|
||||
{
|
||||
let mut v = vec![];
|
||||
let mut index = index;
|
||||
while index < full_ssz.len() - 1 {
|
||||
let (slice, i) = split_one_attestation(full_ssz, index)?;
|
||||
v.push(slice);
|
||||
index = i;
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Given some ssz slice, find the bounds of one serialized AttestationRecord
|
||||
/// and return a slice pointing to that.
|
||||
pub fn split_one_attestation(full_ssz: &[u8], index: usize)
|
||||
-> Result<(&[u8], usize), AttestationSplitError>
|
||||
{
|
||||
if full_ssz.len() < MIN_LENGTH {
|
||||
return Err(AttestationSplitError::TooShort);
|
||||
}
|
||||
|
||||
let hashes_len = decode_length(full_ssz, index + 10, LENGTH_BYTES)
|
||||
.map_err(|_| AttestationSplitError::TooShort)?;
|
||||
|
||||
let bitfield_len = decode_length(
|
||||
full_ssz, index + hashes_len + 46,
|
||||
LENGTH_BYTES)
|
||||
.map_err(|_| AttestationSplitError::TooShort)?;
|
||||
|
||||
// Subtract one because the min length assumes 1 byte of bitfield
|
||||
let len = MIN_LENGTH - 1
|
||||
+ hashes_len
|
||||
+ bitfield_len;
|
||||
|
||||
if full_ssz.len() < index + len {
|
||||
return Err(AttestationSplitError::TooShort);
|
||||
}
|
||||
|
||||
Ok((&full_ssz[index..(index + len)], index + len))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::types::{
|
||||
AttestationRecord,
|
||||
Hash256,
|
||||
Bitfield,
|
||||
};
|
||||
use super::super::bls::AggregateSignature;
|
||||
use super::super::ssz::{
|
||||
SszStream,
|
||||
Decodable,
|
||||
};
|
||||
|
||||
fn get_two_records() -> Vec<AttestationRecord> {
|
||||
let a = AttestationRecord {
|
||||
slot: 7,
|
||||
shard_id: 9,
|
||||
oblique_parent_hashes: vec![Hash256::from(&vec![14; 32][..])],
|
||||
shard_block_hash: Hash256::from(&vec![15; 32][..]),
|
||||
attester_bitfield: Bitfield::from(&vec![17; 42][..]),
|
||||
justified_slot: 19,
|
||||
justified_block_hash: Hash256::from(&vec![15; 32][..]),
|
||||
aggregate_sig: AggregateSignature::new(),
|
||||
};
|
||||
let b = AttestationRecord {
|
||||
slot: 9,
|
||||
shard_id: 7,
|
||||
oblique_parent_hashes: vec![Hash256::from(&vec![15; 32][..])],
|
||||
shard_block_hash: Hash256::from(&vec![14; 32][..]),
|
||||
attester_bitfield: Bitfield::from(&vec![19; 42][..]),
|
||||
justified_slot: 15,
|
||||
justified_block_hash: Hash256::from(&vec![17; 32][..]),
|
||||
aggregate_sig: AggregateSignature::new(),
|
||||
};
|
||||
vec![a, b]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attestation_ssz_split() {
|
||||
let ars = get_two_records();
|
||||
let a = ars[0].clone();
|
||||
let b = ars[1].clone();
|
||||
|
||||
|
||||
/*
|
||||
* Test split one
|
||||
*/
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(&a);
|
||||
let ssz = ssz_stream.drain();
|
||||
let (a_ssz, i) = split_one_attestation(&ssz, 0).unwrap();
|
||||
assert_eq!(i, ssz.len());
|
||||
let (decoded_a, _) = AttestationRecord::ssz_decode(a_ssz, 0)
|
||||
.unwrap();
|
||||
assert_eq!(a, decoded_a);
|
||||
|
||||
/*
|
||||
* Test split two
|
||||
*/
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(&a);
|
||||
ssz_stream.append(&b);
|
||||
let ssz = ssz_stream.drain();
|
||||
let ssz_vec = split_all_attestations(&ssz, 0).unwrap();
|
||||
let (decoded_a, _) =
|
||||
AttestationRecord::ssz_decode(ssz_vec[0], 0)
|
||||
.unwrap();
|
||||
let (decoded_b, _) =
|
||||
AttestationRecord::ssz_decode(ssz_vec[1], 0)
|
||||
.unwrap();
|
||||
assert_eq!(a, decoded_a);
|
||||
assert_eq!(b, decoded_b);
|
||||
|
||||
/*
|
||||
* Test split two with shortened ssz
|
||||
*/
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(&a);
|
||||
ssz_stream.append(&b);
|
||||
let ssz = ssz_stream.drain();
|
||||
let ssz = &ssz[0..ssz.len() - 1];
|
||||
assert!(split_all_attestations(&ssz, 0).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
7
beacon_chain/utils/ssz_helpers/src/lib.rs
Normal file
7
beacon_chain/utils/ssz_helpers/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
extern crate bls;
|
||||
extern crate hashing;
|
||||
extern crate types;
|
||||
extern crate ssz;
|
||||
|
||||
pub mod attestation_ssz_splitter;
|
||||
pub mod ssz_block;
|
||||
358
beacon_chain/utils/ssz_helpers/src/ssz_block.rs
Normal file
358
beacon_chain/utils/ssz_helpers/src/ssz_block.rs
Normal file
@@ -0,0 +1,358 @@
|
||||
use super::ssz::decode::{
|
||||
decode_length,
|
||||
Decodable,
|
||||
};
|
||||
use super::hashing::canonical_hash;
|
||||
use super::types::block::{
|
||||
MIN_SSZ_BLOCK_LENGTH,
|
||||
MAX_SSZ_BLOCK_LENGTH,
|
||||
};
|
||||
use super::types::attestation_record::MIN_SSZ_ATTESTION_RECORD_LENGTH;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SszBlockError {
|
||||
TooShort,
|
||||
TooLong,
|
||||
}
|
||||
|
||||
const LENGTH_BYTES: usize = 4;
|
||||
|
||||
/// Allows for reading of block values directly from serialized ssz bytes.
|
||||
///
|
||||
/// The purpose of this struct is to provide the functionality to read block fields directly from
|
||||
/// some serialized SSZ slice allowing us to read the block without fully
|
||||
/// de-serializing it.
|
||||
///
|
||||
/// This struct should be as "zero-copy" as possible. The `ssz` field is a reference to some slice
|
||||
/// and each function reads from that slice.
|
||||
///
|
||||
/// Use this to perform intial checks before we fully de-serialize a block. It should only really
|
||||
/// be used to verify blocks that come in from the network, for internal operations we should use a
|
||||
/// full `Block`.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SszBlock<'a> {
|
||||
ssz: &'a [u8],
|
||||
attestation_len: usize,
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl<'a> SszBlock<'a> {
|
||||
/// Create a new instance from a slice reference.
|
||||
///
|
||||
/// This function will validate the length of the ssz string, however it will not validate the
|
||||
/// contents.
|
||||
///
|
||||
/// The returned `SszBlock` instance will contain a `len` field which can be used to determine
|
||||
/// how many bytes were read from the slice. In the case of multiple, sequentually serialized
|
||||
/// blocks `len` can be used to assume the location of the next serialized block.
|
||||
pub fn from_slice(vec: &'a [u8])
|
||||
-> Result<Self, SszBlockError>
|
||||
{
|
||||
let untrimmed_ssz = &vec[..];
|
||||
/*
|
||||
* Ensure the SSZ is long enough to be a block with
|
||||
* one attestation record (not necessarily a valid
|
||||
* attestation record).
|
||||
*/
|
||||
if vec.len() < MIN_SSZ_BLOCK_LENGTH + MIN_SSZ_ATTESTION_RECORD_LENGTH {
|
||||
return Err(SszBlockError::TooShort);
|
||||
}
|
||||
/*
|
||||
* Ensure the SSZ slice isn't longer than is possible for a block.
|
||||
*/
|
||||
if vec.len() > MAX_SSZ_BLOCK_LENGTH {
|
||||
return Err(SszBlockError::TooLong);
|
||||
}
|
||||
/*
|
||||
* Determine how many bytes are used to store attestation records.
|
||||
*/
|
||||
let attestation_len = decode_length(untrimmed_ssz, 72, LENGTH_BYTES)
|
||||
.map_err(|_| SszBlockError::TooShort)?;
|
||||
/*
|
||||
* The block only has one variable field, `attestations`, therefore
|
||||
* the size of the block must be the minimum size, plus the length
|
||||
* of the attestations.
|
||||
*/
|
||||
let block_ssz_len = {
|
||||
MIN_SSZ_BLOCK_LENGTH + attestation_len
|
||||
};
|
||||
if vec.len() < block_ssz_len {
|
||||
return Err(SszBlockError::TooShort);
|
||||
}
|
||||
Ok(Self{
|
||||
ssz: &untrimmed_ssz[0..block_ssz_len],
|
||||
attestation_len,
|
||||
len: block_ssz_len,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the canonical hash for this block.
|
||||
pub fn block_hash(&self) -> Vec<u8> {
|
||||
canonical_hash(self.ssz)
|
||||
}
|
||||
|
||||
/// Return the `parent_hash` field.
|
||||
pub fn parent_hash(&self) -> &[u8] {
|
||||
&self.ssz[0..32]
|
||||
}
|
||||
|
||||
/// Return the `slot_number` field.
|
||||
pub fn slot_number(&self) -> u64 {
|
||||
/*
|
||||
* An error should be unreachable from this decode
|
||||
* because we checked the length of the array at
|
||||
* the initalization of this struct.
|
||||
*
|
||||
* If you can make this function panic, please report
|
||||
* it to paul@sigmaprime.io
|
||||
*/
|
||||
if let Ok((n, _)) = u64::ssz_decode(&self.ssz, 32) {
|
||||
n
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `randao_reveal` field.
|
||||
pub fn randao_reveal(&self) -> &[u8] {
|
||||
&self.ssz[40..72]
|
||||
}
|
||||
|
||||
/// Return the `attestations` field.
|
||||
pub fn attestations(&self) -> &[u8] {
|
||||
let start = 72 + LENGTH_BYTES;
|
||||
&self.ssz[start..(start + self.attestation_len)]
|
||||
}
|
||||
|
||||
/// Return the `pow_chain_ref` field.
|
||||
pub fn pow_chain_ref(&self) -> &[u8] {
|
||||
let start = self.len - (32 * 3);
|
||||
&self.ssz[start..(start + 32)]
|
||||
}
|
||||
|
||||
/// Return the `active_state_root` field.
|
||||
pub fn act_state_root(&self) -> &[u8] {
|
||||
let start = self.len - (32 * 2);
|
||||
&self.ssz[start..(start + 32)]
|
||||
}
|
||||
|
||||
/// Return the `active_state_root` field.
|
||||
pub fn cry_state_root(&self) -> &[u8] {
|
||||
let start = self.len - 32;
|
||||
&self.ssz[start..(start + 32)]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::types::{
|
||||
AttestationRecord,
|
||||
Block,
|
||||
};
|
||||
use super::super::ssz::SszStream;
|
||||
use super::super::types::Hash256;
|
||||
|
||||
fn get_block_ssz(b: &Block) -> Vec<u8> {
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(b);
|
||||
ssz_stream.drain()
|
||||
}
|
||||
|
||||
fn get_attestation_record_ssz(ar: &AttestationRecord) -> Vec<u8> {
|
||||
let mut ssz_stream = SszStream::new();
|
||||
ssz_stream.append(ar);
|
||||
ssz_stream.drain()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_zero_attestation_records() {
|
||||
let mut b = Block::zero();
|
||||
b.attestations = vec![];
|
||||
let ssz = get_block_ssz(&b);
|
||||
|
||||
assert_eq!(
|
||||
SszBlock::from_slice(&ssz[..]),
|
||||
Err(SszBlockError::TooShort)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_single_attestation_record_one_byte_short() {
|
||||
let mut b = Block::zero();
|
||||
b.attestations = vec![AttestationRecord::zero()];
|
||||
let ssz = get_block_ssz(&b);
|
||||
|
||||
assert_eq!(
|
||||
SszBlock::from_slice(&ssz[0..(ssz.len() - 1)]),
|
||||
Err(SszBlockError::TooShort)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_single_attestation_record_one_byte_long() {
|
||||
let mut b = Block::zero();
|
||||
b.attestations = vec![AttestationRecord::zero()];
|
||||
let mut ssz = get_block_ssz(&b);
|
||||
let original_len = ssz.len();
|
||||
ssz.push(42);
|
||||
|
||||
let ssz_block = SszBlock::from_slice(&ssz[..]).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.len, original_len);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_single_attestation_record() {
|
||||
let mut b = Block::zero();
|
||||
b.attestations = vec![AttestationRecord::zero()];
|
||||
let ssz = get_block_ssz(&b);
|
||||
|
||||
assert!(SszBlock::from_slice(&ssz[..]).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_attestation_length() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.attestation_len, MIN_SSZ_ATTESTION_RECORD_LENGTH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_block_hash() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
let hash = ssz_block.block_hash();
|
||||
// Note: this hash was not generated by some external program,
|
||||
// it was simply printed then copied into the code. This test
|
||||
// will tell us if the hash changes, not that it matches some
|
||||
// canonical reference.
|
||||
let expected_hash = [
|
||||
64, 176, 117, 210, 228, 229, 237, 100, 66, 66, 98,
|
||||
252, 31, 111, 218, 27, 160, 57, 164, 12, 15, 164,
|
||||
66, 102, 142, 36, 2, 196, 121, 54, 242, 3
|
||||
];
|
||||
assert_eq!(hash, expected_hash);
|
||||
|
||||
/*
|
||||
* Test if you give the SszBlock too many ssz bytes
|
||||
*/
|
||||
let mut too_long = serialized.clone();
|
||||
too_long.push(42);
|
||||
let ssz_block = SszBlock::from_slice(&too_long).unwrap();
|
||||
let hash = ssz_block.block_hash();
|
||||
assert_eq!(hash, expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_parent_hash() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
let reference_hash = Hash256::from([42_u8; 32]);
|
||||
block.parent_hash = reference_hash.clone();
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.parent_hash(), &reference_hash.to_vec()[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_slot_number() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
block.slot_number = 42;
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.slot_number(), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_randao_reveal() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
let reference_hash = Hash256::from([42_u8; 32]);
|
||||
block.randao_reveal = reference_hash.clone();
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.randao_reveal(), &reference_hash.to_vec()[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_attestations() {
|
||||
/*
|
||||
* Single AttestationRecord
|
||||
*/
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
let ssz_ar = get_attestation_record_ssz(&AttestationRecord::zero());
|
||||
|
||||
assert_eq!(ssz_block.attestations(), &ssz_ar[..]);
|
||||
|
||||
/*
|
||||
* Multiple AttestationRecords
|
||||
*/
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
let mut ssz_ar = get_attestation_record_ssz(&AttestationRecord::zero());
|
||||
ssz_ar.append(&mut get_attestation_record_ssz(&AttestationRecord::zero()));
|
||||
|
||||
assert_eq!(ssz_block.attestations(), &ssz_ar[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_pow_chain_ref() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
let reference_hash = Hash256::from([42_u8; 32]);
|
||||
block.pow_chain_ref = reference_hash.clone();
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.pow_chain_ref(), &reference_hash.to_vec()[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_act_state_root() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
let reference_hash = Hash256::from([42_u8; 32]);
|
||||
block.active_state_root = reference_hash.clone();
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.act_state_root(), &reference_hash.to_vec()[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssz_block_cry_state_root() {
|
||||
let mut block = Block::zero();
|
||||
block.attestations.push(AttestationRecord::zero());
|
||||
let reference_hash = Hash256::from([42_u8; 32]);
|
||||
block.crystallized_state_root = reference_hash.clone();
|
||||
|
||||
let serialized = get_block_ssz(&block);
|
||||
let ssz_block = SszBlock::from_slice(&serialized).unwrap();
|
||||
|
||||
assert_eq!(ssz_block.cry_state_root(), &reference_hash.to_vec()[..]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user