Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions beacon_chain/types/src/beacon_block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use super::Hash256;
use super::attestation_record::AttestationRecord;
use super::special_record::SpecialRecord;
use super::ssz::{
Encodable,
Decodable,
DecodeError,
SszStream,
};

pub const MIN_SSZ_BLOCK_LENGTH: usize = {
8 + // slot

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not do something like const SLOT: usize = 8; for all the fields?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented. Great point @HCastano

32 + // randao_reveal
32 + // pow_chain_reference
4 + // ancestor hashes (assuming empty)
32 + // active_state_root
32 + // crystallized_state_root
4 + // attestations (assuming empty)
4 // specials (assuming empty)
};
pub const MAX_SSZ_BLOCK_LENGTH: usize = MIN_SSZ_BLOCK_LENGTH + (1 << 24);

#[derive(Debug, PartialEq, Clone)]
pub struct BeaconBlock {
pub slot: u64,
pub randao_reveal: Hash256,
pub pow_chain_reference: Hash256,
pub ancestor_hashes: Vec<Hash256>,
pub active_state_root: Hash256,
pub crystallized_state_root: Hash256,
pub attestations: Vec<AttestationRecord>,
pub specials: Vec<SpecialRecord>,
}

impl BeaconBlock {
pub fn zero() -> Self {
Self {
slot: 0,
randao_reveal: Hash256::zero(),
pow_chain_reference: Hash256::zero(),
ancestor_hashes: vec![],
active_state_root: Hash256::zero(),
crystallized_state_root: Hash256::zero(),
attestations: vec![],
specials: vec![],
}
}

/// Return a reference to `ancestor_hashes[0]`.
///
/// The first hash in `ancestor_hashes` is the parent of the block.
pub fn parent_hash(&self) -> Option<&Hash256> {
self.ancestor_hashes.get(0)
}
}

impl Encodable for BeaconBlock {
fn ssz_append(&self, s: &mut SszStream) {
s.append(&self.slot);
s.append(&self.randao_reveal);
s.append(&self.pow_chain_reference);
s.append_vec(&self.ancestor_hashes);
s.append(&self.active_state_root);
s.append(&self.crystallized_state_root);
s.append_vec(&self.attestations);
s.append_vec(&self.specials);
}
}

impl Decodable for BeaconBlock {
fn ssz_decode(bytes: &[u8], i: usize)
-> Result<(Self, usize), DecodeError>
{
let (slot, i) = u64::ssz_decode(bytes, i)?;
let (randao_reveal, i) = Hash256::ssz_decode(bytes, i)?;
let (pow_chain_reference, i) = Hash256::ssz_decode(bytes, i)?;
let (ancestor_hashes, i) = Decodable::ssz_decode(bytes, i)?;
let (active_state_root, i) = Hash256::ssz_decode(bytes, i)?;
let (crystallized_state_root, i) = Hash256::ssz_decode(bytes, i)?;
let (attestations, i) = Decodable::ssz_decode(bytes, i)?;
let (specials, i) = Decodable::ssz_decode(bytes, i)?;
let block = BeaconBlock {
slot,
randao_reveal,
pow_chain_reference,
ancestor_hashes,
active_state_root,
crystallized_state_root,
attestations,
specials
};
Ok((block, i))
}
}


#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_block_zero() {
let b = BeaconBlock::zero();
assert_eq!(b.slot, 0);
assert!(b.randao_reveal.is_zero());
assert!(b.pow_chain_reference.is_zero());
assert_eq!(b.ancestor_hashes, vec![]);
assert!(b.active_state_root.is_zero());
assert!(b.crystallized_state_root.is_zero());
assert_eq!(b.attestations.len(), 0);
assert_eq!(b.specials.len(), 0);
}

#[test]
pub fn test_block_ssz_encode_decode() {
let mut b = BeaconBlock::zero();
b.ancestor_hashes = vec![Hash256::zero(); 32];

let mut ssz_stream = SszStream::new();
ssz_stream.append(&b);
let ssz = ssz_stream.drain();

let (b_decoded, _) = BeaconBlock::ssz_decode(&ssz, 0).unwrap();

assert_eq!(b, b_decoded);
}

#[test]
pub fn test_block_min_ssz_length() {
let b = BeaconBlock::zero();

let mut ssz_stream = SszStream::new();
ssz_stream.append(&b);
let ssz = ssz_stream.drain();

assert_eq!(ssz.len(), MIN_SSZ_BLOCK_LENGTH);
}

#[test]
pub fn test_block_parent_hash() {
let mut b = BeaconBlock::zero();
b.ancestor_hashes = vec![
Hash256::from("cats".as_bytes()),
Hash256::from("dogs".as_bytes()),
Hash256::from("birds".as_bytes()),
];

assert_eq!(b.parent_hash().unwrap(), &Hash256::from("cats".as_bytes()));
}
}
80 changes: 0 additions & 80 deletions beacon_chain/types/src/block.rs

This file was deleted.

6 changes: 4 additions & 2 deletions beacon_chain/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ pub mod active_state;
pub mod attestation_record;
pub mod crystallized_state;
pub mod chain_config;
pub mod block;
pub mod beacon_block;
pub mod crosslink_record;
pub mod shard_and_committee;
pub mod special_record;
pub mod validator_record;

use self::ethereum_types::{
Expand All @@ -24,9 +25,10 @@ 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 beacon_block::BeaconBlock;
pub use crosslink_record::CrosslinkRecord;
pub use shard_and_committee::ShardAndCommittee;
pub use special_record::{ SpecialRecord, SpecialRecordKind };
pub use validator_record::ValidatorRecord;

pub type Hash256 = H256;
Expand Down
138 changes: 138 additions & 0 deletions beacon_chain/types/src/special_record.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use super::ssz::{
Encodable,
Decodable,
DecodeError,
SszStream,
};


/// The value of the "type" field of SpecialRecord.
///
/// Note: this value must serialize to a u8 and therefore must not be greater than 255.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SpecialRecordKind {
Logout = 0,
CasperSlashing = 1,
RandaoChange = 2,
}


/// The structure used in the `BeaconBlock.specials` field.
#[derive(Debug, PartialEq, Clone)]
pub struct SpecialRecord {
pub kind: u8,
pub data: Vec<u8>,
}

impl SpecialRecord {
pub fn logout(data: &[u8]) -> Self {
Self {
kind: SpecialRecordKind::Logout as u8,
data: data.to_vec(),
}
}

pub fn casper_slashing(data: &[u8]) -> Self {
Self {
kind: SpecialRecordKind::CasperSlashing as u8,
data: data.to_vec(),
}
}

pub fn randao_change(data: &[u8]) -> Self {
Self {
kind: SpecialRecordKind::RandaoChange as u8,
data: data.to_vec(),
}
}

/// Match `self.kind` to a `SpecialRecordKind`.
///
/// Returns `None` if `self.kind` is an unknown value.
fn resolve_kind(&self) -> Option<SpecialRecordKind> {
match self.kind {
x if x == SpecialRecordKind::Logout as u8
=> Some(SpecialRecordKind::Logout),
x if x == SpecialRecordKind::CasperSlashing as u8
=> Some(SpecialRecordKind::CasperSlashing),
x if x == SpecialRecordKind::RandaoChange as u8
=> Some(SpecialRecordKind::RandaoChange),
_ => None
}
}
}

impl Encodable for SpecialRecord {
fn ssz_append(&self, s: &mut SszStream) {
s.append(&self.kind);
s.append_vec(&self.data);
}
}

impl Decodable for SpecialRecord {
fn ssz_decode(bytes: &[u8], i: usize)
-> Result<(Self, usize), DecodeError>
{
let (kind, i) = u8::ssz_decode(bytes, i)?;
let (data, i) = Decodable::ssz_decode(bytes, i)?;
Ok((SpecialRecord{kind, data}, i))
}
}


#[cfg(test)]
mod tests {
use super::*;

#[test]
pub fn test_special_record_ssz_encode() {
let s = SpecialRecord::logout(&vec![]);
let mut ssz_stream = SszStream::new();
ssz_stream.append(&s);
let ssz = ssz_stream.drain();
assert_eq!(ssz, vec![0, 0, 0, 0, 0]);

let s = SpecialRecord::casper_slashing(&vec![]);
let mut ssz_stream = SszStream::new();
ssz_stream.append(&s);
let ssz = ssz_stream.drain();
assert_eq!(ssz, vec![1, 0, 0, 0, 0]);

let s = SpecialRecord::randao_change(&vec![]);
let mut ssz_stream = SszStream::new();
ssz_stream.append(&s);
let ssz = ssz_stream.drain();
assert_eq!(ssz, vec![2, 0, 0, 0, 0]);

let s = SpecialRecord::randao_change(&vec![42, 43, 44]);
let mut ssz_stream = SszStream::new();
ssz_stream.append(&s);
let ssz = ssz_stream.drain();
assert_eq!(ssz, vec![2, 0, 0, 0, 3, 42, 43, 44]);
}

#[test]
pub fn test_special_record_ssz_encode_decode() {
let s = SpecialRecord::randao_change(&vec![13, 16, 14]);
let mut ssz_stream = SszStream::new();
ssz_stream.append(&s);
let ssz = ssz_stream.drain();
let (s_decoded, _) = SpecialRecord::ssz_decode(&ssz, 0).unwrap();
assert_eq!(s, s_decoded);
}

#[test]
pub fn test_special_record_resolve_kind() {
let s = SpecialRecord::logout(&vec![]);
assert_eq!(s.resolve_kind(), Some(SpecialRecordKind::Logout));

let s = SpecialRecord::casper_slashing(&vec![]);
assert_eq!(s.resolve_kind(), Some(SpecialRecordKind::CasperSlashing));

let s = SpecialRecord::randao_change(&vec![]);
assert_eq!(s.resolve_kind(), Some(SpecialRecordKind::RandaoChange));

let s = SpecialRecord { kind: 88, data: vec![] };
assert_eq!(s.resolve_kind(), None);
}
}
Loading