Cleanup payload types (#3675)
* Add transparent support * Add `Config` struct * Deprecate `enum_behaviour` * Partially remove enum_behaviour from project * Revert "Partially remove enum_behaviour from project" This reverts commit 46ffb7fe77622cf420f7ba2fccf432c0050535d6. * Revert "Deprecate `enum_behaviour`" This reverts commit 89b64a6f53d0f68685be88d5b60d39799d9933b5. * Add `struct_behaviour` * Tidy * Move tests into `ssz_derive` * Bump ssz derive * Fix comment * newtype transaparent ssz * use ssz transparent and create macros for per fork implementations * use superstruct map macros Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
parent
d8a49aad2b
commit
c45b809b76
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -1791,9 +1791,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eth2_ssz_derive"
|
name = "eth2_ssz_derive"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
|
"eth2_ssz",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -33,7 +33,7 @@ slot_clock = { path = "../../common/slot_clock" }
|
|||||||
eth2_hashing = "0.3.0"
|
eth2_hashing = "0.3.0"
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_types = "0.2.2"
|
eth2_ssz_types = "0.2.2"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
state_processing = { path = "../../consensus/state_processing" }
|
state_processing = { path = "../../consensus/state_processing" }
|
||||||
tree_hash = "0.4.1"
|
tree_hash = "0.4.1"
|
||||||
types = { path = "../../consensus/types" }
|
types = { path = "../../consensus/types" }
|
||||||
|
@ -21,7 +21,7 @@ hex = "0.4.2"
|
|||||||
types = { path = "../../consensus/types"}
|
types = { path = "../../consensus/types"}
|
||||||
merkle_proof = { path = "../../consensus/merkle_proof"}
|
merkle_proof = { path = "../../consensus/merkle_proof"}
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
tree_hash = "0.4.1"
|
tree_hash = "0.4.1"
|
||||||
parking_lot = "0.12.0"
|
parking_lot = "0.12.0"
|
||||||
slog = "2.5.2"
|
slog = "2.5.2"
|
||||||
|
@ -13,7 +13,7 @@ parking_lot = "0.12.0"
|
|||||||
types = { path = "../../consensus/types" }
|
types = { path = "../../consensus/types" }
|
||||||
state_processing = { path = "../../consensus/state_processing" }
|
state_processing = { path = "../../consensus/state_processing" }
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
rayon = "1.5.0"
|
rayon = "1.5.0"
|
||||||
serde = "1.0.116"
|
serde = "1.0.116"
|
||||||
serde_derive = "1.0.116"
|
serde_derive = "1.0.116"
|
||||||
|
@ -14,7 +14,7 @@ leveldb = { version = "0.8.6", default-features = false }
|
|||||||
parking_lot = "0.12.0"
|
parking_lot = "0.12.0"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
types = { path = "../../consensus/types" }
|
types = { path = "../../consensus/types" }
|
||||||
state_processing = { path = "../../consensus/state_processing" }
|
state_processing = { path = "../../consensus/state_processing" }
|
||||||
slog = "2.5.2"
|
slog = "2.5.2"
|
||||||
|
@ -21,7 +21,7 @@ bytes = "1.0.1"
|
|||||||
account_utils = { path = "../../common/account_utils" }
|
account_utils = { path = "../../common/account_utils" }
|
||||||
sensitive_url = { path = "../../common/sensitive_url" }
|
sensitive_url = { path = "../../common/sensitive_url" }
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
futures-util = "0.3.8"
|
futures-util = "0.3.8"
|
||||||
futures = "0.3.8"
|
futures = "0.3.8"
|
||||||
store = { path = "../../beacon_node/store", optional = true }
|
store = { path = "../../beacon_node/store", optional = true }
|
||||||
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||||||
ethereum-types = "0.12.1"
|
ethereum-types = "0.12.1"
|
||||||
eth2_ssz_types = "0.2.2"
|
eth2_ssz_types = "0.2.2"
|
||||||
eth2_hashing = "0.3.0"
|
eth2_hashing = "0.3.0"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
tree_hash = "0.4.1"
|
tree_hash = "0.4.1"
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
|
@ -11,7 +11,7 @@ types = { path = "../types" }
|
|||||||
state_processing = { path = "../state_processing" }
|
state_processing = { path = "../state_processing" }
|
||||||
proto_array = { path = "../proto_array" }
|
proto_array = { path = "../proto_array" }
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
slog = { version = "2.5.2", features = ["max_level_trace", "release_max_level_trace"] }
|
slog = { version = "2.5.2", features = ["max_level_trace", "release_max_level_trace"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -11,7 +11,7 @@ path = "src/bin.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
types = { path = "../types" }
|
types = { path = "../types" }
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
serde = "1.0.116"
|
serde = "1.0.116"
|
||||||
serde_derive = "1.0.116"
|
serde_derive = "1.0.116"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.13"
|
||||||
|
@ -10,7 +10,7 @@ license = "Apache-2.0"
|
|||||||
name = "ssz"
|
name = "ssz"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethereum-types = "0.12.1"
|
ethereum-types = "0.12.1"
|
||||||
|
@ -388,145 +388,3 @@ mod round_trip {
|
|||||||
round_trip(data);
|
round_trip(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod derive_macro {
|
|
||||||
use ssz::{Decode, Encode};
|
|
||||||
use ssz_derive::{Decode, Encode};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
fn assert_encode<T: Encode>(item: &T, bytes: &[u8]) {
|
|
||||||
assert_eq!(item.as_ssz_bytes(), bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_encode_decode<T: Encode + Decode + PartialEq + Debug>(item: &T, bytes: &[u8]) {
|
|
||||||
assert_encode(item, bytes);
|
|
||||||
assert_eq!(T::from_ssz_bytes(bytes).unwrap(), *item);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
|
||||||
#[ssz(enum_behaviour = "union")]
|
|
||||||
enum TwoFixedUnion {
|
|
||||||
U8(u8),
|
|
||||||
U16(u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
|
||||||
struct TwoFixedUnionStruct {
|
|
||||||
a: TwoFixedUnion,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_fixed_union() {
|
|
||||||
let eight = TwoFixedUnion::U8(1);
|
|
||||||
let sixteen = TwoFixedUnion::U16(1);
|
|
||||||
|
|
||||||
assert_encode_decode(&eight, &[0, 1]);
|
|
||||||
assert_encode_decode(&sixteen, &[1, 1, 0]);
|
|
||||||
|
|
||||||
assert_encode_decode(&TwoFixedUnionStruct { a: eight }, &[4, 0, 0, 0, 0, 1]);
|
|
||||||
assert_encode_decode(&TwoFixedUnionStruct { a: sixteen }, &[4, 0, 0, 0, 1, 1, 0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
|
||||||
struct VariableA {
|
|
||||||
a: u8,
|
|
||||||
b: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
|
||||||
struct VariableB {
|
|
||||||
a: Vec<u8>,
|
|
||||||
b: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode)]
|
|
||||||
#[ssz(enum_behaviour = "transparent")]
|
|
||||||
enum TwoVariableTrans {
|
|
||||||
A(VariableA),
|
|
||||||
B(VariableB),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode)]
|
|
||||||
struct TwoVariableTransStruct {
|
|
||||||
a: TwoVariableTrans,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
|
||||||
#[ssz(enum_behaviour = "union")]
|
|
||||||
enum TwoVariableUnion {
|
|
||||||
A(VariableA),
|
|
||||||
B(VariableB),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
|
||||||
struct TwoVariableUnionStruct {
|
|
||||||
a: TwoVariableUnion,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_variable_trans() {
|
|
||||||
let trans_a = TwoVariableTrans::A(VariableA {
|
|
||||||
a: 1,
|
|
||||||
b: vec![2, 3],
|
|
||||||
});
|
|
||||||
let trans_b = TwoVariableTrans::B(VariableB {
|
|
||||||
a: vec![1, 2],
|
|
||||||
b: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_encode(&trans_a, &[1, 5, 0, 0, 0, 2, 3]);
|
|
||||||
assert_encode(&trans_b, &[5, 0, 0, 0, 3, 1, 2]);
|
|
||||||
|
|
||||||
assert_encode(
|
|
||||||
&TwoVariableTransStruct { a: trans_a },
|
|
||||||
&[4, 0, 0, 0, 1, 5, 0, 0, 0, 2, 3],
|
|
||||||
);
|
|
||||||
assert_encode(
|
|
||||||
&TwoVariableTransStruct { a: trans_b },
|
|
||||||
&[4, 0, 0, 0, 5, 0, 0, 0, 3, 1, 2],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_variable_union() {
|
|
||||||
let union_a = TwoVariableUnion::A(VariableA {
|
|
||||||
a: 1,
|
|
||||||
b: vec![2, 3],
|
|
||||||
});
|
|
||||||
let union_b = TwoVariableUnion::B(VariableB {
|
|
||||||
a: vec![1, 2],
|
|
||||||
b: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_encode_decode(&union_a, &[0, 1, 5, 0, 0, 0, 2, 3]);
|
|
||||||
assert_encode_decode(&union_b, &[1, 5, 0, 0, 0, 3, 1, 2]);
|
|
||||||
|
|
||||||
assert_encode_decode(
|
|
||||||
&TwoVariableUnionStruct { a: union_a },
|
|
||||||
&[4, 0, 0, 0, 0, 1, 5, 0, 0, 0, 2, 3],
|
|
||||||
);
|
|
||||||
assert_encode_decode(
|
|
||||||
&TwoVariableUnionStruct { a: union_b },
|
|
||||||
&[4, 0, 0, 0, 1, 5, 0, 0, 0, 3, 1, 2],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
|
||||||
#[ssz(enum_behaviour = "union")]
|
|
||||||
enum TwoVecUnion {
|
|
||||||
A(Vec<u8>),
|
|
||||||
B(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_vec_union() {
|
|
||||||
assert_encode_decode(&TwoVecUnion::A(vec![]), &[0]);
|
|
||||||
assert_encode_decode(&TwoVecUnion::B(vec![]), &[1]);
|
|
||||||
|
|
||||||
assert_encode_decode(&TwoVecUnion::A(vec![0]), &[0, 0]);
|
|
||||||
assert_encode_decode(&TwoVecUnion::B(vec![0]), &[1, 0]);
|
|
||||||
|
|
||||||
assert_encode_decode(&TwoVecUnion::A(vec![0, 1]), &[0, 0, 1]);
|
|
||||||
assert_encode_decode(&TwoVecUnion::B(vec![0, 1]), &[1, 0, 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "eth2_ssz_derive"
|
name = "eth2_ssz_derive"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
authors = ["Paul Hauner <paul@sigmaprime.io>"]
|
authors = ["Paul Hauner <paul@sigmaprime.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Procedural derive macros to accompany the eth2_ssz crate."
|
description = "Procedural derive macros to accompany the eth2_ssz crate."
|
||||||
@ -15,3 +15,6 @@ syn = "1.0.42"
|
|||||||
proc-macro2 = "1.0.23"
|
proc-macro2 = "1.0.23"
|
||||||
quote = "1.0.7"
|
quote = "1.0.7"
|
||||||
darling = "0.13.0"
|
darling = "0.13.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
eth2_ssz = "0.4.1"
|
||||||
|
@ -1,7 +1,147 @@
|
|||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
//! Provides procedural derive macros for the `Encode` and `Decode` traits of the `eth2_ssz` crate.
|
//! 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.
|
//! ## Attributes
|
||||||
|
//!
|
||||||
|
//! The following struct/enum attributes are available:
|
||||||
|
//!
|
||||||
|
//! - `#[ssz(enum_behaviour = "union")]`: encodes and decodes an `enum` with a one-byte variant selector.
|
||||||
|
//! - `#[ssz(enum_behaviour = "transparent")]`: allows encoding an `enum` by serializing only the
|
||||||
|
//! value whilst ignoring outermost the `enum`.
|
||||||
|
//! - `#[ssz(struct_behaviour = "container")]`: encodes and decodes the `struct` as an SSZ
|
||||||
|
//! "container".
|
||||||
|
//! - `#[ssz(struct_behaviour = "transparent")]`: encodes and decodes a `struct` with exactly one
|
||||||
|
//! non-skipped field as if the outermost `struct` does not exist.
|
||||||
|
//!
|
||||||
|
//! The following field attributes are available:
|
||||||
|
//!
|
||||||
|
//! - `#[ssz(with = "module")]`: uses the methods in `module` to implement `ssz::Encode` and
|
||||||
|
//! `ssz::Decode`. This is useful when it's not possible to create an `impl` for that type
|
||||||
|
//! (e.g. the type 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 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)]
|
||||||
|
//! #[ssz(struct_behaviour = "container")] // "container" is the default behaviour
|
||||||
|
//! struct TypicalStruct {
|
||||||
|
//! foo: Vec<u8>
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! 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(struct_behaviour = "transparent")]
|
||||||
|
//! struct WrapperStruct {
|
||||||
|
//! foo: Vec<u8>
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! WrapperStruct { 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(struct_behaviour = "transparent")]
|
||||||
|
//! struct WrapperStructSkippedField {
|
||||||
|
//! foo: Vec<u8>,
|
||||||
|
//! #[ssz(skip_serializing, skip_deserializing)]
|
||||||
|
//! bar: u8,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! WrapperStructSkippedField { foo: vec![42], bar: 99 }.as_ssz_bytes(),
|
||||||
|
//! vec![42]
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(
|
||||||
|
//! WrapperStructSkippedField::from_ssz_bytes(&[42]).unwrap(),
|
||||||
|
//! WrapperStructSkippedField { foo: vec![42], bar: 0 }
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! /// Represented as an SSZ "list" *without* an SSZ "container".
|
||||||
|
//! #[derive(Encode, Decode)]
|
||||||
|
//! #[ssz(struct_behaviour = "transparent")]
|
||||||
|
//! struct NewType(Vec<u8>);
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! NewType(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(struct_behaviour = "transparent")]
|
||||||
|
//! struct NewTypeSkippedField(Vec<u8>, #[ssz(skip_serializing, skip_deserializing)] u8);
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! NewTypeSkippedField(vec![42], 99).as_ssz_bytes(),
|
||||||
|
//! vec![42]
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(
|
||||||
|
//! NewTypeSkippedField::from_ssz_bytes(&[42]).unwrap(),
|
||||||
|
//! NewTypeSkippedField(vec![42], 0)
|
||||||
|
//! );
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Enums
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use ssz::{Encode, Decode};
|
||||||
|
//! use ssz_derive::{Encode, Decode};
|
||||||
|
//!
|
||||||
|
//! /// Represented as an SSZ "union".
|
||||||
|
//! #[derive(Debug, PartialEq, Encode, Decode)]
|
||||||
|
//! #[ssz(enum_behaviour = "union")]
|
||||||
|
//! enum UnionEnum {
|
||||||
|
//! Foo(u8),
|
||||||
|
//! Bar(Vec<u8>),
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! UnionEnum::Foo(42).as_ssz_bytes(),
|
||||||
|
//! vec![0, 42]
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(
|
||||||
|
//! UnionEnum::from_ssz_bytes(&[1, 42, 42]).unwrap(),
|
||||||
|
//! UnionEnum::Bar(vec![42, 42]),
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! /// Represented as only the value in the enum variant.
|
||||||
|
//! #[derive(Debug, PartialEq, Encode)]
|
||||||
|
//! #[ssz(enum_behaviour = "transparent")]
|
||||||
|
//! enum TransparentEnum {
|
||||||
|
//! Foo(u8),
|
||||||
|
//! Bar(Vec<u8>),
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! 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 darling::{FromDeriveInput, FromMeta};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
@ -13,11 +153,18 @@ use syn::{parse_macro_input, DataEnum, DataStruct, DeriveInput, Ident};
|
|||||||
/// extensions).
|
/// extensions).
|
||||||
const MAX_UNION_SELECTOR: u8 = 127;
|
const MAX_UNION_SELECTOR: u8 = 127;
|
||||||
|
|
||||||
|
const ENUM_TRANSPARENT: &str = "transparent";
|
||||||
|
const ENUM_UNION: &str = "union";
|
||||||
|
const NO_ENUM_BEHAVIOUR_ERROR: &str = "enums require an \"enum_behaviour\" attribute with \
|
||||||
|
a \"transparent\" or \"union\" value, e.g., #[ssz(enum_behaviour = \"transparent\")]";
|
||||||
|
|
||||||
#[derive(Debug, FromDeriveInput)]
|
#[derive(Debug, FromDeriveInput)]
|
||||||
#[darling(attributes(ssz))]
|
#[darling(attributes(ssz))]
|
||||||
struct StructOpts {
|
struct StructOpts {
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
enum_behaviour: Option<String>,
|
enum_behaviour: Option<String>,
|
||||||
|
#[darling(default)]
|
||||||
|
struct_behaviour: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Field-level configuration.
|
/// Field-level configuration.
|
||||||
@ -31,40 +178,87 @@ struct FieldOpts {
|
|||||||
skip_deserializing: bool,
|
skip_deserializing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ENUM_TRANSPARENT: &str = "transparent";
|
enum Procedure<'a> {
|
||||||
const ENUM_UNION: &str = "union";
|
Struct {
|
||||||
const ENUM_VARIANTS: &[&str] = &[ENUM_TRANSPARENT, ENUM_UNION];
|
data: &'a syn::DataStruct,
|
||||||
const NO_ENUM_BEHAVIOUR_ERROR: &str = "enums require an \"enum_behaviour\" attribute, \
|
behaviour: StructBehaviour,
|
||||||
e.g., #[ssz(enum_behaviour = \"transparent\")]";
|
},
|
||||||
|
Enum {
|
||||||
|
data: &'a syn::DataEnum,
|
||||||
|
behaviour: EnumBehaviour,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StructBehaviour {
|
||||||
|
Container,
|
||||||
|
Transparent,
|
||||||
|
}
|
||||||
|
|
||||||
enum EnumBehaviour {
|
enum EnumBehaviour {
|
||||||
Transparent,
|
|
||||||
Union,
|
Union,
|
||||||
|
Transparent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumBehaviour {
|
impl<'a> Procedure<'a> {
|
||||||
pub fn new(s: Option<String>) -> Option<Self> {
|
fn read(item: &'a DeriveInput) -> Self {
|
||||||
s.map(|s| match s.as_ref() {
|
let opts = StructOpts::from_derive_input(item).unwrap();
|
||||||
ENUM_TRANSPARENT => EnumBehaviour::Transparent,
|
|
||||||
ENUM_UNION => EnumBehaviour::Union,
|
match &item.data {
|
||||||
other => panic!(
|
syn::Data::Struct(data) => {
|
||||||
"{} is an invalid enum_behaviour, use either {:?}",
|
if opts.enum_behaviour.is_some() {
|
||||||
other, ENUM_VARIANTS
|
panic!("cannot use \"enum_behaviour\" for a struct");
|
||||||
|
}
|
||||||
|
|
||||||
|
match opts.struct_behaviour.as_deref() {
|
||||||
|
Some("container") | None => Procedure::Struct {
|
||||||
|
data,
|
||||||
|
behaviour: StructBehaviour::Container,
|
||||||
|
},
|
||||||
|
Some("transparent") => Procedure::Struct {
|
||||||
|
data,
|
||||||
|
behaviour: StructBehaviour::Transparent,
|
||||||
|
},
|
||||||
|
Some(other) => panic!(
|
||||||
|
"{} is not a valid struct behaviour, use \"container\" or \"transparent\"",
|
||||||
|
other
|
||||||
),
|
),
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
syn::Data::Enum(data) => {
|
||||||
|
if opts.struct_behaviour.is_some() {
|
||||||
|
panic!("cannot use \"struct_behaviour\" for an enum");
|
||||||
|
}
|
||||||
|
|
||||||
|
match opts.enum_behaviour.as_deref() {
|
||||||
|
Some("union") => Procedure::Enum {
|
||||||
|
data,
|
||||||
|
behaviour: EnumBehaviour::Union,
|
||||||
|
},
|
||||||
|
Some("transparent") => Procedure::Enum {
|
||||||
|
data,
|
||||||
|
behaviour: EnumBehaviour::Transparent,
|
||||||
|
},
|
||||||
|
Some(other) => panic!(
|
||||||
|
"{} is not a valid enum behaviour, use \"container\" or \"transparent\"",
|
||||||
|
other
|
||||||
|
),
|
||||||
|
None => panic!("{}", NO_ENUM_BEHAVIOUR_ERROR),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("ssz_derive only supports structs and enums"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ssz_fields(struct_data: &syn::DataStruct) -> Vec<(&syn::Type, &syn::Ident, FieldOpts)> {
|
fn parse_ssz_fields(
|
||||||
|
struct_data: &syn::DataStruct,
|
||||||
|
) -> Vec<(&syn::Type, Option<&syn::Ident>, FieldOpts)> {
|
||||||
struct_data
|
struct_data
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| {
|
.map(|field| {
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
let ident = match &field.ident {
|
let ident = field.ident.as_ref();
|
||||||
Some(ref ident) => ident,
|
|
||||||
_ => panic!("ssz_derive only supports named struct fields."),
|
|
||||||
};
|
|
||||||
|
|
||||||
let field_opts_candidates = field
|
let field_opts_candidates = field
|
||||||
.attrs
|
.attrs
|
||||||
@ -93,21 +287,17 @@ fn parse_ssz_fields(struct_data: &syn::DataStruct) -> Vec<(&syn::Type, &syn::Ide
|
|||||||
#[proc_macro_derive(Encode, attributes(ssz))]
|
#[proc_macro_derive(Encode, attributes(ssz))]
|
||||||
pub fn ssz_encode_derive(input: TokenStream) -> TokenStream {
|
pub fn ssz_encode_derive(input: TokenStream) -> TokenStream {
|
||||||
let item = parse_macro_input!(input as DeriveInput);
|
let item = parse_macro_input!(input as DeriveInput);
|
||||||
let opts = StructOpts::from_derive_input(&item).unwrap();
|
let procedure = Procedure::read(&item);
|
||||||
let enum_opt = EnumBehaviour::new(opts.enum_behaviour);
|
|
||||||
|
|
||||||
match &item.data {
|
match procedure {
|
||||||
syn::Data::Struct(s) => {
|
Procedure::Struct { data, behaviour } => match behaviour {
|
||||||
if enum_opt.is_some() {
|
StructBehaviour::Transparent => ssz_encode_derive_struct_transparent(&item, data),
|
||||||
panic!("enum_behaviour is invalid for structs");
|
StructBehaviour::Container => ssz_encode_derive_struct(&item, data),
|
||||||
}
|
},
|
||||||
ssz_encode_derive_struct(&item, s)
|
Procedure::Enum { data, behaviour } => match behaviour {
|
||||||
}
|
EnumBehaviour::Transparent => ssz_encode_derive_enum_transparent(&item, data),
|
||||||
syn::Data::Enum(s) => match enum_opt.expect(NO_ENUM_BEHAVIOUR_ERROR) {
|
EnumBehaviour::Union => ssz_encode_derive_enum_union(&item, data),
|
||||||
EnumBehaviour::Transparent => ssz_encode_derive_enum_transparent(&item, s),
|
|
||||||
EnumBehaviour::Union => ssz_encode_derive_enum_union(&item, s),
|
|
||||||
},
|
},
|
||||||
_ => panic!("ssz_derive only supports structs and enums"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +322,13 @@ fn ssz_encode_derive_struct(derive_input: &DeriveInput, struct_data: &DataStruct
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ident = match ident {
|
||||||
|
Some(ref ident) => ident,
|
||||||
|
_ => panic!(
|
||||||
|
"#[ssz(struct_behaviour = \"container\")] only supports named struct fields."
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(module) = field_opts.with {
|
if let Some(module) = field_opts.with {
|
||||||
let module = quote! { #module::encode };
|
let module = quote! { #module::encode };
|
||||||
field_is_ssz_fixed_len.push(quote! { #module::is_ssz_fixed_len() });
|
field_is_ssz_fixed_len.push(quote! { #module::is_ssz_fixed_len() });
|
||||||
@ -219,6 +416,82 @@ fn ssz_encode_derive_struct(derive_input: &DeriveInput, struct_data: &DataStruct
|
|||||||
output.into()
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derive `ssz::Encode` "transparently" for a struct which has exactly one non-skipped field.
|
||||||
|
///
|
||||||
|
/// The single field is encoded directly, making the outermost `struct` transparent.
|
||||||
|
///
|
||||||
|
/// ## Field attributes
|
||||||
|
///
|
||||||
|
/// - `#[ssz(skip_serializing)]`: the field will not be serialized.
|
||||||
|
fn ssz_encode_derive_struct_transparent(
|
||||||
|
derive_input: &DeriveInput,
|
||||||
|
struct_data: &DataStruct,
|
||||||
|
) -> TokenStream {
|
||||||
|
let name = &derive_input.ident;
|
||||||
|
let (impl_generics, ty_generics, where_clause) = &derive_input.generics.split_for_impl();
|
||||||
|
let ssz_fields = parse_ssz_fields(struct_data);
|
||||||
|
let num_fields = ssz_fields
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, _, field_opts)| !field_opts.skip_deserializing)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if num_fields != 1 {
|
||||||
|
panic!(
|
||||||
|
"A \"transparent\" struct must have exactly one non-skipped field ({} fields found)",
|
||||||
|
num_fields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ty, ident, _field_opts) = ssz_fields
|
||||||
|
.iter()
|
||||||
|
.find(|(_, _, field_opts)| !field_opts.skip_deserializing)
|
||||||
|
.expect("\"transparent\" struct must have at least one non-skipped field");
|
||||||
|
|
||||||
|
let output = if let Some(field_name) = ident {
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ssz::Encode for #name #ty_generics #where_clause {
|
||||||
|
fn is_ssz_fixed_len() -> bool {
|
||||||
|
<#ty as ssz::Encode>::is_ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssz_fixed_len() -> usize {
|
||||||
|
<#ty as ssz::Encode>::ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssz_bytes_len(&self) -> usize {
|
||||||
|
self.#field_name.ssz_bytes_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||||
|
self.#field_name.ssz_append(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ssz::Encode for #name #ty_generics #where_clause {
|
||||||
|
fn is_ssz_fixed_len() -> bool {
|
||||||
|
<#ty as ssz::Encode>::is_ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssz_fixed_len() -> usize {
|
||||||
|
<#ty as ssz::Encode>::ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssz_bytes_len(&self) -> usize {
|
||||||
|
self.0.ssz_bytes_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||||
|
self.0.ssz_append(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Derive `ssz::Encode` for an enum in the "transparent" method.
|
/// Derive `ssz::Encode` for an enum in the "transparent" method.
|
||||||
///
|
///
|
||||||
/// The "transparent" method is distinct from the "union" method specified in the SSZ specification.
|
/// The "transparent" method is distinct from the "union" method specified in the SSZ specification.
|
||||||
@ -367,24 +640,20 @@ fn ssz_encode_derive_enum_union(derive_input: &DeriveInput, enum_data: &DataEnum
|
|||||||
#[proc_macro_derive(Decode, attributes(ssz))]
|
#[proc_macro_derive(Decode, attributes(ssz))]
|
||||||
pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
|
pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
|
||||||
let item = parse_macro_input!(input as DeriveInput);
|
let item = parse_macro_input!(input as DeriveInput);
|
||||||
let opts = StructOpts::from_derive_input(&item).unwrap();
|
let procedure = Procedure::read(&item);
|
||||||
let enum_opt = EnumBehaviour::new(opts.enum_behaviour);
|
|
||||||
|
|
||||||
match &item.data {
|
match procedure {
|
||||||
syn::Data::Struct(s) => {
|
Procedure::Struct { data, behaviour } => match behaviour {
|
||||||
if enum_opt.is_some() {
|
StructBehaviour::Transparent => ssz_decode_derive_struct_transparent(&item, data),
|
||||||
panic!("enum_behaviour is invalid for structs");
|
StructBehaviour::Container => ssz_decode_derive_struct(&item, data),
|
||||||
}
|
},
|
||||||
ssz_decode_derive_struct(&item, s)
|
Procedure::Enum { data, behaviour } => match behaviour {
|
||||||
}
|
EnumBehaviour::Union => ssz_decode_derive_enum_union(&item, data),
|
||||||
syn::Data::Enum(s) => match enum_opt.expect(NO_ENUM_BEHAVIOUR_ERROR) {
|
|
||||||
EnumBehaviour::Transparent => panic!(
|
EnumBehaviour::Transparent => panic!(
|
||||||
"Decode cannot be derived for enum_behaviour \"{}\", only \"{}\" is valid.",
|
"Decode cannot be derived for enum_behaviour \"{}\", only \"{}\" is valid.",
|
||||||
ENUM_TRANSPARENT, ENUM_UNION
|
ENUM_TRANSPARENT, ENUM_UNION
|
||||||
),
|
),
|
||||||
EnumBehaviour::Union => ssz_decode_derive_enum_union(&item, s),
|
|
||||||
},
|
},
|
||||||
_ => panic!("ssz_derive only supports structs and enums"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,6 +678,13 @@ fn ssz_decode_derive_struct(item: &DeriveInput, struct_data: &DataStruct) -> Tok
|
|||||||
let mut fixed_lens = vec![];
|
let mut fixed_lens = vec![];
|
||||||
|
|
||||||
for (ty, ident, field_opts) in parse_ssz_fields(struct_data) {
|
for (ty, ident, field_opts) in parse_ssz_fields(struct_data) {
|
||||||
|
let ident = match ident {
|
||||||
|
Some(ref ident) => ident,
|
||||||
|
_ => panic!(
|
||||||
|
"#[ssz(struct_behaviour = \"container\")] only supports named struct fields."
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
field_names.push(quote! {
|
field_names.push(quote! {
|
||||||
#ident
|
#ident
|
||||||
});
|
});
|
||||||
@ -545,6 +821,90 @@ fn ssz_decode_derive_struct(item: &DeriveInput, struct_data: &DataStruct) -> Tok
|
|||||||
output.into()
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements `ssz::Decode` "transparently" for a `struct` with exactly one non-skipped field.
|
||||||
|
///
|
||||||
|
/// The bytes will be decoded as if they are the inner field, without the outermost struct. The
|
||||||
|
/// outermost struct will then be applied artificially.
|
||||||
|
///
|
||||||
|
/// ## Field attributes
|
||||||
|
///
|
||||||
|
/// - `#[ssz(skip_deserializing)]`: during de-serialization the field will be instantiated from a
|
||||||
|
/// `Default` implementation. The decoder will assume that the field was not serialized at all
|
||||||
|
/// (e.g., if it has been serialized, an error will be raised instead of `Default` overriding it).
|
||||||
|
fn ssz_decode_derive_struct_transparent(
|
||||||
|
item: &DeriveInput,
|
||||||
|
struct_data: &DataStruct,
|
||||||
|
) -> TokenStream {
|
||||||
|
let name = &item.ident;
|
||||||
|
let (impl_generics, ty_generics, where_clause) = &item.generics.split_for_impl();
|
||||||
|
let ssz_fields = parse_ssz_fields(struct_data);
|
||||||
|
let num_fields = ssz_fields
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, _, field_opts)| !field_opts.skip_deserializing)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if num_fields != 1 {
|
||||||
|
panic!(
|
||||||
|
"A \"transparent\" struct must have exactly one non-skipped field ({} fields found)",
|
||||||
|
num_fields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fields = vec![];
|
||||||
|
let mut wrapped_type = None;
|
||||||
|
|
||||||
|
for (i, (ty, ident, field_opts)) in ssz_fields.into_iter().enumerate() {
|
||||||
|
if let Some(name) = ident {
|
||||||
|
if field_opts.skip_deserializing {
|
||||||
|
fields.push(quote! {
|
||||||
|
#name: <_>::default(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fields.push(quote! {
|
||||||
|
#name: <_>::from_ssz_bytes(bytes)?,
|
||||||
|
});
|
||||||
|
wrapped_type = Some(ty);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let index = syn::Index::from(i);
|
||||||
|
if field_opts.skip_deserializing {
|
||||||
|
fields.push(quote! {
|
||||||
|
#index:<_>::default(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fields.push(quote! {
|
||||||
|
#index:<_>::from_ssz_bytes(bytes)?,
|
||||||
|
});
|
||||||
|
wrapped_type = Some(ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = wrapped_type.unwrap();
|
||||||
|
|
||||||
|
let output = quote! {
|
||||||
|
impl #impl_generics ssz::Decode for #name #ty_generics #where_clause {
|
||||||
|
fn is_ssz_fixed_len() -> bool {
|
||||||
|
<#ty as ssz::Decode>::is_ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssz_fixed_len() -> usize {
|
||||||
|
<#ty as ssz::Decode>::ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_ssz_bytes(bytes: &[u8]) -> std::result::Result<Self, ssz::DecodeError> {
|
||||||
|
Ok(Self {
|
||||||
|
#(
|
||||||
|
#fields
|
||||||
|
)*
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Derive `ssz::Decode` for an `enum` following the "union" SSZ spec.
|
/// Derive `ssz::Decode` for an `enum` following the "union" SSZ spec.
|
||||||
fn ssz_decode_derive_enum_union(derive_input: &DeriveInput, enum_data: &DataEnum) -> TokenStream {
|
fn ssz_decode_derive_enum_union(derive_input: &DeriveInput, enum_data: &DataEnum) -> TokenStream {
|
||||||
let name = &derive_input.ident;
|
let name = &derive_input.ident;
|
||||||
|
215
consensus/ssz_derive/tests/tests.rs
Normal file
215
consensus/ssz_derive/tests/tests.rs
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
use ssz::{Decode, Encode};
|
||||||
|
use ssz_derive::{Decode, Encode};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
fn assert_encode<T: Encode>(item: &T, bytes: &[u8]) {
|
||||||
|
assert_eq!(item.as_ssz_bytes(), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_encode_decode<T: Encode + Decode + PartialEq + Debug>(item: &T, bytes: &[u8]) {
|
||||||
|
assert_encode(item, bytes);
|
||||||
|
assert_eq!(T::from_ssz_bytes(bytes).unwrap(), *item);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
#[ssz(enum_behaviour = "union")]
|
||||||
|
enum TwoFixedUnion {
|
||||||
|
U8(u8),
|
||||||
|
U16(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
struct TwoFixedUnionStruct {
|
||||||
|
a: TwoFixedUnion,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_fixed_union() {
|
||||||
|
let eight = TwoFixedUnion::U8(1);
|
||||||
|
let sixteen = TwoFixedUnion::U16(1);
|
||||||
|
|
||||||
|
assert_encode_decode(&eight, &[0, 1]);
|
||||||
|
assert_encode_decode(&sixteen, &[1, 1, 0]);
|
||||||
|
|
||||||
|
assert_encode_decode(&TwoFixedUnionStruct { a: eight }, &[4, 0, 0, 0, 0, 1]);
|
||||||
|
assert_encode_decode(&TwoFixedUnionStruct { a: sixteen }, &[4, 0, 0, 0, 1, 1, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
struct VariableA {
|
||||||
|
a: u8,
|
||||||
|
b: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
struct VariableB {
|
||||||
|
a: Vec<u8>,
|
||||||
|
b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode)]
|
||||||
|
#[ssz(enum_behaviour = "transparent")]
|
||||||
|
enum TwoVariableTrans {
|
||||||
|
A(VariableA),
|
||||||
|
B(VariableB),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode)]
|
||||||
|
struct TwoVariableTransStruct {
|
||||||
|
a: TwoVariableTrans,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
#[ssz(enum_behaviour = "union")]
|
||||||
|
enum TwoVariableUnion {
|
||||||
|
A(VariableA),
|
||||||
|
B(VariableB),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
struct TwoVariableUnionStruct {
|
||||||
|
a: TwoVariableUnion,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_variable_trans() {
|
||||||
|
let trans_a = TwoVariableTrans::A(VariableA {
|
||||||
|
a: 1,
|
||||||
|
b: vec![2, 3],
|
||||||
|
});
|
||||||
|
let trans_b = TwoVariableTrans::B(VariableB {
|
||||||
|
a: vec![1, 2],
|
||||||
|
b: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_encode(&trans_a, &[1, 5, 0, 0, 0, 2, 3]);
|
||||||
|
assert_encode(&trans_b, &[5, 0, 0, 0, 3, 1, 2]);
|
||||||
|
|
||||||
|
assert_encode(
|
||||||
|
&TwoVariableTransStruct { a: trans_a },
|
||||||
|
&[4, 0, 0, 0, 1, 5, 0, 0, 0, 2, 3],
|
||||||
|
);
|
||||||
|
assert_encode(
|
||||||
|
&TwoVariableTransStruct { a: trans_b },
|
||||||
|
&[4, 0, 0, 0, 5, 0, 0, 0, 3, 1, 2],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_variable_union() {
|
||||||
|
let union_a = TwoVariableUnion::A(VariableA {
|
||||||
|
a: 1,
|
||||||
|
b: vec![2, 3],
|
||||||
|
});
|
||||||
|
let union_b = TwoVariableUnion::B(VariableB {
|
||||||
|
a: vec![1, 2],
|
||||||
|
b: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_encode_decode(&union_a, &[0, 1, 5, 0, 0, 0, 2, 3]);
|
||||||
|
assert_encode_decode(&union_b, &[1, 5, 0, 0, 0, 3, 1, 2]);
|
||||||
|
|
||||||
|
assert_encode_decode(
|
||||||
|
&TwoVariableUnionStruct { a: union_a },
|
||||||
|
&[4, 0, 0, 0, 0, 1, 5, 0, 0, 0, 2, 3],
|
||||||
|
);
|
||||||
|
assert_encode_decode(
|
||||||
|
&TwoVariableUnionStruct { a: union_b },
|
||||||
|
&[4, 0, 0, 0, 1, 5, 0, 0, 0, 3, 1, 2],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
#[ssz(enum_behaviour = "union")]
|
||||||
|
enum TwoVecUnion {
|
||||||
|
A(Vec<u8>),
|
||||||
|
B(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_vec_union() {
|
||||||
|
assert_encode_decode(&TwoVecUnion::A(vec![]), &[0]);
|
||||||
|
assert_encode_decode(&TwoVecUnion::B(vec![]), &[1]);
|
||||||
|
|
||||||
|
assert_encode_decode(&TwoVecUnion::A(vec![0]), &[0, 0]);
|
||||||
|
assert_encode_decode(&TwoVecUnion::B(vec![0]), &[1, 0]);
|
||||||
|
|
||||||
|
assert_encode_decode(&TwoVecUnion::A(vec![0, 1]), &[0, 0, 1]);
|
||||||
|
assert_encode_decode(&TwoVecUnion::B(vec![0, 1]), &[1, 0, 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
#[ssz(struct_behaviour = "transparent")]
|
||||||
|
struct TransparentStruct {
|
||||||
|
inner: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransparentStruct {
|
||||||
|
fn new(inner: u8) -> Self {
|
||||||
|
Self { inner: vec![inner] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transparent_struct() {
|
||||||
|
assert_encode_decode(&TransparentStruct::new(42), &vec![42_u8].as_ssz_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
#[ssz(struct_behaviour = "transparent")]
|
||||||
|
struct TransparentStructSkippedField {
|
||||||
|
inner: Vec<u8>,
|
||||||
|
#[ssz(skip_serializing, skip_deserializing)]
|
||||||
|
skipped: PhantomData<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransparentStructSkippedField {
|
||||||
|
fn new(inner: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: vec![inner],
|
||||||
|
skipped: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transparent_struct_skipped_field() {
|
||||||
|
assert_encode_decode(
|
||||||
|
&TransparentStructSkippedField::new(42),
|
||||||
|
&vec![42_u8].as_ssz_bytes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
#[ssz(struct_behaviour = "transparent")]
|
||||||
|
struct TransparentStructNewType(Vec<u8>);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transparent_struct_newtype() {
|
||||||
|
assert_encode_decode(
|
||||||
|
&TransparentStructNewType(vec![42_u8]),
|
||||||
|
&vec![42_u8].as_ssz_bytes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
|
#[ssz(struct_behaviour = "transparent")]
|
||||||
|
struct TransparentStructNewTypeSkippedField(
|
||||||
|
Vec<u8>,
|
||||||
|
#[ssz(skip_serializing, skip_deserializing)] PhantomData<u64>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl TransparentStructNewTypeSkippedField {
|
||||||
|
fn new(inner: Vec<u8>) -> Self {
|
||||||
|
Self(inner, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transparent_struct_newtype_skipped_field() {
|
||||||
|
assert_encode_decode(
|
||||||
|
&TransparentStructNewTypeSkippedField::new(vec![42_u8]),
|
||||||
|
&vec![42_u8].as_ssz_bytes(),
|
||||||
|
);
|
||||||
|
}
|
@ -14,7 +14,7 @@ bls = { path = "../../crypto/bls" }
|
|||||||
integer-sqrt = "0.1.5"
|
integer-sqrt = "0.1.5"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
eth2_ssz_types = "0.2.2"
|
eth2_ssz_types = "0.2.2"
|
||||||
merkle_proof = { path = "../merkle_proof" }
|
merkle_proof = { path = "../merkle_proof" }
|
||||||
safe_arith = { path = "../safe_arith" }
|
safe_arith = { path = "../safe_arith" }
|
||||||
|
@ -12,7 +12,7 @@ tree_hash_derive = "0.4.0"
|
|||||||
types = { path = "../types" }
|
types = { path = "../types" }
|
||||||
beacon_chain = { path = "../../beacon_node/beacon_chain" }
|
beacon_chain = { path = "../../beacon_node/beacon_chain" }
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethereum-types = "0.12.1"
|
ethereum-types = "0.12.1"
|
||||||
|
@ -26,7 +26,7 @@ serde = {version = "1.0.116" , features = ["rc"] }
|
|||||||
serde_derive = "1.0.116"
|
serde_derive = "1.0.116"
|
||||||
slog = "2.5.2"
|
slog = "2.5.2"
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
eth2_ssz_types = "0.2.2"
|
eth2_ssz_types = "0.2.2"
|
||||||
swap_or_not_shuffle = { path = "../swap_or_not_shuffle" }
|
swap_or_not_shuffle = { path = "../swap_or_not_shuffle" }
|
||||||
test_random_derive = { path = "../../common/test_random_derive" }
|
test_random_derive = { path = "../../common/test_random_derive" }
|
||||||
|
@ -120,6 +120,7 @@ pub enum Error {
|
|||||||
ArithError(ArithError),
|
ArithError(ArithError),
|
||||||
MissingBeaconBlock(SignedBeaconBlockHash),
|
MissingBeaconBlock(SignedBeaconBlockHash),
|
||||||
MissingBeaconState(BeaconStateHash),
|
MissingBeaconState(BeaconStateHash),
|
||||||
|
PayloadConversionLogicFlaw,
|
||||||
SyncCommitteeNotKnown {
|
SyncCommitteeNotKnown {
|
||||||
current_epoch: Epoch,
|
current_epoch: Epoch,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
|
@ -121,8 +121,4 @@ impl<T: EthSpec> ExecutionPayload<T> {
|
|||||||
// Max size of variable length `withdrawals` field
|
// Max size of variable length `withdrawals` field
|
||||||
+ (T::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
|
+ (T::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blob_txns_iter(&self) -> Iter<'_, Transaction<T::MaxBytesPerTransaction>> {
|
|
||||||
self.transactions().iter()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,12 @@ pub struct ExecutionPayloadHeader<T: EthSpec> {
|
|||||||
pub withdrawals_root: Hash256,
|
pub withdrawals_root: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> ExecutionPayloadHeader<T> {
|
||||||
|
pub fn transactions(&self) -> Option<&Transactions<T>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T: EthSpec> ExecutionPayloadHeaderRef<'a, T> {
|
impl<'a, T: EthSpec> ExecutionPayloadHeaderRef<'a, T> {
|
||||||
// FIXME: maybe this could be a derived trait..
|
// FIXME: maybe this could be a derived trait..
|
||||||
pub fn is_default(self) -> bool {
|
pub fn is_default(self) -> bool {
|
||||||
@ -210,6 +216,34 @@ impl<T: EthSpec> From<ExecutionPayloadEip4844<T>> for ExecutionPayloadHeaderEip4
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> From<ExecutionPayloadMerge<T>> for ExecutionPayloadHeader<T> {
|
||||||
|
fn from(payload: ExecutionPayloadMerge<T>) -> Self {
|
||||||
|
Self::Merge(ExecutionPayloadHeaderMerge::from(payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> From<ExecutionPayloadCapella<T>> for ExecutionPayloadHeader<T> {
|
||||||
|
fn from(payload: ExecutionPayloadCapella<T>) -> Self {
|
||||||
|
Self::Capella(ExecutionPayloadHeaderCapella::from(payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> From<ExecutionPayloadEip4844<T>> for ExecutionPayloadHeader<T> {
|
||||||
|
fn from(payload: ExecutionPayloadEip4844<T>) -> Self {
|
||||||
|
Self::Eip4844(ExecutionPayloadHeaderEip4844::from(payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> From<ExecutionPayload<T>> for ExecutionPayloadHeader<T> {
|
||||||
|
fn from(payload: ExecutionPayload<T>) -> Self {
|
||||||
|
match payload {
|
||||||
|
ExecutionPayload::Merge(payload) => Self::from(payload),
|
||||||
|
ExecutionPayload::Capella(payload) => Self::from(payload),
|
||||||
|
ExecutionPayload::Eip4844(payload) => Self::from(payload),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for ExecutionPayloadHeaderMerge<T> {
|
impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for ExecutionPayloadHeaderMerge<T> {
|
||||||
type Error = BeaconStateError;
|
type Error = BeaconStateError;
|
||||||
fn try_from(header: ExecutionPayloadHeader<T>) -> Result<Self, Self::Error> {
|
fn try_from(header: ExecutionPayloadHeader<T>) -> Result<Self, Self::Error> {
|
||||||
|
@ -2,13 +2,14 @@ use crate::test_utils::TestRandom;
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use ssz::{Decode, DecodeError, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use tree_hash::{PackedEncoding, TreeHash};
|
use tree_hash::{PackedEncoding, TreeHash};
|
||||||
|
|
||||||
#[derive(Derivative, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Derivative, Debug, Clone, Encode, Decode, Serialize, Deserialize)]
|
||||||
#[derivative(PartialEq, Eq, Hash)]
|
#[derivative(PartialEq, Eq, Hash)]
|
||||||
|
#[ssz(struct_behaviour = "transparent")]
|
||||||
pub struct KzgCommitment(#[serde(with = "BigArray")] pub [u8; 48]);
|
pub struct KzgCommitment(#[serde(with = "BigArray")] pub [u8; 48]);
|
||||||
|
|
||||||
impl Display for KzgCommitment {
|
impl Display for KzgCommitment {
|
||||||
@ -40,27 +41,3 @@ impl TestRandom for KzgCommitment {
|
|||||||
KzgCommitment(<[u8; 48] as TestRandom>::random_for_test(rng))
|
KzgCommitment(<[u8; 48] as TestRandom>::random_for_test(rng))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decode for KzgCommitment {
|
|
||||||
fn is_ssz_fixed_len() -> bool {
|
|
||||||
<[u8; 48] as Decode>::is_ssz_fixed_len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
||||||
<[u8; 48] as Decode>::from_ssz_bytes(bytes).map(KzgCommitment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for KzgCommitment {
|
|
||||||
fn is_ssz_fixed_len() -> bool {
|
|
||||||
<[u8; 48] as Encode>::is_ssz_fixed_len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
|
||||||
self.0.ssz_append(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssz_bytes_len(&self) -> usize {
|
|
||||||
self.0.ssz_bytes_len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,13 +2,15 @@ use crate::test_utils::{RngCore, TestRandom};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_big_array::BigArray;
|
use serde_big_array::BigArray;
|
||||||
use ssz::{Decode, DecodeError, Encode};
|
use ssz::{Decode, DecodeError, Encode};
|
||||||
|
use ssz_derive::{Decode, Encode};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use tree_hash::{PackedEncoding, TreeHash};
|
use tree_hash::{PackedEncoding, TreeHash};
|
||||||
|
|
||||||
const KZG_PROOF_BYTES_LEN: usize = 48;
|
const KZG_PROOF_BYTES_LEN: usize = 48;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Hash, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Hash, Clone, Copy, Encode, Decode, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
|
#[ssz(struct_behaviour = "transparent")]
|
||||||
pub struct KzgProof(#[serde(with = "BigArray")] pub [u8; KZG_PROOF_BYTES_LEN]);
|
pub struct KzgProof(#[serde(with = "BigArray")] pub [u8; KZG_PROOF_BYTES_LEN]);
|
||||||
|
|
||||||
impl fmt::Display for KzgProof {
|
impl fmt::Display for KzgProof {
|
||||||
@ -35,38 +37,6 @@ impl Into<[u8; KZG_PROOF_BYTES_LEN]> for KzgProof {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for KzgProof {
|
|
||||||
fn is_ssz_fixed_len() -> bool {
|
|
||||||
<[u8; KZG_PROOF_BYTES_LEN] as Encode>::is_ssz_fixed_len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssz_fixed_len() -> usize {
|
|
||||||
<[u8; KZG_PROOF_BYTES_LEN] as Encode>::ssz_fixed_len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssz_bytes_len(&self) -> usize {
|
|
||||||
self.0.ssz_bytes_len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
|
||||||
self.0.ssz_append(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decode for KzgProof {
|
|
||||||
fn is_ssz_fixed_len() -> bool {
|
|
||||||
<[u8; KZG_PROOF_BYTES_LEN] as Decode>::is_ssz_fixed_len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ssz_fixed_len() -> usize {
|
|
||||||
<[u8; KZG_PROOF_BYTES_LEN] as Decode>::ssz_fixed_len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
||||||
<[u8; KZG_PROOF_BYTES_LEN]>::from_ssz_bytes(bytes).map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TreeHash for KzgProof {
|
impl TreeHash for KzgProof {
|
||||||
fn tree_hash_type() -> tree_hash::TreeHashType {
|
fn tree_hash_type() -> tree_hash::TreeHashType {
|
||||||
<[u8; KZG_PROOF_BYTES_LEN]>::tree_hash_type()
|
<[u8; KZG_PROOF_BYTES_LEN]>::tree_hash_type()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ lmdb = ["lmdb-rkv", "lmdb-rkv-sys"]
|
|||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
flate2 = { version = "1.0.14", features = ["zlib"], default-features = false }
|
flate2 = { version = "1.0.14", features = ["zlib"], default-features = false }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
lighthouse_metrics = { path = "../common/lighthouse_metrics" }
|
lighthouse_metrics = { path = "../common/lighthouse_metrics" }
|
||||||
|
@ -23,7 +23,7 @@ serde_derive = "1.0.116"
|
|||||||
serde_repr = "0.1.6"
|
serde_repr = "0.1.6"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.13"
|
||||||
eth2_ssz = "0.4.1"
|
eth2_ssz = "0.4.1"
|
||||||
eth2_ssz_derive = "0.3.0"
|
eth2_ssz_derive = "0.3.1"
|
||||||
tree_hash = "0.4.1"
|
tree_hash = "0.4.1"
|
||||||
tree_hash_derive = "0.4.0"
|
tree_hash_derive = "0.4.0"
|
||||||
cached_tree_hash = { path = "../../consensus/cached_tree_hash" }
|
cached_tree_hash = { path = "../../consensus/cached_tree_hash" }
|
||||||
|
Loading…
Reference in New Issue
Block a user