diff --git a/Cargo.lock b/Cargo.lock index 4200bc27b0..2be8b1a064 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -393,7 +393,7 @@ dependencies = [ "eth2", "eth2_hashing", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "eth2_ssz_types", "execution_layer", "exit-future", @@ -714,7 +714,7 @@ version = "0.1.0" dependencies = [ "eth2_hashing", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "eth2_ssz_types", "ethereum-types 0.12.1", "quickcheck", @@ -1498,7 +1498,7 @@ dependencies = [ "compare_fields_derive", "derivative", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "ethereum-types 0.12.1", "fork_choice", "fs2", @@ -1661,7 +1661,7 @@ dependencies = [ "eth1_test_rig", "eth2", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "execution_layer", "futures", "hex", @@ -1705,7 +1705,7 @@ dependencies = [ "eth2_keystore", "eth2_serde_utils", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "futures", "futures-util", "libsecp256k1", @@ -1821,7 +1821,7 @@ dependencies = [ name = "eth2_ssz" version = "0.4.1" dependencies = [ - "eth2_ssz_derive", + "eth2_ssz_derive 0.4.0", "ethereum-types 0.12.1", "itertools", "smallvec", @@ -1829,7 +1829,9 @@ dependencies = [ [[package]] name = "eth2_ssz_derive" -version = "0.3.1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "635b86d2c941bb71e7419a571e1763d65c93e51a1bafc400352e3bef6ff59fc9" dependencies = [ "darling", "proc-macro2", @@ -1837,6 +1839,17 @@ dependencies = [ "syn", ] +[[package]] +name = "eth2_ssz_derive" +version = "0.4.0" +dependencies = [ + "darling", + "eth2_ssz", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "eth2_ssz_types" version = "0.2.2" @@ -2240,7 +2253,7 @@ version = "0.1.0" dependencies = [ "beacon_chain", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "proto_array", "slog", "state_processing", @@ -3660,7 +3673,7 @@ dependencies = [ "discv5", "error-chain", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "eth2_ssz_types", "exit-future", "fnv", @@ -4420,7 +4433,7 @@ dependencies = [ "bitvec 1.0.1", "derivative", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "itertools", "lazy_static", "lighthouse_metrics", @@ -5007,7 +5020,7 @@ name = "proto_array" version = "0.2.0" dependencies = [ "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "serde", "serde_derive", "serde_yaml", @@ -5980,7 +5993,7 @@ dependencies = [ "bincode", "byteorder", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "filesystem", "flate2", "lazy_static", @@ -6267,7 +6280,7 @@ dependencies = [ "env_logger 0.9.0", "eth2_hashing", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "eth2_ssz_types", "int_to_bytes", "integer-sqrt", @@ -6309,7 +6322,7 @@ dependencies = [ "db-key", "directory", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "itertools", "lazy_static", "leveldb", @@ -6946,7 +6959,7 @@ dependencies = [ "beacon_chain", "eth2_hashing", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "ethereum-types 0.12.1", "rand 0.8.5", "smallvec", @@ -7085,7 +7098,7 @@ dependencies = [ "eth2_interop_keypairs", "eth2_serde_utils", "eth2_ssz", - "eth2_ssz_derive", + "eth2_ssz_derive 0.3.0", "eth2_ssz_types", "ethereum-types 0.12.1", "hex", diff --git a/consensus/ssz/Cargo.toml b/consensus/ssz/Cargo.toml index e521853c21..633ffc8e94 100644 --- a/consensus/ssz/Cargo.toml +++ b/consensus/ssz/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0" name = "ssz" [dev-dependencies] -eth2_ssz_derive = "0.3.1" +eth2_ssz_derive = "0.4.0" [dependencies] ethereum-types = "0.12.1" diff --git a/consensus/ssz/tests/tests.rs b/consensus/ssz/tests/tests.rs index 72b71fcbf9..1b61b8510a 100644 --- a/consensus/ssz/tests/tests.rs +++ b/consensus/ssz/tests/tests.rs @@ -387,7 +387,7 @@ mod derive_macro { } #[derive(PartialEq, Debug, Encode, Decode)] - #[ssz(enum_behaviour = "union")] + #[ssz(union)] enum TwoFixedUnion { U8(u8), U16(u16), @@ -422,16 +422,9 @@ mod derive_macro { b: u8, } - #[derive(PartialEq, Debug, Encode)] - #[ssz(enum_behaviour = "transparent")] - enum TwoVariableTrans { - A(VariableA), - B(VariableB), - } - #[derive(PartialEq, Debug, Encode)] #[ssz(transparent)] - enum TwoVariableTransDirectTag { + enum TwoVariableTrans { A(VariableA), B(VariableB), } @@ -441,16 +434,9 @@ mod derive_macro { a: TwoVariableTrans, } - #[derive(PartialEq, Debug, Encode, Decode)] - #[ssz(enum_behaviour = "union")] - enum TwoVariableUnion { - A(VariableA), - B(VariableB), - } - #[derive(PartialEq, Debug, Encode, Decode)] #[ssz(union)] - enum TwoVariableUnionDirectTag { + enum TwoVariableUnion { A(VariableA), B(VariableB), } @@ -509,7 +495,7 @@ mod derive_macro { } #[derive(PartialEq, Debug, Encode, Decode)] - #[ssz(enum_behaviour = "union")] + #[ssz(union)] enum TwoVecUnion { A(Vec), B(Vec), diff --git a/consensus/ssz_derive/Cargo.toml b/consensus/ssz_derive/Cargo.toml index 57d65e5573..2f656a9bf2 100644 --- a/consensus/ssz_derive/Cargo.toml +++ b/consensus/ssz_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "eth2_ssz_derive" -version = "0.3.1" +version = "0.4.0" authors = ["Paul Hauner "] edition = "2021" description = "Procedural derive macros to accompany the eth2_ssz crate." @@ -17,3 +17,4 @@ quote = "1.0.7" darling = "0.13.0" [dev-dependencies] +eth2_ssz = "0.4.1" diff --git a/consensus/ssz_derive/src/lib.rs b/consensus/ssz_derive/src/lib.rs index 82f15e4cfb..cf97acb502 100644 --- a/consensus/ssz_derive/src/lib.rs +++ b/consensus/ssz_derive/src/lib.rs @@ -1,7 +1,127 @@ #![recursion_limit = "256"] //! Provides procedural derive macros for the `Encode` and `Decode` traits of the `eth2_ssz` crate. //! -//! Supports field attributes, see each derive macro for more information. +//! ## Struct Attributes +//! +//! The following struct attributes are available: +//! +//! - `#[ssz(union)]` +//! - For `enum`, implements `ssz::Encode` and `ssz::Decode` such that the union variant is +//! encoded as a single byte in the SSZ vector. +//! - Not supported for `struct`. +//! - `#[ssz(transparent)]` +//! - For `enum`, implements `ssz::Encode` such that the `enum` is ignored. `ssz::Decode` is not +//! supported. +//! - For `struct`, implements `ssz::Encode` and `ssz::Decode` such that the outermost `struct` +//! is ignored for encoding and decoding. This is useful for new-type wrappers. The `struct` +//! must have exactly one non-skipped field. +//! +//! ## Field Attributes +//! +//! The following field attributes are available: +//! +//! - `#[ssz(with = "module")]`: uses the methods in `module` to implement `ssz::Encode` or +//! `ssz::Decode`. This is useful when it's not possible to create an `impl` for that type +//! (perhaps it is defined in another crate). +//! - `#[ssz(skip_serializing)]`: this field will not be included in the serialized SSZ vector. +//! - `#[ssz(skip_deserializing)]`: this field will not be expected to be included in the serialized +//! SSZ vector and it will be initialized from a `Default` implementation. +//! +//! ## Examples +//! +//! ### Structs +//! +//! ```rust +//! use ssz::{Encode, Decode}; +//! use ssz_derive::{Encode, Decode}; +//! +//! /// Represented as an SSZ "list" wrapped in an SSZ "container". +//! #[derive(Debug, PartialEq, Encode, Decode)] +//! struct TypicalStruct { +//! foo: Vec +//! } +//! +//! assert_eq!( +//! TypicalStruct { foo: vec![42] }.as_ssz_bytes(), +//! vec![4, 0, 0, 0, 42] +//! ); +//! +//! assert_eq!( +//! TypicalStruct::from_ssz_bytes(&[4, 0, 0, 0, 42]).unwrap(), +//! TypicalStruct { foo: vec![42] }, +//! ); +//! +//! /// Represented as an SSZ "list" *without* an SSZ "container". +//! #[derive(Encode, Decode)] +//! #[ssz(transparent)] +//! struct NewType { +//! foo: Vec +//! } +//! +//! assert_eq!( +//! NewType { foo: vec![42] }.as_ssz_bytes(), +//! vec![42] +//! ); +//! +//! /// Represented as an SSZ "list" *without* an SSZ "container". The `bar` byte is ignored. +//! #[derive(Debug, PartialEq, Encode, Decode)] +//! #[ssz(transparent)] +//! struct NewTypeSkippedField { +//! foo: Vec, +//! #[ssz(skip_serializing, skip_deserializing)] +//! bar: u8, +//! } +//! +//! assert_eq!( +//! NewTypeSkippedField { foo: vec![42], bar: 99 }.as_ssz_bytes(), +//! vec![42] +//! ); +//! assert_eq!( +//! NewTypeSkippedField::from_ssz_bytes(&[42]).unwrap(), +//! NewTypeSkippedField { foo: vec![42], bar: 0 } +//! ); +//! ``` +//! +//! ### Enums +//! +//! ```rust +//! use ssz::{Encode, Decode}; +//! use ssz_derive::{Encode, Decode}; +//! +//! /// Represented as an SSZ "union". +//! #[derive(Debug, PartialEq, Encode, Decode)] +//! #[ssz(union)] +//! enum UnionEnum { +//! Foo(u8), +//! Bar(u8), +//! } +//! +//! assert_eq!( +//! UnionEnum::Foo(42).as_ssz_bytes(), +//! vec![0, 42] +//! ); +//! assert_eq!( +//! UnionEnum::from_ssz_bytes(&[1, 42]).unwrap(), +//! UnionEnum::Bar(42), +//! ); +//! +//! /// Represented as only the value in the enum variant. +//! #[derive(Debug, PartialEq, Encode)] +//! #[ssz(transparent)] +//! enum TransparentEnum { +//! Foo(u8), +//! Bar(Vec), +//! } +//! +//! assert_eq!( +//! TransparentEnum::Foo(42).as_ssz_bytes(), +//! vec![42] +//! ); +//! assert_eq!( +//! TransparentEnum::Bar(vec![42, 42]).as_ssz_bytes(), +//! vec![42, 42] +//! ); +//! ``` use darling::{FromDeriveInput, FromMeta}; use proc_macro::TokenStream; @@ -13,11 +133,8 @@ use syn::{parse_macro_input, DataEnum, DataStruct, DeriveInput, Ident}; /// extensions). const MAX_UNION_SELECTOR: u8 = 127; -const ENUM_TRANSPARENT: &str = "transparent"; -const ENUM_UNION: &str = "union"; -const ENUM_VARIANTS: &[&str] = &[ENUM_TRANSPARENT, ENUM_UNION]; const NO_ENUM_BEHAVIOUR_ERROR: &str = "enums require a \"transparent\" or \"union\" attribute, \ - e.g., #[ssz(transparent)]"; + e.g., #[ssz(transparent)] or #[ssz(union)]"; #[derive(Debug, FromDeriveInput)] #[darling(attributes(ssz))] @@ -54,18 +171,11 @@ impl Config { (false, false, None) => EnumBehaviour::Unspecified, (true, false, None) => EnumBehaviour::Transparent, (false, true, None) => EnumBehaviour::Union, - (false, false, Some(behaviour_string)) => match behaviour_string.as_ref() { - ENUM_TRANSPARENT => EnumBehaviour::Transparent, - ENUM_UNION => EnumBehaviour::Union, - other => panic!( - "{} is an invalid enum_behaviour, use either {:?}", - other, ENUM_VARIANTS - ), - }, (true, true, _) => panic!("cannot provide both \"transparent\" and \"union\""), - (_, _, Some(_)) => { - panic!("\"enum_behaviour\" cannot be used with \"transparent\" or \"union\"") - } + (_, _, Some(_)) => panic!( + "the \"enum_behaviour\" attribute has been removed, please use \ + either #[ssz(transparent)] or #[ssz(union)] instead" + ), }; // Don't allow `enum_behaviour` for structs. @@ -470,8 +580,7 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream { syn::Data::Enum(s) => match config.enum_behaviour { EnumBehaviour::Union => ssz_decode_derive_enum_union(&item, s), EnumBehaviour::Transparent => panic!( - "Decode cannot be derived for enum_behaviour \"{}\", only \"{}\" is valid.", - ENUM_TRANSPARENT, ENUM_UNION + "Decode cannot be derived for enum_behaviour \"transparent\", only \"union\" is valid." ), EnumBehaviour::Unspecified => panic!("{}", NO_ENUM_BEHAVIOUR_ERROR), },