mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-27 01:33:33 +00:00
Implement standard eth2.0 API (#1569)
- Resolves #1550 - Resolves #824 - Resolves #825 - Resolves #1131 - Resolves #1411 - Resolves #1256 - Resolve #1177 - Includes the `ShufflingId` struct initially defined in #1492. That PR is now closed and the changes are included here, with significant bug fixes. - Implement the https://github.com/ethereum/eth2.0-APIs in a new `http_api` crate using `warp`. This replaces the `rest_api` crate. - Add a new `common/eth2` crate which provides a wrapper around `reqwest`, providing the HTTP client that is used by the validator client and for testing. This replaces the `common/remote_beacon_node` crate. - Create a `http_metrics` crate which is a dedicated server for Prometheus metrics (they are no longer served on the same port as the REST API). We now have flags for `--metrics`, `--metrics-address`, etc. - Allow the `subnet_id` to be an optional parameter for `VerifiedUnaggregatedAttestation::verify`. This means it does not need to be provided unnecessarily by the validator client. - Move `fn map_attestation_committee` in `mod beacon_chain::attestation_verification` to a new `fn with_committee_cache` on the `BeaconChain` so the same cache can be used for obtaining validator duties. - Add some other helpers to `BeaconChain` to assist with common API duties (e.g., `block_root_at_slot`, `head_beacon_block_root`). - Change the `NaiveAggregationPool` so it can index attestations by `hash_tree_root(attestation.data)`. This is a requirement of the API. - Add functions to `BeaconChainHarness` to allow it to create slashings and exits. - Allow for `eth1::Eth1NetworkId` to go to/from a `String`. - Add functions to the `OperationPool` to allow getting all objects in the pool. - Add function to `BeaconState` to check if a committee cache is initialized. - Fix bug where `seconds_per_eth1_block` was not transferring over from `YamlConfig` to `ChainSpec`. - Add the `deposit_contract_address` to `YamlConfig` and `ChainSpec`. We needed to be able to return it in an API response. - Change some uses of serde `serialize_with` and `deserialize_with` to a single use of `with` (code quality). - Impl `Display` and `FromStr` for several BLS fields. - Check for clock discrepancy when VC polls BN for sync state (with +/- 1 slot tolerance). This is not intended to be comprehensive, it was just easy to do. - See #1434 for a per-endpoint overview. - Seeking clarity here: https://github.com/ethereum/eth2.0-APIs/issues/75 - [x] Add docs for prom port to close #1256 - [x] Follow up on this #1177 - [x] ~~Follow up with #1424~~ Will fix in future PR. - [x] Follow up with #1411 - [x] ~~Follow up with #1260~~ Will fix in future PR. - [x] Add quotes to all integers. - [x] Remove `rest_types` - [x] Address missing beacon block error. (#1629) - [x] ~~Add tests for lighthouse/peers endpoints~~ Wontfix - [x] ~~Follow up with validator status proposal~~ Tracked in #1434 - [x] Unify graffiti structs - [x] ~~Start server when waiting for genesis?~~ Will fix in future PR. - [x] TODO in http_api tests - [x] Move lighthouse endpoints off /eth/v1 - [x] Update docs to link to standard - ~~Blocked on #1586~~ Co-authored-by: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
@@ -39,6 +39,8 @@ tempfile = "3.1.0"
|
||||
derivative = "2.1.1"
|
||||
rusqlite = { version = "0.23.1", features = ["bundled"], optional = true }
|
||||
arbitrary = { version = "0.4.4", features = ["derive"], optional = true }
|
||||
serde_utils = { path = "../serde_utils" }
|
||||
regex = "1.3.9"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0.52"
|
||||
|
||||
@@ -16,6 +16,7 @@ use tree_hash_derive::TreeHash;
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct AggregateAndProof<T: EthSpec> {
|
||||
/// The index of the validator that created the attestation.
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub aggregator_index: u64,
|
||||
/// The aggregate attestation.
|
||||
pub aggregate: Attestation<T>,
|
||||
|
||||
@@ -26,6 +26,7 @@ use tree_hash_derive::TreeHash;
|
||||
)]
|
||||
pub struct AttestationData {
|
||||
pub slot: Slot,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub index: u64,
|
||||
|
||||
// LMD GHOST vote
|
||||
|
||||
@@ -12,4 +12,7 @@ pub struct AttestationDuty {
|
||||
pub committee_position: usize,
|
||||
/// The total number of attesters in the committee.
|
||||
pub committee_len: usize,
|
||||
/// The committee count at `attestation_slot`.
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub committees_at_slot: u64,
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use tree_hash_derive::TreeHash;
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BeaconBlock<T: EthSpec> {
|
||||
pub slot: Slot,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub proposer_index: u64,
|
||||
pub parent_root: Hash256,
|
||||
pub state_root: Hash256,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::utils::{graffiti_from_hex_str, graffiti_to_hex_str, Graffiti};
|
||||
use crate::*;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@@ -17,10 +16,6 @@ use tree_hash_derive::TreeHash;
|
||||
pub struct BeaconBlockBody<T: EthSpec> {
|
||||
pub randao_reveal: Signature,
|
||||
pub eth1_data: Eth1Data,
|
||||
#[serde(
|
||||
serialize_with = "graffiti_to_hex_str",
|
||||
deserialize_with = "graffiti_from_hex_str"
|
||||
)]
|
||||
pub graffiti: Graffiti,
|
||||
pub proposer_slashings: VariableList<ProposerSlashing, T::MaxProposerSlashings>,
|
||||
pub attester_slashings: VariableList<AttesterSlashing<T>, T::MaxAttesterSlashings>,
|
||||
|
||||
@@ -14,6 +14,7 @@ use tree_hash_derive::TreeHash;
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct BeaconBlockHeader {
|
||||
pub slot: Slot,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub proposer_index: u64,
|
||||
pub parent_root: Hash256,
|
||||
pub state_root: Hash256,
|
||||
|
||||
@@ -157,6 +157,7 @@ where
|
||||
T: EthSpec,
|
||||
{
|
||||
// Versioning
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub genesis_time: u64,
|
||||
pub genesis_validators_root: Hash256,
|
||||
pub slot: Slot,
|
||||
@@ -173,6 +174,7 @@ where
|
||||
// Ethereum 1.0 chain data
|
||||
pub eth1_data: Eth1Data,
|
||||
pub eth1_data_votes: VariableList<Eth1Data, T::SlotsPerEth1VotingPeriod>,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub eth1_deposit_index: u64,
|
||||
|
||||
// Registry
|
||||
@@ -913,6 +915,13 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
self.exit_cache = ExitCache::default();
|
||||
}
|
||||
|
||||
/// Returns `true` if the committee cache for `relative_epoch` is built and ready to use.
|
||||
pub fn committee_cache_is_initialized(&self, relative_epoch: RelativeEpoch) -> bool {
|
||||
let i = Self::committee_cache_index(relative_epoch);
|
||||
|
||||
self.committee_caches[i].is_initialized_at(relative_epoch.into_epoch(self.current_epoch()))
|
||||
}
|
||||
|
||||
/// Build an epoch cache, unless it is has already been built.
|
||||
pub fn build_committee_cache(
|
||||
&mut self,
|
||||
|
||||
@@ -186,6 +186,7 @@ impl CommitteeCache {
|
||||
index,
|
||||
committee_position,
|
||||
committee_len,
|
||||
committees_at_slot: self.committees_per_slot(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,10 +4,6 @@ use serde_derive::{Deserialize, Serialize};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use tree_hash::TreeHash;
|
||||
use utils::{
|
||||
fork_from_hex_str, fork_to_hex_str, u32_from_hex_str, u32_to_hex_str, u8_from_hex_str,
|
||||
u8_to_hex_str,
|
||||
};
|
||||
|
||||
/// Each of the BLS signature domains.
|
||||
///
|
||||
@@ -65,12 +61,9 @@ pub struct ChainSpec {
|
||||
/*
|
||||
* Initial Values
|
||||
*/
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
deserialize_with = "fork_from_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
pub genesis_fork_version: [u8; 4],
|
||||
#[serde(deserialize_with = "u8_from_hex_str", serialize_with = "u8_to_hex_str")]
|
||||
#[serde(with = "serde_utils::u8_hex")]
|
||||
pub bls_withdrawal_prefix_byte: u8,
|
||||
|
||||
/*
|
||||
@@ -115,6 +108,7 @@ pub struct ChainSpec {
|
||||
*/
|
||||
pub eth1_follow_distance: u64,
|
||||
pub seconds_per_eth1_block: u64,
|
||||
pub deposit_contract_address: Address,
|
||||
|
||||
/*
|
||||
* Networking
|
||||
@@ -326,6 +320,9 @@ impl ChainSpec {
|
||||
*/
|
||||
eth1_follow_distance: 1_024,
|
||||
seconds_per_eth1_block: 14,
|
||||
deposit_contract_address: "1234567890123456789012345678901234567890"
|
||||
.parse()
|
||||
.expect("chain spec deposit contract address"),
|
||||
|
||||
/*
|
||||
* Network specific
|
||||
@@ -448,104 +445,127 @@ pub struct YamlConfig {
|
||||
#[serde(default)]
|
||||
config_name: String,
|
||||
// ChainSpec
|
||||
max_committees_per_slot: usize,
|
||||
target_committee_size: usize,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
max_committees_per_slot: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
target_committee_size: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_per_epoch_churn_limit: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
churn_limit_quotient: u64,
|
||||
#[serde(with = "serde_utils::quoted_u8")]
|
||||
shuffle_round_count: u8,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_genesis_active_validator_count: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_genesis_time: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
genesis_delay: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_deposit_amount: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
max_effective_balance: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
ejection_balance: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
effective_balance_increment: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
hysteresis_quotient: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
hysteresis_downward_multiplier: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
hysteresis_upward_multiplier: u64,
|
||||
// Proportional slashing multiplier defaults to 3 for compatibility with Altona and Medalla.
|
||||
#[serde(default = "default_proportional_slashing_multiplier")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
proportional_slashing_multiplier: u64,
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
deserialize_with = "fork_from_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
genesis_fork_version: [u8; 4],
|
||||
#[serde(deserialize_with = "u8_from_hex_str", serialize_with = "u8_to_hex_str")]
|
||||
#[serde(with = "serde_utils::u8_hex")]
|
||||
bls_withdrawal_prefix: u8,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
seconds_per_slot: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_attestation_inclusion_delay: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_seed_lookahead: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
max_seed_lookahead: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_epochs_to_inactivity_penalty: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_validator_withdrawability_delay: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
shard_committee_period: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
base_reward_factor: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
whistleblower_reward_quotient: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
proposer_reward_quotient: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
inactivity_penalty_quotient: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_slashing_penalty_quotient: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
safe_slots_to_update_justified: u64,
|
||||
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::u32_hex")]
|
||||
domain_beacon_proposer: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::u32_hex")]
|
||||
domain_beacon_attester: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::u32_hex")]
|
||||
domain_randao: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::u32_hex")]
|
||||
domain_deposit: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::u32_hex")]
|
||||
domain_voluntary_exit: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::u32_hex")]
|
||||
domain_selection_proof: u32,
|
||||
#[serde(
|
||||
deserialize_with = "u32_from_hex_str",
|
||||
serialize_with = "u32_to_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::u32_hex")]
|
||||
domain_aggregate_and_proof: u32,
|
||||
// EthSpec
|
||||
#[serde(with = "serde_utils::quoted_u32")]
|
||||
max_validators_per_committee: u32,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
slots_per_epoch: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
epochs_per_eth1_voting_period: u64,
|
||||
slots_per_historical_root: usize,
|
||||
epochs_per_historical_vector: usize,
|
||||
epochs_per_slashings_vector: usize,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
slots_per_historical_root: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
epochs_per_historical_vector: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
epochs_per_slashings_vector: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
historical_roots_limit: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
validator_registry_limit: u64,
|
||||
#[serde(with = "serde_utils::quoted_u32")]
|
||||
max_proposer_slashings: u32,
|
||||
#[serde(with = "serde_utils::quoted_u32")]
|
||||
max_attester_slashings: u32,
|
||||
#[serde(with = "serde_utils::quoted_u32")]
|
||||
max_attestations: u32,
|
||||
#[serde(with = "serde_utils::quoted_u32")]
|
||||
max_deposits: u32,
|
||||
#[serde(with = "serde_utils::quoted_u32")]
|
||||
max_voluntary_exits: u32,
|
||||
// Validator
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
eth1_follow_distance: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
target_aggregators_per_committee: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
random_subnets_per_validator: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
epochs_per_random_subnet_subscription: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
seconds_per_eth1_block: u64,
|
||||
deposit_contract_address: Address,
|
||||
/* TODO: incorporate these into ChainSpec and turn on `serde(deny_unknown_fields)`
|
||||
deposit_chain_id: u64,
|
||||
deposit_network_id: u64,
|
||||
deposit_contract_address: String,
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -568,8 +588,8 @@ impl YamlConfig {
|
||||
Self {
|
||||
config_name: T::spec_name().to_string(),
|
||||
// ChainSpec
|
||||
max_committees_per_slot: spec.max_committees_per_slot,
|
||||
target_committee_size: spec.target_committee_size,
|
||||
max_committees_per_slot: spec.max_committees_per_slot as u64,
|
||||
target_committee_size: spec.target_committee_size as u64,
|
||||
min_per_epoch_churn_limit: spec.min_per_epoch_churn_limit,
|
||||
churn_limit_quotient: spec.churn_limit_quotient,
|
||||
shuffle_round_count: spec.shuffle_round_count,
|
||||
@@ -611,9 +631,9 @@ impl YamlConfig {
|
||||
max_validators_per_committee: T::MaxValidatorsPerCommittee::to_u32(),
|
||||
slots_per_epoch: T::slots_per_epoch(),
|
||||
epochs_per_eth1_voting_period: T::EpochsPerEth1VotingPeriod::to_u64(),
|
||||
slots_per_historical_root: T::slots_per_historical_root(),
|
||||
epochs_per_historical_vector: T::epochs_per_historical_vector(),
|
||||
epochs_per_slashings_vector: T::EpochsPerSlashingsVector::to_usize(),
|
||||
slots_per_historical_root: T::slots_per_historical_root() as u64,
|
||||
epochs_per_historical_vector: T::epochs_per_historical_vector() as u64,
|
||||
epochs_per_slashings_vector: T::EpochsPerSlashingsVector::to_u64(),
|
||||
historical_roots_limit: T::HistoricalRootsLimit::to_u64(),
|
||||
validator_registry_limit: T::ValidatorRegistryLimit::to_u64(),
|
||||
max_proposer_slashings: T::MaxProposerSlashings::to_u32(),
|
||||
@@ -628,6 +648,7 @@ impl YamlConfig {
|
||||
random_subnets_per_validator: spec.random_subnets_per_validator,
|
||||
epochs_per_random_subnet_subscription: spec.epochs_per_random_subnet_subscription,
|
||||
seconds_per_eth1_block: spec.seconds_per_eth1_block,
|
||||
deposit_contract_address: spec.deposit_contract_address,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,9 +664,9 @@ impl YamlConfig {
|
||||
if self.max_validators_per_committee != T::MaxValidatorsPerCommittee::to_u32()
|
||||
|| self.slots_per_epoch != T::slots_per_epoch()
|
||||
|| self.epochs_per_eth1_voting_period != T::EpochsPerEth1VotingPeriod::to_u64()
|
||||
|| self.slots_per_historical_root != T::slots_per_historical_root()
|
||||
|| self.epochs_per_historical_vector != T::epochs_per_historical_vector()
|
||||
|| self.epochs_per_slashings_vector != T::EpochsPerSlashingsVector::to_usize()
|
||||
|| self.slots_per_historical_root != T::slots_per_historical_root() as u64
|
||||
|| self.epochs_per_historical_vector != T::epochs_per_historical_vector() as u64
|
||||
|| self.epochs_per_slashings_vector != T::EpochsPerSlashingsVector::to_u64()
|
||||
|| self.historical_roots_limit != T::HistoricalRootsLimit::to_u64()
|
||||
|| self.validator_registry_limit != T::ValidatorRegistryLimit::to_u64()
|
||||
|| self.max_proposer_slashings != T::MaxProposerSlashings::to_u32()
|
||||
@@ -662,8 +683,8 @@ impl YamlConfig {
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
max_committees_per_slot: self.max_committees_per_slot,
|
||||
target_committee_size: self.target_committee_size,
|
||||
max_committees_per_slot: self.max_committees_per_slot as usize,
|
||||
target_committee_size: self.target_committee_size as usize,
|
||||
min_per_epoch_churn_limit: self.min_per_epoch_churn_limit,
|
||||
churn_limit_quotient: self.churn_limit_quotient,
|
||||
shuffle_round_count: self.shuffle_round_count,
|
||||
@@ -685,6 +706,7 @@ impl YamlConfig {
|
||||
random_subnets_per_validator: self.random_subnets_per_validator,
|
||||
epochs_per_random_subnet_subscription: self.epochs_per_random_subnet_subscription,
|
||||
seconds_per_eth1_block: self.seconds_per_eth1_block,
|
||||
deposit_contract_address: self.deposit_contract_address,
|
||||
/*
|
||||
* Gwei values
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@ use tree_hash_derive::TreeHash;
|
||||
pub struct DepositData {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
pub withdrawal_credentials: Hash256,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub amount: u64,
|
||||
pub signature: SignatureBytes,
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use tree_hash_derive::TreeHash;
|
||||
pub struct DepositMessage {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
pub withdrawal_credentials: Hash256,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub amount: u64,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::utils::{fork_from_hex_str, fork_to_hex_str};
|
||||
use crate::Epoch;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@@ -16,15 +15,9 @@ use tree_hash_derive::TreeHash;
|
||||
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||
)]
|
||||
pub struct EnrForkId {
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
deserialize_with = "fork_from_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
pub fork_digest: [u8; 4],
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
deserialize_with = "fork_from_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
pub next_fork_version: [u8; 4],
|
||||
pub next_fork_epoch: Epoch,
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ use tree_hash_derive::TreeHash;
|
||||
)]
|
||||
pub struct Eth1Data {
|
||||
pub deposit_root: Hash256,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub deposit_count: u64,
|
||||
pub block_hash: Hash256,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::utils::{fork_from_hex_str, fork_to_hex_str};
|
||||
use crate::Epoch;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@@ -25,15 +24,9 @@ use tree_hash_derive::TreeHash;
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct Fork {
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
deserialize_with = "fork_from_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
pub previous_version: [u8; 4],
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
deserialize_with = "fork_from_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
pub current_version: [u8; 4],
|
||||
pub epoch: Epoch,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::utils::{fork_from_hex_str, fork_to_hex_str};
|
||||
use crate::{Hash256, SignedRoot};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@@ -15,10 +14,7 @@ use tree_hash_derive::TreeHash;
|
||||
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||
)]
|
||||
pub struct ForkData {
|
||||
#[serde(
|
||||
serialize_with = "fork_to_hex_str",
|
||||
deserialize_with = "fork_from_hex_str"
|
||||
)]
|
||||
#[serde(with = "serde_utils::bytes_4_hex")]
|
||||
pub current_version: [u8; 4],
|
||||
pub genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ use serde_derive::Serialize;
|
||||
pub struct FreeAttestation {
|
||||
pub data: AttestationData,
|
||||
pub signature: Signature,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub validator_index: u64,
|
||||
}
|
||||
|
||||
132
consensus/types/src/graffiti.rs
Normal file
132
consensus/types/src/graffiti.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use crate::{
|
||||
test_utils::{RngCore, TestRandom},
|
||||
Hash256,
|
||||
};
|
||||
use regex::bytes::Regex;
|
||||
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use ssz::{Decode, DecodeError, Encode};
|
||||
use std::fmt;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
pub const GRAFFITI_BYTES_LEN: usize = 32;
|
||||
|
||||
/// The 32-byte `graffiti` field on a beacon block.
|
||||
#[derive(Default, Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct Graffiti(#[serde(with = "serde_graffiti")] pub [u8; GRAFFITI_BYTES_LEN]);
|
||||
|
||||
impl Graffiti {
|
||||
pub fn as_utf8_lossy(&self) -> String {
|
||||
#[allow(clippy::invalid_regex)]
|
||||
let re = Regex::new("\\p{C}").expect("graffiti regex is valid");
|
||||
String::from_utf8_lossy(&re.replace_all(&self.0[..], &b""[..])).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Graffiti {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", serde_utils::hex::encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; GRAFFITI_BYTES_LEN]> for Graffiti {
|
||||
fn from(bytes: [u8; GRAFFITI_BYTES_LEN]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; GRAFFITI_BYTES_LEN]> for Graffiti {
|
||||
fn into(self) -> [u8; GRAFFITI_BYTES_LEN] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub mod serde_graffiti {
|
||||
use super::*;
|
||||
|
||||
pub fn serialize<S>(bytes: &[u8; GRAFFITI_BYTES_LEN], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&serde_utils::hex::encode(bytes))
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; GRAFFITI_BYTES_LEN], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
|
||||
let bytes = serde_utils::hex::decode(&s).map_err(D::Error::custom)?;
|
||||
|
||||
if bytes.len() != GRAFFITI_BYTES_LEN {
|
||||
return Err(D::Error::custom(format!(
|
||||
"incorrect byte length {}, expected {}",
|
||||
bytes.len(),
|
||||
GRAFFITI_BYTES_LEN
|
||||
)));
|
||||
}
|
||||
|
||||
let mut array = [0; GRAFFITI_BYTES_LEN];
|
||||
array[..].copy_from_slice(&bytes);
|
||||
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Graffiti {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
<[u8; GRAFFITI_BYTES_LEN] as Encode>::is_ssz_fixed_len()
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
<[u8; GRAFFITI_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 Graffiti {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
<[u8; GRAFFITI_BYTES_LEN] as Decode>::is_ssz_fixed_len()
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
<[u8; GRAFFITI_BYTES_LEN] as Decode>::ssz_fixed_len()
|
||||
}
|
||||
|
||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
<[u8; GRAFFITI_BYTES_LEN]>::from_ssz_bytes(bytes).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TreeHash for Graffiti {
|
||||
fn tree_hash_type() -> tree_hash::TreeHashType {
|
||||
<[u8; GRAFFITI_BYTES_LEN]>::tree_hash_type()
|
||||
}
|
||||
|
||||
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
|
||||
self.0.tree_hash_packed_encoding()
|
||||
}
|
||||
|
||||
fn tree_hash_packing_factor() -> usize {
|
||||
<[u8; GRAFFITI_BYTES_LEN]>::tree_hash_packing_factor()
|
||||
}
|
||||
|
||||
fn tree_hash_root(&self) -> tree_hash::Hash256 {
|
||||
self.0.tree_hash_root()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestRandom for Graffiti {
|
||||
fn random_for_test(rng: &mut impl RngCore) -> Self {
|
||||
Self::from(Hash256::random_for_test(rng).to_fixed_bytes())
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ use tree_hash_derive::TreeHash;
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct IndexedAttestation<T: EthSpec> {
|
||||
/// Lists validator registry indices, not committee indices.
|
||||
#[serde(with = "quoted_variable_list_u64")]
|
||||
pub attesting_indices: VariableList<u64, T::MaxValidatorsPerCommittee>,
|
||||
pub data: AttestationData,
|
||||
pub signature: AggregateSignature,
|
||||
@@ -53,6 +54,43 @@ impl<T: EthSpec> Hash for IndexedAttestation<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize a variable list of `u64` such that each int is quoted. Deserialize a variable
|
||||
/// list supporting both quoted and un-quoted ints.
|
||||
///
|
||||
/// E.g.,`["0", "1", "2"]`
|
||||
mod quoted_variable_list_u64 {
|
||||
use super::*;
|
||||
use crate::Unsigned;
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserializer, Serializer};
|
||||
use serde_utils::quoted_u64_vec::{QuotedIntVecVisitor, QuotedIntWrapper};
|
||||
|
||||
pub fn serialize<S, T>(value: &VariableList<u64, T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
T: Unsigned,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(value.len()))?;
|
||||
for &int in value.iter() {
|
||||
seq.serialize_element(&QuotedIntWrapper { int })?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<VariableList<u64, T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: Unsigned,
|
||||
{
|
||||
deserializer
|
||||
.deserialize_any(QuotedIntVecVisitor)
|
||||
.and_then(|vec| {
|
||||
VariableList::new(vec)
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid length: {:?}", e)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -29,19 +29,21 @@ pub mod eth_spec;
|
||||
pub mod fork;
|
||||
pub mod fork_data;
|
||||
pub mod free_attestation;
|
||||
pub mod graffiti;
|
||||
pub mod historical_batch;
|
||||
pub mod indexed_attestation;
|
||||
pub mod pending_attestation;
|
||||
pub mod proposer_slashing;
|
||||
pub mod relative_epoch;
|
||||
pub mod selection_proof;
|
||||
pub mod shuffling_id;
|
||||
pub mod signed_aggregate_and_proof;
|
||||
pub mod signed_beacon_block;
|
||||
pub mod signed_beacon_block_header;
|
||||
pub mod signed_voluntary_exit;
|
||||
pub mod signing_data;
|
||||
pub mod utils;
|
||||
pub mod validator;
|
||||
pub mod validator_subscription;
|
||||
pub mod voluntary_exit;
|
||||
#[macro_use]
|
||||
pub mod slot_epoch_macros;
|
||||
@@ -74,12 +76,14 @@ pub use crate::eth1_data::Eth1Data;
|
||||
pub use crate::fork::Fork;
|
||||
pub use crate::fork_data::ForkData;
|
||||
pub use crate::free_attestation::FreeAttestation;
|
||||
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
|
||||
pub use crate::historical_batch::HistoricalBatch;
|
||||
pub use crate::indexed_attestation::IndexedAttestation;
|
||||
pub use crate::pending_attestation::PendingAttestation;
|
||||
pub use crate::proposer_slashing::ProposerSlashing;
|
||||
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
|
||||
pub use crate::selection_proof::SelectionProof;
|
||||
pub use crate::shuffling_id::ShufflingId;
|
||||
pub use crate::signed_aggregate_and_proof::SignedAggregateAndProof;
|
||||
pub use crate::signed_beacon_block::{SignedBeaconBlock, SignedBeaconBlockHash};
|
||||
pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader;
|
||||
@@ -88,6 +92,7 @@ pub use crate::signing_data::{SignedRoot, SigningData};
|
||||
pub use crate::slot_epoch::{Epoch, Slot};
|
||||
pub use crate::subnet_id::SubnetId;
|
||||
pub use crate::validator::Validator;
|
||||
pub use crate::validator_subscription::ValidatorSubscription;
|
||||
pub use crate::voluntary_exit::VoluntaryExit;
|
||||
|
||||
pub type CommitteeIndex = u64;
|
||||
@@ -99,4 +104,3 @@ pub use bls::{
|
||||
AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey, Signature, SignatureBytes,
|
||||
};
|
||||
pub use ssz_types::{typenum, typenum::Unsigned, BitList, BitVector, FixedVector, VariableList};
|
||||
pub use utils::{Graffiti, GRAFFITI_BYTES_LEN};
|
||||
|
||||
@@ -13,7 +13,9 @@ use tree_hash_derive::TreeHash;
|
||||
pub struct PendingAttestation<T: EthSpec> {
|
||||
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
|
||||
pub data: AttestationData,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub inclusion_delay: u64,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub proposer_index: u64,
|
||||
}
|
||||
|
||||
|
||||
61
consensus/types/src/shuffling_id.rs
Normal file
61
consensus/types/src/shuffling_id.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use crate::*;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Can be used to key (ID) the shuffling in some chain, in some epoch.
|
||||
///
|
||||
/// ## Reasoning
|
||||
///
|
||||
/// We say that the ID of some shuffling is always equal to a 2-tuple:
|
||||
///
|
||||
/// - The epoch for which the shuffling should be effective.
|
||||
/// - A block root, where this is the root at the *last* slot of the penultimate epoch. I.e., the
|
||||
/// final block which contributed a randao reveal to the seed for the shuffling.
|
||||
///
|
||||
/// The struct stores exactly that 2-tuple.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize, Encode, Decode)]
|
||||
pub struct ShufflingId {
|
||||
pub shuffling_epoch: Epoch,
|
||||
shuffling_decision_block: Hash256,
|
||||
}
|
||||
|
||||
impl ShufflingId {
|
||||
/// Using the given `state`, return the shuffling id for the shuffling at the given
|
||||
/// `relative_epoch`.
|
||||
///
|
||||
/// The `block_root` provided should be either:
|
||||
///
|
||||
/// - The root of the block which produced this state.
|
||||
/// - If the state is from a skip slot, the root of the latest block in that state.
|
||||
pub fn new<E: EthSpec>(
|
||||
block_root: Hash256,
|
||||
state: &BeaconState<E>,
|
||||
relative_epoch: RelativeEpoch,
|
||||
) -> Result<Self, BeaconStateError> {
|
||||
let shuffling_epoch = relative_epoch.into_epoch(state.current_epoch());
|
||||
|
||||
let shuffling_decision_slot = shuffling_epoch
|
||||
.saturating_sub(1_u64)
|
||||
.start_slot(E::slots_per_epoch())
|
||||
.saturating_sub(1_u64);
|
||||
|
||||
let shuffling_decision_block = if state.slot == shuffling_decision_slot {
|
||||
block_root
|
||||
} else {
|
||||
*state.get_block_root(shuffling_decision_slot)?
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
shuffling_epoch,
|
||||
shuffling_decision_block,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_components(shuffling_epoch: Epoch, shuffling_decision_block: Hash256) -> Self {
|
||||
Self {
|
||||
shuffling_epoch,
|
||||
shuffling_decision_block,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,6 +313,18 @@ macro_rules! impl_ssz {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_from_str {
|
||||
($type: ident) => {
|
||||
impl std::str::FromStr for $type {
|
||||
type Err = std::num::ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<$type, Self::Err> {
|
||||
u64::from_str(s).map($type)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_common {
|
||||
($type: ident) => {
|
||||
impl_from_into_u64!($type);
|
||||
@@ -328,6 +340,7 @@ macro_rules! impl_common {
|
||||
impl_display!($type);
|
||||
impl_debug!($type);
|
||||
impl_ssz!($type);
|
||||
impl_from_str!($type);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ 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);
|
||||
#[serde(transparent)]
|
||||
pub struct SubnetId(#[serde(with = "serde_utils::quoted_u64")] u64);
|
||||
|
||||
impl SubnetId {
|
||||
pub fn new(id: u64) -> Self {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
mod serde_utils;
|
||||
|
||||
pub use self::serde_utils::*;
|
||||
@@ -1,134 +0,0 @@
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub const FORK_BYTES_LEN: usize = 4;
|
||||
pub const GRAFFITI_BYTES_LEN: usize = 32;
|
||||
|
||||
/// Type for a slice of `GRAFFITI_BYTES_LEN` bytes.
|
||||
///
|
||||
/// Gets included inside each `BeaconBlockBody`.
|
||||
pub type Graffiti = [u8; GRAFFITI_BYTES_LEN];
|
||||
|
||||
pub fn u8_from_hex_str<'de, D>(deserializer: D) -> Result<u8, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
|
||||
let start = match s.as_str().get(2..) {
|
||||
Some(start) => start,
|
||||
None => return Err(D::Error::custom("string length too small")),
|
||||
};
|
||||
u8::from_str_radix(&start, 16).map_err(D::Error::custom)
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)] // Serde requires the `byte` to be a ref.
|
||||
pub fn u8_to_hex_str<S>(byte: &u8, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut hex: String = "0x".to_string();
|
||||
hex.push_str(&hex::encode(&[*byte]));
|
||||
|
||||
serializer.serialize_str(&hex)
|
||||
}
|
||||
|
||||
pub fn u32_from_hex_str<'de, D>(deserializer: D) -> Result<u32, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
let start = s
|
||||
.as_str()
|
||||
.get(2..)
|
||||
.ok_or_else(|| D::Error::custom("string length too small"))?;
|
||||
|
||||
u32::from_str_radix(&start, 16)
|
||||
.map_err(D::Error::custom)
|
||||
.map(u32::from_be)
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)] // Serde requires the `num` to be a ref.
|
||||
pub fn u32_to_hex_str<S>(num: &u32, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut hex: String = "0x".to_string();
|
||||
let bytes = num.to_le_bytes();
|
||||
hex.push_str(&hex::encode(&bytes));
|
||||
|
||||
serializer.serialize_str(&hex)
|
||||
}
|
||||
|
||||
pub fn fork_from_hex_str<'de, D>(deserializer: D) -> Result<[u8; FORK_BYTES_LEN], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
let mut array = [0 as u8; FORK_BYTES_LEN];
|
||||
|
||||
let start = s
|
||||
.as_str()
|
||||
.get(2..)
|
||||
.ok_or_else(|| D::Error::custom("string length too small"))?;
|
||||
let decoded: Vec<u8> = hex::decode(&start).map_err(D::Error::custom)?;
|
||||
|
||||
if decoded.len() != FORK_BYTES_LEN {
|
||||
return Err(D::Error::custom("Fork length too long"));
|
||||
}
|
||||
|
||||
for (i, item) in array.iter_mut().enumerate() {
|
||||
if i > decoded.len() {
|
||||
break;
|
||||
}
|
||||
*item = decoded[i];
|
||||
}
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn fork_to_hex_str<S>(bytes: &[u8; FORK_BYTES_LEN], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut hex_string: String = "0x".to_string();
|
||||
hex_string.push_str(&hex::encode(&bytes));
|
||||
|
||||
serializer.serialize_str(&hex_string)
|
||||
}
|
||||
|
||||
pub fn graffiti_to_hex_str<S>(bytes: &Graffiti, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut hex_string: String = "0x".to_string();
|
||||
hex_string.push_str(&hex::encode(&bytes));
|
||||
|
||||
serializer.serialize_str(&hex_string)
|
||||
}
|
||||
|
||||
pub fn graffiti_from_hex_str<'de, D>(deserializer: D) -> Result<Graffiti, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
let mut array = Graffiti::default();
|
||||
|
||||
let start = s
|
||||
.as_str()
|
||||
.get(2..)
|
||||
.ok_or_else(|| D::Error::custom("string length too small"))?;
|
||||
let decoded: Vec<u8> = hex::decode(&start).map_err(D::Error::custom)?;
|
||||
|
||||
if decoded.len() > GRAFFITI_BYTES_LEN {
|
||||
return Err(D::Error::custom("Fork length too long"));
|
||||
}
|
||||
|
||||
for (i, item) in array.iter_mut().enumerate() {
|
||||
if i > decoded.len() {
|
||||
break;
|
||||
}
|
||||
*item = decoded[i];
|
||||
}
|
||||
Ok(array)
|
||||
}
|
||||
21
consensus/types/src/validator_subscription.rs
Normal file
21
consensus/types/src/validator_subscription.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use crate::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
|
||||
/// A validator subscription, created when a validator subscribes to a slot to perform optional aggregation
|
||||
/// duties.
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)]
|
||||
pub struct ValidatorSubscription {
|
||||
/// The validators index.
|
||||
pub validator_index: u64,
|
||||
/// The index of the committee within `slot` of which the validator is a member. Used by the
|
||||
/// beacon node to quickly evaluate the associated `SubnetId`.
|
||||
pub attestation_committee_index: CommitteeIndex,
|
||||
/// The slot in which to subscribe.
|
||||
pub slot: Slot,
|
||||
/// Committee count at slot to subscribe.
|
||||
pub committee_count_at_slot: u64,
|
||||
/// If true, the validator is an aggregator and the beacon node should aggregate attestations
|
||||
/// for this slot.
|
||||
pub is_aggregator: bool,
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use tree_hash_derive::TreeHash;
|
||||
pub struct VoluntaryExit {
|
||||
/// Earliest epoch when voluntary exit can be processed.
|
||||
pub epoch: Epoch,
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub validator_index: u64,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user