Merge pull request #365 from sigp/sos

SSZ with SOS
This commit is contained in:
Paul Hauner 2019-05-15 11:21:15 +10:00 committed by GitHub
commit 44657d44c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 2211 additions and 15232 deletions

View File

@ -1,6 +1,6 @@
use super::BLOCKS_DB_COLUMN as DB_COLUMN; use super::BLOCKS_DB_COLUMN as DB_COLUMN;
use super::{ClientDB, DBError}; use super::{ClientDB, DBError};
use ssz::decode; use ssz::Decode;
use std::sync::Arc; use std::sync::Arc;
use types::{BeaconBlock, Hash256, Slot}; use types::{BeaconBlock, Hash256, Slot};
@ -30,7 +30,7 @@ impl<T: ClientDB> BeaconBlockStore<T> {
match self.get(&hash)? { match self.get(&hash)? {
None => Ok(None), None => Ok(None),
Some(ssz) => { 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(), message: "Bad BeaconBlock SSZ.".to_string(),
})?; })?;
Ok(Some(block)) Ok(Some(block))

View File

@ -1,6 +1,6 @@
use super::STATES_DB_COLUMN as DB_COLUMN; use super::STATES_DB_COLUMN as DB_COLUMN;
use super::{ClientDB, DBError}; use super::{ClientDB, DBError};
use ssz::decode; use ssz::Decode;
use std::sync::Arc; use std::sync::Arc;
use types::{BeaconState, EthSpec, Hash256}; use types::{BeaconState, EthSpec, Hash256};
@ -26,7 +26,7 @@ impl<T: ClientDB> BeaconStateStore<T> {
match self.get(&hash)? { match self.get(&hash)? {
None => Ok(None), None => Ok(None),
Some(ssz) => { 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(), message: "Bad State SSZ.".to_string(),
})?; })?;
Ok(Some(state)) Ok(Some(state))

View File

@ -4,7 +4,7 @@ use self::bytes::{BufMut, BytesMut};
use super::VALIDATOR_DB_COLUMN as DB_COLUMN; use super::VALIDATOR_DB_COLUMN as DB_COLUMN;
use super::{ClientDB, DBError}; use super::{ClientDB, DBError};
use bls::PublicKey; use bls::PublicKey;
use ssz::{decode, ssz_encode}; use ssz::{Decode, Encode};
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -55,7 +55,7 @@ impl<T: ClientDB> ValidatorStore<T> {
public_key: &PublicKey, public_key: &PublicKey,
) -> Result<(), ValidatorStoreError> { ) -> Result<(), ValidatorStoreError> {
let key = self.get_db_key_for_index(&KeyPrefixes::PublicKey, index); 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 self.db
.put(DB_COLUMN, &key[..], &val[..]) .put(DB_COLUMN, &key[..], &val[..])
.map_err(ValidatorStoreError::from) .map_err(ValidatorStoreError::from)
@ -69,7 +69,7 @@ impl<T: ClientDB> ValidatorStore<T> {
let val = self.db.get(DB_COLUMN, &key[..])?; let val = self.db.get(DB_COLUMN, &key[..])?;
match val { match val {
None => Ok(None), None => Ok(None),
Some(val) => match decode::<PublicKey>(&val) { Some(val) => match PublicKey::from_ssz_bytes(&val) {
Ok(key) => Ok(Some(key)), Ok(key) => Ok(Some(key)),
Err(_) => Err(ValidatorStoreError::DecodeError), Err(_) => Err(ValidatorStoreError::DecodeError),
}, },
@ -125,7 +125,7 @@ mod tests {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(public_key_at_index, ssz_encode(&public_key)); assert_eq!(public_key_at_index, public_key.as_ssz_bytes());
} }
#[test] #[test]
@ -139,7 +139,7 @@ mod tests {
db.put( db.put(
DB_COLUMN, DB_COLUMN,
&store.get_db_key_for_index(&KeyPrefixes::PublicKey, index)[..], &store.get_db_key_for_index(&KeyPrefixes::PublicKey, index)[..],
&ssz_encode(&public_key)[..], &public_key.as_ssz_bytes(),
) )
.unwrap(); .unwrap();
@ -157,7 +157,7 @@ mod tests {
db.put( db.put(
DB_COLUMN, DB_COLUMN,
&store.get_db_key_for_index(&KeyPrefixes::PublicKey, 3)[..], &store.get_db_key_for_index(&KeyPrefixes::PublicKey, 3)[..],
&ssz_encode(&public_key)[..], &public_key.as_ssz_bytes(),
) )
.unwrap(); .unwrap();

View File

@ -13,7 +13,7 @@ use libp2p::{
NetworkBehaviour, PeerId, NetworkBehaviour, PeerId,
}; };
use slog::{debug, o, trace, warn}; 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::{Attestation, BeaconBlock};
use types::{Topic, TopicHash}; use types::{Topic, TopicHash};
@ -49,7 +49,7 @@ impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubE
GossipsubEvent::Message(gs_msg) => { GossipsubEvent::Message(gs_msg) => {
trace!(self.log, "Received GossipEvent"; "msg" => format!("{:?}", 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 //TODO: Punish peer on error
Err(e) => { Err(e) => {
warn!( warn!(
@ -59,7 +59,7 @@ impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubE
); );
return; return;
} }
Ok((msg, _index)) => msg, Ok(msg) => msg,
}; };
self.events.push(BehaviourEvent::GossipMessage { self.events.push(BehaviourEvent::GossipMessage {
@ -197,34 +197,59 @@ pub enum PubsubMessage {
} }
//TODO: Correctly encode/decode enums. Prefixing with integer for now. //TODO: Correctly encode/decode enums. Prefixing with integer for now.
impl Encodable for PubsubMessage { impl Encode for PubsubMessage {
fn ssz_append(&self, s: &mut SszStream) { 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 { match self {
PubsubMessage::Block(block_gossip) => { PubsubMessage::Block(block_gossip) => {
0u32.ssz_append(s); encoder.append(&0_u32);
block_gossip.ssz_append(s);
// Encode the gossip as a Vec<u8>;
encoder.append(&block_gossip.as_ssz_bytes());
} }
PubsubMessage::Attestation(attestation_gossip) => { PubsubMessage::Attestation(attestation_gossip) => {
1u32.ssz_append(s); encoder.append(&1_u32);
attestation_gossip.ssz_append(s);
// Encode the gossip as a Vec<u8>;
encoder.append(&attestation_gossip.as_ssz_bytes());
} }
} }
encoder.finalize();
} }
} }
impl Decodable for PubsubMessage { impl Decode for PubsubMessage {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> { fn is_ssz_fixed_len() -> bool {
let (id, index) = u32::ssz_decode(bytes, index)?; 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 { match id {
0 => { 0 => Ok(PubsubMessage::Block(BeaconBlock::from_ssz_bytes(&body)?)),
let (block, index) = BeaconBlock::ssz_decode(bytes, index)?; 1 => Ok(PubsubMessage::Attestation(Attestation::from_ssz_bytes(
Ok((PubsubMessage::Block(block), index)) &body,
} )?)),
1 => { _ => Err(DecodeError::BytesInvalid(
let (attestation, index) = Attestation::ssz_decode(bytes, index)?; "Invalid PubsubMessage id".to_string(),
Ok((PubsubMessage::Attestation(attestation), index)) )),
}
_ => Err(DecodeError::Invalid),
} }
} }
} }
@ -240,9 +265,7 @@ mod test {
let encoded = ssz_encode(&original); let encoded = ssz_encode(&original);
println!("{:?}", encoded); let decoded = PubsubMessage::from_ssz_bytes(&encoded).unwrap();
let (decoded, _i) = PubsubMessage::ssz_decode(&encoded, 0).unwrap();
assert_eq!(original, decoded); assert_eq!(original, decoded);
} }

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 ssz_derive::{Decode, Encode};
use types::{BeaconBlockBody, BeaconBlockHeader, Epoch, Hash256, Slot}; use types::{BeaconBlockBody, BeaconBlockHeader, Epoch, Hash256, Slot};
@ -149,19 +150,8 @@ impl Into<u64> for GoodbyeReason {
} }
} }
impl Encodable for GoodbyeReason { impl_encode_via_from!(GoodbyeReason, u64);
fn ssz_append(&self, s: &mut SszStream) { impl_decode_via_from!(GoodbyeReason, u64);
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))
}
}
/// Request a number of beacon block roots from a peer. /// Request a number of beacon block roots from a peer.
#[derive(Encode, Decode, Clone, Debug, PartialEq)] #[derive(Encode, Decode, Clone, Debug, PartialEq)]

View File

@ -1,6 +1,6 @@
use super::methods::*; use super::methods::*;
use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; 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::hash::{Hash, Hasher};
use std::io; use std::io;
use std::iter; use std::iter;
@ -72,18 +72,8 @@ impl Into<u64> for RequestId {
} }
} }
impl Encodable for RequestId { impl_encode_via_from!(RequestId, u64);
fn ssz_append(&self, s: &mut SszStream) { impl_decode_via_from!(RequestId, u64);
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))
}
}
/// The RPC types which are sent/received in this protocol. /// The RPC types which are sent/received in this protocol.
#[derive(Debug, Clone)] #[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> { fn decode(packet: Vec<u8>) -> Result<RPCEvent, DecodeError> {
// decode the header of the rpc let mut builder = ssz::SszDecoderBuilder::new(&packet);
// request/response
let (request, index) = bool::ssz_decode(&packet, 0)?; builder.register_type::<bool>()?;
let (id, index) = RequestId::ssz_decode(&packet, index)?; builder.register_type::<RequestId>()?;
let (method_id, index) = u16::ssz_decode(&packet, index)?; 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 { if request {
let body = match RPCMethod::from(method_id) { let body = match RPCMethod::from(method_id) {
RPCMethod::Hello => { RPCMethod::Hello => RPCRequest::Hello(HelloMessage::from_ssz_bytes(&bytes)?),
let (hello_body, _index) = HelloMessage::ssz_decode(&packet, index)?; RPCMethod::Goodbye => RPCRequest::Goodbye(GoodbyeReason::from_ssz_bytes(&bytes)?),
RPCRequest::Hello(hello_body)
}
RPCMethod::Goodbye => {
let (goodbye_reason, _index) = GoodbyeReason::ssz_decode(&packet, index)?;
RPCRequest::Goodbye(goodbye_reason)
}
RPCMethod::BeaconBlockRoots => { RPCMethod::BeaconBlockRoots => {
let (block_roots_request, _index) = RPCRequest::BeaconBlockRoots(BeaconBlockRootsRequest::from_ssz_bytes(&bytes)?)
BeaconBlockRootsRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconBlockRoots(block_roots_request)
} }
RPCMethod::BeaconBlockHeaders => { RPCMethod::BeaconBlockHeaders => {
let (block_headers_request, _index) = RPCRequest::BeaconBlockHeaders(BeaconBlockHeadersRequest::from_ssz_bytes(&bytes)?)
BeaconBlockHeadersRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconBlockHeaders(block_headers_request)
} }
RPCMethod::BeaconBlockBodies => { RPCMethod::BeaconBlockBodies => {
let (block_bodies_request, _index) = RPCRequest::BeaconBlockBodies(BeaconBlockBodiesRequest::from_ssz_bytes(&bytes)?)
BeaconBlockBodiesRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconBlockBodies(block_bodies_request)
} }
RPCMethod::BeaconChainState => { RPCMethod::BeaconChainState => {
let (chain_state_request, _index) = RPCRequest::BeaconChainState(BeaconChainStateRequest::from_ssz_bytes(&bytes)?)
BeaconChainStateRequest::ssz_decode(&packet, index)?;
RPCRequest::BeaconChainState(chain_state_request)
} }
RPCMethod::Unknown => return Err(DecodeError::UnknownRPCMethod), RPCMethod::Unknown => return Err(DecodeError::UnknownRPCMethod),
}; };
@ -174,29 +162,24 @@ fn decode(packet: Vec<u8>) -> Result<RPCEvent, DecodeError> {
// we have received a response // we have received a response
else { else {
let result = match RPCMethod::from(method_id) { let result = match RPCMethod::from(method_id) {
RPCMethod::Hello => { RPCMethod::Hello => RPCResponse::Hello(HelloMessage::from_ssz_bytes(&bytes)?),
let (body, _index) = HelloMessage::ssz_decode(&packet, index)?;
RPCResponse::Hello(body)
}
RPCMethod::Goodbye => unreachable!("Should never receive a goodbye response"),
RPCMethod::BeaconBlockRoots => { RPCMethod::BeaconBlockRoots => {
let (body, _index) = BeaconBlockRootsResponse::ssz_decode(&packet, index)?; RPCResponse::BeaconBlockRoots(BeaconBlockRootsResponse::from_ssz_bytes(&bytes)?)
RPCResponse::BeaconBlockRoots(body)
} }
RPCMethod::BeaconBlockHeaders => { RPCMethod::BeaconBlockHeaders => {
let (body, _index) = BeaconBlockHeadersResponse::ssz_decode(&packet, index)?; RPCResponse::BeaconBlockHeaders(BeaconBlockHeadersResponse::from_ssz_bytes(&bytes)?)
RPCResponse::BeaconBlockHeaders(body)
} }
RPCMethod::BeaconBlockBodies => { RPCMethod::BeaconBlockBodies => {
let (body, _index) = BeaconBlockBodiesResponse::ssz_decode(&packet, index)?; RPCResponse::BeaconBlockBodies(BeaconBlockBodiesResponse::from_ssz_bytes(&packet)?)
RPCResponse::BeaconBlockBodies(body)
} }
RPCMethod::BeaconChainState => { RPCMethod::BeaconChainState => {
let (body, _index) = BeaconChainStateResponse::ssz_decode(&packet, index)?; RPCResponse::BeaconChainState(BeaconChainStateResponse::from_ssz_bytes(&packet)?)
RPCResponse::BeaconChainState(body)
} }
// We should never receive a goodbye response; it is invalid.
RPCMethod::Goodbye => return Err(DecodeError::UnknownRPCMethod),
RPCMethod::Unknown => return Err(DecodeError::UnknownRPCMethod), RPCMethod::Unknown => return Err(DecodeError::UnknownRPCMethod),
}; };
Ok(RPCEvent::Response { Ok(RPCEvent::Response {
id, id,
method_id, method_id,
@ -220,35 +203,51 @@ where
} }
} }
impl Encodable for RPCEvent { impl Encode for RPCEvent {
fn ssz_append(&self, s: &mut SszStream) { 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 { match self {
RPCEvent::Request { RPCEvent::Request {
id, id,
method_id, method_id,
body, body,
} => { } => {
s.append(&true); encoder.append(&true);
s.append(id); encoder.append(id);
s.append(method_id); encoder.append(method_id);
// Encode the `body` as a `Vec<u8>`.
match body { match body {
RPCRequest::Hello(body) => { RPCRequest::Hello(body) => {
s.append(body); encoder.append(&body.as_ssz_bytes());
} }
RPCRequest::Goodbye(body) => { RPCRequest::Goodbye(body) => {
s.append(body); encoder.append(&body.as_ssz_bytes());
} }
RPCRequest::BeaconBlockRoots(body) => { RPCRequest::BeaconBlockRoots(body) => {
s.append(body); encoder.append(&body.as_ssz_bytes());
} }
RPCRequest::BeaconBlockHeaders(body) => { RPCRequest::BeaconBlockHeaders(body) => {
s.append(body); encoder.append(&body.as_ssz_bytes());
} }
RPCRequest::BeaconBlockBodies(body) => { RPCRequest::BeaconBlockBodies(body) => {
s.append(body); encoder.append(&body.as_ssz_bytes());
} }
RPCRequest::BeaconChainState(body) => { RPCRequest::BeaconChainState(body) => {
s.append(body); encoder.append(&body.as_ssz_bytes());
} }
} }
} }
@ -257,28 +256,32 @@ impl Encodable for RPCEvent {
method_id, method_id,
result, result,
} => { } => {
s.append(&false); encoder.append(&true);
s.append(id); encoder.append(id);
s.append(method_id); encoder.append(method_id);
match result { match result {
RPCResponse::Hello(response) => { RPCResponse::Hello(response) => {
s.append(response); encoder.append(&response.as_ssz_bytes());
} }
RPCResponse::BeaconBlockRoots(response) => { RPCResponse::BeaconBlockRoots(response) => {
s.append(response); encoder.append(&response.as_ssz_bytes());
} }
RPCResponse::BeaconBlockHeaders(response) => { RPCResponse::BeaconBlockHeaders(response) => {
s.append(response); encoder.append(&response.as_ssz_bytes());
} }
RPCResponse::BeaconBlockBodies(response) => { RPCResponse::BeaconBlockBodies(response) => {
s.append(response); encoder.append(&response.as_ssz_bytes());
} }
RPCResponse::BeaconChainState(response) => { RPCResponse::BeaconChainState(response) => {
s.append(response); encoder.append(&response.as_ssz_bytes());
} }
} }
} }
} }
// Finalize the encoder, writing to `buf`.
encoder.finalize();
} }
} }

View File

@ -7,7 +7,7 @@ use protos::services::{
}; };
use protos::services_grpc::AttestationService; use protos::services_grpc::AttestationService;
use slog::{error, info, trace, warn}; use slog::{error, info, trace, warn};
use ssz::{ssz_encode, Decodable}; use ssz::{ssz_encode, Decode};
use std::sync::Arc; use std::sync::Arc;
use types::{Attestation, EthSpec}; use types::{Attestation, EthSpec};
@ -110,8 +110,8 @@ impl<E: EthSpec> AttestationService for AttestationServiceInstance<E> {
let mut resp = PublishAttestationResponse::new(); let mut resp = PublishAttestationResponse::new();
let ssz_serialized_attestation = req.get_attestation().get_ssz(); let ssz_serialized_attestation = req.get_attestation().get_ssz();
let attestation = match Attestation::ssz_decode(ssz_serialized_attestation, 0) { let attestation = match Attestation::from_ssz_bytes(ssz_serialized_attestation) {
Ok((v, _index)) => v, Ok(v) => v,
Err(_) => { Err(_) => {
let log_clone = self.log.clone(); let log_clone = self.log.clone();
let f = sink let f = sink

View File

@ -11,7 +11,7 @@ use protos::services::{
use protos::services_grpc::BeaconBlockService; use protos::services_grpc::BeaconBlockService;
use slog::Logger; use slog::Logger;
use slog::{error, info, trace, warn}; use slog::{error, info, trace, warn};
use ssz::{ssz_encode, Decodable}; use ssz::{ssz_encode, Decode};
use std::sync::Arc; use std::sync::Arc;
use types::{BeaconBlock, EthSpec, Signature, Slot}; use types::{BeaconBlock, EthSpec, Signature, Slot};
@ -35,8 +35,8 @@ impl<E: EthSpec> BeaconBlockService for BeaconBlockServiceInstance<E> {
// decode the request // decode the request
// TODO: requested slot currently unused, see: https://github.com/sigp/lighthouse/issues/336 // TODO: requested slot currently unused, see: https://github.com/sigp/lighthouse/issues/336
let _requested_slot = Slot::from(req.get_slot()); let _requested_slot = Slot::from(req.get_slot());
let randao_reveal = match Signature::ssz_decode(req.get_randao_reveal(), 0) { let randao_reveal = match Signature::from_ssz_bytes(req.get_randao_reveal()) {
Ok((reveal, _index)) => reveal, Ok(reveal) => reveal,
Err(_) => { Err(_) => {
// decode error, incorrect signature // decode error, incorrect signature
let log_clone = self.log.clone(); 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(); let ssz_serialized_block = req.get_block().get_ssz();
match BeaconBlock::ssz_decode(ssz_serialized_block, 0) { match BeaconBlock::from_ssz_bytes(ssz_serialized_block) {
Ok((block, _i)) => { Ok(block) => {
match self.chain.process_block(block.clone()) { match self.chain.process_block(block.clone()) {
Ok(outcome) => { Ok(outcome) => {
if outcome.sucessfully_processed() { if outcome.sucessfully_processed() {

View File

@ -5,7 +5,7 @@ use grpcio::{RpcContext, RpcStatus, RpcStatusCode, UnarySink};
use protos::services::{ActiveValidator, GetDutiesRequest, GetDutiesResponse, ValidatorDuty}; use protos::services::{ActiveValidator, GetDutiesRequest, GetDutiesResponse, ValidatorDuty};
use protos::services_grpc::ValidatorService; use protos::services_grpc::ValidatorService;
use slog::{trace, warn}; use slog::{trace, warn};
use ssz::decode; use ssz::Decode;
use std::sync::Arc; use std::sync::Arc;
use types::{Epoch, EthSpec, RelativeEpoch}; use types::{Epoch, EthSpec, RelativeEpoch};
@ -74,7 +74,7 @@ impl<E: EthSpec> ValidatorService for ValidatorServiceInstance<E> {
for validator_pk in validators.get_public_keys() { for validator_pk in validators.get_public_keys() {
let mut active_validator = ActiveValidator::new(); 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, Ok(v) => v,
Err(_) => { Err(_) => {
let log_clone = self.log.clone(); let log_clone = self.log.clone();

View File

@ -30,7 +30,7 @@ Rust crates containing logic common across the Lighthouse project.
- [`ssz`](utils/ssz/): an implementation of the SimpleSerialize - [`ssz`](utils/ssz/): an implementation of the SimpleSerialize
serialization/deserialization protocol used by Eth 2.0. serialization/deserialization protocol used by Eth 2.0.
- [`ssz_derive`](utils/ssz_derive/): provides procedural macros for - [`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 - [`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. method which is slow, but allows for a subset of indices to be shuffled.
- [`test_random_derive`](utils/test_random_derive/): provides procedural - [`test_random_derive`](utils/test_random_derive/): provides procedural

View File

@ -2,12 +2,13 @@ use self::epoch_cache::{get_active_validator_indices, EpochCache, Error as Epoch
use crate::test_utils::TestRandom; use crate::test_utils::TestRandom;
use crate::*; use crate::*;
use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache}; use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache};
use hashing::hash;
use int_to_bytes::int_to_bytes32; use int_to_bytes::int_to_bytes32;
use pubkey_cache::PubkeyCache; use pubkey_cache::PubkeyCache;
use fixed_len_vec::{typenum::Unsigned, FixedLenVec}; use fixed_len_vec::{typenum::Unsigned, FixedLenVec};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz::{hash, ssz_encode}; use ssz::ssz_encode;
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::TreeHash; use tree_hash::TreeHash;

View File

@ -15,7 +15,7 @@ use crate::test_utils::TestRandom;
use rand::RngCore; use rand::RngCore;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use slog; use slog;
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream}; use ssz::{ssz_encode, Decode, DecodeError, Encode};
use std::cmp::{Ord, Ordering}; use std::cmp::{Ord, Ordering};
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -145,11 +145,13 @@ mod epoch_tests {
#[test] #[test]
fn max_epoch_ssz() { fn max_epoch_ssz() {
let max_epoch = Epoch::max_value(); let max_epoch = Epoch::max_value();
let mut ssz = SszStream::new(); assert_eq!(
ssz.append(&max_epoch); &max_epoch.as_ssz_bytes(),
let encoded = ssz.drain(); &[255, 255, 255, 255, 255, 255, 255, 255]
assert_eq!(&encoded, &[255, 255, 255, 255, 255, 255, 255, 255]); );
let (decoded, _i): (Epoch, usize) = <_>::ssz_decode(&encoded, 0).unwrap(); assert_eq!(
assert_eq!(max_epoch, decoded); max_epoch,
Epoch::from_ssz_bytes(&max_epoch.as_ssz_bytes()).unwrap()
);
} }
} }

View File

@ -192,17 +192,31 @@ macro_rules! impl_display {
macro_rules! impl_ssz { macro_rules! impl_ssz {
($type: ident) => { ($type: ident) => {
impl Encodable for $type { impl Encode for $type {
fn ssz_append(&self, s: &mut SszStream) { fn is_ssz_fixed_len() -> bool {
s.append(&self.0); <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 { impl Decode for $type {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { fn is_ssz_fixed_len() -> bool {
let (value, i) = <_>::ssz_decode(bytes, i)?; <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)?))
} }
} }

View File

@ -3,7 +3,7 @@ use crate::test_utils::TestRandom;
use rand::RngCore; use rand::RngCore;
use serde_derive::Serialize; 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::cmp::{Ord, Ordering};
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};

View File

@ -5,13 +5,13 @@ macro_rules! ssz_tests {
#[test] #[test]
pub fn test_ssz_round_trip() { pub fn test_ssz_round_trip() {
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; 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 mut rng = XorShiftRng::from_seed([42; 16]);
let original = $type::random_for_test(&mut rng); let original = $type::random_for_test(&mut rng);
let bytes = ssz_encode(&original); 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); assert_eq!(original, decoded);
} }

View File

@ -1,4 +1,4 @@
use super::{AggregatePublicKey, Signature, BLS_AGG_SIG_BYTE_SIZE}; use super::*;
use bls_aggregates::{ use bls_aggregates::{
AggregatePublicKey as RawAggregatePublicKey, AggregateSignature as RawAggregateSignature, 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::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor}; 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; use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A BLS aggregate signature. /// A BLS aggregate signature.
@ -99,8 +99,12 @@ impl AggregateSignature {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> { pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
for byte in bytes { for byte in bytes {
if *byte != 0 { if *byte != 0 {
let sig = let sig = RawAggregateSignature::from_bytes(&bytes).map_err(|_| {
RawAggregateSignature::from_bytes(&bytes).map_err(|_| DecodeError::Invalid)?; DecodeError::BytesInvalid(
format!("Invalid AggregateSignature bytes: {:?}", bytes).to_string(),
)
})?;
return Ok(Self { return Ok(Self {
aggregate_signature: sig, aggregate_signature: sig,
is_empty: false, is_empty: false,
@ -127,22 +131,11 @@ impl AggregateSignature {
} }
} }
impl Encodable for AggregateSignature { impl_ssz!(
fn ssz_append(&self, s: &mut SszStream) { AggregateSignature,
s.append_encoded_raw(&self.as_bytes()); BLS_AGG_SIG_BYTE_SIZE,
} "AggregateSignature"
} );
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 Serialize for AggregateSignature { impl Serialize for AggregateSignature {
/// Serde serialization is compliant the Ethereum YAML test format. /// Serde serialization is compliant the Ethereum YAML test format.
@ -161,8 +154,9 @@ impl<'de> Deserialize<'de> for AggregateSignature {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let bytes = deserializer.deserialize_str(HexVisitor)?; 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)))?; .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(agg_sig) Ok(agg_sig)
} }
} }
@ -174,7 +168,7 @@ cached_tree_hash_ssz_encoding_as_vector!(AggregateSignature, 96);
mod tests { mod tests {
use super::super::{Keypair, Signature}; use super::super::{Keypair, Signature};
use super::*; use super::*;
use ssz::{decode, ssz_encode}; use ssz::Encode;
#[test] #[test]
pub fn test_ssz_round_trip() { pub fn test_ssz_round_trip() {
@ -183,8 +177,8 @@ mod tests {
let mut original = AggregateSignature::new(); let mut original = AggregateSignature::new();
original.add(&Signature::new(&[42, 42], 0, &keypair.sk)); original.add(&Signature::new(&[42, 42], 0, &keypair.sk));
let bytes = ssz_encode(&original); let bytes = original.as_ssz_bytes();
let decoded = decode::<AggregateSignature>(&bytes).unwrap(); let decoded = AggregateSignature::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded); assert_eq!(original, decoded);
} }

View File

@ -3,7 +3,7 @@ use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, PrefixedHexVisitor}; 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; use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A BLS aggregate signature. /// A BLS aggregate signature.
@ -57,27 +57,31 @@ impl FakeAggregateSignature {
) -> bool { ) -> bool {
true true
} }
}
impl Encodable for FakeAggregateSignature { /// Convert bytes to fake BLS aggregate signature
fn ssz_append(&self, s: &mut SszStream) { pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
s.append_encoded_raw(&self.bytes); 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(),
})
}
}
pub fn as_bytes(&self) -> Vec<u8> {
self.bytes.clone()
} }
} }
impl Decodable for FakeAggregateSignature { impl_ssz!(
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { FakeAggregateSignature,
if bytes.len() - i < BLS_AGG_SIG_BYTE_SIZE { BLS_AGG_SIG_BYTE_SIZE,
return Err(DecodeError::TooShort); "FakeAggregateSignature"
} );
Ok((
FakeAggregateSignature {
bytes: bytes[i..(i + BLS_AGG_SIG_BYTE_SIZE)].to_vec(),
},
i + BLS_AGG_SIG_BYTE_SIZE,
))
}
}
impl Serialize for FakeAggregateSignature { impl Serialize for FakeAggregateSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -94,7 +98,7 @@ impl<'de> Deserialize<'de> for FakeAggregateSignature {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?; 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)))?; .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(obj) Ok(obj)
} }
@ -117,7 +121,7 @@ mod tests {
original.add(&Signature::new(&[42, 42], 0, &keypair.sk)); original.add(&Signature::new(&[42, 42], 0, &keypair.sk));
let bytes = ssz_encode(&original); 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); assert_eq!(original, decoded);
} }

View File

@ -4,7 +4,7 @@ use hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor; 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; use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature. /// A single BLS signature.
@ -49,31 +49,31 @@ impl FakeSignature {
true 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. /// Returns a new empty signature.
pub fn empty_signature() -> Self { pub fn empty_signature() -> Self {
FakeSignature::zero() FakeSignature::zero()
} }
} }
impl Encodable for FakeSignature { impl_ssz!(FakeSignature, BLS_SIG_BYTE_SIZE, "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,
))
}
}
tree_hash_ssz_encoding_as_vector!(FakeSignature); tree_hash_ssz_encoding_as_vector!(FakeSignature);
cached_tree_hash_ssz_encoding_as_vector!(FakeSignature, 96); cached_tree_hash_ssz_encoding_as_vector!(FakeSignature, 96);
@ -93,7 +93,7 @@ impl<'de> Deserialize<'de> for FakeSignature {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let bytes = deserializer.deserialize_str(HexVisitor)?; 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)))?; .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(pubkey) Ok(pubkey)
} }
@ -112,7 +112,7 @@ mod tests {
let original = FakeSignature::new(&[42, 42], 0, &keypair.sk); let original = FakeSignature::new(&[42, 42], 0, &keypair.sk);
let bytes = ssz_encode(&original); 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); assert_eq!(original, decoded);
} }

View File

@ -1,6 +1,8 @@
extern crate bls_aggregates; extern crate bls_aggregates;
extern crate ssz; extern crate ssz;
#[macro_use]
mod macros;
mod aggregate_public_key; mod aggregate_public_key;
mod keypair; mod keypair;
mod public_key; mod public_key;

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)
}
}
}
};
}

View File

@ -4,7 +4,7 @@ use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor}; 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::default;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -27,9 +27,19 @@ impl PublicKey {
&self.0 &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 /// Converts compressed bytes to PublicKey
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> { 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)) Ok(PublicKey(pubkey))
} }
@ -40,8 +50,9 @@ impl PublicKey {
/// Converts (x, y) bytes to PublicKey /// Converts (x, y) bytes to PublicKey
pub fn from_uncompressed_bytes(bytes: &[u8]) -> Result<Self, DecodeError> { pub fn from_uncompressed_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let pubkey = let pubkey = RawPublicKey::from_uncompressed_bytes(&bytes).map_err(|_| {
RawPublicKey::from_uncompressed_bytes(&bytes).map_err(|_| DecodeError::Invalid)?; DecodeError::BytesInvalid("Invalid PublicKey uncompressed bytes.".to_string())
})?;
Ok(PublicKey(pubkey)) Ok(PublicKey(pubkey))
} }
@ -68,22 +79,7 @@ impl default::Default for PublicKey {
} }
} }
impl Encodable for PublicKey { impl_ssz!(PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE, "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 Serialize for PublicKey { impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -100,7 +96,7 @@ impl<'de> Deserialize<'de> for PublicKey {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let bytes = deserializer.deserialize_str(HexVisitor)?; 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)))?; .map_err(|e| serde::de::Error::custom(format!("invalid pubkey ({:?})", e)))?;
Ok(pubkey) Ok(pubkey)
} }
@ -139,7 +135,7 @@ mod tests {
let original = PublicKey::from_secret_key(&sk); let original = PublicKey::from_secret_key(&sk);
let bytes = ssz_encode(&original); 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); assert_eq!(original, decoded);
} }

View File

@ -1,10 +1,10 @@
use super::BLS_SECRET_KEY_BYTE_SIZE; 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 hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor; 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; use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature. /// A single BLS signature.
@ -19,11 +19,21 @@ impl SecretKey {
SecretKey(RawSecretKey::random()) 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. /// Instantiate a SecretKey from existing bytes.
/// ///
/// Note: this is _not_ SSZ decoding. /// Note: this is _not_ SSZ decoding.
pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, BlsDecodeError> { pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, DecodeError> {
Ok(SecretKey(RawSecretKey::from_bytes(bytes)?)) Ok(SecretKey(RawSecretKey::from_bytes(bytes).map_err(|e| {
DecodeError::BytesInvalid(format!(
"Invalid SecretKey bytes: {:?} Error: {:?}",
bytes, e
))
})?))
} }
/// Returns the underlying secret key. /// Returns the underlying secret key.
@ -32,22 +42,7 @@ impl SecretKey {
} }
} }
impl Encodable for SecretKey { impl_ssz!(SecretKey, BLS_SECRET_KEY_BYTE_SIZE, "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 Serialize for SecretKey { impl Serialize for SecretKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -64,7 +59,7 @@ impl<'de> Deserialize<'de> for SecretKey {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let bytes = deserializer.deserialize_str(HexVisitor)?; 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)))?; .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(secret_key) Ok(secret_key)
} }
@ -84,7 +79,7 @@ mod tests {
.unwrap(); .unwrap();
let bytes = ssz_encode(&original); 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); assert_eq!(original, decoded);
} }

View File

@ -5,7 +5,7 @@ use hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor; 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; use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature. /// A single BLS signature.
@ -83,8 +83,11 @@ impl Signature {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> { pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
for byte in bytes { for byte in bytes {
if *byte != 0 { if *byte != 0 {
let raw_signature = let raw_signature = RawSignature::from_bytes(&bytes).map_err(|_| {
RawSignature::from_bytes(&bytes).map_err(|_| DecodeError::Invalid)?; DecodeError::BytesInvalid(
format!("Invalid Signature bytes: {:?}", bytes).to_string(),
)
})?;
return Ok(Signature { return Ok(Signature {
signature: raw_signature, signature: raw_signature,
is_empty: false, is_empty: false,
@ -100,21 +103,7 @@ impl Signature {
} }
} }
impl Encodable for Signature { impl_ssz!(Signature, BLS_SIG_BYTE_SIZE, "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))
}
}
tree_hash_ssz_encoding_as_vector!(Signature); tree_hash_ssz_encoding_as_vector!(Signature);
cached_tree_hash_ssz_encoding_as_vector!(Signature, 96); cached_tree_hash_ssz_encoding_as_vector!(Signature, 96);
@ -136,7 +125,7 @@ impl<'de> Deserialize<'de> for Signature {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let bytes = deserializer.deserialize_str(HexVisitor)?; 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)))?; .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(signature) Ok(signature)
} }
@ -156,7 +145,7 @@ mod tests {
let original = Signature::new(&[42, 42], 0, &keypair.sk); let original = Signature::new(&[42, 42], 0, &keypair.sk);
let bytes = ssz_encode(&original); let bytes = ssz_encode(&original);
let decoded = decode::<Signature>(&bytes).unwrap(); let decoded = Signature::from_ssz_bytes(&bytes).unwrap();
assert_eq!(original, decoded); assert_eq!(original, decoded);
} }

View File

@ -7,7 +7,7 @@ use cached_tree_hash::cached_tree_hash_bytes_as_list;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use serde_hex::{encode, PrefixedHexVisitor}; use serde_hex::{encode, PrefixedHexVisitor};
use ssz::{Decodable, Encodable}; use ssz::{Decode, Encode};
use std::cmp; use std::cmp;
use std::default; use std::default;
@ -194,29 +194,23 @@ impl std::ops::BitOr for BooleanBitfield {
} }
} }
impl Encodable for BooleanBitfield { impl Encode for BooleanBitfield {
// ssz_append encodes Self according to the `ssz` spec. fn is_ssz_fixed_len() -> bool {
fn ssz_append(&self, s: &mut ssz::SszStream) { false
s.append_vec(&self.to_bytes()) }
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.append(&mut self.to_bytes())
} }
} }
impl Decodable for BooleanBitfield { impl Decode for BooleanBitfield {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), ssz::DecodeError> { fn is_ssz_fixed_len() -> bool {
let len = ssz::decode::decode_length(bytes, index, ssz::LENGTH_BYTES)?; false
if (ssz::LENGTH_BYTES + len) > bytes.len() {
return Err(ssz::DecodeError::TooShort);
} }
if len == 0 { fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
Ok((BooleanBitfield::new(), index + ssz::LENGTH_BYTES)) Ok(BooleanBitfield::from_bytes(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))
}
} }
} }
@ -277,7 +271,7 @@ cached_tree_hash_bytes_as_list!(BooleanBitfield);
mod tests { mod tests {
use super::*; use super::*;
use serde_yaml; use serde_yaml;
use ssz::{decode, ssz_encode, SszStream}; use ssz::ssz_encode;
use tree_hash::TreeHash; use tree_hash::TreeHash;
#[test] #[test]
@ -452,30 +446,17 @@ mod tests {
#[test] #[test]
fn test_ssz_encode() { fn test_ssz_encode() {
let field = create_test_bitfield(); let field = create_test_bitfield();
let mut stream = SszStream::new(); assert_eq!(field.as_ssz_bytes(), vec![0b0000_0011, 0b1000_0111]);
stream.append(&field);
assert_eq!(stream.drain(), vec![2, 0, 0, 0, 0b0000_0011, 0b1000_0111]);
let field = BooleanBitfield::from_elem(18, true); let field = BooleanBitfield::from_elem(18, true);
let mut stream = SszStream::new();
stream.append(&field);
assert_eq!( assert_eq!(
stream.drain(), field.as_ssz_bytes(),
vec![3, 0, 0, 0, 0b0000_0011, 0b1111_1111, 0b1111_1111] vec![0b0000_0011, 0b1111_1111, 0b1111_1111]
); );
let mut b = BooleanBitfield::new(); let mut b = BooleanBitfield::new();
b.set(1, true); b.set(1, true);
assert_eq!( assert_eq!(ssz_encode(&b), vec![0b0000_0010]);
ssz_encode(&b),
vec![
0b0000_0001,
0b0000_0000,
0b0000_0000,
0b0000_0000,
0b0000_0010
]
);
} }
fn create_test_bitfield() -> BooleanBitfield { fn create_test_bitfield() -> BooleanBitfield {
@ -491,13 +472,13 @@ mod tests {
#[test] #[test]
fn test_ssz_decode() { fn test_ssz_decode() {
let encoded = vec![2, 0, 0, 0, 0b0000_0011, 0b1000_0111]; let encoded = vec![0b0000_0011, 0b1000_0111];
let field = decode::<BooleanBitfield>(&encoded).unwrap(); let field = BooleanBitfield::from_ssz_bytes(&encoded).unwrap();
let expected = create_test_bitfield(); let expected = create_test_bitfield();
assert_eq!(field, expected); assert_eq!(field, expected);
let encoded = vec![3, 0, 0, 0, 255, 255, 3]; let encoded = vec![255, 255, 3];
let field = decode::<BooleanBitfield>(&encoded).unwrap(); let field = BooleanBitfield::from_ssz_bytes(&encoded).unwrap();
let expected = BooleanBitfield::from_bytes(&[255, 255, 3]); let expected = BooleanBitfield::from_bytes(&[255, 255, 3]);
assert_eq!(field, expected); assert_eq!(field, expected);
} }
@ -527,7 +508,7 @@ mod tests {
fn test_ssz_round_trip() { fn test_ssz_round_trip() {
let original = BooleanBitfield::from_bytes(&vec![18; 12][..]); let original = BooleanBitfield::from_bytes(&vec![18; 12][..]);
let ssz = ssz_encode(&original); let ssz = ssz_encode(&original);
let decoded = decode::<BooleanBitfield>(&ssz).unwrap(); let decoded = BooleanBitfield::from_ssz_bytes(&ssz).unwrap();
assert_eq!(original, decoded); assert_eq!(original, decoded);
} }

View File

@ -1,7 +1,4 @@
use super::*; 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> impl<T, N: Unsigned> tree_hash::TreeHash for FixedLenVec<T, N>
where 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 where
T: ssz::Encodable, T: ssz::Encode,
{ {
fn ssz_append(&self, s: &mut ssz::SszStream) { fn is_ssz_fixed_len() -> bool {
s.append_vec(&self.vec) 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 where
T: ssz::Decodable + Default, T: ssz::Decode + Default,
{ {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), ssz::DecodeError> { fn is_ssz_fixed_len() -> bool {
ssz::decode_ssz_list(bytes, index).and_then(|(vec, i)| Ok((vec.into(), i))) 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());
} }
} }

View File

@ -9,10 +9,7 @@ pub use typenum;
mod impls; mod impls;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct FixedLenVec<T, N> pub struct FixedLenVec<T, N> {
where
N: Unsigned,
{
vec: Vec<T>, vec: Vec<T>,
_phantom: PhantomData<N>, _phantom: PhantomData<N>,
} }

View File

@ -4,6 +4,14 @@ version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018" edition = "2018"
[[bench]]
name = "benches"
harness = false
[dev-dependencies]
criterion = "0.2"
ssz_derive = { path = "../ssz_derive" }
[dependencies] [dependencies]
bytes = "0.4.9" bytes = "0.4.9"
ethereum-types = "0.5" ethereum-types = "0.5"

View File

@ -38,8 +38,8 @@ spec is decided.*\
+ [bytes v0.4.9](#bytes-v049) + [bytes v0.4.9](#bytes-v049)
+ [ethereum-types](#ethereum-types) + [ethereum-types](#ethereum-types)
* [Interface](#interface) * [Interface](#interface)
+ [Encodable](#encodable) + [Encode](#encodable)
+ [Decodable](#decodable) + [Decode](#decodable)
+ [SszStream](#sszstream) + [SszStream](#sszstream)
- [new()](#new) - [new()](#new)
- [append(&mut self, value: &E) -> &mut Self](#appendmut-self-value-e---mut-self) - [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 ## 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. used to ensure that the object/type can be serialized.
```rust ```rust
pub trait Encodable { pub trait Encode {
fn ssz_append(&self, s: &mut SszStream); 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. used to ensure the object is deserializable.
```rust ```rust
pub trait Decodable: Sized { pub trait Decode: Sized {
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError>; 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 | | Parameter | Description |
|:---------:|:-----------------------------------------| |:---------:|:-----------------------------------------|
| ``value`` | Encodable value to append to the stream. | | ``value`` | Encode value to append to the stream. |
**Example** **Example**
@ -371,7 +371,7 @@ Appends some vector (list) of encodable values to the stream.
| Parameter | Description | | Parameter | Description |
|:---------:|:----------------------------------------------| |:---------:|:----------------------------------------------|
| ``vec`` | Vector of Encodable objects to be serialized. | | ``vec`` | Vector of Encode objects to be serialized. |
**Example** **Example**

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);

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());
}

View File

@ -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());
}

View File

@ -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);
}

View File

@ -1,215 +1,248 @@
use super::LENGTH_BYTES; use super::*;
pub mod impls;
/// Returned when SSZ decoding fails.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DecodeError { pub enum DecodeError {
TooShort, /// The bytes supplied were too short to be decoded into the specified type.
TooLong, InvalidByteLength { len: usize, expected: usize },
Invalid, /// 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 { /// Provides SSZ decoding (de-serialization) via the `from_ssz_bytes(&bytes)` method.
fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError>;
}
/// Decode the given bytes for the given type
/// ///
/// The single ssz encoded value/container/list will be decoded as the given type, /// See `examples/` for manual implementations or the crate root for implementations using
/// by recursively calling `ssz_decode`. /// `#[derive(Decode)]`.
/// Check on totality for underflowing the length of bytes and overflow checks done per container pub trait Decode: Sized {
pub fn decode<T>(ssz_bytes: &[u8]) -> Result<(T), DecodeError> /// Returns `true` if this object has a fixed-length.
where ///
T: Decodable, /// I.e., there are no variable length items in this object or any of it's contained objects.
{ fn is_ssz_fixed_len() -> bool;
let (decoded, i): (T, usize) = match T::ssz_decode(ssz_bytes, 0) {
Err(e) => return Err(e),
Ok(v) => v,
};
if i < ssz_bytes.len() { /// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes.
return Err(DecodeError::TooLong); ///
/// 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. /// The purpose of this struct is to split some SSZ bytes into individual slices. The builder is
pub fn decode_ssz_list<T>(ssz_bytes: &[u8], index: usize) -> Result<(Vec<T>, usize), DecodeError> /// then converted into a `SszDecoder` which decodes those values into object instances.
where ///
T: Decodable, /// See [`SszDecoder`](struct.SszDecoder.html) for usage examples.
{ pub struct SszDecoderBuilder<'a> {
if index + LENGTH_BYTES > ssz_bytes.len() { bytes: &'a [u8],
return Err(DecodeError::TooShort); items: Vec<&'a [u8]>,
}; offsets: Vec<Offset>,
items_index: usize,
// get the length
let serialized_length = match decode_length(ssz_bytes, index, LENGTH_BYTES) {
Err(v) => return Err(v),
Ok(v) => v,
};
let final_len: usize = index + LENGTH_BYTES + serialized_length;
if final_len > ssz_bytes.len() {
return Err(DecodeError::TooShort);
};
let mut tmp_index = index + LENGTH_BYTES;
let mut res_vec: Vec<T> = Vec::new();
while tmp_index < final_len {
match T::ssz_decode(ssz_bytes, tmp_index) {
Err(v) => return Err(v),
Ok(v) => {
tmp_index = v.1;
res_vec.push(v.0);
}
};
}
Ok((res_vec, final_len))
} }
/// Given some number of bytes, interpret the first four impl<'a> SszDecoderBuilder<'a> {
/// bytes as a 32-bit little-endian integer and return the /// Instantiate a new builder that should build a `SszDecoder` over the given `bytes` which
/// result. /// are assumed to be the SSZ encoding of some object.
pub fn decode_length( pub fn new(bytes: &'a [u8]) -> Self {
bytes: &[u8], Self {
index: usize, bytes,
length_bytes: usize, items: vec![],
) -> Result<usize, DecodeError> { offsets: vec![],
if bytes.len() < index + length_bytes { items_index: 0,
return Err(DecodeError::TooShort);
};
let mut len: usize = 0;
for (i, byte) in bytes
.iter()
.enumerate()
.take(index + length_bytes)
.skip(index)
{
let offset = (i - index) * 8;
len |= (*byte as usize) << offset;
}
Ok(len)
}
#[cfg(test)]
mod tests {
use super::super::encode::*;
use super::*;
#[test]
fn test_ssz_decode_length() {
let decoded = decode_length(&vec![1, 0, 0, 0], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 1);
let decoded = decode_length(&vec![0, 1, 0, 0], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 256);
let decoded = decode_length(&vec![255, 1, 0, 0], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 511);
let decoded = decode_length(&vec![255, 255, 255, 255], 0, LENGTH_BYTES);
assert_eq!(decoded.unwrap(), 4294967295);
}
#[test]
fn test_encode_decode_length() {
let params: Vec<usize> = vec![
0,
1,
2,
3,
7,
8,
16,
2 ^ 8,
2 ^ 8 + 1,
2 ^ 16,
2 ^ 16 + 1,
2 ^ 24,
2 ^ 24 + 1,
2 ^ 32,
];
for i in params {
let decoded = decode_length(&encode_length(i, LENGTH_BYTES), 0, LENGTH_BYTES).unwrap();
assert_eq!(i, decoded);
} }
} }
#[test] /// Declares that some type `T` is the next item in `bytes`.
fn test_encode_decode_ssz_list() { pub fn register_type<T: Decode>(&mut self) -> Result<(), DecodeError> {
let test_vec: Vec<u16> = vec![256; 12]; if T::is_ssz_fixed_len() {
let mut stream = SszStream::new(); let start = self.items_index;
stream.append_vec(&test_vec); self.items_index += T::ssz_fixed_len();
let ssz = stream.drain();
// u16 let slice = self.bytes.get(start..self.items_index).ok_or_else(|| {
let decoded: (Vec<u16>, usize) = decode_ssz_list(&ssz, 0).unwrap(); DecodeError::InvalidByteLength {
len: self.bytes.len(),
expected: self.items_index,
}
})?;
assert_eq!(decoded.0, test_vec); self.items.push(slice);
assert_eq!(decoded.1, LENGTH_BYTES + (12 * 2)); } 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 });
} }
#[test] self.offsets.push(Offset {
fn test_decode_ssz_list() { position: self.items.len(),
// u16
let v: Vec<u16> = vec![10, 10, 10, 10];
let decoded: (Vec<u16>, usize) =
decode_ssz_list(&vec![8, 0, 0, 0, 10, 0, 10, 0, 10, 0, 10, 0], 0).unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, LENGTH_BYTES + (4 * 2));
// u32
let v: Vec<u32> = vec![10, 10, 10, 10];
let decoded: (Vec<u32>, usize) = decode_ssz_list(
&vec![
16, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 00,
],
0,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, 20);
// u64
let v: Vec<u64> = vec![10, 10, 10, 10];
let decoded: (Vec<u64>, usize) = decode_ssz_list(
&vec![
32, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0,
0, 0, 10, 0, 0, 0, 0, 0, 0, 0,
],
0,
)
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, LENGTH_BYTES + (8 * 4));
// Check that it can accept index
let v: Vec<usize> = vec![15, 15, 15, 15];
let offset = 10;
let decoded: (Vec<usize>, usize) = decode_ssz_list(
&vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 32, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0,
0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0,
],
offset, offset,
) });
.unwrap();
assert_eq!(decoded.0, v);
assert_eq!(decoded.1, offset + LENGTH_BYTES + (8 * 4));
// Check that length > bytes throws error // Push an empty slice into items; it will be replaced later.
let decoded: Result<(Vec<usize>, usize), DecodeError> = self.items.push(&[]);
decode_ssz_list(&vec![32, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0], 0);
assert_eq!(decoded, Err(DecodeError::TooShort));
// Check that incorrect index throws error self.items_index += BYTES_PER_LENGTH_OFFSET;
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(())
}
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,
});
}
// 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];
self.items[a.position] = &self.bytes[a.offset..b.offset];
}
// 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,
});
}
}
Ok(())
}
/// Finalizes the builder, returning a `SszDecoder` that may be used to instantiate objects.
pub fn build(mut self) -> Result<SszDecoder<'a>, DecodeError> {
self.finalize()?;
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)
} }
} }

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
})
);
}
}

View File

@ -1,85 +1,159 @@
use super::LENGTH_BYTES; use super::*;
pub trait Encodable { mod impls;
fn ssz_append(&self, s: &mut SszStream);
}
/// 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 /// See `examples/` for manual implementations or the crate root for implementations using
/// the `drain()` method to consume the struct and return the /// `#[derive(Encode)]`.
/// ssz encoded bytes. pub trait Encode {
#[derive(Default)] /// Returns `true` if this object has a fixed-length.
pub struct SszStream {
buffer: Vec<u8>,
}
impl SszStream {
/// Create a new, empty stream for writing ssz values.
pub fn new() -> Self {
SszStream { buffer: Vec::new() }
}
/// Append some ssz encodable value to the stream.
pub fn append<E>(&mut self, value: &E) -> &mut Self
where
E: Encodable,
{
value.ssz_append(self);
self
}
/// Append some ssz encoded bytes to the stream.
/// ///
/// The length of the supplied bytes will be concatenated /// I.e., there are no variable length items in this object or any of it's contained objects.
/// to the stream before the supplied bytes. fn is_ssz_fixed_len() -> bool;
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);
}
/// Append some ssz encoded bytes to the stream without calculating length /// Append the encoding `self` to `buf`.
/// ///
/// The raw bytes will be concatenated to the stream. /// Note, variable length objects need only to append their "variable length" portion, they do
pub fn append_encoded_raw(&mut self, vec: &[u8]) { /// not need to provide their offset.
self.buffer.extend_from_slice(&vec); fn ssz_append(&self, buf: &mut Vec<u8>);
}
/// Append some vector (list) of encodable values to the stream. /// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes.
/// ///
/// The length of the list will be concatenated to the stream, then /// By default, this is set to `BYTES_PER_LENGTH_OFFSET` which is suitable for variable length
/// each item in the vector will be encoded and concatenated. /// objects, but not fixed-length objects. Fixed-length objects _must_ return a value which
pub fn append_vec<E>(&mut self, vec: &[E]) /// represents their length.
where fn ssz_fixed_len() -> usize {
E: Encodable, BYTES_PER_LENGTH_OFFSET
{
let mut list_stream = SszStream::new();
for item in vec {
item.ssz_append(&mut list_stream);
}
self.append_encoded_val(&list_stream.drain());
} }
/// Consume the stream and return the underlying bytes. /// Returns the full-form encoding of this object.
pub fn drain(self) -> Vec<u8> { ///
self.buffer /// 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
} }
} }
/// Encode some length into a ssz size prefix. /// Allow for encoding an ordered series of distinct or indistinct objects as SSZ bytes.
/// ///
/// The ssz size prefix is 4 bytes, which is treated as a continuious /// **You must call `finalize(..)` after the final `append(..)` call** to ensure the bytes are
/// 32bit little-endian integer. /// written to `buf`.
pub fn encode_length(len: usize, length_bytes: usize) -> Vec<u8> { ///
assert!(length_bytes > 0); // For sanity /// ## Example
assert!((len as usize) < 2usize.pow(length_bytes as u32 * 8)); ///
let mut header: Vec<u8> = vec![0; length_bytes]; /// Use `SszEncoder` to produce identical output to `foo.as_ssz_bytes()`:
for (i, header_byte) in header.iter_mut().enumerate() { ///
let offset = i * 8; /// ```rust
*header_byte = ((len >> offset) & 0xff) as u8; /// 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>,
}
impl<'a> SszEncoder<'a> {
/// Instantiate a new encoder for encoding a SSZ list.
///
/// Identical to `Self::container`.
pub fn list(buf: &'a mut Vec<u8>, num_fixed_bytes: usize) -> Self {
Self::container(buf, num_fixed_bytes)
} }
header
/// 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);
Self {
offset: num_fixed_bytes,
buf,
variable_bytes: vec![],
}
}
/// 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 `len` as a little-endian byte vec of `BYTES_PER_LENGTH_OFFSET` length.
///
/// If `len` is larger than `2 ^ BYTES_PER_LENGTH_OFFSET`, a `debug_assert` is raised.
pub fn encode_length(len: usize) -> Vec<u8> {
// 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)] #[cfg(test)]
@ -87,84 +161,27 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[should_panic] fn test_encode_length() {
fn test_encode_length_0_bytes_panic() { assert_eq!(encode_length(0), vec![0; 4]);
encode_length(0, 0);
} 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!( assert_eq!(
encode_length(4294967295, LENGTH_BYTES), // 2^(3*8) - 1 encode_length(MAX_LENGTH_VALUE),
vec![255, 255, 255, 255] 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] #[test]
#[should_panic] #[should_panic]
fn test_encode_length_4_bytes_panic() { #[cfg(debug_assertions)]
encode_length(4294967296, LENGTH_BYTES); // 2^(3*8) fn test_encode_length_above_max_debug_panics() {
encode_length(MAX_LENGTH_VALUE + 1);
} }
#[test] #[test]
fn test_encode_list() { #[cfg(not(debug_assertions))]
let test_vec: Vec<u16> = vec![256; 12]; fn test_encode_length_above_max_not_debug_does_not_panic() {
let mut stream = SszStream::new(); assert_eq!(encode_length(MAX_LENGTH_VALUE + 1), vec![0; 4]);
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]);
} }
} }

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]);
}
}

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]);
}
}

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]);
}
}

View File

@ -1,225 +1,59 @@
/* //! Provides encoding (serialization) and decoding (deserialization) in the SimpleSerialize (SSZ)
* This is a WIP of implementing an alternative //! format designed for use in Ethereum 2.0.
* serialization strategy. It attempts to follow Vitalik's //!
* "simpleserialize" format here: //! Conforms to
* https://github.com/ethereum/beacon_chain/blob/master/beacon_chain/utils/simpleserialize.py //! [v0.6.1](https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/simple-serialize.md) of the
* //! Ethereum 2.0 specification.
* This implementation is not final and would almost certainly //!
* have issues. //! ## Example
*/ //!
extern crate bytes; //! ```rust
extern crate ethereum_types; //! 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; mod decode;
pub mod encode; mod encode;
mod macros;
mod impl_decode; pub use decode::{
mod impl_encode; 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}; /// The number of bytes used to represent an offset.
pub use crate::encode::{Encodable, SszStream}; pub const BYTES_PER_LENGTH_OFFSET: usize = 4;
/// The maximum value that can be represented using `BYTES_PER_LENGTH_OFFSET`.
pub use hashing::hash; pub const MAX_LENGTH_VALUE: usize = (1 << (BYTES_PER_LENGTH_OFFSET * 8)) - 1;
pub const LENGTH_BYTES: usize = 4;
pub const MAX_LIST_SIZE: usize = 1 << (4 * 8);
/// Convenience function to SSZ encode an object supporting ssz::Encode. /// 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> pub fn ssz_encode<T>(val: &T) -> Vec<u8>
where where
T: Encodable, T: Encode,
{ {
let mut ssz_stream = SszStream::new(); val.as_ssz_bytes()
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,
};
}
}
} }

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

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 })
);
}
}

View File

@ -1,39 +1,4 @@
//! Provides the following procedural derive macros: #![recursion_limit = "128"]
//!
//! - `#[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);
//! }
//! ```
extern crate proc_macro; extern crate proc_macro;
@ -41,21 +6,6 @@ use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput}; 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 /// Returns a Vec of `syn::Ident` for each named field in the struct, whilst filtering out fields
/// that should not be serialized. /// that should not be serialized.
/// ///
@ -80,6 +30,22 @@ fn get_serializable_named_field_idents<'a>(
.collect() .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. /// Returns true if some field has an attribute declaring it should not be serialized.
/// ///
/// The field attribute is: `#[ssz(skip_serializing)]` /// The field attribute is: `#[ssz(skip_serializing)]`
@ -92,7 +58,7 @@ fn should_skip_serializing(field: &syn::Field) -> bool {
false false
} }
/// Implements `ssz::Encodable` for some `struct`. /// Implements `ssz::Encode` for some `struct`.
/// ///
/// Fields are encoded in the order they are defined. /// Fields are encoded in the order they are defined.
#[proc_macro_derive(Encode, attributes(ssz))] #[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_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! { let output = quote! {
impl #impl_generics ssz::Encodable for #name #ty_generics #where_clause { impl #impl_generics ssz::Encode for #name #ty_generics #where_clause {
fn ssz_append(&self, s: &mut ssz::SszStream) { 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 false
} }
/// Implements `ssz::Decodable` for some `struct`. /// Implements `ssz::Decode` for some `struct`.
/// ///
/// Fields are decoded in the order they are defined. /// Fields are decoded in the order they are defined.
#[proc_macro_derive(Decode)] #[proc_macro_derive(Decode)]
@ -148,21 +144,38 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
_ => panic!("ssz_derive only supports structs."), _ => 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 // Build quotes for fields that should be deserialized and those that should be built from
// `Default`. // `Default`.
let mut quotes = vec![];
for field in &struct_data.fields { for field in &struct_data.fields {
match &field.ident { match &field.ident {
Some(ref ident) => { Some(ref ident) => {
if should_skip_deserializing(field) { if should_skip_deserializing(field) {
quotes.push(quote! { // Field should not be deserialized; use a `Default` impl to instantiate.
let #ident = <_>::default(); decodes.push(quote! {
#ident: <_>::default()
}); });
} else { } else {
quotes.push(quote! { let ty = &field.ty;
let (#ident, i) = <_>::ssz_decode(bytes, i)?;
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! { let output = quote! {
impl #impl_generics ssz::Decodable for #name #ty_generics #where_clause { impl #impl_generics ssz::Decode for #name #ty_generics #where_clause {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), ssz::DecodeError> { 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(( let mut decoder = builder.build()?;
Self {
Ok(Self {
#( #(
#all_idents, #decodes,
)* )*
}, })
i
))
} }
} }
}; };

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);
}

View File

@ -1,7 +1,7 @@
use super::beacon_node_attestation::BeaconNodeAttestation; use super::beacon_node_attestation::BeaconNodeAttestation;
use crate::block_producer::{BeaconNodeError, PublishOutcome}; use crate::block_producer::{BeaconNodeError, PublishOutcome};
use protos::services_grpc::AttestationServiceClient; use protos::services_grpc::AttestationServiceClient;
use ssz::{ssz_encode, Decodable}; use ssz::{Decode, Encode};
use protos::services::{ use protos::services::{
Attestation as GrpcAttestation, ProduceAttestationDataRequest, PublishAttestationRequest, Attestation as GrpcAttestation, ProduceAttestationDataRequest, PublishAttestationRequest,
@ -22,8 +22,8 @@ impl BeaconNodeAttestation for AttestationServiceClient {
.produce_attestation_data(&req) .produce_attestation_data(&req)
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?; .map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
let (attestation_data, _index) = let attestation_data =
AttestationData::ssz_decode(reply.get_attestation_data().get_ssz(), 0) AttestationData::from_ssz_bytes(reply.get_attestation_data().get_ssz())
.map_err(|_| BeaconNodeError::DecodeFailure)?; .map_err(|_| BeaconNodeError::DecodeFailure)?;
Ok(attestation_data) Ok(attestation_data)
} }
@ -34,7 +34,7 @@ impl BeaconNodeAttestation for AttestationServiceClient {
) -> Result<PublishOutcome, BeaconNodeError> { ) -> Result<PublishOutcome, BeaconNodeError> {
let mut req = PublishAttestationRequest::new(); let mut req = PublishAttestationRequest::new();
let ssz = ssz_encode(&attestation); let ssz = attestation.as_ssz_bytes();
let mut grpc_attestation = GrpcAttestation::new(); let mut grpc_attestation = GrpcAttestation::new();
grpc_attestation.set_ssz(ssz); grpc_attestation.set_ssz(ssz);

View File

@ -3,7 +3,7 @@ use protos::services::{
BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest, BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest,
}; };
use protos::services_grpc::BeaconBlockServiceClient; use protos::services_grpc::BeaconBlockServiceClient;
use ssz::{decode, ssz_encode}; use ssz::{Decode, Encode};
use std::sync::Arc; use std::sync::Arc;
use types::{BeaconBlock, Signature, Slot}; use types::{BeaconBlock, Signature, Slot};
@ -33,7 +33,7 @@ impl BeaconNodeBlock for BeaconBlockGrpcClient {
// request a beacon block from the node // request a beacon block from the node
let mut req = ProduceBeaconBlockRequest::new(); let mut req = ProduceBeaconBlockRequest::new();
req.set_slot(slot.as_u64()); 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 //TODO: Determine if we want an explicit timeout
let reply = self let reply = self
@ -46,7 +46,8 @@ impl BeaconNodeBlock for BeaconBlockGrpcClient {
let block = reply.get_block(); let block = reply.get_block();
let ssz = block.get_ssz(); 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)) Ok(Some(block))
} else { } else {
@ -61,7 +62,7 @@ impl BeaconNodeBlock for BeaconBlockGrpcClient {
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError> { fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError> {
let mut req = PublishBeaconBlockRequest::new(); let mut req = PublishBeaconBlockRequest::new();
let ssz = ssz_encode(&block); let ssz = block.as_ssz_bytes();
let mut grpc_block = GrpcBeaconBlock::new(); let mut grpc_block = GrpcBeaconBlock::new();
grpc_block.set_ssz(ssz); grpc_block.set_ssz(ssz);