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