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