Merge remote-tracking branch 'origin/sos' into spec-0.6

This commit is contained in:
Michael Sproul
2019-05-15 11:09:00 +10:00
48 changed files with 2211 additions and 15232 deletions
@@ -1,6 +1,6 @@
use super::BLOCKS_DB_COLUMN as DB_COLUMN;
use super::{ClientDB, DBError};
use ssz::decode;
use ssz::Decode;
use std::sync::Arc;
use types::{BeaconBlock, Hash256, Slot};
@@ -30,7 +30,7 @@ impl<T: ClientDB> BeaconBlockStore<T> {
match self.get(&hash)? {
None => Ok(None),
Some(ssz) => {
let block = decode::<BeaconBlock>(&ssz).map_err(|_| DBError {
let block = BeaconBlock::from_ssz_bytes(&ssz).map_err(|_| DBError {
message: "Bad BeaconBlock SSZ.".to_string(),
})?;
Ok(Some(block))
@@ -1,6 +1,6 @@
use super::STATES_DB_COLUMN as DB_COLUMN;
use super::{ClientDB, DBError};
use ssz::decode;
use ssz::Decode;
use std::sync::Arc;
use types::{BeaconState, EthSpec, Hash256};
@@ -26,7 +26,7 @@ impl<T: ClientDB> BeaconStateStore<T> {
match self.get(&hash)? {
None => Ok(None),
Some(ssz) => {
let state = decode::<BeaconState<E>>(&ssz).map_err(|_| DBError {
let state = BeaconState::from_ssz_bytes(&ssz).map_err(|_| DBError {
message: "Bad State SSZ.".to_string(),
})?;
Ok(Some(state))
+6 -6
View File
@@ -4,7 +4,7 @@ use self::bytes::{BufMut, BytesMut};
use super::VALIDATOR_DB_COLUMN as DB_COLUMN;
use super::{ClientDB, DBError};
use bls::PublicKey;
use ssz::{decode, ssz_encode};
use ssz::{Decode, Encode};
use std::sync::Arc;
#[derive(Debug, PartialEq)]
@@ -55,7 +55,7 @@ impl<T: ClientDB> ValidatorStore<T> {
public_key: &PublicKey,
) -> Result<(), ValidatorStoreError> {
let key = self.get_db_key_for_index(&KeyPrefixes::PublicKey, index);
let val = ssz_encode(public_key);
let val = public_key.as_ssz_bytes();
self.db
.put(DB_COLUMN, &key[..], &val[..])
.map_err(ValidatorStoreError::from)
@@ -69,7 +69,7 @@ impl<T: ClientDB> ValidatorStore<T> {
let val = self.db.get(DB_COLUMN, &key[..])?;
match val {
None => Ok(None),
Some(val) => match decode::<PublicKey>(&val) {
Some(val) => match PublicKey::from_ssz_bytes(&val) {
Ok(key) => Ok(Some(key)),
Err(_) => Err(ValidatorStoreError::DecodeError),
},
@@ -125,7 +125,7 @@ mod tests {
.unwrap()
.unwrap();
assert_eq!(public_key_at_index, ssz_encode(&public_key));
assert_eq!(public_key_at_index, public_key.as_ssz_bytes());
}
#[test]
@@ -139,7 +139,7 @@ mod tests {
db.put(
DB_COLUMN,
&store.get_db_key_for_index(&KeyPrefixes::PublicKey, index)[..],
&ssz_encode(&public_key)[..],
&public_key.as_ssz_bytes(),
)
.unwrap();
@@ -157,7 +157,7 @@ mod tests {
db.put(
DB_COLUMN,
&store.get_db_key_for_index(&KeyPrefixes::PublicKey, 3)[..],
&ssz_encode(&public_key)[..],
&public_key.as_ssz_bytes(),
)
.unwrap();
+47 -24
View File
@@ -13,7 +13,7 @@ use libp2p::{
NetworkBehaviour, PeerId,
};
use slog::{debug, o, trace, warn};
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError, Encode};
use types::{Attestation, BeaconBlock};
use types::{Topic, TopicHash};
@@ -49,7 +49,7 @@ impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubE
GossipsubEvent::Message(gs_msg) => {
trace!(self.log, "Received GossipEvent"; "msg" => format!("{:?}", gs_msg));
let pubsub_message = match PubsubMessage::ssz_decode(&gs_msg.data, 0) {
let pubsub_message = match PubsubMessage::from_ssz_bytes(&gs_msg.data) {
//TODO: Punish peer on error
Err(e) => {
warn!(
@@ -59,7 +59,7 @@ impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubE
);
return;
}
Ok((msg, _index)) => msg,
Ok(msg) => msg,
};
self.events.push(BehaviourEvent::GossipMessage {
@@ -197,34 +197,59 @@ pub enum PubsubMessage {
}
//TODO: Correctly encode/decode enums. Prefixing with integer for now.
impl Encodable for PubsubMessage {
fn ssz_append(&self, s: &mut SszStream) {
impl Encode for PubsubMessage {
fn is_ssz_fixed_len() -> bool {
false
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
let offset = <u32 as Encode>::ssz_fixed_len() + <Vec<u8> as Encode>::ssz_fixed_len();
let mut encoder = ssz::SszEncoder::container(buf, offset);
match self {
PubsubMessage::Block(block_gossip) => {
0u32.ssz_append(s);
block_gossip.ssz_append(s);
encoder.append(&0_u32);
// Encode the gossip as a Vec<u8>;
encoder.append(&block_gossip.as_ssz_bytes());
}
PubsubMessage::Attestation(attestation_gossip) => {
1u32.ssz_append(s);
attestation_gossip.ssz_append(s);
encoder.append(&1_u32);
// Encode the gossip as a Vec<u8>;
encoder.append(&attestation_gossip.as_ssz_bytes());
}
}
encoder.finalize();
}
}
impl Decodable for PubsubMessage {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
let (id, index) = u32::ssz_decode(bytes, index)?;
impl Decode for PubsubMessage {
fn is_ssz_fixed_len() -> bool {
false
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
let mut builder = ssz::SszDecoderBuilder::new(&bytes);
builder.register_type::<u32>()?;
builder.register_type::<Vec<u8>>()?;
let mut decoder = builder.build()?;
let id: u32 = decoder.decode_next()?;
let body: Vec<u8> = decoder.decode_next()?;
match id {
0 => {
let (block, index) = BeaconBlock::ssz_decode(bytes, index)?;
Ok((PubsubMessage::Block(block), index))
}
1 => {
let (attestation, index) = Attestation::ssz_decode(bytes, index)?;
Ok((PubsubMessage::Attestation(attestation), index))
}
_ => Err(DecodeError::Invalid),
0 => Ok(PubsubMessage::Block(BeaconBlock::from_ssz_bytes(&body)?)),
1 => Ok(PubsubMessage::Attestation(Attestation::from_ssz_bytes(
&body,
)?)),
_ => Err(DecodeError::BytesInvalid(
"Invalid PubsubMessage id".to_string(),
)),
}
}
}
@@ -240,9 +265,7 @@ mod test {
let encoded = ssz_encode(&original);
println!("{:?}", encoded);
let (decoded, _i) = PubsubMessage::ssz_decode(&encoded, 0).unwrap();
let decoded = PubsubMessage::from_ssz_bytes(&encoded).unwrap();
assert_eq!(original, decoded);
}
+5 -15
View File
@@ -1,5 +1,6 @@
use ssz::{Decodable, DecodeError, Encodable, SszStream};
/// Available RPC methods types and ids.
//!Available RPC methods types and ids.
use ssz::{impl_decode_via_from, impl_encode_via_from};
use ssz_derive::{Decode, Encode};
use types::{BeaconBlockBody, BeaconBlockHeader, Epoch, Hash256, Slot};
@@ -149,19 +150,8 @@ impl Into<u64> for GoodbyeReason {
}
}
impl Encodable for GoodbyeReason {
fn ssz_append(&self, s: &mut SszStream) {
let id: u64 = (*self).clone().into();
id.ssz_append(s);
}
}
impl Decodable for GoodbyeReason {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
let (id, index) = u64::ssz_decode(bytes, index)?;
Ok((Self::from(id), index))
}
}
impl_encode_via_from!(GoodbyeReason, u64);
impl_decode_via_from!(GoodbyeReason, u64);
/// Request a number of beacon block roots from a peer.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
+73 -70
View File
@@ -1,6 +1,6 @@
use super::methods::*;
use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo};
use ssz::{ssz_encode, Decodable, DecodeError as SSZDecodeError, Encodable, SszStream};
use ssz::{impl_decode_via_from, impl_encode_via_from, ssz_encode, Decode, Encode};
use std::hash::{Hash, Hasher};
use std::io;
use std::iter;
@@ -72,18 +72,8 @@ impl Into<u64> for RequestId {
}
}
impl Encodable for RequestId {
fn ssz_append(&self, s: &mut SszStream) {
self.0.ssz_append(s);
}
}
impl Decodable for RequestId {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), SSZDecodeError> {
let (id, index) = u64::ssz_decode(bytes, index)?;
Ok((Self::from(id), index))
}
}
impl_encode_via_from!(RequestId, u64);
impl_decode_via_from!(RequestId, u64);
/// The RPC types which are sent/received in this protocol.
#[derive(Debug, Clone)]
@@ -125,42 +115,40 @@ where
}
}
// NOTE!
//
// This code has not been tested, it is a placeholder until we can update to the new libp2p
// spec.
fn decode(packet: Vec<u8>) -> Result<RPCEvent, DecodeError> {
// decode the header of the rpc
// request/response
let (request, index) = bool::ssz_decode(&packet, 0)?;
let (id, index) = RequestId::ssz_decode(&packet, index)?;
let (method_id, index) = u16::ssz_decode(&packet, index)?;
let mut builder = ssz::SszDecoderBuilder::new(&packet);
builder.register_type::<bool>()?;
builder.register_type::<RequestId>()?;
builder.register_type::<u16>()?;
builder.register_type::<Vec<u8>>()?;
let mut decoder = builder.build()?;
let request: bool = decoder.decode_next()?;
let id: RequestId = decoder.decode_next()?;
let method_id: u16 = decoder.decode_next()?;
let bytes: Vec<u8> = decoder.decode_next()?;
if request {
let body = match RPCMethod::from(method_id) {
RPCMethod::Hello => {
let (hello_body, _index) = HelloMessage::ssz_decode(&packet, index)?;
RPCRequest::Hello(hello_body)
}
RPCMethod::Goodbye => {
let (goodbye_reason, _index) = GoodbyeReason::ssz_decode(&packet, index)?;
RPCRequest::Goodbye(goodbye_reason)
}
RPCMethod::Hello => RPCRequest::Hello(HelloMessage::from_ssz_bytes(&bytes)?),
RPCMethod::Goodbye => RPCRequest::Goodbye(GoodbyeReason::from_ssz_bytes(&bytes)?),
RPCMethod::BeaconBlockRoots => {
let (block_roots_request, _index) =
BeaconBlockRootsRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconBlockRoots(block_roots_request)
RPCRequest::BeaconBlockRoots(BeaconBlockRootsRequest::from_ssz_bytes(&bytes)?)
}
RPCMethod::BeaconBlockHeaders => {
let (block_headers_request, _index) =
BeaconBlockHeadersRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconBlockHeaders(block_headers_request)
RPCRequest::BeaconBlockHeaders(BeaconBlockHeadersRequest::from_ssz_bytes(&bytes)?)
}
RPCMethod::BeaconBlockBodies => {
let (block_bodies_request, _index) =
BeaconBlockBodiesRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconBlockBodies(block_bodies_request)
RPCRequest::BeaconBlockBodies(BeaconBlockBodiesRequest::from_ssz_bytes(&bytes)?)
}
RPCMethod::BeaconChainState => {
let (chain_state_request, _index) =
BeaconChainStateRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconChainState(chain_state_request)
RPCRequest::BeaconChainState(BeaconChainStateRequest::from_ssz_bytes(&bytes)?)
}
RPCMethod::Unknown => return Err(DecodeError::UnknownRPCMethod),
};
@@ -174,29 +162,24 @@ fn decode(packet: Vec<u8>) -> Result<RPCEvent, DecodeError> {
// we have received a response
else {
let result = match RPCMethod::from(method_id) {
RPCMethod::Hello => {
let (body, _index) = HelloMessage::ssz_decode(&packet, index)?;
RPCResponse::Hello(body)
}
RPCMethod::Goodbye => unreachable!("Should never receive a goodbye response"),
RPCMethod::Hello => RPCResponse::Hello(HelloMessage::from_ssz_bytes(&bytes)?),
RPCMethod::BeaconBlockRoots => {
let (body, _index) = BeaconBlockRootsResponse::ssz_decode(&packet, index)?;
RPCResponse::BeaconBlockRoots(body)
RPCResponse::BeaconBlockRoots(BeaconBlockRootsResponse::from_ssz_bytes(&bytes)?)
}
RPCMethod::BeaconBlockHeaders => {
let (body, _index) = BeaconBlockHeadersResponse::ssz_decode(&packet, index)?;
RPCResponse::BeaconBlockHeaders(body)
RPCResponse::BeaconBlockHeaders(BeaconBlockHeadersResponse::from_ssz_bytes(&bytes)?)
}
RPCMethod::BeaconBlockBodies => {
let (body, _index) = BeaconBlockBodiesResponse::ssz_decode(&packet, index)?;
RPCResponse::BeaconBlockBodies(body)
RPCResponse::BeaconBlockBodies(BeaconBlockBodiesResponse::from_ssz_bytes(&packet)?)
}
RPCMethod::BeaconChainState => {
let (body, _index) = BeaconChainStateResponse::ssz_decode(&packet, index)?;
RPCResponse::BeaconChainState(body)
RPCResponse::BeaconChainState(BeaconChainStateResponse::from_ssz_bytes(&packet)?)
}
// We should never receive a goodbye response; it is invalid.
RPCMethod::Goodbye => return Err(DecodeError::UnknownRPCMethod),
RPCMethod::Unknown => return Err(DecodeError::UnknownRPCMethod),
};
Ok(RPCEvent::Response {
id,
method_id,
@@ -220,35 +203,51 @@ where
}
}
impl Encodable for RPCEvent {
fn ssz_append(&self, s: &mut SszStream) {
impl Encode for RPCEvent {
fn is_ssz_fixed_len() -> bool {
false
}
// NOTE!
//
// This code has not been tested, it is a placeholder until we can update to the new libp2p
// spec.
fn ssz_append(&self, buf: &mut Vec<u8>) {
let offset = <bool as Encode>::ssz_fixed_len()
+ <u16 as Encode>::ssz_fixed_len()
+ <Vec<u8> as Encode>::ssz_fixed_len();
let mut encoder = ssz::SszEncoder::container(buf, offset);
match self {
RPCEvent::Request {
id,
method_id,
body,
} => {
s.append(&true);
s.append(id);
s.append(method_id);
encoder.append(&true);
encoder.append(id);
encoder.append(method_id);
// Encode the `body` as a `Vec<u8>`.
match body {
RPCRequest::Hello(body) => {
s.append(body);
encoder.append(&body.as_ssz_bytes());
}
RPCRequest::Goodbye(body) => {
s.append(body);
encoder.append(&body.as_ssz_bytes());
}
RPCRequest::BeaconBlockRoots(body) => {
s.append(body);
encoder.append(&body.as_ssz_bytes());
}
RPCRequest::BeaconBlockHeaders(body) => {
s.append(body);
encoder.append(&body.as_ssz_bytes());
}
RPCRequest::BeaconBlockBodies(body) => {
s.append(body);
encoder.append(&body.as_ssz_bytes());
}
RPCRequest::BeaconChainState(body) => {
s.append(body);
encoder.append(&body.as_ssz_bytes());
}
}
}
@@ -257,28 +256,32 @@ impl Encodable for RPCEvent {
method_id,
result,
} => {
s.append(&false);
s.append(id);
s.append(method_id);
encoder.append(&true);
encoder.append(id);
encoder.append(method_id);
match result {
RPCResponse::Hello(response) => {
s.append(response);
encoder.append(&response.as_ssz_bytes());
}
RPCResponse::BeaconBlockRoots(response) => {
s.append(response);
encoder.append(&response.as_ssz_bytes());
}
RPCResponse::BeaconBlockHeaders(response) => {
s.append(response);
encoder.append(&response.as_ssz_bytes());
}
RPCResponse::BeaconBlockBodies(response) => {
s.append(response);
encoder.append(&response.as_ssz_bytes());
}
RPCResponse::BeaconChainState(response) => {
s.append(response);
encoder.append(&response.as_ssz_bytes());
}
}
}
}
// Finalize the encoder, writing to `buf`.
encoder.finalize();
}
}
+3 -3
View File
@@ -7,7 +7,7 @@ use protos::services::{
};
use protos::services_grpc::AttestationService;
use slog::{error, info, trace, warn};
use ssz::{ssz_encode, Decodable};
use ssz::{ssz_encode, Decode};
use std::sync::Arc;
use types::{Attestation, EthSpec};
@@ -110,8 +110,8 @@ impl<E: EthSpec> AttestationService for AttestationServiceInstance<E> {
let mut resp = PublishAttestationResponse::new();
let ssz_serialized_attestation = req.get_attestation().get_ssz();
let attestation = match Attestation::ssz_decode(ssz_serialized_attestation, 0) {
Ok((v, _index)) => v,
let attestation = match Attestation::from_ssz_bytes(ssz_serialized_attestation) {
Ok(v) => v,
Err(_) => {
let log_clone = self.log.clone();
let f = sink
+5 -5
View File
@@ -11,7 +11,7 @@ use protos::services::{
use protos::services_grpc::BeaconBlockService;
use slog::Logger;
use slog::{error, info, trace, warn};
use ssz::{ssz_encode, Decodable};
use ssz::{ssz_encode, Decode};
use std::sync::Arc;
use types::{BeaconBlock, EthSpec, Signature, Slot};
@@ -35,8 +35,8 @@ impl<E: EthSpec> BeaconBlockService for BeaconBlockServiceInstance<E> {
// decode the request
// TODO: requested slot currently unused, see: https://github.com/sigp/lighthouse/issues/336
let _requested_slot = Slot::from(req.get_slot());
let randao_reveal = match Signature::ssz_decode(req.get_randao_reveal(), 0) {
Ok((reveal, _index)) => reveal,
let randao_reveal = match Signature::from_ssz_bytes(req.get_randao_reveal()) {
Ok(reveal) => reveal,
Err(_) => {
// decode error, incorrect signature
let log_clone = self.log.clone();
@@ -91,8 +91,8 @@ impl<E: EthSpec> BeaconBlockService for BeaconBlockServiceInstance<E> {
let ssz_serialized_block = req.get_block().get_ssz();
match BeaconBlock::ssz_decode(ssz_serialized_block, 0) {
Ok((block, _i)) => {
match BeaconBlock::from_ssz_bytes(ssz_serialized_block) {
Ok(block) => {
match self.chain.process_block(block.clone()) {
Ok(outcome) => {
if outcome.sucessfully_processed() {
+2 -2
View File
@@ -5,7 +5,7 @@ use grpcio::{RpcContext, RpcStatus, RpcStatusCode, UnarySink};
use protos::services::{ActiveValidator, GetDutiesRequest, GetDutiesResponse, ValidatorDuty};
use protos::services_grpc::ValidatorService;
use slog::{trace, warn};
use ssz::decode;
use ssz::Decode;
use std::sync::Arc;
use types::{Epoch, EthSpec, RelativeEpoch};
@@ -74,7 +74,7 @@ impl<E: EthSpec> ValidatorService for ValidatorServiceInstance<E> {
for validator_pk in validators.get_public_keys() {
let mut active_validator = ActiveValidator::new();
let public_key = match decode::<PublicKey>(validator_pk) {
let public_key = match PublicKey::from_ssz_bytes(validator_pk) {
Ok(v) => v,
Err(_) => {
let log_clone = self.log.clone();
+1 -1
View File
@@ -30,7 +30,7 @@ Rust crates containing logic common across the Lighthouse project.
- [`ssz`](utils/ssz/): an implementation of the SimpleSerialize
serialization/deserialization protocol used by Eth 2.0.
- [`ssz_derive`](utils/ssz_derive/): provides procedural macros for
deriving SSZ `Encodable`, `Decodable`, and `TreeHash` methods.
deriving SSZ `Encode`, `Decode`, and `TreeHash` methods.
- [`swap_or_not_shuffle`](utils/swap_or_not_shuffle/): a list-shuffling
method which is slow, but allows for a subset of indices to be shuffled.
- [`test_random_derive`](utils/test_random_derive/): provides procedural
+2 -1
View File
@@ -3,12 +3,13 @@ use self::exit_cache::ExitCache;
use crate::test_utils::TestRandom;
use crate::*;
use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache};
use hashing::hash;
use int_to_bytes::int_to_bytes32;
use pubkey_cache::PubkeyCache;
use fixed_len_vec::{typenum::Unsigned, FixedLenVec};
use serde_derive::{Deserialize, Serialize};
use ssz::{hash, ssz_encode};
use ssz::ssz_encode;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
+9 -7
View File
@@ -15,7 +15,7 @@ use crate::test_utils::TestRandom;
use rand::RngCore;
use serde_derive::{Deserialize, Serialize};
use slog;
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError, Encode};
use std::cmp::{Ord, Ordering};
use std::fmt;
use std::hash::{Hash, Hasher};
@@ -145,11 +145,13 @@ mod epoch_tests {
#[test]
fn max_epoch_ssz() {
let max_epoch = Epoch::max_value();
let mut ssz = SszStream::new();
ssz.append(&max_epoch);
let encoded = ssz.drain();
assert_eq!(&encoded, &[255, 255, 255, 255, 255, 255, 255, 255]);
let (decoded, _i): (Epoch, usize) = <_>::ssz_decode(&encoded, 0).unwrap();
assert_eq!(max_epoch, decoded);
assert_eq!(
&max_epoch.as_ssz_bytes(),
&[255, 255, 255, 255, 255, 255, 255, 255]
);
assert_eq!(
max_epoch,
Epoch::from_ssz_bytes(&max_epoch.as_ssz_bytes()).unwrap()
);
}
}
+21 -7
View File
@@ -192,17 +192,31 @@ macro_rules! impl_display {
macro_rules! impl_ssz {
($type: ident) => {
impl Encodable for $type {
fn ssz_append(&self, s: &mut SszStream) {
s.append(&self.0);
impl Encode for $type {
fn is_ssz_fixed_len() -> bool {
<u64 as Encode>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<u64 as Encode>::ssz_fixed_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
self.0.ssz_append(buf)
}
}
impl Decodable for $type {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
let (value, i) = <_>::ssz_decode(bytes, i)?;
impl Decode for $type {
fn is_ssz_fixed_len() -> bool {
<u64 as Decode>::is_ssz_fixed_len()
}
Ok(($type(value), i))
fn ssz_fixed_len() -> usize {
<u64 as Decode>::ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
Ok($type(u64::from_ssz_bytes(bytes)?))
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ use crate::test_utils::TestRandom;
use rand::RngCore;
use serde_derive::Serialize;
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError, Encode};
use std::cmp::{Ord, Ordering};
use std::fmt;
use std::hash::{Hash, Hasher};
+2 -2
View File
@@ -5,13 +5,13 @@ macro_rules! ssz_tests {
#[test]
pub fn test_ssz_round_trip() {
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
use ssz::{ssz_encode, Decodable};
use ssz::{ssz_encode, Decode};
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = $type::random_for_test(&mut rng);
let bytes = ssz_encode(&original);
let (decoded, _): ($type, usize) = <_>::ssz_decode(&bytes, 0).unwrap();
let decoded = $type::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded);
}
+18 -24
View File
@@ -1,4 +1,4 @@
use super::{AggregatePublicKey, Signature, BLS_AGG_SIG_BYTE_SIZE};
use super::*;
use bls_aggregates::{
AggregatePublicKey as RawAggregatePublicKey, AggregateSignature as RawAggregateSignature,
};
@@ -6,7 +6,7 @@ use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor};
use ssz::{decode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A BLS aggregate signature.
@@ -99,8 +99,12 @@ impl AggregateSignature {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
for byte in bytes {
if *byte != 0 {
let sig =
RawAggregateSignature::from_bytes(&bytes).map_err(|_| DecodeError::Invalid)?;
let sig = RawAggregateSignature::from_bytes(&bytes).map_err(|_| {
DecodeError::BytesInvalid(
format!("Invalid AggregateSignature bytes: {:?}", bytes).to_string(),
)
})?;
return Ok(Self {
aggregate_signature: sig,
is_empty: false,
@@ -127,22 +131,11 @@ impl AggregateSignature {
}
}
impl Encodable for AggregateSignature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.as_bytes());
}
}
impl Decodable for AggregateSignature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_AGG_SIG_BYTE_SIZE {
return Err(DecodeError::TooShort);
}
let agg_sig = AggregateSignature::from_bytes(&bytes[i..(i + BLS_AGG_SIG_BYTE_SIZE)])
.map_err(|_| DecodeError::Invalid)?;
Ok((agg_sig, i + BLS_AGG_SIG_BYTE_SIZE))
}
}
impl_ssz!(
AggregateSignature,
BLS_AGG_SIG_BYTE_SIZE,
"AggregateSignature"
);
impl Serialize for AggregateSignature {
/// Serde serialization is compliant the Ethereum YAML test format.
@@ -161,8 +154,9 @@ impl<'de> Deserialize<'de> for AggregateSignature {
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(HexVisitor)?;
let agg_sig = decode(&bytes[..])
let agg_sig = AggregateSignature::from_ssz_bytes(&bytes)
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(agg_sig)
}
}
@@ -174,7 +168,7 @@ cached_tree_hash_ssz_encoding_as_vector!(AggregateSignature, 96);
mod tests {
use super::super::{Keypair, Signature};
use super::*;
use ssz::{decode, ssz_encode};
use ssz::Encode;
#[test]
pub fn test_ssz_round_trip() {
@@ -183,8 +177,8 @@ mod tests {
let mut original = AggregateSignature::new();
original.add(&Signature::new(&[42, 42], 0, &keypair.sk));
let bytes = ssz_encode(&original);
let decoded = decode::<AggregateSignature>(&bytes).unwrap();
let bytes = original.as_ssz_bytes();
let decoded = AggregateSignature::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded);
}
+24 -20
View File
@@ -3,7 +3,7 @@ use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A BLS aggregate signature.
@@ -57,28 +57,32 @@ impl FakeAggregateSignature {
) -> bool {
true
}
}
impl Encodable for FakeAggregateSignature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.bytes);
}
}
impl Decodable for FakeAggregateSignature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_AGG_SIG_BYTE_SIZE {
return Err(DecodeError::TooShort);
/// Convert bytes to fake BLS aggregate signature
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
if bytes.len() != BLS_AGG_SIG_BYTE_SIZE {
Err(DecodeError::InvalidByteLength {
len: bytes.len(),
expected: BLS_AGG_SIG_BYTE_SIZE,
})
} else {
Ok(Self {
bytes: bytes.to_vec(),
})
}
Ok((
FakeAggregateSignature {
bytes: bytes[i..(i + BLS_AGG_SIG_BYTE_SIZE)].to_vec(),
},
i + BLS_AGG_SIG_BYTE_SIZE,
))
}
pub fn as_bytes(&self) -> Vec<u8> {
self.bytes.clone()
}
}
impl_ssz!(
FakeAggregateSignature,
BLS_AGG_SIG_BYTE_SIZE,
"FakeAggregateSignature"
);
impl Serialize for FakeAggregateSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -94,7 +98,7 @@ impl<'de> Deserialize<'de> for FakeAggregateSignature {
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
let (obj, _) = <_>::ssz_decode(&bytes[..], 0)
let obj = <_>::from_ssz_bytes(&bytes[..])
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(obj)
}
@@ -117,7 +121,7 @@ mod tests {
original.add(&Signature::new(&[42, 42], 0, &keypair.sk));
let bytes = ssz_encode(&original);
let (decoded, _) = FakeAggregateSignature::ssz_decode(&bytes, 0).unwrap();
let decoded = FakeAggregateSignature::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded);
}
+22 -22
View File
@@ -4,7 +4,7 @@ use hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor;
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature.
@@ -49,31 +49,31 @@ impl FakeSignature {
true
}
/// Convert bytes to fake BLS Signature
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
if bytes.len() != BLS_SIG_BYTE_SIZE {
Err(DecodeError::InvalidByteLength {
len: bytes.len(),
expected: BLS_SIG_BYTE_SIZE,
})
} else {
Ok(Self {
bytes: bytes.to_vec(),
})
}
}
pub fn as_bytes(&self) -> Vec<u8> {
self.bytes.clone()
}
/// Returns a new empty signature.
pub fn empty_signature() -> Self {
FakeSignature::zero()
}
}
impl Encodable for FakeSignature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.bytes);
}
}
impl Decodable for FakeSignature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_SIG_BYTE_SIZE {
return Err(DecodeError::TooShort);
}
Ok((
FakeSignature {
bytes: bytes[i..(i + BLS_SIG_BYTE_SIZE)].to_vec(),
},
i + BLS_SIG_BYTE_SIZE,
))
}
}
impl_ssz!(FakeSignature, BLS_SIG_BYTE_SIZE, "FakeSignature");
tree_hash_ssz_encoding_as_vector!(FakeSignature);
cached_tree_hash_ssz_encoding_as_vector!(FakeSignature, 96);
@@ -93,7 +93,7 @@ impl<'de> Deserialize<'de> for FakeSignature {
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(HexVisitor)?;
let (pubkey, _) = <_>::ssz_decode(&bytes[..], 0)
let pubkey = <_>::from_ssz_bytes(&bytes[..])
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(pubkey)
}
@@ -112,7 +112,7 @@ mod tests {
let original = FakeSignature::new(&[42, 42], 0, &keypair.sk);
let bytes = ssz_encode(&original);
let (decoded, _) = FakeSignature::ssz_decode(&bytes, 0).unwrap();
let decoded = FakeSignature::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded);
}
+2
View File
@@ -1,6 +1,8 @@
extern crate bls_aggregates;
extern crate ssz;
#[macro_use]
mod macros;
mod aggregate_public_key;
mod keypair;
mod public_key;
+38
View File
@@ -0,0 +1,38 @@
macro_rules! impl_ssz {
($type: ident, $byte_size: expr, $item_str: expr) => {
impl ssz::Encode for $type {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
$byte_size
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.append(&mut self.as_bytes())
}
}
impl ssz::Decode for $type {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
$byte_size
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let len = bytes.len();
let expected = <Self as ssz::Decode>::ssz_fixed_len();
if len != expected {
Err(ssz::DecodeError::InvalidByteLength { len, expected })
} else {
$type::from_bytes(bytes)
}
}
}
};
}
+18 -22
View File
@@ -4,7 +4,7 @@ use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor};
use ssz::{decode, ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError};
use std::default;
use std::fmt;
use std::hash::{Hash, Hasher};
@@ -27,9 +27,19 @@ impl PublicKey {
&self.0
}
/// Returns the underlying point as compressed bytes.
///
/// Identical to `self.as_uncompressed_bytes()`.
fn as_bytes(&self) -> Vec<u8> {
self.as_raw().as_bytes()
}
/// Converts compressed bytes to PublicKey
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let pubkey = RawPublicKey::from_bytes(&bytes).map_err(|_| DecodeError::Invalid)?;
let pubkey = RawPublicKey::from_bytes(&bytes).map_err(|_| {
DecodeError::BytesInvalid(format!("Invalid PublicKey bytes: {:?}", bytes).to_string())
})?;
Ok(PublicKey(pubkey))
}
@@ -40,8 +50,9 @@ impl PublicKey {
/// Converts (x, y) bytes to PublicKey
pub fn from_uncompressed_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let pubkey =
RawPublicKey::from_uncompressed_bytes(&bytes).map_err(|_| DecodeError::Invalid)?;
let pubkey = RawPublicKey::from_uncompressed_bytes(&bytes).map_err(|_| {
DecodeError::BytesInvalid("Invalid PublicKey uncompressed bytes.".to_string())
})?;
Ok(PublicKey(pubkey))
}
@@ -68,22 +79,7 @@ impl default::Default for PublicKey {
}
}
impl Encodable for PublicKey {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.0.as_bytes());
}
}
impl Decodable for PublicKey {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_PUBLIC_KEY_BYTE_SIZE {
return Err(DecodeError::TooShort);
}
let raw_sig = RawPublicKey::from_bytes(&bytes[i..(i + BLS_PUBLIC_KEY_BYTE_SIZE)])
.map_err(|_| DecodeError::TooShort)?;
Ok((PublicKey(raw_sig), i + BLS_PUBLIC_KEY_BYTE_SIZE))
}
}
impl_ssz!(PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE, "PublicKey");
impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -100,7 +96,7 @@ impl<'de> Deserialize<'de> for PublicKey {
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(HexVisitor)?;
let pubkey = decode(&bytes[..])
let pubkey = Self::from_ssz_bytes(&bytes[..])
.map_err(|e| serde::de::Error::custom(format!("invalid pubkey ({:?})", e)))?;
Ok(pubkey)
}
@@ -139,7 +135,7 @@ mod tests {
let original = PublicKey::from_secret_key(&sk);
let bytes = ssz_encode(&original);
let (decoded, _) = PublicKey::ssz_decode(&bytes, 0).unwrap();
let decoded = PublicKey::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded);
}
+17 -22
View File
@@ -1,10 +1,10 @@
use super::BLS_SECRET_KEY_BYTE_SIZE;
use bls_aggregates::{DecodeError as BlsDecodeError, SecretKey as RawSecretKey};
use bls_aggregates::SecretKey as RawSecretKey;
use hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor;
use ssz::{decode, ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature.
@@ -19,11 +19,21 @@ impl SecretKey {
SecretKey(RawSecretKey::random())
}
/// Returns the underlying point as compressed bytes.
fn as_bytes(&self) -> Vec<u8> {
self.as_raw().as_bytes()
}
/// Instantiate a SecretKey from existing bytes.
///
/// Note: this is _not_ SSZ decoding.
pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, BlsDecodeError> {
Ok(SecretKey(RawSecretKey::from_bytes(bytes)?))
pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, DecodeError> {
Ok(SecretKey(RawSecretKey::from_bytes(bytes).map_err(|e| {
DecodeError::BytesInvalid(format!(
"Invalid SecretKey bytes: {:?} Error: {:?}",
bytes, e
))
})?))
}
/// Returns the underlying secret key.
@@ -32,22 +42,7 @@ impl SecretKey {
}
}
impl Encodable for SecretKey {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.0.as_bytes());
}
}
impl Decodable for SecretKey {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_SECRET_KEY_BYTE_SIZE {
return Err(DecodeError::TooShort);
}
let raw_sig = RawSecretKey::from_bytes(&bytes[i..(i + BLS_SECRET_KEY_BYTE_SIZE)])
.map_err(|_| DecodeError::TooShort)?;
Ok((SecretKey(raw_sig), i + BLS_SECRET_KEY_BYTE_SIZE))
}
}
impl_ssz!(SecretKey, BLS_SECRET_KEY_BYTE_SIZE, "SecretKey");
impl Serialize for SecretKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -64,7 +59,7 @@ impl<'de> Deserialize<'de> for SecretKey {
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(HexVisitor)?;
let secret_key = decode::<SecretKey>(&bytes[..])
let secret_key = SecretKey::from_ssz_bytes(&bytes[..])
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(secret_key)
}
@@ -84,7 +79,7 @@ mod tests {
.unwrap();
let bytes = ssz_encode(&original);
let (decoded, _) = SecretKey::ssz_decode(&bytes, 0).unwrap();
let decoded = SecretKey::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded);
}
+9 -20
View File
@@ -5,7 +5,7 @@ use hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor;
use ssz::{decode, ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature.
@@ -83,8 +83,11 @@ impl Signature {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
for byte in bytes {
if *byte != 0 {
let raw_signature =
RawSignature::from_bytes(&bytes).map_err(|_| DecodeError::Invalid)?;
let raw_signature = RawSignature::from_bytes(&bytes).map_err(|_| {
DecodeError::BytesInvalid(
format!("Invalid Signature bytes: {:?}", bytes).to_string(),
)
})?;
return Ok(Signature {
signature: raw_signature,
is_empty: false,
@@ -100,21 +103,7 @@ impl Signature {
}
}
impl Encodable for Signature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.as_bytes());
}
}
impl Decodable for Signature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_SIG_BYTE_SIZE {
return Err(DecodeError::TooShort);
}
let signature = Signature::from_bytes(&bytes[i..(i + BLS_SIG_BYTE_SIZE)])?;
Ok((signature, i + BLS_SIG_BYTE_SIZE))
}
}
impl_ssz!(Signature, BLS_SIG_BYTE_SIZE, "Signature");
tree_hash_ssz_encoding_as_vector!(Signature);
cached_tree_hash_ssz_encoding_as_vector!(Signature, 96);
@@ -136,7 +125,7 @@ impl<'de> Deserialize<'de> for Signature {
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(HexVisitor)?;
let signature = decode(&bytes[..])
let signature = Self::from_ssz_bytes(&bytes[..])
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(signature)
}
@@ -156,7 +145,7 @@ mod tests {
let original = Signature::new(&[42, 42], 0, &keypair.sk);
let bytes = ssz_encode(&original);
let decoded = decode::<Signature>(&bytes).unwrap();
let decoded = Signature::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded);
}
+24 -43
View File
@@ -7,7 +7,7 @@ use cached_tree_hash::cached_tree_hash_bytes_as_list;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode, PrefixedHexVisitor};
use ssz::{Decodable, Encodable};
use ssz::{Decode, Encode};
use std::cmp;
use std::default;
@@ -194,29 +194,23 @@ impl std::ops::BitOr for BooleanBitfield {
}
}
impl Encodable for BooleanBitfield {
// ssz_append encodes Self according to the `ssz` spec.
fn ssz_append(&self, s: &mut ssz::SszStream) {
s.append_vec(&self.to_bytes())
impl Encode for BooleanBitfield {
fn is_ssz_fixed_len() -> bool {
false
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.append(&mut self.to_bytes())
}
}
impl 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);
}
impl Decode for BooleanBitfield {
fn is_ssz_fixed_len() -> bool {
false
}
if len == 0 {
Ok((BooleanBitfield::new(), index + ssz::LENGTH_BYTES))
} else {
let bytes = &bytes[(index + 4)..(index + len + 4)];
let field = BooleanBitfield::from_bytes(bytes);
let index = index + ssz::LENGTH_BYTES + len;
Ok((field, index))
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
Ok(BooleanBitfield::from_bytes(bytes))
}
}
@@ -277,7 +271,7 @@ cached_tree_hash_bytes_as_list!(BooleanBitfield);
mod tests {
use super::*;
use serde_yaml;
use ssz::{decode, ssz_encode, SszStream};
use ssz::ssz_encode;
use tree_hash::TreeHash;
#[test]
@@ -452,30 +446,17 @@ mod tests {
#[test]
fn test_ssz_encode() {
let field = create_test_bitfield();
let mut stream = SszStream::new();
stream.append(&field);
assert_eq!(stream.drain(), vec![2, 0, 0, 0, 0b0000_0011, 0b1000_0111]);
assert_eq!(field.as_ssz_bytes(), vec![0b0000_0011, 0b1000_0111]);
let field = BooleanBitfield::from_elem(18, true);
let mut stream = SszStream::new();
stream.append(&field);
assert_eq!(
stream.drain(),
vec![3, 0, 0, 0, 0b0000_0011, 0b1111_1111, 0b1111_1111]
field.as_ssz_bytes(),
vec![0b0000_0011, 0b1111_1111, 0b1111_1111]
);
let mut b = BooleanBitfield::new();
b.set(1, true);
assert_eq!(
ssz_encode(&b),
vec![
0b0000_0001,
0b0000_0000,
0b0000_0000,
0b0000_0000,
0b0000_0010
]
);
assert_eq!(ssz_encode(&b), vec![0b0000_0010]);
}
fn create_test_bitfield() -> BooleanBitfield {
@@ -491,13 +472,13 @@ mod tests {
#[test]
fn test_ssz_decode() {
let encoded = vec![2, 0, 0, 0, 0b0000_0011, 0b1000_0111];
let field = decode::<BooleanBitfield>(&encoded).unwrap();
let encoded = vec![0b0000_0011, 0b1000_0111];
let field = BooleanBitfield::from_ssz_bytes(&encoded).unwrap();
let expected = create_test_bitfield();
assert_eq!(field, expected);
let encoded = vec![3, 0, 0, 0, 255, 255, 3];
let field = decode::<BooleanBitfield>(&encoded).unwrap();
let encoded = vec![255, 255, 3];
let field = BooleanBitfield::from_ssz_bytes(&encoded).unwrap();
let expected = BooleanBitfield::from_bytes(&[255, 255, 3]);
assert_eq!(field, expected);
}
@@ -527,7 +508,7 @@ mod tests {
fn test_ssz_round_trip() {
let original = BooleanBitfield::from_bytes(&vec![18; 12][..]);
let ssz = ssz_encode(&original);
let decoded = decode::<BooleanBitfield>(&ssz).unwrap();
let decoded = BooleanBitfield::from_ssz_bytes(&ssz).unwrap();
assert_eq!(original, decoded);
}
+81 -11
View File
@@ -1,7 +1,4 @@
use super::*;
// use cached_tree_hash::CachedTreeHash;
// use ssz::{Decodable, Encodable};
// use tree_hash::TreeHash;
impl<T, N: Unsigned> tree_hash::TreeHash for FixedLenVec<T, N>
where
@@ -51,20 +48,93 @@ where
}
}
impl<T, N: Unsigned> ssz::Encodable for FixedLenVec<T, N>
impl<T, N: Unsigned> ssz::Encode for FixedLenVec<T, N>
where
T: ssz::Encodable,
T: ssz::Encode,
{
fn ssz_append(&self, s: &mut ssz::SszStream) {
s.append_vec(&self.vec)
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
if <Self as ssz::Encode>::is_ssz_fixed_len() {
T::ssz_fixed_len() * N::to_usize()
} else {
ssz::BYTES_PER_LENGTH_OFFSET
}
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
if T::is_ssz_fixed_len() {
buf.reserve(T::ssz_fixed_len() * self.len());
for item in &self.vec {
item.ssz_append(buf);
}
} else {
let mut encoder = ssz::SszEncoder::list(buf, self.len() * ssz::BYTES_PER_LENGTH_OFFSET);
for item in &self.vec {
encoder.append(item);
}
encoder.finalize();
}
}
}
impl<T, N: Unsigned> ssz::Decodable for FixedLenVec<T, N>
impl<T, N: Unsigned> ssz::Decode for FixedLenVec<T, N>
where
T: ssz::Decodable + Default,
T: ssz::Decode + Default,
{
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), ssz::DecodeError> {
ssz::decode_ssz_list(bytes, index).and_then(|(vec, i)| Ok((vec.into(), i)))
fn is_ssz_fixed_len() -> bool {
T::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
if <Self as ssz::Decode>::is_ssz_fixed_len() {
T::ssz_fixed_len() * N::to_usize()
} else {
ssz::BYTES_PER_LENGTH_OFFSET
}
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
if bytes.len() == 0 {
Ok(FixedLenVec::from(vec![]))
} else if T::is_ssz_fixed_len() {
bytes
.chunks(T::ssz_fixed_len())
.map(|chunk| T::from_ssz_bytes(chunk))
.collect::<Result<Vec<T>, _>>()
.and_then(|vec| Ok(vec.into()))
} else {
ssz::decode_list_of_variable_length_items(bytes).and_then(|vec| Ok(vec.into()))
}
}
}
#[cfg(test)]
mod ssz_tests {
use super::*;
use ssz::*;
use typenum::*;
#[test]
fn encode() {
let vec: FixedLenVec<u16, U2> = vec![0; 2].into();
assert_eq!(vec.as_ssz_bytes(), vec![0, 0, 0, 0]);
assert_eq!(<FixedLenVec<u16, U2> as Encode>::ssz_fixed_len(), 4);
}
fn round_trip<T: Encode + Decode + std::fmt::Debug + PartialEq>(item: T) {
let encoded = &item.as_ssz_bytes();
assert_eq!(T::from_ssz_bytes(&encoded), Ok(item));
}
#[test]
fn u16_len_8() {
round_trip::<FixedLenVec<u16, U8>>(vec![42; 8].into());
round_trip::<FixedLenVec<u16, U8>>(vec![0; 8].into());
}
}
+1 -4
View File
@@ -9,10 +9,7 @@ pub use typenum;
mod impls;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct FixedLenVec<T, N>
where
N: Unsigned,
{
pub struct FixedLenVec<T, N> {
vec: Vec<T>,
_phantom: PhantomData<N>,
}
+8
View File
@@ -4,6 +4,14 @@ version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[[bench]]
name = "benches"
harness = false
[dev-dependencies]
criterion = "0.2"
ssz_derive = { path = "../ssz_derive" }
[dependencies]
bytes = "0.4.9"
ethereum-types = "0.5"
+10 -10
View File
@@ -38,8 +38,8 @@ spec is decided.*\
+ [bytes v0.4.9](#bytes-v049)
+ [ethereum-types](#ethereum-types)
* [Interface](#interface)
+ [Encodable](#encodable)
+ [Decodable](#decodable)
+ [Encode](#encodable)
+ [Decode](#decodable)
+ [SszStream](#sszstream)
- [new()](#new)
- [append(&mut self, value: &E) -> &mut Self](#appendmut-self-value-e---mut-self)
@@ -299,24 +299,24 @@ Github: [ https://github.com/paritytech/primitives ](https://github.com/parityte
## Interface
### Encodable
### Encode
A type is **Encodable** if it has a valid ``ssz_append`` function. This is
A type is **Encode** if it has a valid ``ssz_append`` function. This is
used to ensure that the object/type can be serialized.
```rust
pub trait Encodable {
pub trait Encode {
fn ssz_append(&self, s: &mut SszStream);
}
```
### Decodable
### Decode
A type is **Decodable** if it has a valid ``ssz_decode`` function. This is
A type is **Decode** if it has a valid ``ssz_decode`` function. This is
used to ensure the object is deserializable.
```rust
pub trait Decodable: Sized {
pub trait Decode: Sized {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError>;
}
```
@@ -342,7 +342,7 @@ Appends a value that can be encoded into the stream.
| Parameter | Description |
|:---------:|:-----------------------------------------|
| ``value`` | Encodable value to append to the stream. |
| ``value`` | Encode value to append to the stream. |
**Example**
@@ -371,7 +371,7 @@ Appends some vector (list) of encodable values to the stream.
| Parameter | Description |
|:---------:|:----------------------------------------------|
| ``vec`` | Vector of Encodable objects to be serialized. |
| ``vec`` | Vector of Encode objects to be serialized. |
**Example**
+80
View File
@@ -0,0 +1,80 @@
#[macro_use]
extern crate criterion;
use criterion::black_box;
use criterion::{Benchmark, Criterion};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
#[derive(Clone, Copy, Encode, Decode)]
pub struct FixedLen {
a: u64,
b: u64,
c: u64,
d: u64,
}
fn criterion_benchmark(c: &mut Criterion) {
let n = 8196;
let vec: Vec<u64> = vec![4242; 8196];
c.bench(
&format!("vec_of_{}_u64", n),
Benchmark::new("as_ssz_bytes", move |b| {
b.iter_with_setup(|| vec.clone(), |vec| black_box(vec.as_ssz_bytes()))
})
.sample_size(100),
);
let vec: Vec<u64> = vec![4242; 8196];
let bytes = vec.as_ssz_bytes();
c.bench(
&format!("vec_of_{}_u64", n),
Benchmark::new("from_ssz_bytes", move |b| {
b.iter_with_setup(
|| bytes.clone(),
|bytes| {
let vec: Vec<u64> = Vec::from_ssz_bytes(&bytes).unwrap();
black_box(vec)
},
)
})
.sample_size(100),
);
let fixed_len = FixedLen {
a: 42,
b: 42,
c: 42,
d: 42,
};
let fixed_len_vec: Vec<FixedLen> = vec![fixed_len; 8196];
let vec = fixed_len_vec.clone();
c.bench(
&format!("vec_of_{}_struct", n),
Benchmark::new("as_ssz_bytes", move |b| {
b.iter_with_setup(|| vec.clone(), |vec| black_box(vec.as_ssz_bytes()))
})
.sample_size(100),
);
let vec = fixed_len_vec.clone();
let bytes = vec.as_ssz_bytes();
c.bench(
&format!("vec_of_{}_struct", n),
Benchmark::new("from_ssz_bytes", move |b| {
b.iter_with_setup(
|| bytes.clone(),
|bytes| {
let vec: Vec<u64> = Vec::from_ssz_bytes(&bytes).unwrap();
black_box(vec)
},
)
})
.sample_size(100),
);
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
+16
View File
@@ -0,0 +1,16 @@
//! Encode and decode a list many times.
//!
//! Useful for `cargo flamegraph`.
use ssz::{Decode, Encode};
fn main() {
let vec: Vec<u64> = vec![4242; 8196];
let output: Vec<Vec<u64>> = (0..40_000)
.into_iter()
.map(|_| Vec::from_ssz_bytes(&vec.as_ssz_bytes()).unwrap())
.collect();
println!("{}", output.len());
}
@@ -0,0 +1,32 @@
//! Encode and decode a list many times.
//!
//! Useful for `cargo flamegraph`.
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
#[derive(Clone, Copy, Encode, Decode)]
pub struct FixedLen {
a: u64,
b: u64,
c: u64,
d: u64,
}
fn main() {
let fixed_len = FixedLen {
a: 42,
b: 42,
c: 42,
d: 42,
};
let vec: Vec<FixedLen> = vec![fixed_len; 8196];
let output: Vec<Vec<u64>> = (0..40_000)
.into_iter()
.map(|_| Vec::from_ssz_bytes(&vec.as_ssz_bytes()).unwrap())
.collect();
println!("{}", output.len());
}
@@ -0,0 +1,66 @@
use ssz::{Decode, DecodeError, Encode, SszDecoderBuilder, SszEncoder};
#[derive(Debug, PartialEq)]
pub struct Foo {
a: u16,
b: Vec<u8>,
c: u16,
}
impl Encode for Foo {
fn is_ssz_fixed_len() -> bool {
<u16 as Encode>::is_ssz_fixed_len() && <Vec<u16> as Encode>::is_ssz_fixed_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
let offset = <u16 as Encode>::ssz_fixed_len()
+ <Vec<u16> as Encode>::ssz_fixed_len()
+ <u16 as Encode>::ssz_fixed_len();
let mut encoder = SszEncoder::container(buf, offset);
encoder.append(&self.a);
encoder.append(&self.b);
encoder.append(&self.c);
encoder.finalize();
}
}
impl Decode for Foo {
fn is_ssz_fixed_len() -> bool {
<u16 as Decode>::is_ssz_fixed_len() && <Vec<u16> as Decode>::is_ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let mut builder = SszDecoderBuilder::new(bytes);
builder.register_type::<u16>()?;
builder.register_type::<Vec<u8>>()?;
builder.register_type::<u16>()?;
let mut decoder = builder.build()?;
Ok(Self {
a: decoder.decode_next()?,
b: decoder.decode_next()?,
c: decoder.decode_next()?,
})
}
}
fn main() {
let foo = Foo {
a: 42,
b: vec![0, 1, 2, 3],
c: 11,
};
let bytes = vec![42, 0, 8, 0, 0, 0, 11, 0, 0, 1, 2, 3];
assert_eq!(foo.as_ssz_bytes(), bytes);
let decoded_foo = Foo::from_ssz_bytes(&bytes).unwrap();
assert_eq!(foo, decoded_foo);
}
+219 -186
View File
@@ -1,215 +1,248 @@
use super::LENGTH_BYTES;
use super::*;
pub mod impls;
/// Returned when SSZ decoding fails.
#[derive(Debug, PartialEq)]
pub enum DecodeError {
TooShort,
TooLong,
Invalid,
/// The bytes supplied were too short to be decoded into the specified type.
InvalidByteLength { len: usize, expected: usize },
/// The given bytes were too short to be read as a length prefix.
InvalidLengthPrefix { len: usize, expected: usize },
/// A length offset pointed to a byte that was out-of-bounds (OOB).
///
/// A bytes may be OOB for the following reasons:
///
/// - It is `>= bytes.len()`.
/// - When decoding variable length items, the 1st offset points "backwards" into the fixed
/// length items (i.e., `length[0] < BYTES_PER_LENGTH_OFFSET`).
/// - When decoding variable-length items, the `n`'th offset was less than the `n-1`'th offset.
OutOfBoundsByte { i: usize },
/// The given bytes were invalid for some application-level reason.
BytesInvalid(String),
}
pub trait Decodable: Sized {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError>;
}
/// Decode the given bytes for the given type
/// Provides SSZ decoding (de-serialization) via the `from_ssz_bytes(&bytes)` method.
///
/// The single ssz encoded value/container/list will be decoded as the given type,
/// by recursively calling `ssz_decode`.
/// Check on totality for underflowing the length of bytes and overflow checks done per container
pub fn decode<T>(ssz_bytes: &[u8]) -> Result<(T), DecodeError>
where
T: Decodable,
{
let (decoded, i): (T, usize) = match T::ssz_decode(ssz_bytes, 0) {
Err(e) => return Err(e),
Ok(v) => v,
};
/// See `examples/` for manual implementations or the crate root for implementations using
/// `#[derive(Decode)]`.
pub trait Decode: Sized {
/// Returns `true` if this object has a fixed-length.
///
/// I.e., there are no variable length items in this object or any of it's contained objects.
fn is_ssz_fixed_len() -> bool;
if i < ssz_bytes.len() {
return Err(DecodeError::TooLong);
/// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes.
///
/// By default, this is set to `BYTES_PER_LENGTH_OFFSET` which is suitable for variable length
/// objects, but not fixed-length objects. Fixed-length objects _must_ return a value which
/// represents their length.
fn ssz_fixed_len() -> usize {
BYTES_PER_LENGTH_OFFSET
}
Ok(decoded)
/// Attempts to decode `Self` from `bytes`, returning a `DecodeError` on failure.
///
/// The supplied bytes must be the exact length required to decode `Self`, excess bytes will
/// result in an error.
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError>;
}
/// Decode a vector (list) of encoded bytes.
#[derive(Copy, Clone, Debug)]
pub struct Offset {
position: usize,
offset: usize,
}
/// Builds an `SszDecoder`.
///
/// 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))
/// The purpose of this struct is to split some SSZ bytes into individual slices. The builder is
/// then converted into a `SszDecoder` which decodes those values into object instances.
///
/// See [`SszDecoder`](struct.SszDecoder.html) for usage examples.
pub struct SszDecoderBuilder<'a> {
bytes: &'a [u8],
items: Vec<&'a [u8]>,
offsets: Vec<Offset>,
items_index: usize,
}
/// Given some number of bytes, interpret the first four
/// bytes as a 32-bit little-endian integer and return the
/// result.
pub fn decode_length(
bytes: &[u8],
index: usize,
length_bytes: usize,
) -> Result<usize, DecodeError> {
if bytes.len() < index + length_bytes {
return Err(DecodeError::TooShort);
};
let mut len: usize = 0;
for (i, byte) in bytes
.iter()
.enumerate()
.take(index + length_bytes)
.skip(index)
{
let offset = (i - index) * 8;
len |= (*byte as usize) << offset;
}
Ok(len)
}
#[cfg(test)]
mod tests {
use super::super::encode::*;
use super::*;
#[test]
fn test_ssz_decode_length() {
let decoded = decode_length(&vec![1, 0, 0, 0], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 1);
let decoded = decode_length(&vec![0, 1, 0, 0], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 256);
let decoded = decode_length(&vec![255, 1, 0, 0], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 511);
let decoded = decode_length(&vec![255, 255, 255, 255], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 4294967295);
}
#[test]
fn test_encode_decode_length() {
let params: Vec<usize> = vec![
0,
1,
2,
3,
7,
8,
16,
2 ^ 8,
2 ^ 8 + 1,
2 ^ 16,
2 ^ 16 + 1,
2 ^ 24,
2 ^ 24 + 1,
2 ^ 32,
];
for i in params {
let decoded = decode_length(&encode_length(i, LENGTH_BYTES), 0, LENGTH_BYTES).unwrap();
assert_eq!(i, decoded);
impl<'a> SszDecoderBuilder<'a> {
/// Instantiate a new builder that should build a `SszDecoder` over the given `bytes` which
/// are assumed to be the SSZ encoding of some object.
pub fn new(bytes: &'a [u8]) -> Self {
Self {
bytes,
items: vec![],
offsets: vec![],
items_index: 0,
}
}
#[test]
fn test_encode_decode_ssz_list() {
let test_vec: Vec<u16> = vec![256; 12];
let mut stream = SszStream::new();
stream.append_vec(&test_vec);
let ssz = stream.drain();
/// Declares that some type `T` is the next item in `bytes`.
pub fn register_type<T: Decode>(&mut self) -> Result<(), DecodeError> {
if T::is_ssz_fixed_len() {
let start = self.items_index;
self.items_index += T::ssz_fixed_len();
// u16
let decoded: (Vec<u16>, usize) = decode_ssz_list(&ssz, 0).unwrap();
let slice = self.bytes.get(start..self.items_index).ok_or_else(|| {
DecodeError::InvalidByteLength {
len: self.bytes.len(),
expected: self.items_index,
}
})?;
assert_eq!(decoded.0, test_vec);
assert_eq!(decoded.1, LENGTH_BYTES + (12 * 2));
self.items.push(slice);
} else {
let offset = read_offset(&self.bytes[self.items_index..])?;
let previous_offset = self
.offsets
.last()
.and_then(|o| Some(o.offset))
.unwrap_or_else(|| BYTES_PER_LENGTH_OFFSET);
if previous_offset > offset {
return Err(DecodeError::OutOfBoundsByte { i: offset });
} else if offset > self.bytes.len() {
return Err(DecodeError::OutOfBoundsByte { i: offset });
}
self.offsets.push(Offset {
position: self.items.len(),
offset,
});
// Push an empty slice into items; it will be replaced later.
self.items.push(&[]);
self.items_index += BYTES_PER_LENGTH_OFFSET;
}
Ok(())
}
#[test]
fn test_decode_ssz_list() {
// u16
let v: Vec<u16> = vec![10, 10, 10, 10];
let decoded: (Vec<u16>, usize) =
decode_ssz_list(&vec![8, 0, 0, 0, 10, 0, 10, 0, 10, 0, 10, 0], 0).unwrap();
fn finalize(&mut self) -> Result<(), DecodeError> {
if !self.offsets.is_empty() {
// Check to ensure the first offset points to the byte immediately following the
// fixed-length bytes.
if self.offsets[0].offset != self.items_index {
return Err(DecodeError::OutOfBoundsByte {
i: self.offsets[0].offset,
});
}
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, LENGTH_BYTES + (4 * 2));
// Iterate through each pair of offsets, grabbing the slice between each of the offsets.
for pair in self.offsets.windows(2) {
let a = pair[0];
let b = pair[1];
// u32
let v: Vec<u32> = vec![10, 10, 10, 10];
let decoded: (Vec<u32>, usize) = decode_ssz_list(
&vec![
16, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 00,
],
0,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, 20);
self.items[a.position] = &self.bytes[a.offset..b.offset];
}
// u64
let v: Vec<u64> = vec![10, 10, 10, 10];
let decoded: (Vec<u64>, usize) = decode_ssz_list(
&vec![
32, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0,
0, 0, 10, 0, 0, 0, 0, 0, 0, 0,
],
0,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, LENGTH_BYTES + (8 * 4));
// Handle the last offset, pushing a slice from it's start through to the end of
// `self.bytes`.
if let Some(last) = self.offsets.last() {
self.items[last.position] = &self.bytes[last.offset..]
}
} else {
// If the container is fixed-length, ensure there are no excess bytes.
if self.items_index != self.bytes.len() {
return Err(DecodeError::InvalidByteLength {
len: self.bytes.len(),
expected: self.items_index,
});
}
}
// Check that it can accept index
let v: Vec<usize> = vec![15, 15, 15, 15];
let offset = 10;
let decoded: (Vec<usize>, usize) = decode_ssz_list(
&vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 32, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0,
0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0,
],
offset,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, offset + LENGTH_BYTES + (8 * 4));
Ok(())
}
// Check that length > bytes throws error
let decoded: Result<(Vec<usize>, usize), DecodeError> =
decode_ssz_list(&vec![32, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0], 0);
assert_eq!(decoded, Err(DecodeError::TooShort));
/// Finalizes the builder, returning a `SszDecoder` that may be used to instantiate objects.
pub fn build(mut self) -> Result<SszDecoder<'a>, DecodeError> {
self.finalize()?;
// Check that incorrect index throws error
let decoded: Result<(Vec<usize>, usize), DecodeError> =
decode_ssz_list(&vec![15, 0, 0, 0, 0, 0, 0, 0], 16);
assert_eq!(decoded, Err(DecodeError::TooShort));
Ok(SszDecoder { items: self.items })
}
}
/// Decodes some slices of SSZ into object instances. Should be instantiated using
/// [`SszDecoderBuilder`](struct.SszDecoderBuilder.html).
///
/// ## Example
///
/// ```rust
/// use ssz_derive::{Encode, Decode};
/// use ssz::{Decode, Encode, SszDecoder, SszDecoderBuilder};
///
/// #[derive(PartialEq, Debug, Encode, Decode)]
/// struct Foo {
/// a: u64,
/// b: Vec<u16>,
/// }
///
/// fn main() {
/// let foo = Foo {
/// a: 42,
/// b: vec![1, 3, 3, 7]
/// };
///
/// let bytes = foo.as_ssz_bytes();
///
/// let mut builder = SszDecoderBuilder::new(&bytes);
///
/// builder.register_type::<u64>().unwrap();
/// builder.register_type::<Vec<u16>>().unwrap();
///
/// let mut decoder = builder.build().unwrap();
///
/// let decoded_foo = Foo {
/// a: decoder.decode_next().unwrap(),
/// b: decoder.decode_next().unwrap(),
/// };
///
/// assert_eq!(foo, decoded_foo);
/// }
///
/// ```
pub struct SszDecoder<'a> {
items: Vec<&'a [u8]>,
}
impl<'a> SszDecoder<'a> {
/// Decodes the next item.
///
/// # Panics
///
/// Panics when attempting to decode more items than actually exist.
pub fn decode_next<T: Decode>(&mut self) -> Result<T, DecodeError> {
T::from_ssz_bytes(self.items.remove(0))
}
}
/// Reads a `BYTES_PER_LENGTH_OFFSET`-byte length from `bytes`, where `bytes.len() >=
/// BYTES_PER_LENGTH_OFFSET`.
fn read_offset(bytes: &[u8]) -> Result<usize, DecodeError> {
decode_offset(bytes.get(0..BYTES_PER_LENGTH_OFFSET).ok_or_else(|| {
DecodeError::InvalidLengthPrefix {
len: bytes.len(),
expected: BYTES_PER_LENGTH_OFFSET,
}
})?)
}
/// Decode bytes as a little-endian usize, returning an `Err` if `bytes.len() !=
/// BYTES_PER_LENGTH_OFFSET`.
fn decode_offset(bytes: &[u8]) -> Result<usize, DecodeError> {
let len = bytes.len();
let expected = BYTES_PER_LENGTH_OFFSET;
if len != expected {
Err(DecodeError::InvalidLengthPrefix { len, expected })
} else {
let mut array: [u8; BYTES_PER_LENGTH_OFFSET] = std::default::Default::default();
array.clone_from_slice(bytes);
Ok(u32::from_le_bytes(array) as usize)
}
}
+422
View File
@@ -0,0 +1,422 @@
use super::*;
use ethereum_types::H256;
macro_rules! impl_decodable_for_uint {
($type: ident, $bit_size: expr) => {
impl Decode for $type {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
$bit_size / 8
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let len = bytes.len();
let expected = <Self as Decode>::ssz_fixed_len();
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
let mut array: [u8; $bit_size / 8] = std::default::Default::default();
array.clone_from_slice(bytes);
Ok(Self::from_le_bytes(array))
}
}
}
};
}
impl_decodable_for_uint!(u8, 8);
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 Decode for bool {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
1
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let len = bytes.len();
let expected = <Self as Decode>::ssz_fixed_len();
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
match bytes[0] {
0b0000_0000 => Ok(false),
0b0000_0001 => Ok(true),
_ => {
return Err(DecodeError::BytesInvalid(
format!("Out-of-range for boolean: {}", bytes[0]).to_string(),
))
}
}
}
}
}
impl Decode for H256 {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
32
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let len = bytes.len();
let expected = <Self as Decode>::ssz_fixed_len();
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
Ok(H256::from_slice(bytes))
}
}
}
macro_rules! impl_decodable_for_u8_array {
($len: expr) => {
impl Decode for [u8; $len] {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
$len
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let len = bytes.len();
let expected = <Self as Decode>::ssz_fixed_len();
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
let mut array: [u8; $len] = [0; $len];
array.copy_from_slice(&bytes[..]);
Ok(array)
}
}
}
};
}
impl_decodable_for_u8_array!(4);
impl<T: Decode> Decode for Vec<T> {
fn is_ssz_fixed_len() -> bool {
false
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
if bytes.len() == 0 {
Ok(vec![])
} else if T::is_ssz_fixed_len() {
bytes
.chunks(T::ssz_fixed_len())
.map(|chunk| T::from_ssz_bytes(chunk))
.collect()
} else {
decode_list_of_variable_length_items(bytes)
}
}
}
/// Decodes `bytes` as if it were a list of variable-length items.
///
/// The `ssz::SszDecoder` can also perform this functionality, however it it significantly faster
/// as it is optimized to read same-typed items whilst `ssz::SszDecoder` supports reading items of
/// differing types.
pub fn decode_list_of_variable_length_items<T: Decode>(
bytes: &[u8],
) -> Result<Vec<T>, DecodeError> {
let mut next_variable_byte = read_offset(bytes)?;
// The value of the first offset must not point back into the same bytes that defined
// it.
if next_variable_byte < BYTES_PER_LENGTH_OFFSET {
return Err(DecodeError::OutOfBoundsByte {
i: next_variable_byte,
});
}
let num_items = next_variable_byte / BYTES_PER_LENGTH_OFFSET;
// The fixed-length section must be a clean multiple of `BYTES_PER_LENGTH_OFFSET`.
if next_variable_byte != num_items * BYTES_PER_LENGTH_OFFSET {
return Err(DecodeError::InvalidByteLength {
len: next_variable_byte,
expected: num_items * BYTES_PER_LENGTH_OFFSET,
});
}
let mut values = Vec::with_capacity(num_items);
for i in 1..=num_items {
let slice_option = if i == num_items {
bytes.get(next_variable_byte..)
} else {
let offset = read_offset(&bytes[(i * BYTES_PER_LENGTH_OFFSET)..])?;
let start = next_variable_byte;
next_variable_byte = offset;
bytes.get(start..next_variable_byte)
};
let slice = slice_option.ok_or_else(|| DecodeError::OutOfBoundsByte {
i: next_variable_byte,
})?;
values.push(T::from_ssz_bytes(slice)?);
}
Ok(values)
}
#[cfg(test)]
mod tests {
use super::*;
// Note: decoding of valid bytes is generally tested "indirectly" in the `/tests` dir, by
// encoding then decoding the element.
#[test]
fn invalid_u8_array_4() {
assert_eq!(
<[u8; 4]>::from_ssz_bytes(&[0; 3]),
Err(DecodeError::InvalidByteLength {
len: 3,
expected: 4
})
);
assert_eq!(
<[u8; 4]>::from_ssz_bytes(&[0; 5]),
Err(DecodeError::InvalidByteLength {
len: 5,
expected: 4
})
);
}
#[test]
fn invalid_bool() {
assert_eq!(
bool::from_ssz_bytes(&[0; 2]),
Err(DecodeError::InvalidByteLength {
len: 2,
expected: 1
})
);
assert_eq!(
bool::from_ssz_bytes(&[]),
Err(DecodeError::InvalidByteLength {
len: 0,
expected: 1
})
);
if let Err(DecodeError::BytesInvalid(_)) = bool::from_ssz_bytes(&[2]) {
// Success.
} else {
panic!("Did not return error on invalid bool val")
}
}
#[test]
fn invalid_h256() {
assert_eq!(
H256::from_ssz_bytes(&[0; 33]),
Err(DecodeError::InvalidByteLength {
len: 33,
expected: 32
})
);
assert_eq!(
H256::from_ssz_bytes(&[0; 31]),
Err(DecodeError::InvalidByteLength {
len: 31,
expected: 32
})
);
}
#[test]
fn first_length_points_backwards() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[0, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 0 })
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[1, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 1 })
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[2, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 2 })
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[3, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 3 })
);
}
#[test]
fn lengths_are_decreasing() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[12, 0, 0, 0, 14, 0, 0, 0, 12, 0, 0, 0, 1, 0, 1, 0]),
Err(DecodeError::OutOfBoundsByte { i: 12 })
);
}
#[test]
fn awkward_fixed_lenth_portion() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[10, 0, 0, 0, 10, 0, 0, 0, 0, 0]),
Err(DecodeError::InvalidByteLength {
len: 10,
expected: 8
})
);
}
#[test]
fn length_out_of_bounds() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[5, 0, 0, 0]),
Err(DecodeError::InvalidByteLength {
len: 5,
expected: 4
})
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[8, 0, 0, 0, 9, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 9 })
);
}
#[test]
fn vec_of_vec_of_u16() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[4, 0, 0, 0]),
Ok(vec![vec![]])
);
assert_eq!(
<Vec<u16>>::from_ssz_bytes(&[0, 0, 1, 0, 2, 0, 3, 0]),
Ok(vec![0, 1, 2, 3])
);
assert_eq!(<u16>::from_ssz_bytes(&[16, 0]), Ok(16));
assert_eq!(<u16>::from_ssz_bytes(&[0, 1]), Ok(256));
assert_eq!(<u16>::from_ssz_bytes(&[255, 255]), Ok(65535));
assert_eq!(
<u16>::from_ssz_bytes(&[255]),
Err(DecodeError::InvalidByteLength {
len: 1,
expected: 2
})
);
assert_eq!(
<u16>::from_ssz_bytes(&[]),
Err(DecodeError::InvalidByteLength {
len: 0,
expected: 2
})
);
assert_eq!(
<u16>::from_ssz_bytes(&[0, 1, 2]),
Err(DecodeError::InvalidByteLength {
len: 3,
expected: 2
})
);
}
#[test]
fn vec_of_u16() {
assert_eq!(<Vec<u16>>::from_ssz_bytes(&[0, 0, 0, 0]), Ok(vec![0, 0]));
assert_eq!(
<Vec<u16>>::from_ssz_bytes(&[0, 0, 1, 0, 2, 0, 3, 0]),
Ok(vec![0, 1, 2, 3])
);
assert_eq!(<u16>::from_ssz_bytes(&[16, 0]), Ok(16));
assert_eq!(<u16>::from_ssz_bytes(&[0, 1]), Ok(256));
assert_eq!(<u16>::from_ssz_bytes(&[255, 255]), Ok(65535));
assert_eq!(
<u16>::from_ssz_bytes(&[255]),
Err(DecodeError::InvalidByteLength {
len: 1,
expected: 2
})
);
assert_eq!(
<u16>::from_ssz_bytes(&[]),
Err(DecodeError::InvalidByteLength {
len: 0,
expected: 2
})
);
assert_eq!(
<u16>::from_ssz_bytes(&[0, 1, 2]),
Err(DecodeError::InvalidByteLength {
len: 3,
expected: 2
})
);
}
#[test]
fn u16() {
assert_eq!(<u16>::from_ssz_bytes(&[0, 0]), Ok(0));
assert_eq!(<u16>::from_ssz_bytes(&[16, 0]), Ok(16));
assert_eq!(<u16>::from_ssz_bytes(&[0, 1]), Ok(256));
assert_eq!(<u16>::from_ssz_bytes(&[255, 255]), Ok(65535));
assert_eq!(
<u16>::from_ssz_bytes(&[255]),
Err(DecodeError::InvalidByteLength {
len: 1,
expected: 2
})
);
assert_eq!(
<u16>::from_ssz_bytes(&[]),
Err(DecodeError::InvalidByteLength {
len: 0,
expected: 2
})
);
assert_eq!(
<u16>::from_ssz_bytes(&[0, 1, 2]),
Err(DecodeError::InvalidByteLength {
len: 3,
expected: 2
})
);
}
}
+151 -134
View File
@@ -1,85 +1,159 @@
use super::LENGTH_BYTES;
use super::*;
pub trait Encodable {
fn ssz_append(&self, s: &mut SszStream);
}
mod impls;
/// Provides a buffer for appending ssz-encodable values.
/// Provides SSZ encoding (serialization) via the `as_ssz_bytes(&self)` method.
///
/// Use the `append()` fn to add a value to a list, then use
/// the `drain()` method to consume the struct and return the
/// ssz encoded bytes.
#[derive(Default)]
pub struct SszStream {
buffer: Vec<u8>,
/// See `examples/` for manual implementations or the crate root for implementations using
/// `#[derive(Encode)]`.
pub trait Encode {
/// Returns `true` if this object has a fixed-length.
///
/// I.e., there are no variable length items in this object or any of it's contained objects.
fn is_ssz_fixed_len() -> bool;
/// Append the encoding `self` to `buf`.
///
/// Note, variable length objects need only to append their "variable length" portion, they do
/// not need to provide their offset.
fn ssz_append(&self, buf: &mut Vec<u8>);
/// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes.
///
/// By default, this is set to `BYTES_PER_LENGTH_OFFSET` which is suitable for variable length
/// objects, but not fixed-length objects. Fixed-length objects _must_ return a value which
/// represents their length.
fn ssz_fixed_len() -> usize {
BYTES_PER_LENGTH_OFFSET
}
/// Returns the full-form encoding of this object.
///
/// The default implementation of this method should suffice for most cases.
fn as_ssz_bytes(&self) -> Vec<u8> {
let mut buf = vec![];
self.ssz_append(&mut buf);
buf
}
}
impl SszStream {
/// Create a new, empty stream for writing ssz values.
pub fn new() -> Self {
SszStream { buffer: Vec::new() }
}
/// Allow for encoding an ordered series of distinct or indistinct objects as SSZ bytes.
///
/// **You must call `finalize(..)` after the final `append(..)` call** to ensure the bytes are
/// written to `buf`.
///
/// ## Example
///
/// Use `SszEncoder` to produce identical output to `foo.as_ssz_bytes()`:
///
/// ```rust
/// use ssz_derive::{Encode, Decode};
/// use ssz::{Decode, Encode, SszEncoder};
///
/// #[derive(PartialEq, Debug, Encode, Decode)]
/// struct Foo {
/// a: u64,
/// b: Vec<u16>,
/// }
///
/// fn main() {
/// let foo = Foo {
/// a: 42,
/// b: vec![1, 3, 3, 7]
/// };
///
/// let mut buf: Vec<u8> = vec![];
/// let offset = <u64 as Encode>::ssz_fixed_len() + <Vec<u16> as Encode>::ssz_fixed_len();
///
/// let mut encoder = SszEncoder::container(&mut buf, offset);
///
/// encoder.append(&foo.a);
/// encoder.append(&foo.b);
///
/// encoder.finalize();
///
/// assert_eq!(foo.as_ssz_bytes(), buf);
/// }
///
/// ```
pub struct SszEncoder<'a> {
offset: usize,
buf: &'a mut Vec<u8>,
variable_bytes: Vec<u8>,
}
/// 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.
impl<'a> SszEncoder<'a> {
/// Instantiate a new encoder for encoding a SSZ list.
///
/// The length of the supplied bytes will be concatenated
/// to the stream before the supplied bytes.
pub fn append_encoded_val(&mut self, vec: &[u8]) {
self.buffer
.extend_from_slice(&encode_length(vec.len(), LENGTH_BYTES));
self.buffer.extend_from_slice(&vec);
/// Identical to `Self::container`.
pub fn list(buf: &'a mut Vec<u8>, num_fixed_bytes: usize) -> Self {
Self::container(buf, num_fixed_bytes)
}
/// Append some ssz encoded bytes to the stream without calculating length
///
/// The raw bytes will be concatenated to the stream.
pub fn append_encoded_raw(&mut self, vec: &[u8]) {
self.buffer.extend_from_slice(&vec);
}
/// Instantiate a new encoder for encoding a SSZ container.
pub fn container(buf: &'a mut Vec<u8>, num_fixed_bytes: usize) -> Self {
buf.reserve(num_fixed_bytes);
/// Append some vector (list) of encodable values to the stream.
///
/// The length of the list will be concatenated to the stream, then
/// each item in the vector will be encoded and concatenated.
pub fn append_vec<E>(&mut self, vec: &[E])
where
E: Encodable,
{
let mut list_stream = SszStream::new();
for item in vec {
item.ssz_append(&mut list_stream);
Self {
offset: num_fixed_bytes,
buf,
variable_bytes: vec![],
}
self.append_encoded_val(&list_stream.drain());
}
/// Consume the stream and return the underlying bytes.
pub fn drain(self) -> Vec<u8> {
self.buffer
/// Append some `item` to the SSZ bytes.
pub fn append<T: Encode>(&mut self, item: &T) {
if T::is_ssz_fixed_len() {
item.ssz_append(&mut self.buf);
} else {
self.buf
.append(&mut encode_length(self.offset + self.variable_bytes.len()));
item.ssz_append(&mut self.variable_bytes);
}
}
/// Write the variable bytes to `self.bytes`.
///
/// This method must be called after the final `append(..)` call when serializing
/// variable-length items.
pub fn finalize(&mut self) -> &mut Vec<u8> {
self.buf.append(&mut self.variable_bytes);
&mut self.buf
}
}
/// Encode some length into a ssz size prefix.
/// Encode `len` as a little-endian byte vec of `BYTES_PER_LENGTH_OFFSET` length.
///
/// The ssz size prefix is 4 bytes, which is treated as a continuious
/// 32bit little-endian integer.
pub fn encode_length(len: usize, length_bytes: usize) -> Vec<u8> {
assert!(length_bytes > 0); // For sanity
assert!((len as usize) < 2usize.pow(length_bytes as u32 * 8));
let mut header: Vec<u8> = vec![0; length_bytes];
for (i, header_byte) in header.iter_mut().enumerate() {
let offset = i * 8;
*header_byte = ((len >> offset) & 0xff) as u8;
}
header
/// If `len` is larger than `2 ^ BYTES_PER_LENGTH_OFFSET`, a `debug_assert` is raised.
pub fn encode_length(len: usize) -> Vec<u8> {
// Note: it is possible for `len` to be larger than what can be encoded in
// `BYTES_PER_LENGTH_OFFSET` bytes, triggering this debug assertion.
//
// These are the alternatives to using a `debug_assert` here:
//
// 1. Use `assert`.
// 2. Push an error to the caller (e.g., `Option` or `Result`).
// 3. Ignore it completely.
//
// I have avoided (1) because it's basically a choice between "produce invalid SSZ" or "kill
// the entire program". I figure it may be possible for an attacker to trigger this assert and
// take the program down -- I think producing invalid SSZ is a better option than this.
//
// I have avoided (2) because this error will need to be propagated upstream, making encoding a
// function which may fail. I don't think this is ergonomic and the upsides don't outweigh the
// downsides.
//
// I figure a `debug_assertion` is better than (3) as it will give us a change to detect the
// error during testing.
//
// If you have a different opinion, feel free to start an issue and tag @paulhauner.
debug_assert!(len <= MAX_LENGTH_VALUE);
len.to_le_bytes()[0..BYTES_PER_LENGTH_OFFSET].to_vec()
}
#[cfg(test)]
@@ -87,84 +161,27 @@ mod tests {
use super::*;
#[test]
#[should_panic]
fn test_encode_length_0_bytes_panic() {
encode_length(0, 0);
}
fn test_encode_length() {
assert_eq!(encode_length(0), vec![0; 4]);
assert_eq!(encode_length(1), vec![1, 0, 0, 0]);
#[test]
fn test_encode_length_4_bytes() {
assert_eq!(encode_length(0, LENGTH_BYTES), vec![0; 4]);
assert_eq!(encode_length(1, LENGTH_BYTES), vec![1, 0, 0, 0]);
assert_eq!(encode_length(255, LENGTH_BYTES), vec![255, 0, 0, 0]);
assert_eq!(encode_length(256, LENGTH_BYTES), vec![0, 1, 0, 0]);
assert_eq!(
encode_length(4294967295, LENGTH_BYTES), // 2^(3*8) - 1
vec![255, 255, 255, 255]
encode_length(MAX_LENGTH_VALUE),
vec![255; BYTES_PER_LENGTH_OFFSET]
);
}
#[test]
fn test_encode_lower_length() {
assert_eq!(encode_length(0, LENGTH_BYTES - 2), vec![0; 2]);
assert_eq!(encode_length(1, LENGTH_BYTES - 2), vec![1, 0]);
}
#[test]
fn test_encode_higher_length() {
assert_eq!(encode_length(0, LENGTH_BYTES + 2), vec![0; 6]);
assert_eq!(encode_length(1, LENGTH_BYTES + 2), vec![1, 0, 0, 0, 0, 0]);
}
#[test]
#[should_panic]
fn test_encode_length_4_bytes_panic() {
encode_length(4294967296, LENGTH_BYTES); // 2^(3*8)
#[cfg(debug_assertions)]
fn test_encode_length_above_max_debug_panics() {
encode_length(MAX_LENGTH_VALUE + 1);
}
#[test]
fn test_encode_list() {
let test_vec: Vec<u16> = vec![256; 12];
let mut stream = SszStream::new();
stream.append_vec(&test_vec);
let ssz = stream.drain();
assert_eq!(ssz.len(), LENGTH_BYTES + (12 * 2));
assert_eq!(ssz[0..4], *vec![24, 0, 0, 0]);
assert_eq!(ssz[4..6], *vec![0, 1]);
}
#[test]
fn test_encode_mixed_prefixed() {
let test_vec: Vec<u16> = vec![100, 200];
let test_value: u8 = 5;
let mut stream = SszStream::new();
stream.append_vec(&test_vec);
stream.append(&test_value);
let ssz = stream.drain();
assert_eq!(ssz.len(), LENGTH_BYTES + (2 * 2) + 1);
assert_eq!(ssz[0..4], *vec![4, 0, 0, 0]);
assert_eq!(ssz[4..6], *vec![100, 0]);
assert_eq!(ssz[6..8], *vec![200, 0]);
assert_eq!(ssz[8], 5);
}
#[test]
fn test_encode_mixed_postfixed() {
let test_value: u8 = 5;
let test_vec: Vec<u16> = vec![100, 200];
let mut stream = SszStream::new();
stream.append(&test_value);
stream.append_vec(&test_vec);
let ssz = stream.drain();
assert_eq!(ssz.len(), 1 + LENGTH_BYTES + (2 * 2));
assert_eq!(ssz[0], 5);
assert_eq!(ssz[1..5], *vec![4, 0, 0, 0]);
assert_eq!(ssz[5..7], *vec![100, 0]);
assert_eq!(ssz[7..9], *vec![200, 0]);
#[cfg(not(debug_assertions))]
fn test_encode_length_above_max_not_debug_does_not_panic() {
assert_eq!(encode_length(MAX_LENGTH_VALUE + 1), vec![0; 4]);
}
}
+202
View File
@@ -0,0 +1,202 @@
use super::*;
use ethereum_types::H256;
macro_rules! impl_encodable_for_uint {
($type: ident, $bit_size: expr) => {
impl Encode for $type {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
$bit_size / 8
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&self.to_le_bytes());
}
}
};
}
impl_encodable_for_uint!(u8, 8);
impl_encodable_for_uint!(u16, 16);
impl_encodable_for_uint!(u32, 32);
impl_encodable_for_uint!(u64, 64);
impl_encodable_for_uint!(usize, 64);
impl<T: Encode> Encode for Vec<T> {
fn is_ssz_fixed_len() -> bool {
false
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
if T::is_ssz_fixed_len() {
buf.reserve(T::ssz_fixed_len() * self.len());
for item in self {
item.ssz_append(buf);
}
} else {
let mut encoder = SszEncoder::list(buf, self.len() * BYTES_PER_LENGTH_OFFSET);
for item in self {
encoder.append(item);
}
encoder.finalize();
}
}
}
impl Encode for bool {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
1
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&(*self as u8).to_le_bytes());
}
}
impl Encode for H256 {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
32
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(self.as_bytes());
}
}
macro_rules! impl_encodable_for_u8_array {
($len: expr) => {
impl Encode for [u8; $len] {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
$len
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&self[..]);
}
}
};
}
impl_encodable_for_u8_array!(4);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vec_of_u8() {
let vec: Vec<u8> = vec![];
assert_eq!(vec.as_ssz_bytes(), vec![]);
let vec: Vec<u8> = vec![1];
assert_eq!(vec.as_ssz_bytes(), vec![1]);
let vec: Vec<u8> = vec![0, 1, 2, 3];
assert_eq!(vec.as_ssz_bytes(), vec![0, 1, 2, 3]);
}
#[test]
fn vec_of_vec_of_u8() {
let vec: Vec<Vec<u8>> = vec![];
assert_eq!(vec.as_ssz_bytes(), vec![]);
let vec: Vec<Vec<u8>> = vec![vec![]];
assert_eq!(vec.as_ssz_bytes(), vec![4, 0, 0, 0]);
let vec: Vec<Vec<u8>> = vec![vec![], vec![]];
assert_eq!(vec.as_ssz_bytes(), vec![8, 0, 0, 0, 8, 0, 0, 0]);
let vec: Vec<Vec<u8>> = vec![vec![0, 1, 2], vec![11, 22, 33]];
assert_eq!(
vec.as_ssz_bytes(),
vec![8, 0, 0, 0, 11, 0, 0, 0, 0, 1, 2, 11, 22, 33]
);
}
#[test]
fn ssz_encode_u8() {
assert_eq!(0_u8.as_ssz_bytes(), vec![0]);
assert_eq!(1_u8.as_ssz_bytes(), vec![1]);
assert_eq!(100_u8.as_ssz_bytes(), vec![100]);
assert_eq!(255_u8.as_ssz_bytes(), vec![255]);
}
#[test]
fn ssz_encode_u16() {
assert_eq!(1_u16.as_ssz_bytes(), vec![1, 0]);
assert_eq!(100_u16.as_ssz_bytes(), vec![100, 0]);
assert_eq!((1_u16 << 8).as_ssz_bytes(), vec![0, 1]);
assert_eq!(65535_u16.as_ssz_bytes(), vec![255, 255]);
}
#[test]
fn ssz_encode_u32() {
assert_eq!(1_u32.as_ssz_bytes(), vec![1, 0, 0, 0]);
assert_eq!(100_u32.as_ssz_bytes(), vec![100, 0, 0, 0]);
assert_eq!((1_u32 << 16).as_ssz_bytes(), vec![0, 0, 1, 0]);
assert_eq!((1_u32 << 24).as_ssz_bytes(), vec![0, 0, 0, 1]);
assert_eq!((!0_u32).as_ssz_bytes(), vec![255, 255, 255, 255]);
}
#[test]
fn ssz_encode_u64() {
assert_eq!(1_u64.as_ssz_bytes(), vec![1, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(
(!0_u64).as_ssz_bytes(),
vec![255, 255, 255, 255, 255, 255, 255, 255]
);
}
#[test]
fn ssz_encode_usize() {
assert_eq!(1_usize.as_ssz_bytes(), vec![1, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(
(!0_usize).as_ssz_bytes(),
vec![255, 255, 255, 255, 255, 255, 255, 255]
);
}
#[test]
fn ssz_encode_bool() {
assert_eq!(true.as_ssz_bytes(), vec![1]);
assert_eq!(false.as_ssz_bytes(), vec![0]);
}
#[test]
fn ssz_encode_h256() {
assert_eq!(H256::from(&[0; 32]).as_ssz_bytes(), vec![0; 32]);
assert_eq!(H256::from(&[1; 32]).as_ssz_bytes(), vec![1; 32]);
let bytes = vec![
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
];
assert_eq!(H256::from_slice(&bytes).as_ssz_bytes(), bytes);
}
#[test]
fn ssz_encode_u8_array_4() {
assert_eq!([0, 0, 0, 0].as_ssz_bytes(), vec![0; 4]);
assert_eq!([1, 0, 0, 0].as_ssz_bytes(), vec![1, 0, 0, 0]);
assert_eq!([1, 2, 3, 4].as_ssz_bytes(), vec![1, 2, 3, 4]);
}
}
-306
View File
@@ -1,306 +0,0 @@
use super::decode::decode_ssz_list;
use super::ethereum_types::{Address, H256};
use super::{Decodable, DecodeError};
macro_rules! impl_decodable_for_uint {
($type: ident, $bit_size: expr) => {
impl Decodable for $type {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
assert!((0 < $bit_size) & ($bit_size <= 64) & ($bit_size % 8 == 0));
let max_bytes = $bit_size / 8;
if bytes.len() >= (index + max_bytes) {
let end_bytes = index + max_bytes;
let mut result: $type = 0;
for (i, byte) in bytes.iter().enumerate().take(end_bytes).skip(index) {
let offset = (i - index) * 8;
result |= ($type::from(*byte)) << offset;
}
Ok((result, end_bytes))
} else {
Err(DecodeError::TooShort)
}
}
}
};
}
macro_rules! impl_decodable_for_u8_array {
($len: expr) => {
impl Decodable for [u8; $len] {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if index + $len > bytes.len() {
Err(DecodeError::TooShort)
} else {
let mut array: [u8; $len] = [0; $len];
array.copy_from_slice(&bytes[index..index + $len]);
Ok((array, index + $len))
}
}
}
};
}
impl_decodable_for_uint!(u16, 16);
impl_decodable_for_uint!(u32, 32);
impl_decodable_for_uint!(u64, 64);
impl_decodable_for_uint!(usize, 64);
impl_decodable_for_u8_array!(4);
impl Decodable for u8 {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if index >= bytes.len() {
Err(DecodeError::TooShort)
} else {
Ok((bytes[index], index + 1))
}
}
}
impl Decodable for bool {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if index >= bytes.len() {
Err(DecodeError::TooShort)
} else {
let result = match bytes[index] {
0b0000_0000 => false,
0b0000_0001 => true,
_ => return Err(DecodeError::Invalid),
};
Ok((result, index + 1))
}
}
}
impl Decodable for H256 {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() < 32 || bytes.len() - 32 < index {
Err(DecodeError::TooShort)
} else {
Ok((H256::from_slice(&bytes[index..(index + 32)]), index + 32))
}
}
}
impl Decodable for Address {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() < 20 || bytes.len() - 20 < index {
Err(DecodeError::TooShort)
} else {
Ok((Address::from_slice(&bytes[index..(index + 20)]), index + 20))
}
}
}
impl<T> Decodable for Vec<T>
where
T: Decodable,
{
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
decode_ssz_list(bytes, index)
}
}
#[cfg(test)]
mod tests {
use super::super::{decode, DecodeError};
use super::*;
#[test]
fn test_ssz_decode_h256() {
/*
* Input is exact length
*/
let input = vec![42_u8; 32];
let (decoded, i) = H256::ssz_decode(&input, 0).unwrap();
assert_eq!(decoded.as_bytes(), &input[..]);
assert_eq!(i, 32);
/*
* Input is too long
*/
let mut input = vec![42_u8; 32];
input.push(12);
let (decoded, i) = H256::ssz_decode(&input, 0).unwrap();
assert_eq!(decoded.as_bytes(), &input[0..32]);
assert_eq!(i, 32);
/*
* Input is too short
*/
let input = vec![42_u8; 31];
let res = H256::ssz_decode(&input, 0);
assert_eq!(res, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_u16() {
let ssz = vec![0, 0];
let (result, index): (u16, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(result, 0);
assert_eq!(index, 2);
let ssz = vec![16, 0];
let (result, index): (u16, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(result, 16);
assert_eq!(index, 2);
let ssz = vec![0, 1];
let (result, index): (u16, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(result, 256);
assert_eq!(index, 2);
let ssz = vec![255, 255];
let (result, index): (u16, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 2);
assert_eq!(result, 65535);
let ssz = vec![1];
let result: Result<(u16, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_u32() {
let ssz = vec![0, 0, 0, 0];
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(result, 0);
assert_eq!(index, 4);
let ssz = vec![0, 1, 0, 0];
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 4);
assert_eq!(result, 256);
let ssz = vec![255, 255, 255, 0, 1, 0, 0];
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 3).unwrap();
assert_eq!(index, 7);
assert_eq!(result, 256);
let ssz = vec![0, 1, 200, 0];
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 4);
assert_eq!(result, 13107456);
let ssz = vec![255, 255, 255, 255];
let (result, index): (u32, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 4);
assert_eq!(result, 4294967295);
let ssz = vec![1, 0, 0];
let result: Result<(u32, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_u64() {
let ssz = vec![0, 0, 0, 0, 0, 0, 0, 0];
let (result, index): (u64, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 0);
let ssz = vec![255, 255, 255, 255, 255, 255, 255, 255];
let (result, index): (u64, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 18446744073709551615);
let ssz = vec![0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 255];
let (result, index): (u64, usize) = <_>::ssz_decode(&ssz, 3).unwrap();
assert_eq!(index, 11);
assert_eq!(result, 18374686479671623680);
let ssz = vec![0, 0, 0, 0, 0, 0, 0];
let result: Result<(u64, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_ssz_decode_usize() {
let ssz = vec![0, 0, 0, 0, 0, 0, 0, 0];
let (result, index): (usize, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 0);
let ssz = vec![0, 0, 8, 255, 255, 255, 255, 255, 255, 255, 255];
let (result, index): (usize, usize) = <_>::ssz_decode(&ssz, 3).unwrap();
assert_eq!(index, 11);
assert_eq!(result, 18446744073709551615);
let ssz = vec![255, 255, 255, 255, 255, 255, 255, 255, 255];
let (result, index): (usize, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 8);
assert_eq!(result, 18446744073709551615);
let ssz = vec![0, 0, 0, 0, 0, 0, 1];
let result: Result<(usize, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
fn test_decode_ssz_bounds() {
let err: Result<(u16, usize), DecodeError> = <_>::ssz_decode(&vec![1], 2);
assert_eq!(err, Err(DecodeError::TooShort));
let err: Result<(u16, usize), DecodeError> = <_>::ssz_decode(&vec![0, 0, 0, 0], 3);
assert_eq!(err, Err(DecodeError::TooShort));
let result: u16 = <_>::ssz_decode(&vec![0, 0, 0, 1, 0], 3).unwrap().0;
assert_eq!(result, 1);
}
#[test]
fn test_decode_ssz_bool() {
let ssz = vec![0b0000_0000, 0b0000_0001];
let (result, index): (bool, usize) = <_>::ssz_decode(&ssz, 0).unwrap();
assert_eq!(index, 1);
assert_eq!(result, false);
let (result, index): (bool, usize) = <_>::ssz_decode(&ssz, 1).unwrap();
assert_eq!(index, 2);
assert_eq!(result, true);
let ssz = vec![0b0100_0000];
let result: Result<(bool, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
assert_eq!(result, Err(DecodeError::Invalid));
let ssz = vec![];
let result: Result<(bool, usize), DecodeError> = <_>::ssz_decode(&ssz, 0);
assert_eq!(result, Err(DecodeError::TooShort));
}
#[test]
#[should_panic]
fn test_decode_ssz_list_underflow() {
// SSZ encoded (u16::[1, 1, 1], u16::2)
let mut encoded = vec![6, 0, 0, 0, 1, 0, 1, 0, 1, 0, 2, 0];
let (decoded_array, i): (Vec<u16>, usize) = <_>::ssz_decode(&encoded, 0).unwrap();
let (decoded_u16, i): (u16, usize) = <_>::ssz_decode(&encoded, i).unwrap();
assert_eq!(decoded_array, vec![1, 1, 1]);
assert_eq!(decoded_u16, 2);
assert_eq!(i, 12);
// Underflow
encoded[0] = 4; // change length to 4 from 6
let (decoded_array, i): (Vec<u16>, usize) = <_>::ssz_decode(&encoded, 0).unwrap();
let (decoded_u16, _): (u16, usize) = <_>::ssz_decode(&encoded, i).unwrap();
assert_eq!(decoded_array, vec![1, 1]);
assert_eq!(decoded_u16, 2);
}
#[test]
fn test_decode_too_long() {
let encoded = vec![6, 0, 0, 0, 1, 0, 1, 0, 1, 0, 2];
let decoded_array: Result<Vec<u16>, DecodeError> = decode(&encoded);
assert_eq!(decoded_array, Err(DecodeError::TooLong));
}
#[test]
fn test_decode_u8_array() {
let ssz = vec![0, 1, 2, 3];
let result: [u8; 4] = decode(&ssz).unwrap();
assert_eq!(result.len(), 4);
assert_eq!(result, [0, 1, 2, 3]);
}
}
-275
View File
@@ -1,275 +0,0 @@
extern crate bytes;
use self::bytes::{BufMut, BytesMut};
use super::ethereum_types::{Address, H256};
use super::{Encodable, SszStream};
/*
* Note: there is a "to_bytes" function for integers
* in Rust nightly. When it is in stable, we should
* use it instead.
*/
macro_rules! impl_encodable_for_uint {
($type: ident, $bit_size: expr) => {
impl Encodable for $type {
#[allow(clippy::cast_lossless)]
fn ssz_append(&self, s: &mut SszStream) {
// Ensure bit size is valid
assert!(
(0 < $bit_size)
&& ($bit_size % 8 == 0)
&& (2_u128.pow($bit_size) > *self as u128)
);
// Serialize to bytes
let mut buf = BytesMut::with_capacity($bit_size / 8);
// Match bit size with encoding
match $bit_size {
8 => buf.put_u8(*self as u8),
16 => buf.put_u16_le(*self as u16),
32 => buf.put_u32_le(*self as u32),
64 => buf.put_u64_le(*self as u64),
_ => {}
}
// Append bytes to the SszStream
s.append_encoded_raw(&buf.to_vec());
}
}
};
}
macro_rules! impl_encodable_for_u8_array {
($len: expr) => {
impl Encodable for [u8; $len] {
fn ssz_append(&self, s: &mut SszStream) {
let bytes: Vec<u8> = self.iter().cloned().collect();
s.append_encoded_raw(&bytes);
}
}
};
}
impl_encodable_for_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_u8_array!(4);
impl Encodable for bool {
fn ssz_append(&self, s: &mut SszStream) {
let byte = if *self { 0b0000_0001 } else { 0b0000_0000 };
s.append_encoded_raw(&[byte]);
}
}
impl Encodable for H256 {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(self.as_bytes());
}
}
impl Encodable for Address {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(self.as_bytes());
}
}
impl<T> Encodable for Vec<T>
where
T: Encodable,
{
fn ssz_append(&self, s: &mut SszStream) {
s.append_vec(&self);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ssz_encode;
#[test]
fn test_ssz_encode_h256() {
let h = H256::zero();
let mut ssz = SszStream::new();
ssz.append(&h);
assert_eq!(ssz.drain(), vec![0; 32]);
}
#[test]
fn test_ssz_encode_address() {
let h = Address::zero();
let mut ssz = SszStream::new();
ssz.append(&h);
assert_eq!(ssz.drain(), vec![0; 20]);
}
#[test]
fn test_ssz_encode_u8() {
let x: u8 = 0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0]);
let x: u8 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1]);
let x: u8 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![100]);
let x: u8 = 255;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255]);
}
#[test]
fn test_ssz_encode_u16() {
let x: u16 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1, 0]);
let x: u16 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![100, 0]);
let x: u16 = 1 << 8;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 1]);
let x: u16 = 65535;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255]);
}
#[test]
fn test_ssz_encode_u32() {
let x: u32 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1, 0, 0, 0]);
let x: u32 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![100, 0, 0, 0]);
let x: u32 = 1 << 16;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 1, 0]);
let x: u32 = 1 << 24;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 1]);
let x: u32 = !0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255, 255, 255]);
}
#[test]
fn test_ssz_encode_u64() {
let x: u64 = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1, 0, 0, 0, 0, 0, 0, 0]);
let x: u64 = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![100, 0, 0, 0, 0, 0, 0, 0]);
let x: u64 = 1 << 32;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 1, 0, 0, 0]);
let x: u64 = !0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255, 255, 255, 255, 255, 255, 255]);
}
#[test]
fn test_ssz_encode_usize() {
let x: usize = 1;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![1, 0, 0, 0, 0, 0, 0, 0]);
let x: usize = 100;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![100, 0, 0, 0, 0, 0, 0, 0]);
let x: usize = 1 << 32;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0, 0, 0, 0, 1, 0, 0, 0]);
let x: usize = !0;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![255, 255, 255, 255, 255, 255, 255, 255]);
}
#[test]
fn test_ssz_mixed() {
let mut stream = SszStream::new();
let h = Address::zero();
let a: u8 = 100;
let b: u16 = 65535;
let c: u32 = 1 << 24;
stream.append(&h);
stream.append(&a);
stream.append(&b);
stream.append(&c);
let ssz = stream.drain();
assert_eq!(ssz[0..20], *vec![0; 20]);
assert_eq!(ssz[20], 100);
assert_eq!(ssz[21..23], *vec![255, 255]);
assert_eq!(ssz[23..27], *vec![0, 0, 0, 1]);
}
#[test]
fn test_ssz_encode_bool() {
let x: bool = false;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0b0000_0000]);
let x: bool = true;
let mut ssz = SszStream::new();
ssz.append(&x);
assert_eq!(ssz.drain(), vec![0b0000_0001]);
}
#[test]
fn test_ssz_encode_u8_array() {
let x: [u8; 4] = [0, 1, 7, 8];
let ssz = ssz_encode(&x);
assert_eq!(ssz, vec![0, 1, 7, 8]);
let x: [u8; 4] = [255, 255, 255, 255];
let ssz = ssz_encode(&x);
assert_eq!(ssz, vec![255, 255, 255, 255]);
}
}
+50 -216
View File
@@ -1,225 +1,59 @@
/*
* 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;
//! Provides encoding (serialization) and decoding (deserialization) in the SimpleSerialize (SSZ)
//! format designed for use in Ethereum 2.0.
//!
//! Conforms to
//! [v0.6.1](https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/simple-serialize.md) of the
//! Ethereum 2.0 specification.
//!
//! ## Example
//!
//! ```rust
//! use ssz_derive::{Encode, Decode};
//! use ssz::{Decode, Encode};
//!
//! #[derive(PartialEq, Debug, Encode, Decode)]
//! struct Foo {
//! a: u64,
//! b: Vec<u16>,
//! }
//!
//! fn main() {
//! let foo = Foo {
//! a: 42,
//! b: vec![1, 3, 3, 7]
//! };
//!
//! let ssz_bytes: Vec<u8> = foo.as_ssz_bytes();
//!
//! let decoded_foo = Foo::from_ssz_bytes(&ssz_bytes).unwrap();
//!
//! assert_eq!(foo, decoded_foo);
//! }
//!
//! ```
//!
//! See `examples/` for manual implementations of the `Encode` and `Decode` traits.
pub mod decode;
pub mod encode;
mod decode;
mod encode;
mod macros;
mod impl_decode;
mod impl_encode;
pub use decode::{
impls::decode_list_of_variable_length_items, Decode, DecodeError, SszDecoder, SszDecoderBuilder,
};
pub use encode::{Encode, SszEncoder};
pub use crate::decode::{decode, decode_ssz_list, Decodable, DecodeError};
pub use crate::encode::{Encodable, SszStream};
pub use hashing::hash;
pub const LENGTH_BYTES: usize = 4;
pub const MAX_LIST_SIZE: usize = 1 << (4 * 8);
/// The number of bytes used to represent an offset.
pub const BYTES_PER_LENGTH_OFFSET: usize = 4;
/// The maximum value that can be represented using `BYTES_PER_LENGTH_OFFSET`.
pub const MAX_LENGTH_VALUE: usize = (1 << (BYTES_PER_LENGTH_OFFSET * 8)) - 1;
/// Convenience function to SSZ encode an object supporting ssz::Encode.
///
/// Equivalent to `val.as_ssz_bytes()`.
pub fn ssz_encode<T>(val: &T) -> Vec<u8>
where
T: Encodable,
T: Encode,
{
let mut ssz_stream = SszStream::new();
ssz_stream.append(val);
ssz_stream.drain()
}
#[cfg(test)]
mod tests {
extern crate hex;
extern crate yaml_rust;
use self::yaml_rust::yaml;
use super::*;
use std::{fs::File, io::prelude::*, path::PathBuf};
#[test]
pub fn test_vector_uint_bounds() {
let mut file = {
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
file_path_buf.push("src/test_vectors/uint_bounds.yaml");
File::open(file_path_buf).unwrap()
};
let mut yaml_str = String::new();
file.read_to_string(&mut yaml_str).unwrap();
let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
let doc = &docs[0];
// Load test cases
let test_cases = doc["test_cases"].clone();
for test_case in test_cases {
// Only the valid cases are checked as parse::<uX>() will fail for all invalid cases
if test_case["valid"].as_bool().unwrap() {
// Convert test vector 'ssz' encoded yaml to Vec<u8>
let ssz = test_case["ssz"].as_str().unwrap().trim_start_matches("0x");
let test_vector_bytes = hex::decode(ssz).unwrap();
// Convert test vector 'value' to ssz encoded bytes
let mut bytes: Vec<u8>;
match test_case["type"].as_str().unwrap() {
"uint8" => {
let value: u8 = test_case["value"].as_str().unwrap().parse::<u8>().unwrap();
bytes = ssz_encode::<u8>(&value); // check encoding
// Check decoding
let decoded = decode::<u8>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
"uint16" => {
let value: u16 =
test_case["value"].as_str().unwrap().parse::<u16>().unwrap();
bytes = ssz_encode::<u16>(&value);
// Check decoding
let decoded = decode::<u16>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
"uint32" => {
let value: u32 =
test_case["value"].as_str().unwrap().parse::<u32>().unwrap();
bytes = ssz_encode::<u32>(&value);
// Check decoding
let decoded = decode::<u32>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
"uint64" => {
let value: u64 =
test_case["value"].as_str().unwrap().parse::<u64>().unwrap();
bytes = ssz_encode::<u64>(&value);
// Check decoding
let decoded = decode::<u64>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
_ => continue,
};
assert_eq!(test_vector_bytes, bytes);
}
}
}
#[test]
pub fn test_vector_uint_random() {
let mut file = {
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
file_path_buf.push("src/test_vectors/uint_random.yaml");
File::open(file_path_buf).unwrap()
};
let mut yaml_str = String::new();
file.read_to_string(&mut yaml_str).unwrap();
let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
let doc = &docs[0];
// Load test cases
let test_cases = doc["test_cases"].clone();
for test_case in test_cases {
// Only the valid cases are checked as parse::<uX>() will fail for all invalid cases
if test_case["valid"].as_bool().unwrap() {
// Convert test vector 'ssz' encoded yaml to Vec<u8>
let ssz = test_case["ssz"].as_str().unwrap().trim_start_matches("0x");
let test_vector_bytes = hex::decode(ssz).unwrap();
// Convert test vector 'value' to ssz encoded bytes
let mut bytes: Vec<u8>;
match test_case["type"].as_str().unwrap() {
"uint8" => {
let value: u8 = test_case["value"].as_str().unwrap().parse::<u8>().unwrap();
bytes = ssz_encode::<u8>(&value); // check encoding
// Check decoding
let decoded = decode::<u8>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
"uint16" => {
let value: u16 =
test_case["value"].as_str().unwrap().parse::<u16>().unwrap();
bytes = ssz_encode::<u16>(&value);
// Check decoding
let decoded = decode::<u16>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
"uint32" => {
let value: u32 =
test_case["value"].as_str().unwrap().parse::<u32>().unwrap();
bytes = ssz_encode::<u32>(&value);
// Check decoding
let decoded = decode::<u32>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
"uint64" => {
let value: u64 =
test_case["value"].as_str().unwrap().parse::<u64>().unwrap();
bytes = ssz_encode::<u64>(&value);
// Check decoding
let decoded = decode::<u64>(&test_vector_bytes).unwrap();
assert_eq!(decoded, value);
}
_ => continue,
};
assert_eq!(test_vector_bytes, bytes);
}
}
}
#[test]
pub fn test_vector_uint_wrong_length() {
let mut file = {
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
file_path_buf.push("src/test_vectors/uint_wrong_length.yaml");
File::open(file_path_buf).unwrap()
};
let mut yaml_str = String::new();
file.read_to_string(&mut yaml_str).unwrap();
let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
let doc = &docs[0];
// Load test cases
let test_cases = doc["test_cases"].clone();
for test_case in test_cases {
// Convert test vector 'ssz' encoded yaml to Vec<u8>
let ssz = test_case["ssz"].as_str().unwrap().trim_start_matches("0x");
let test_vector_bytes = hex::decode(ssz).unwrap();
// Attempt to decode invalid ssz bytes
match test_case["type"].as_str().unwrap() {
"uint8" => {
let decoded = decode::<u8>(&test_vector_bytes);
assert!(decoded.is_err());
}
"uint16" => {
let decoded = decode::<u16>(&test_vector_bytes);
assert!(decoded.is_err());
}
"uint32" => {
let decoded = decode::<u32>(&test_vector_bytes);
assert!(decoded.is_err());
}
"uint64" => {
let decoded = decode::<u64>(&test_vector_bytes);
assert!(decoded.is_err());
}
_ => continue,
};
}
}
val.as_ssz_bytes()
}
+96
View File
@@ -0,0 +1,96 @@
/// Implements `Encode` for `$impl_type` using an implementation of `From<$impl_type> for
/// $from_type`.
///
/// In effect, this allows for easy implementation of `Encode` for some type that implements a
/// `From` conversion into another type that already has `Encode` implemented.
#[macro_export]
macro_rules! impl_encode_via_from {
($impl_type: ty, $from_type: ty) => {
impl ssz::Encode for $impl_type {
fn is_ssz_fixed_len() -> bool {
<$from_type as ssz::Encode>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<$from_type as ssz::Encode>::ssz_fixed_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
let conv: $from_type = self.clone().into();
conv.ssz_append(buf)
}
}
};
}
/// Implements `Decode` for `$impl_type` using an implementation of `From<$impl_type> for
/// $from_type`.
///
/// In effect, this allows for easy implementation of `Decode` for some type that implements a
/// `From` conversion into another type that already has `Decode` implemented.
#[macro_export]
macro_rules! impl_decode_via_from {
($impl_type: ty, $from_type: tt) => {
impl ssz::Decode for $impl_type {
fn is_ssz_fixed_len() -> bool {
<$from_type as ssz::Decode>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<$from_type as ssz::Decode>::ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
$from_type::from_ssz_bytes(bytes).and_then(|dec| Ok(dec.into()))
}
}
};
}
#[cfg(test)]
mod tests {
use crate as ssz;
use ssz::{Decode, Encode};
#[derive(PartialEq, Debug, Clone, Copy)]
struct Wrapper(u64);
impl From<u64> for Wrapper {
fn from(x: u64) -> Wrapper {
Wrapper(x)
}
}
impl From<Wrapper> for u64 {
fn from(x: Wrapper) -> u64 {
x.0
}
}
impl_encode_via_from!(Wrapper, u64);
impl_decode_via_from!(Wrapper, u64);
#[test]
fn impl_encode_via_from() {
let check_encode = |a: u64, b: Wrapper| assert_eq!(a.as_ssz_bytes(), b.as_ssz_bytes());
check_encode(0, Wrapper(0));
check_encode(1, Wrapper(1));
check_encode(42, Wrapper(42));
}
#[test]
fn impl_decode_via_from() {
let check_decode = |bytes: Vec<u8>| {
let a = u64::from_ssz_bytes(&bytes).unwrap();
let b = Wrapper::from_ssz_bytes(&bytes).unwrap();
assert_eq!(a, b.into())
};
check_decode(vec![0, 0, 0, 0, 0, 0, 0, 0]);
check_decode(vec![1, 0, 0, 0, 0, 0, 0, 0]);
check_decode(vec![1, 0, 0, 0, 2, 0, 0, 0]);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+288
View File
@@ -0,0 +1,288 @@
use ethereum_types::H256;
use ssz::{Decode, DecodeError, Encode};
use ssz_derive::{Decode, Encode};
mod round_trip {
use super::*;
fn round_trip<T: Encode + Decode + std::fmt::Debug + PartialEq>(items: Vec<T>) {
for item in items {
let encoded = &item.as_ssz_bytes();
assert_eq!(T::from_ssz_bytes(&encoded), Ok(item));
}
}
#[test]
fn bool() {
let items: Vec<bool> = vec![true, false];
round_trip(items);
}
#[test]
fn u8_array_4() {
let items: Vec<[u8; 4]> = vec![[0, 0, 0, 0], [1, 0, 0, 0], [1, 2, 3, 4], [1, 2, 0, 4]];
round_trip(items);
}
#[test]
fn h256() {
let items: Vec<H256> = vec![H256::zero(), H256::from([1; 32]), H256::random()];
round_trip(items);
}
#[test]
fn vec_of_h256() {
let items: Vec<Vec<H256>> = vec![
vec![],
vec![H256::zero(), H256::from([1; 32]), H256::random()],
];
round_trip(items);
}
#[test]
fn vec_u16() {
let items: Vec<Vec<u16>> = vec![
vec![],
vec![255],
vec![0, 1, 2],
vec![100; 64],
vec![255, 0, 255],
];
round_trip(items);
}
#[test]
fn vec_of_vec_u16() {
let items: Vec<Vec<Vec<u16>>> = vec![
vec![],
vec![vec![]],
vec![vec![1, 2, 3]],
vec![vec![], vec![]],
vec![vec![], vec![1, 2, 3]],
vec![vec![1, 2, 3], vec![1, 2, 3]],
vec![vec![1, 2, 3], vec![], vec![1, 2, 3]],
vec![vec![], vec![], vec![1, 2, 3]],
vec![vec![], vec![1], vec![1, 2, 3]],
vec![vec![], vec![1], vec![1, 2, 3]],
];
round_trip(items);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct FixedLen {
a: u16,
b: u64,
c: u32,
}
#[test]
fn fixed_len_struct_encoding() {
let items: Vec<FixedLen> = vec![
FixedLen { a: 0, b: 0, c: 0 },
FixedLen { a: 1, b: 1, c: 1 },
FixedLen { a: 1, b: 0, c: 1 },
];
let expected_encodings = vec![
// | u16--| u64----------------------------| u32----------|
vec![00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00],
vec![01, 00, 01, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00],
vec![01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00],
];
for i in 0..items.len() {
assert_eq!(
items[i].as_ssz_bytes(),
expected_encodings[i],
"Failed on {}",
i
);
}
}
#[test]
fn fixed_len_excess_bytes() {
let fixed = FixedLen { a: 1, b: 2, c: 3 };
let mut bytes = fixed.as_ssz_bytes();
bytes.append(&mut vec![0]);
assert_eq!(
FixedLen::from_ssz_bytes(&bytes),
Err(DecodeError::InvalidByteLength {
len: 15,
expected: 14,
})
);
}
#[test]
fn vec_of_fixed_len_struct() {
let items: Vec<FixedLen> = vec![
FixedLen { a: 0, b: 0, c: 0 },
FixedLen { a: 1, b: 1, c: 1 },
FixedLen { a: 1, b: 0, c: 1 },
];
round_trip(items);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct VariableLen {
a: u16,
b: Vec<u16>,
c: u32,
}
#[test]
fn offset_into_fixed_bytes() {
let bytes = vec![
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// | offset | u32 | variable
01, 00, 09, 00, 00, 00, 01, 00, 00, 00, 00, 00, 01, 00, 02, 00,
];
assert_eq!(
VariableLen::from_ssz_bytes(&bytes),
Err(DecodeError::OutOfBoundsByte { i: 9 })
);
}
#[test]
fn variable_len_excess_bytes() {
let variable = VariableLen {
a: 1,
b: vec![2],
c: 3,
};
let mut bytes = variable.as_ssz_bytes();
bytes.append(&mut vec![0]);
// The error message triggered is not so helpful, it's caught by a side-effect. Just
// checking there is _some_ error is fine.
assert!(VariableLen::from_ssz_bytes(&bytes).is_err());
}
#[test]
fn first_offset_skips_byte() {
let bytes = vec![
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// | offset | u32 | variable
01, 00, 11, 00, 00, 00, 01, 00, 00, 00, 00, 00, 01, 00, 02, 00,
];
assert_eq!(
VariableLen::from_ssz_bytes(&bytes),
Err(DecodeError::OutOfBoundsByte { i: 11 })
);
}
#[test]
fn variable_len_struct_encoding() {
let items: Vec<VariableLen> = vec![
VariableLen {
a: 0,
b: vec![],
c: 0,
},
VariableLen {
a: 1,
b: vec![0],
c: 1,
},
VariableLen {
a: 1,
b: vec![0, 1, 2],
c: 1,
},
];
let expected_encodings = vec![
// 00..................................09
// | u16--| vec offset-----| u32------------| vec payload --------|
vec![00, 00, 10, 00, 00, 00, 00, 00, 00, 00],
vec![01, 00, 10, 00, 00, 00, 01, 00, 00, 00, 00, 00],
vec![
01, 00, 10, 00, 00, 00, 01, 00, 00, 00, 00, 00, 01, 00, 02, 00,
],
];
for i in 0..items.len() {
assert_eq!(
items[i].as_ssz_bytes(),
expected_encodings[i],
"Failed on {}",
i
);
}
}
#[test]
fn vec_of_variable_len_struct() {
let items: Vec<VariableLen> = vec![
VariableLen {
a: 0,
b: vec![],
c: 0,
},
VariableLen {
a: 255,
b: vec![0, 1, 2, 3],
c: 99,
},
VariableLen {
a: 255,
b: vec![0],
c: 99,
},
VariableLen {
a: 50,
b: vec![0],
c: 0,
},
];
round_trip(items);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct ThreeVariableLen {
a: u16,
b: Vec<u16>,
c: Vec<u16>,
d: Vec<u16>,
}
#[test]
fn three_variable_len() {
let vec: Vec<ThreeVariableLen> = vec![ThreeVariableLen {
a: 42,
b: vec![0],
c: vec![1],
d: vec![2],
}];
round_trip(vec);
}
#[test]
fn offsets_decreasing() {
let bytes = vec![
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// | offset | ofset | offset | variable
01, 00, 14, 00, 00, 00, 15, 00, 00, 00, 14, 00, 00, 00, 00, 00,
];
assert_eq!(
ThreeVariableLen::from_ssz_bytes(&bytes),
Err(DecodeError::OutOfBoundsByte { i: 14 })
);
}
}
+105 -73
View File
@@ -1,39 +1,4 @@
//! Provides the following procedural derive macros:
//!
//! - `#[derive(Encode)]`
//! - `#[derive(Decode)]`
//! - `#[derive(TreeHash)]`
//!
//! These macros provide SSZ encoding/decoding for a `struct`. Fields are encoded/decoded in the
//! order they are defined.
//!
//! Presently, only `structs` with named fields are supported. `enum`s and tuple-structs are
//! unsupported.
//!
//! Example:
//! ```
//! use ssz::{ssz_encode, Decodable};
//! use ssz_derive::{Encode, Decode};
//!
//! #[derive(Encode, Decode)]
//! struct Foo {
//! pub bar: bool,
//! pub baz: u64,
//! }
//!
//! fn main() {
//! let foo = Foo {
//! bar: true,
//! baz: 42,
//! };
//!
//! let bytes = ssz_encode(&foo);
//!
//! let (decoded_foo, _i) = Foo::ssz_decode(&bytes, 0).unwrap();
//!
//! assert_eq!(foo.baz, decoded_foo.baz);
//! }
//! ```
#![recursion_limit = "128"]
extern crate proc_macro;
@@ -41,21 +6,6 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
/// Returns a Vec of `syn::Ident` for each named field in the struct.
///
/// # Panics
/// Any unnamed struct field (like in a tuple struct) will raise a panic at compile time.
fn get_named_field_idents<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Ident> {
struct_data
.fields
.iter()
.map(|f| match &f.ident {
Some(ref ident) => ident,
_ => panic!("ssz_derive only supports named struct fields."),
})
.collect()
}
/// Returns a Vec of `syn::Ident` for each named field in the struct, whilst filtering out fields
/// that should not be serialized.
///
@@ -80,6 +30,22 @@ fn get_serializable_named_field_idents<'a>(
.collect()
}
/// Returns a Vec of `syn::Type` for each named field in the struct, whilst filtering out fields
/// that should not be serialized.
fn get_serializable_field_types<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Type> {
struct_data
.fields
.iter()
.filter_map(|f| {
if should_skip_serializing(&f) {
None
} else {
Some(&f.ty)
}
})
.collect()
}
/// Returns true if some field has an attribute declaring it should not be serialized.
///
/// The field attribute is: `#[ssz(skip_serializing)]`
@@ -92,7 +58,7 @@ fn should_skip_serializing(field: &syn::Field) -> bool {
false
}
/// Implements `ssz::Encodable` for some `struct`.
/// Implements `ssz::Encode` for some `struct`.
///
/// Fields are encoded in the order they are defined.
#[proc_macro_derive(Encode, attributes(ssz))]
@@ -108,13 +74,43 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream {
};
let field_idents = get_serializable_named_field_idents(&struct_data);
let field_types_a = get_serializable_field_types(&struct_data);
let field_types_b = field_types_a.clone();
let field_types_c = field_types_a.clone();
let output = quote! {
impl #impl_generics ssz::Encodable for #name #ty_generics #where_clause {
fn ssz_append(&self, s: &mut ssz::SszStream) {
impl #impl_generics ssz::Encode for #name #ty_generics #where_clause {
fn is_ssz_fixed_len() -> bool {
#(
s.append(&self.#field_idents);
<#field_types_a as ssz::Encode>::is_ssz_fixed_len() &&
)*
true
}
fn ssz_fixed_len() -> usize {
if <Self as ssz::Encode>::is_ssz_fixed_len() {
#(
<#field_types_b as ssz::Encode>::ssz_fixed_len() +
)*
0
} else {
ssz::BYTES_PER_LENGTH_OFFSET
}
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
let offset = #(
<#field_types_c as ssz::Encode>::ssz_fixed_len() +
)*
0;
let mut encoder = ssz::SszEncoder::container(buf, offset);
#(
encoder.append(&self.#field_idents);
)*
encoder.finalize();
}
}
};
@@ -133,7 +129,7 @@ fn should_skip_deserializing(field: &syn::Field) -> bool {
false
}
/// Implements `ssz::Decodable` for some `struct`.
/// Implements `ssz::Decode` for some `struct`.
///
/// Fields are decoded in the order they are defined.
#[proc_macro_derive(Decode)]
@@ -148,21 +144,38 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
_ => panic!("ssz_derive only supports structs."),
};
let all_idents = get_named_field_idents(&struct_data);
let mut register_types = vec![];
let mut decodes = vec![];
let mut is_fixed_lens = vec![];
let mut fixed_lens = vec![];
// Build quotes for fields that should be deserialized and those that should be built from
// `Default`.
let mut quotes = vec![];
for field in &struct_data.fields {
match &field.ident {
Some(ref ident) => {
if should_skip_deserializing(field) {
quotes.push(quote! {
let #ident = <_>::default();
// Field should not be deserialized; use a `Default` impl to instantiate.
decodes.push(quote! {
#ident: <_>::default()
});
} else {
quotes.push(quote! {
let (#ident, i) = <_>::ssz_decode(bytes, i)?;
let ty = &field.ty;
register_types.push(quote! {
builder.register_type::<#ty>()?;
});
decodes.push(quote! {
#ident: decoder.decode_next()?
});
is_fixed_lens.push(quote! {
<#ty as ssz::Decode>::is_ssz_fixed_len()
});
fixed_lens.push(quote! {
<#ty as ssz::Decode>::ssz_fixed_len()
});
}
}
@@ -171,20 +184,39 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
}
let output = quote! {
impl #impl_generics ssz::Decodable for #name #ty_generics #where_clause {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), ssz::DecodeError> {
impl #impl_generics ssz::Decode for #name #ty_generics #where_clause {
fn is_ssz_fixed_len() -> bool {
#(
#quotes
#is_fixed_lens &&
)*
true
}
fn ssz_fixed_len() -> usize {
if <Self as ssz::Decode>::is_ssz_fixed_len() {
#(
#fixed_lens +
)*
0
} else {
ssz::BYTES_PER_LENGTH_OFFSET
}
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
#(
#register_types
)*
Ok((
Self {
#(
#all_idents,
)*
},
i
))
let mut decoder = builder.build()?;
Ok(Self {
#(
#decodes,
)*
})
}
}
};
+22
View File
@@ -0,0 +1,22 @@
use ssz::Encode;
use ssz_derive::Encode;
#[derive(Debug, PartialEq, Encode)]
pub struct Foo {
a: u16,
b: Vec<u8>,
c: u16,
}
#[test]
fn encode() {
let foo = Foo {
a: 42,
b: vec![0, 1, 2, 3],
c: 11,
};
let bytes = vec![42, 0, 8, 0, 0, 0, 11, 0, 0, 1, 2, 3];
assert_eq!(foo.as_ssz_bytes(), bytes);
}
@@ -1,7 +1,7 @@
use super::beacon_node_attestation::BeaconNodeAttestation;
use crate::block_producer::{BeaconNodeError, PublishOutcome};
use protos::services_grpc::AttestationServiceClient;
use ssz::{ssz_encode, Decodable};
use ssz::{Decode, Encode};
use protos::services::{
Attestation as GrpcAttestation, ProduceAttestationDataRequest, PublishAttestationRequest,
@@ -22,8 +22,8 @@ impl BeaconNodeAttestation for AttestationServiceClient {
.produce_attestation_data(&req)
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
let (attestation_data, _index) =
AttestationData::ssz_decode(reply.get_attestation_data().get_ssz(), 0)
let attestation_data =
AttestationData::from_ssz_bytes(reply.get_attestation_data().get_ssz())
.map_err(|_| BeaconNodeError::DecodeFailure)?;
Ok(attestation_data)
}
@@ -34,7 +34,7 @@ impl BeaconNodeAttestation for AttestationServiceClient {
) -> Result<PublishOutcome, BeaconNodeError> {
let mut req = PublishAttestationRequest::new();
let ssz = ssz_encode(&attestation);
let ssz = attestation.as_ssz_bytes();
let mut grpc_attestation = GrpcAttestation::new();
grpc_attestation.set_ssz(ssz);
+5 -4
View File
@@ -3,7 +3,7 @@ use protos::services::{
BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest,
};
use protos::services_grpc::BeaconBlockServiceClient;
use ssz::{decode, ssz_encode};
use ssz::{Decode, Encode};
use std::sync::Arc;
use types::{BeaconBlock, Signature, Slot};
@@ -33,7 +33,7 @@ impl BeaconNodeBlock for BeaconBlockGrpcClient {
// request a beacon block from the node
let mut req = ProduceBeaconBlockRequest::new();
req.set_slot(slot.as_u64());
req.set_randao_reveal(ssz_encode(randao_reveal));
req.set_randao_reveal(randao_reveal.as_ssz_bytes());
//TODO: Determine if we want an explicit timeout
let reply = self
@@ -46,7 +46,8 @@ impl BeaconNodeBlock for BeaconBlockGrpcClient {
let block = reply.get_block();
let ssz = block.get_ssz();
let block = decode::<BeaconBlock>(&ssz).map_err(|_| BeaconNodeError::DecodeFailure)?;
let block =
BeaconBlock::from_ssz_bytes(&ssz).map_err(|_| BeaconNodeError::DecodeFailure)?;
Ok(Some(block))
} else {
@@ -61,7 +62,7 @@ impl BeaconNodeBlock for BeaconBlockGrpcClient {
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError> {
let mut req = PublishBeaconBlockRequest::new();
let ssz = ssz_encode(&block);
let ssz = block.as_ssz_bytes();
let mut grpc_block = GrpcBeaconBlock::new();
grpc_block.set_ssz(ssz);