Arbitrary trait for eth2/types (#1040)

* Add the arbitrary type to eth2/types and their deps

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Wrap arbitrary in a feature flag

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Fix feature for types

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Fix comment

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Patch versioning

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Allow expanded crate reference for arbitrary 0.4.3

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Add arbitrary to remaining types

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* use cmp::min

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Derive Arbitrary trait for ValidatorStatus, TotalBalances and InclusionInfo

* Add CI check for state processing arbitrary faetures

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Fix indentation

Signed-off-by: Kirk Baird <baird.k@outlook.com>

Co-authored-by: Mehdi Zerouali <mehdi@sigmaprime.io>
This commit is contained in:
Kirk Baird
2020-05-06 09:12:28 +10:00
committed by GitHub
parent fcccf63d29
commit 611a0c7d19
70 changed files with 436 additions and 292 deletions

View File

@@ -11,6 +11,7 @@ use tree_hash_derive::TreeHash;
/// A Validators aggregate attestation and selection proof.
///
/// Spec v0.10.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
#[serde(bound = "T: EthSpec")]
pub struct AggregateAndProof<T: EthSpec> {

View File

@@ -20,6 +20,7 @@ pub enum Error {
/// Details an attestation that can be slashable.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[serde(bound = "T: EthSpec")]
pub struct Attestation<T: EthSpec> {

View File

@@ -9,6 +9,7 @@ use tree_hash_derive::TreeHash;
/// The data upon which an attestation is based.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug,
Clone,

View File

@@ -1,6 +1,7 @@
use crate::*;
use serde_derive::{Deserialize, Serialize};
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize)]
pub struct AttestationDuty {
/// The slot during which the attester must attest.

View File

@@ -8,6 +8,7 @@ use tree_hash_derive::TreeHash;
/// Two conflicting attestations.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[serde(bound = "T: EthSpec")]
pub struct AttesterSlashing<T: EthSpec> {

View File

@@ -11,6 +11,7 @@ use tree_hash_derive::TreeHash;
/// A block of the `BeaconChain`.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[serde(bound = "T: EthSpec")]
pub struct BeaconBlock<T: EthSpec> {

View File

@@ -11,6 +11,7 @@ use tree_hash_derive::TreeHash;
/// The body of a `BeaconChain` block, containing operations.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[serde(bound = "T: EthSpec")]
pub struct BeaconBlockBody<T: EthSpec> {

View File

@@ -10,6 +10,7 @@ use tree_hash_derive::TreeHash;
/// A header of a `BeaconBlock`.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct BeaconBlockHeader {
pub slot: Slot,

View File

@@ -17,6 +17,7 @@ impl<'a> BeaconCommittee<'a> {
}
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Default, Clone, Debug, PartialEq)]
pub struct OwnedBeaconCommittee {
pub slot: Slot,

View File

@@ -2,6 +2,7 @@ use self::committee_cache::get_active_validator_indices;
use self::exit_cache::ExitCache;
use crate::test_utils::TestRandom;
use crate::*;
use cached_tree_hash::{CacheArena, CachedTreeHash};
use compare_fields_derive::CompareFields;
use eth2_hashing::hash;
@@ -102,6 +103,7 @@ impl AllowNextEpoch {
}
}
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct BeaconStateHash(Hash256);
@@ -1155,3 +1157,46 @@ impl From<ArithError> for Error {
Error::ArithError(e)
}
}
#[cfg(feature = "arbitrary-fuzz")]
impl<T: EthSpec> arbitrary::Arbitrary for BeaconState<T> {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self {
genesis_time: u64::arbitrary(u)?,
genesis_validators_root: Hash256::arbitrary(u)?,
slot: Slot::arbitrary(u)?,
fork: Fork::arbitrary(u)?,
latest_block_header: BeaconBlockHeader::arbitrary(u)?,
block_roots: <FixedVector<Hash256, T::SlotsPerHistoricalRoot>>::arbitrary(u)?,
state_roots: <FixedVector<Hash256, T::SlotsPerHistoricalRoot>>::arbitrary(u)?,
historical_roots: <VariableList<Hash256, T::HistoricalRootsLimit>>::arbitrary(u)?,
eth1_data: Eth1Data::arbitrary(u)?,
eth1_data_votes: <VariableList<Eth1Data, T::SlotsPerEth1VotingPeriod>>::arbitrary(u)?,
eth1_deposit_index: u64::arbitrary(u)?,
validators: <VariableList<Validator, T::ValidatorRegistryLimit>>::arbitrary(u)?,
balances: <VariableList<u64, T::ValidatorRegistryLimit>>::arbitrary(u)?,
randao_mixes: <FixedVector<Hash256, T::EpochsPerHistoricalVector>>::arbitrary(u)?,
slashings: <FixedVector<u64, T::EpochsPerSlashingsVector>>::arbitrary(u)?,
previous_epoch_attestations: <VariableList<
PendingAttestation<T>,
T::MaxPendingAttestations,
>>::arbitrary(u)?,
current_epoch_attestations: <VariableList<
PendingAttestation<T>,
T::MaxPendingAttestations,
>>::arbitrary(u)?,
justification_bits: <BitVector<T::JustificationBitsLength>>::arbitrary(u)?,
previous_justified_checkpoint: Checkpoint::arbitrary(u)?,
current_justified_checkpoint: Checkpoint::arbitrary(u)?,
finalized_checkpoint: Checkpoint::arbitrary(u)?,
committee_caches: [
CommitteeCache::arbitrary(u)?,
CommitteeCache::arbitrary(u)?,
CommitteeCache::arbitrary(u)?,
],
pubkey_cache: PubkeyCache::arbitrary(u)?,
exit_cache: ExitCache::arbitrary(u)?,
tree_hash_cache: None,
})
}
}

View File

@@ -277,3 +277,10 @@ pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> V
active
}
#[cfg(feature = "arbitrary-fuzz")]
impl arbitrary::Arbitrary for CommitteeCache {
fn arbitrary(_u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self::default())
}
}

View File

@@ -70,3 +70,10 @@ impl ExitCache {
Ok(self.exits_per_epoch.get(&epoch).cloned().unwrap_or(0))
}
}
#[cfg(feature = "arbitrary-fuzz")]
impl arbitrary::Arbitrary for ExitCache {
fn arbitrary(_u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self::default())
}
}

View File

@@ -39,3 +39,10 @@ impl PubkeyCache {
self.map.get(pubkey).copied()
}
}
#[cfg(feature = "arbitrary-fuzz")]
impl arbitrary::Arbitrary for PubkeyCache {
fn arbitrary(_u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self::default())
}
}

View File

@@ -26,6 +26,7 @@ pub enum Domain {
/// Holds all the "constants" for a BeaconChain.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ChainSpec {

View File

@@ -8,6 +8,7 @@ use tree_hash_derive::TreeHash;
/// Casper FFG checkpoint, used in attestations.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug,
Clone,

View File

@@ -1,9 +1,8 @@
use crate::test_utils::TestRandom;
use crate::*;
use ssz_types::typenum::U33;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use ssz_types::typenum::U33;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
@@ -12,6 +11,7 @@ pub const DEPOSIT_TREE_DEPTH: usize = 32;
/// A deposit to potentially become a beacon chain validator.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct Deposit {
pub proof: FixedVector<Hash256, U33>,

View File

@@ -1,7 +1,7 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::{PublicKeyBytes, SignatureBytes};
use bls::{PublicKeyBytes, SignatureBytes};
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -10,6 +10,7 @@ use tree_hash_derive::TreeHash;
/// The data supplied by the user to the deposit contract.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct DepositData {
pub pubkey: PublicKeyBytes,

View File

@@ -1,7 +1,7 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::PublicKeyBytes;
use bls::PublicKeyBytes;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -10,6 +10,7 @@ use tree_hash_derive::TreeHash;
/// The data supplied by the user to the deposit contract.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct DepositMessage {
pub pubkey: PublicKeyBytes,

View File

@@ -11,6 +11,7 @@ use tree_hash_derive::TreeHash;
/// a nodes local ENR.
///
/// Spec v0.11
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]

View File

@@ -9,6 +9,7 @@ use tree_hash_derive::TreeHash;
/// Contains data obtained from the Eth1 chain.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug,
PartialEq,

View File

@@ -1,4 +1,5 @@
use crate::*;
use safe_arith::SafeArith;
use serde_derive::{Deserialize, Serialize};
use ssz_types::typenum::{
@@ -131,6 +132,7 @@ macro_rules! params_from_eth_spec {
/// Ethereum Foundation specifications.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
pub struct MainnetEthSpec;
@@ -164,6 +166,7 @@ pub type FoundationBeaconState = BeaconState<MainnetEthSpec>;
/// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
pub struct MinimalEthSpec;
@@ -198,6 +201,7 @@ impl EthSpec for MinimalEthSpec {
pub type MinimalBeaconState = BeaconState<MinimalEthSpec>;
/// Interop testnet spec
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
pub struct InteropEthSpec;

View File

@@ -10,6 +10,7 @@ use tree_hash_derive::TreeHash;
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]

View File

@@ -10,6 +10,7 @@ use tree_hash_derive::TreeHash;
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]

View File

@@ -4,6 +4,7 @@
use super::{AttestationData, Signature};
use serde_derive::Serialize;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct FreeAttestation {
pub data: AttestationData,

View File

@@ -10,6 +10,7 @@ use tree_hash_derive::TreeHash;
/// Historical block and state roots.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct HistoricalBatch<T: EthSpec> {
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,

View File

@@ -1,4 +1,5 @@
use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec, VariableList};
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
@@ -9,6 +10,7 @@ use tree_hash_derive::TreeHash;
/// To be included in an `AttesterSlashing`.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[serde(bound = "T: EthSpec")]
pub struct IndexedAttestation<T: EthSpec> {

View File

@@ -17,6 +17,18 @@ pub struct PendingAttestation<T: EthSpec> {
pub proposer_index: u64,
}
#[cfg(feature = "arbitrary-fuzz")]
impl<T: EthSpec> arbitrary::Arbitrary for PendingAttestation<T> {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self {
aggregation_bits: <BitList<T::MaxValidatorsPerCommittee>>::arbitrary(u)?,
data: AttestationData::arbitrary(u)?,
inclusion_delay: u64::arbitrary(u)?,
proposer_index: u64::arbitrary(u)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -9,6 +9,7 @@ use tree_hash_derive::TreeHash;
/// Two conflicting proposals from the same proposer (validator).
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct ProposerSlashing {
pub signed_header_1: SignedBeaconBlockHeader,

View File

@@ -6,10 +6,14 @@ pub enum Error {
EpochTooHigh { base: Epoch, other: Epoch },
}
#[cfg(feature = "arbitrary-fuzz")]
use arbitrary::Arbitrary;
/// Defines the epochs relative to some epoch. Most useful when referring to the committees prior
/// to and following some epoch.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(Arbitrary))]
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RelativeEpoch {
/// The prior epoch.

View File

@@ -3,6 +3,7 @@ use safe_arith::{ArithError, SafeArith};
use std::convert::TryInto;
use tree_hash::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Debug, Clone)]
pub struct SelectionProof(Signature);

View File

@@ -12,6 +12,7 @@ use tree_hash_derive::TreeHash;
/// gossipsub topic.
///
/// Spec v0.10.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
#[serde(bound = "T: EthSpec")]
pub struct SignedAggregateAndProof<T: EthSpec> {

View File

@@ -1,13 +1,13 @@
use crate::{test_utils::TestRandom, BeaconBlock, EthSpec, Hash256, Slot};
use bls::Signature;
use std::fmt;
use bls::Signature;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct SignedBeaconBlockHash(Hash256);
@@ -38,6 +38,7 @@ impl From<SignedBeaconBlockHash> for Hash256 {
/// A `BeaconBlock` and a signature from its proposer.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TestRandom)]
#[serde(bound = "E: EthSpec")]
pub struct SignedBeaconBlock<E: EthSpec> {

View File

@@ -9,6 +9,7 @@ use tree_hash_derive::TreeHash;
/// An exit voluntarily submitted a validator who wishes to withdraw.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct SignedBeaconBlockHeader {
pub message: BeaconBlockHeader,

View File

@@ -9,6 +9,7 @@ use tree_hash_derive::TreeHash;
/// An exit voluntarily submitted a validator who wishes to withdraw.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct SignedVoluntaryExit {
pub message: VoluntaryExit,

View File

@@ -1,11 +1,13 @@
use crate::test_utils::TestRandom;
use crate::Hash256;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct SigningRoot {
pub object_root: Hash256,

View File

@@ -12,6 +12,7 @@
use crate::test_utils::TestRandom;
use crate::SignedRoot;
use rand::RngCore;
use serde_derive::{Deserialize, Serialize};
use ssz::{ssz_encode, Decode, DecodeError, Encode};
@@ -21,10 +22,12 @@ use std::hash::{Hash, Hasher};
use std::iter::Iterator;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Eq, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Slot(u64);
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Eq, Clone, Copy, Default, Serialize, Deserialize)]
pub struct Epoch(u64);

View File

@@ -2,6 +2,7 @@
use serde_derive::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SubnetId(u64);

View File

@@ -9,6 +9,7 @@ use tree_hash_derive::TreeHash;
/// Information about a `BeaconChain` validator.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
pub struct Validator {
pub pubkey: PublicKeyBytes,

View File

@@ -11,6 +11,7 @@ use tree_hash_derive::TreeHash;
/// An exit voluntarily submitted a validator who wishes to withdraw.
///
/// Spec v0.11.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct VoluntaryExit {
/// Earliest epoch when voluntary exit can be processed.